Commit 4687c9a

HPCesia <me@hpcesia.com>
2025-02-17 06:33:59
feat: links page
1 parent 395ef31
src/content/spec/links.md
@@ -0,0 +1,12 @@
+---
+title: Links
+---
+
+
+## Lorem
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+## Ipsum
+
+Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
\ No newline at end of file
src/i18n/langs/en.ts
@@ -6,6 +6,7 @@ export const en: Translation = {
   [Key.about]: 'About',
   [Key.archive]: 'Archive',
   [Key.search]: 'Search',
+  [Key.links]: 'Links',
 
   [Key.tags]: 'Tags',
   [Key.categories]: 'Categories',
src/i18n/langs/zh_CN.ts
@@ -6,6 +6,7 @@ export const zh_CN: Translation = {
   [Key.about]: '关于',
   [Key.archive]: '归档',
   [Key.search]: '搜索',
+  [Key.links]: '友链',
 
   [Key.tags]: '标签',
   [Key.categories]: '分类',
src/i18n/langs/zh_TW.ts
@@ -6,6 +6,7 @@ export const zh_TW: Translation = {
   [Key.about]: '關於',
   [Key.archive]: '彙整',
   [Key.search]: '搜尋',
+  [Key.links]: '連結',
 
   [Key.tags]: '標籤',
   [Key.categories]: '分類',
src/i18n/I18nKey.ts
@@ -3,6 +3,7 @@ enum I18nKey {
   about = 'about',
   archive = 'archive',
   search = 'search',
+  links = 'links',
 
   tags = 'tags',
   categories = 'categories',
src/pages/links.astro
@@ -0,0 +1,83 @@
+---
+import { linksConfig } from '@/config';
+import PostInfo from '@components/misc/PostInfo.astro';
+import ImageWrapper from '@components/utils/ImageWrapper.astro';
+import Markdown from '@components/utils/Markdown.astro';
+import I18nKey from '@i18n/I18nKey';
+import { i18n } from '@i18n/translation';
+import PostPageLayout from '@layouts/PostPageLayout.astro';
+import { getEntry, render } from 'astro:content';
+
+const linksMd = await getEntry('spec', 'links');
+const groupHeadings = linksConfig.items.map((item) => ({
+  depth: 2,
+  slug: `links-group-${item.groupName.toLowerCase().replace(/\s/g, '-')}`,
+  text: item.groupName,
+}));
+const { Content, headings } = linksMd
+  ? await render(linksMd)
+  : { Content: Fragment, headings: [] };
+---
+
+<PostPageLayout
+  title={i18n(I18nKey.links) as string}
+  comment={linksMd?.data.comment}
+  headings={[...groupHeadings, ...headings]}
+>
+  <Fragment slot="header-content">
+    <PostInfo title={i18n(I18nKey.links) as string} />
+  </Fragment>
+  {
+    linksConfig.items.map((item) => (
+      <Fragment>
+        <h2
+          class="mt-4 mb-1 flex items-baseline justify-between gap-4 text-2xl font-bold"
+          id={`links-group-${item.groupName.toLowerCase().replace(/\s/g, '-')}`}
+        >
+          {item.groupName}
+          {item.groupDescription && (
+            <span class="text-base-content/60 line-clamp-1 text-sm font-medium">
+              {item.groupDescription}
+            </span>
+          )}
+        </h2>
+        <hr class="text-base-content/25 my-1" />
+        <div class="flex flex-wrap">
+          {item.groupItems.map((item) => (
+            <div class="card bg-base-200 max-md:card-side m-1 w-full md:w-[calc(25%-calc(var(--spacing)*2))] lg:w-[calc(20%-calc(var(--spacing)*2))]">
+              <figure class="min-w-23">
+                <a
+                  href={item.url}
+                  target="_blank"
+                  rel="noopener noreferrer"
+                  class="w-fit"
+                  title={item.name}
+                >
+                  <ImageWrapper src={item.avatar} class="max-md:w-23" />
+                </a>
+              </figure>
+              <div class="card-body px-4 py-2">
+                <a
+                  href={item.url}
+                  target="_blank"
+                  rel="noopener noreferrer"
+                  class="w-fit"
+                  title={item.name}
+                >
+                  <h3 class="card-title line-clamp-1">{item.name}</h3>
+                </a>
+                {item.description && (
+                  <p class="text-base-content/60 line-clamp-2 text-sm">{item.description}</p>
+                )}
+              </div>
+            </div>
+          ))}
+        </div>
+      </Fragment>
+    ))
+  }
+  <hr class="text-base-content/25 mt-8" />
+  <Markdown>
+    <Content />
+  </Markdown>
+</PostPageLayout>
src/types/config.ts
@@ -225,6 +225,59 @@ export type ProfileConfig = {
   }[];
 };
 
+export type LinksConfig = {
+  /**
+   * The items displayed in the links.
+   *
+   * 在友情链接中显示的项目。
+   */
+  items: {
+    /**
+     * The name of the group.
+     *
+     * 组的名称。
+     */
+    groupName: string;
+    /**
+     * The description of the group.
+     *
+     * 组的描述。
+     */
+    groupDescription?: string;
+    /**
+     * The items displayed in the group.
+     *
+     * 在组中显示的项目。
+     */
+    groupItems: {
+      /**
+       * The name of the link.
+       *
+       * 链接的名称。
+       */
+      name: string;
+      /**
+       * The URL of the link.
+       *
+       * 链接的 URL。
+       */
+      url: string;
+      /**
+       * The avatar of the link.
+       *
+       * 链接的头像。
+       */
+      avatar: string;
+      /**
+       * The description of the link.
+       *
+       * 链接的描述。
+       */
+      description?: string;
+    }[];
+  }[];
+};
+
 export type NavbarConfig = {
   /**
    * The items displayed in the center of the navbar.
src/config.ts
@@ -4,6 +4,7 @@ import type {
   CommentConfig,
   FooterConfig,
   LicenseConfig,
+  LinksConfig,
   NavbarConfig,
   ProfileConfig,
   SearchConfig,
@@ -41,7 +42,7 @@ export const profileConfig: ProfileConfig = {
   avatar: 'assets/img/avatar.jpg',
   name: 'Lorem Ipsum',
   bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
-  links: [
+  socialLinks: [
     {
       name: 'GitHub',
       url: 'https://github.com/example',
@@ -55,11 +56,31 @@ export const profileConfig: ProfileConfig = {
   ],
 };
 
+export const linksConfig: LinksConfig = {
+  items: [
+    {
+      groupName: 'Lorem Ipsum',
+      groupDescription: 'Lorem ipsum dolor sit amet.',
+      groupItems: [
+        { name: 'Item 1', url: 'https://example.com', avatar: 'assets/img/avatar.jpg' },
+        {
+          name: 'Item 2',
+          url: 'https://example.com',
+          avatar: 'assets/img/avatar.jpg',
+          description: 'Lorem ipsum dolor sit amet.',
+        },
+        { name: 'Item 3', url: 'https://example.com', avatar: 'assets/img/avatar.jpg' },
+      ],
+    },
+  ],
+};
+
 export const navbarConfig: NavbarConfig = {
   navbarCenterItems: [
     { text: I18nKey.archive, href: '/archives/' },
     { text: I18nKey.categories, href: '/archives/categories/' },
     { text: I18nKey.tags, href: '/archives/tags/' },
+    { text: I18nKey.links, href: '/links/' },
     { text: I18nKey.about, href: '/about/' },
   ],
   navbarRightItems: {