@jet-w/astro-blog 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HVQKQN6B.js → chunk-6D3XRDNY.js} +1 -1
- package/dist/{chunk-ATRISB7B.js → chunk-A2E2VSAQ.js} +43 -3
- package/dist/chunk-DAH2XP4W.js +154 -0
- package/dist/{chunk-AZHCNNAC.js → chunk-PG43JO4O.js} +1 -153
- package/dist/chunk-PZICDGJG.js +69 -0
- package/dist/chunk-Z3O3JK56.js +186 -0
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +9 -7
- package/dist/{i18n-5H4W145i.d.ts → i18n-DYYPTq4o.d.ts} +21 -1
- package/dist/index.d.ts +10 -184
- package/dist/index.js +37 -210
- package/dist/integration.d.ts +2 -2
- package/dist/integration.js +2 -2
- package/dist/{sidebar-Da-W_4Lr.d.ts → sidebar-DNdiCKBw.d.ts} +1 -1
- package/dist/utils/i18n.d.ts +133 -0
- package/dist/utils/i18n.js +49 -0
- package/dist/utils/sidebar.d.ts +1 -1
- package/dist/utils/useI18n.d.ts +74 -0
- package/dist/utils/useI18n.js +15 -0
- package/package.json +9 -1
- package/src/components/blog/FloatingToc.vue +11 -3
- package/src/components/blog/Hero.astro +17 -2
- package/src/components/blog/NavigationTabs.vue +46 -15
- package/src/components/blog/PostCard.astro +28 -10
- package/src/components/blog/RelatedPosts.astro +23 -7
- package/src/components/blog/TableOfContents.astro +10 -4
- package/src/components/blog/TagCloud.astro +4 -3
- package/src/components/home/FeaturedPostsSection.astro +22 -6
- package/src/components/home/QuickNavSection.astro +33 -4
- package/src/components/home/RecentPostsSection.astro +22 -6
- package/src/components/home/StatsSection.astro +24 -6
- package/src/components/layout/Header.astro +9 -5
- package/src/components/layout/Sidebar.astro +14 -11
- package/src/components/ui/SearchBox.vue +13 -5
- package/src/components/ui/SearchInterface.vue +49 -25
- package/src/pages/archives/[year]/[month].astro +36 -17
- package/src/pages/archives/index.astro +36 -20
- package/src/pages/categories/[category].astro +33 -16
- package/src/pages/categories/index.astro +37 -14
- package/src/pages/posts/[...slug].astro +125 -18
- package/src/pages/posts/index.astro +59 -37
- package/src/pages/posts/page/[page].astro +65 -27
- package/src/pages/search.astro +50 -14
- package/src/pages/slides/index.astro +25 -6
- package/src/pages/tags/[tag].astro +32 -15
- package/src/pages/tags/index.astro +39 -16
- package/src/plugins/remark-containers.mjs +351 -322
- package/src/plugins/remark-protect-code.mjs +69 -0
- package/src/styles/global.css +35 -1
- package/templates/default/.claude/ralph-loop.local.md +48 -0
- package/templates/default/astro.config.mjs +13 -4
- package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/01-intro.md +1 -1
- package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/02-install.md +1 -1
- package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/03-create-post.md +1 -1
- package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/04-structure.md +1 -1
- package/templates/default/content/posts/blog_docs_en/01.get-started/05-deploy.md +208 -0
- package/templates/default/content/posts/blog_docs_en/{get-started → 01.get-started}/README.md +1 -1
- package/templates/default/content/posts/blog_docs_en/02.guide/02-containers.md +245 -0
- package/templates/default/content/posts/blog_docs_en/{guide/markdown → 02.guide}/03-code-blocks.md +2 -1
- package/templates/default/content/posts/blog_docs_en/{guide/features/01-mermaid.md → 02.guide/03-mermaid.md} +1 -1
- package/templates/default/content/posts/blog_docs_en/{guide/features → 02.guide}/04-icons.md +4 -2
- package/templates/default/content/posts/blog_docs_en/{guide/features/02-latex.md → 02.guide/06-latex.md} +1 -1
- package/templates/default/content/posts/blog_docs_en/{guide/features/03-video.md → 02.guide/07-video.md} +1 -1
- package/templates/default/content/posts/blog_docs_en/02.guide/08-slides.md +359 -0
- package/templates/default/content/posts/blog_docs_en/{guide/markdown → 02.guide}/README.md +22 -3
- package/templates/default/content/posts/blog_docs_en/{config → 03.config}/01-site.md +1 -1
- package/templates/default/content/posts/blog_docs_en/{config → 03.config}/02-sidebar.md +1 -1
- package/templates/default/content/posts/blog_docs_en/{config → 03.config}/03-i18n.md +88 -24
- package/templates/default/content/posts/blog_docs_en/{config → 03.config}/README.md +1 -1
- package/templates/default/content/posts/blog_docs_en/README.md +2 -1
- package/templates/default/content/posts/blog_docs_zh/01.get-started/01-intro.md +81 -0
- package/templates/default/content/posts/blog_docs_zh/01.get-started/02-install.md +137 -0
- package/templates/default/content/posts/blog_docs_zh/01.get-started/03-create-post.md +176 -0
- package/templates/default/content/posts/blog_docs_zh/01.get-started/04-structure.md +173 -0
- package/templates/default/content/posts/blog_docs_zh/01.get-started/05-deploy.md +208 -0
- package/templates/default/content/posts/blog_docs_zh/01.get-started/README.md +52 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/02-containers.md +245 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/03-code-blocks.md +206 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/03-mermaid.md +194 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/04-icons.md +229 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/06-latex.md +233 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/07-video.md +184 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/08-slides.md +359 -0
- package/templates/default/content/posts/blog_docs_zh/02.guide/README.md +213 -0
- package/templates/default/content/posts/blog_docs_zh/03.config/01-site.md +208 -0
- package/templates/default/content/posts/blog_docs_zh/03.config/02-sidebar.md +240 -0
- package/templates/default/content/posts/blog_docs_zh/03.config/03-i18n.md +348 -0
- package/templates/default/content/posts/blog_docs_zh/03.config/README.md +85 -0
- package/templates/default/content/posts/blog_docs_zh/README.md +78 -0
- package/templates/default/package.dev.json +31 -0
- package/templates/default/package.json +1 -1
- package/templates/default/src/config/locales/en/index.ts +5 -1
- package/templates/default/src/config/locales/en/menu.ts +3 -1
- package/templates/default/src/config/locales/en/sidebar.ts +18 -2
- package/templates/default/src/config/locales/en/site.ts +1 -1
- package/templates/default/src/config/locales/en/ui.ts +29 -0
- package/templates/default/src/config/locales/zh-CN/index.ts +5 -1
- package/templates/default/src/config/locales/zh-CN/menu.ts +7 -5
- package/templates/default/src/config/locales/zh-CN/sidebar.ts +22 -6
- package/templates/default/src/config/locales/zh-CN/site.ts +2 -2
- package/templates/default/src/config/locales/zh-CN/ui.ts +29 -0
- package/templates/default/src/config/site.ts +2 -2
- package/templates/default/src/content.config.ts +15 -3
- package/templates/default/content/posts/blog_docs/01-quick-start.md +0 -162
- package/templates/default/content/posts/blog_docs/02-frontmatter.md +0 -277
- package/templates/default/content/posts/blog_docs/03-markdown-basic.md +0 -350
- package/templates/default/content/posts/blog_docs/04-containers.md +0 -331
- package/templates/default/content/posts/blog_docs/05-code-blocks.md +0 -388
- package/templates/default/content/posts/blog_docs/06-mermaid.md +0 -431
- package/templates/default/content/posts/blog_docs/07-video.md +0 -243
- package/templates/default/content/posts/blog_docs/08-latex.md +0 -382
- package/templates/default/content/posts/blog_docs/09-icons.md +0 -326
- package/templates/default/content/posts/blog_docs/10-sidebar.md +0 -445
- package/templates/default/content/posts/blog_docs/11-config.md +0 -334
- package/templates/default/content/posts/blog_docs/12-i18n.md +0 -355
- package/templates/default/content/posts/blog_docs/12-slides.mdx +0 -552
- package/templates/default/content/posts/blog_docs/README.md +0 -152
- package/templates/default/content/posts/blog_docs_en/get-started/05-deploy.md +0 -197
- package/templates/default/content/posts/blog_docs_en/guide/README.md +0 -59
- package/templates/default/content/posts/blog_docs_en/guide/features/README.md +0 -51
- package/templates/default/content/posts/blog_docs_en/guide/markdown/02-containers.md +0 -226
|
@@ -3,6 +3,9 @@ import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
|
3
3
|
import PostCard from '@jet-w/astro-blog/components/blog/PostCard.astro';
|
|
4
4
|
import Pagination from '@jet-w/astro-blog/components/ui/Pagination.astro';
|
|
5
5
|
import { getCollection } from 'astro:content';
|
|
6
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
|
+
import { defaultI18nConfig } from '../../../config/i18n';
|
|
8
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../../utils/i18n';
|
|
6
9
|
|
|
7
10
|
export async function getStaticPaths() {
|
|
8
11
|
const allPostsCollection = await getCollection('posts');
|
|
@@ -20,9 +23,20 @@ export async function getStaticPaths() {
|
|
|
20
23
|
const { page } = Astro.params;
|
|
21
24
|
const currentPage = parseInt(page as string, 10);
|
|
22
25
|
|
|
26
|
+
// Get i18n config
|
|
27
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
28
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
29
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
30
|
+
const ui = localeConfig.ui;
|
|
31
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
32
|
+
|
|
23
33
|
// 从新的内容目录获取文章数据
|
|
24
34
|
const allPostsCollection = await getCollection('posts');
|
|
25
|
-
|
|
35
|
+
|
|
36
|
+
// Filter posts by current locale
|
|
37
|
+
const localeFilteredPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
|
|
38
|
+
|
|
39
|
+
const publishedPosts = localeFilteredPosts
|
|
26
40
|
.filter(post => !post.data.draft)
|
|
27
41
|
.sort((a, b) => (b.data.pubDate?.getTime() ?? 0) - (a.data.pubDate?.getTime() ?? 0));
|
|
28
42
|
|
|
@@ -52,27 +66,41 @@ const allCategories = [...new Set(allPosts.flatMap(post => post.categories || []
|
|
|
52
66
|
---
|
|
53
67
|
|
|
54
68
|
<PageLayout
|
|
55
|
-
title={
|
|
56
|
-
description=
|
|
69
|
+
title={`${ui.postList} - ${ui.page} ${currentPage}`}
|
|
70
|
+
description={localeConfig.site.description}
|
|
57
71
|
showSidebar={true}
|
|
72
|
+
i18nConfig={i18nConfig}
|
|
58
73
|
>
|
|
74
|
+
<!-- 面包屑导航 -->
|
|
75
|
+
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
76
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
77
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
78
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
79
|
+
</svg>
|
|
80
|
+
<a href={`${localePrefix}/posts`} class="hover:text-primary-500 transition-colors">{ui.postList}</a>
|
|
81
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
83
|
+
</svg>
|
|
84
|
+
<span class="text-slate-900 dark:text-slate-100">{ui.page} {currentPage}</span>
|
|
85
|
+
</nav>
|
|
86
|
+
|
|
59
87
|
<!-- 页面头部 -->
|
|
60
88
|
<div class="mb-12">
|
|
61
89
|
<div class="text-center">
|
|
62
90
|
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
63
|
-
|
|
91
|
+
{ui.postList}
|
|
64
92
|
</h1>
|
|
65
93
|
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
|
|
66
|
-
|
|
94
|
+
{localeConfig.site.description}
|
|
67
95
|
</p>
|
|
68
96
|
|
|
69
97
|
<!-- 统计信息 -->
|
|
70
98
|
<div class="inline-flex items-center space-x-6 text-sm text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800 px-6 py-3 rounded-lg">
|
|
71
|
-
<span
|
|
99
|
+
<span>{totalPosts} {ui.posts}</span>
|
|
72
100
|
<span>•</span>
|
|
73
|
-
<span>{allTags.length}
|
|
101
|
+
<span>{allTags.length} {ui.tags}</span>
|
|
74
102
|
<span>•</span>
|
|
75
|
-
<span>{allCategories.length}
|
|
103
|
+
<span>{allCategories.length} {ui.categories}</span>
|
|
76
104
|
</div>
|
|
77
105
|
</div>
|
|
78
106
|
</div>
|
|
@@ -83,34 +111,44 @@ const allCategories = [...new Set(allPosts.flatMap(post => post.categories || []
|
|
|
83
111
|
<!-- 按标签筛选 -->
|
|
84
112
|
<div>
|
|
85
113
|
<h3 class="text-sm font-semibold text-slate-900 dark:text-slate-100 mb-3">
|
|
86
|
-
|
|
114
|
+
{ui.filterByTag}
|
|
87
115
|
</h3>
|
|
88
116
|
<div class="flex flex-wrap gap-2">
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
</
|
|
92
|
-
{allTags.map((tag) => (
|
|
93
|
-
<
|
|
117
|
+
<a href={`${localePrefix}/tags`} class="px-3 py-1 text-xs rounded-full bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 hover:bg-primary-200 dark:hover:bg-primary-900/50 transition-colors">
|
|
118
|
+
{ui.allTags}
|
|
119
|
+
</a>
|
|
120
|
+
{allTags.slice(0, 10).map((tag) => (
|
|
121
|
+
<a href={`${localePrefix}/tags/${tag.toLowerCase().replace(/\s+/g, '-')}`} class="px-3 py-1 text-xs rounded-full bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors">
|
|
94
122
|
{tag}
|
|
95
|
-
</
|
|
123
|
+
</a>
|
|
96
124
|
))}
|
|
125
|
+
{allTags.length > 10 && (
|
|
126
|
+
<a href={`${localePrefix}/tags`} class="px-3 py-1 text-xs rounded-full bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors">
|
|
127
|
+
+{allTags.length - 10}
|
|
128
|
+
</a>
|
|
129
|
+
)}
|
|
97
130
|
</div>
|
|
98
131
|
</div>
|
|
99
132
|
|
|
100
133
|
<!-- 按分类筛选 -->
|
|
101
134
|
<div>
|
|
102
135
|
<h3 class="text-sm font-semibold text-slate-900 dark:text-slate-100 mb-3">
|
|
103
|
-
|
|
136
|
+
{ui.filterByCategory}
|
|
104
137
|
</h3>
|
|
105
138
|
<div class="flex flex-wrap gap-2">
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
</
|
|
109
|
-
{allCategories.map((category) => (
|
|
110
|
-
<
|
|
139
|
+
<a href={`${localePrefix}/categories`} class="px-3 py-1 text-xs rounded-full bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 hover:bg-primary-200 dark:hover:bg-primary-900/50 transition-colors">
|
|
140
|
+
{ui.allCategories}
|
|
141
|
+
</a>
|
|
142
|
+
{allCategories.slice(0, 8).map((category) => (
|
|
143
|
+
<a href={`${localePrefix}/categories/${category.toLowerCase().replace(/\s+/g, '-')}`} class="px-3 py-1 text-xs rounded-full bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors">
|
|
111
144
|
{category}
|
|
112
|
-
</
|
|
145
|
+
</a>
|
|
113
146
|
))}
|
|
147
|
+
{allCategories.length > 8 && (
|
|
148
|
+
<a href={`${localePrefix}/categories`} class="px-3 py-1 text-xs rounded-full bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors">
|
|
149
|
+
+{allCategories.length - 8}
|
|
150
|
+
</a>
|
|
151
|
+
)}
|
|
114
152
|
</div>
|
|
115
153
|
</div>
|
|
116
154
|
</div>
|
|
@@ -118,19 +156,19 @@ const allCategories = [...new Set(allPosts.flatMap(post => post.categories || []
|
|
|
118
156
|
|
|
119
157
|
<!-- 文章列表 -->
|
|
120
158
|
{posts.length > 0 ? (
|
|
121
|
-
<div class="
|
|
159
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
|
122
160
|
{posts.map((post) => (
|
|
123
|
-
<PostCard post={post} layout="horizontal" />
|
|
161
|
+
<PostCard post={post} layout="horizontal" localePrefix={localePrefix} locale={localeConfig.locale.dateLocale} ui={ui} />
|
|
124
162
|
))}
|
|
125
163
|
</div>
|
|
126
164
|
) : (
|
|
127
165
|
<div class="text-center py-16">
|
|
128
166
|
<div class="text-6xl mb-4">📝</div>
|
|
129
167
|
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
|
|
130
|
-
|
|
168
|
+
{ui.noPostsFound}
|
|
131
169
|
</h3>
|
|
132
170
|
<p class="text-slate-600 dark:text-slate-400">
|
|
133
|
-
|
|
171
|
+
{ui.noPostsFound}
|
|
134
172
|
</p>
|
|
135
173
|
</div>
|
|
136
174
|
)}
|
|
@@ -140,7 +178,7 @@ const allCategories = [...new Set(allPosts.flatMap(post => post.categories || []
|
|
|
140
178
|
<Pagination
|
|
141
179
|
currentPage={currentPage}
|
|
142
180
|
totalPages={totalPages}
|
|
143
|
-
baseUrl=
|
|
181
|
+
baseUrl={`${localePrefix}/posts`}
|
|
144
182
|
/>
|
|
145
183
|
)}
|
|
146
184
|
</PageLayout>
|
package/src/pages/search.astro
CHANGED
|
@@ -1,47 +1,83 @@
|
|
|
1
1
|
---
|
|
2
2
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
3
3
|
import SearchInterface from '@jet-w/astro-blog/components/ui/SearchInterface.vue';
|
|
4
|
+
import type { I18nConfig } from '../config/i18n';
|
|
5
|
+
import { defaultI18nConfig } from '../config/i18n';
|
|
6
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
|
+
import { getLocaleFromPath, getLocaleConfig } from '../utils/i18n';
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
i18nConfig?: I18nConfig;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
14
|
+
|
|
15
|
+
// Get current locale from URL
|
|
16
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
17
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
18
|
+
const ui = localeConfig.ui;
|
|
19
|
+
|
|
20
|
+
// Get date locale for formatting
|
|
21
|
+
const locale = i18nConfig.locales.find(l => l.code === currentLocale);
|
|
22
|
+
const dateLocale = locale?.dateLocale || 'zh-CN';
|
|
4
23
|
---
|
|
5
24
|
|
|
6
25
|
<PageLayout
|
|
7
|
-
title=
|
|
8
|
-
description=
|
|
26
|
+
title={ui.search}
|
|
27
|
+
description={ui.searchInAllArticles}
|
|
9
28
|
showSidebar={true}
|
|
10
29
|
>
|
|
11
30
|
<div class="max-w-4xl mx-auto">
|
|
12
31
|
<!-- 页面头部 -->
|
|
13
32
|
<div class="text-center mb-12">
|
|
14
33
|
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
15
|
-
|
|
34
|
+
{ui.searchArticles}
|
|
16
35
|
</h1>
|
|
17
36
|
<p class="text-xl text-slate-600 dark:text-slate-400">
|
|
18
|
-
|
|
37
|
+
{ui.searchInAllArticles}
|
|
19
38
|
</p>
|
|
20
39
|
</div>
|
|
21
40
|
|
|
22
41
|
<!-- 搜索界面 -->
|
|
23
|
-
<SearchInterface
|
|
42
|
+
<SearchInterface
|
|
43
|
+
client:load
|
|
44
|
+
placeholder={ui.searchPlaceholder}
|
|
45
|
+
noResults={ui.noResults}
|
|
46
|
+
tags={ui.tags}
|
|
47
|
+
categories={ui.categories}
|
|
48
|
+
sortLabel={ui.sortBy}
|
|
49
|
+
relevance={ui.sortByDate}
|
|
50
|
+
publishTime={ui.publishedOn}
|
|
51
|
+
titleLabel={ui.sortByTitle}
|
|
52
|
+
all={ui.allPosts}
|
|
53
|
+
searching={ui.searching}
|
|
54
|
+
noResultsTitle={ui.noResults}
|
|
55
|
+
clearSearch={ui.clearFilter}
|
|
56
|
+
previousPage={ui.previousPage}
|
|
57
|
+
nextPage={ui.nextPage}
|
|
58
|
+
dateLocale={dateLocale}
|
|
59
|
+
/>
|
|
24
60
|
|
|
25
61
|
<!-- 搜索提示 -->
|
|
26
62
|
<div class="mt-12 p-6 bg-slate-50 dark:bg-slate-800 rounded-xl">
|
|
27
63
|
<h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-4">
|
|
28
|
-
|
|
64
|
+
{ui.searchTips}
|
|
29
65
|
</h2>
|
|
30
66
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-slate-600 dark:text-slate-400">
|
|
31
67
|
<div>
|
|
32
|
-
<h3 class="font-medium text-slate-900 dark:text-slate-100 mb-2"
|
|
68
|
+
<h3 class="font-medium text-slate-900 dark:text-slate-100 mb-2">{ui.basicSearch}</h3>
|
|
33
69
|
<ul class="space-y-1">
|
|
34
|
-
<li>•
|
|
35
|
-
<li>•
|
|
36
|
-
<li>•
|
|
70
|
+
<li>• {ui.searchTipKeyword}</li>
|
|
71
|
+
<li>• {ui.searchTipMixedLang}</li>
|
|
72
|
+
<li>• {ui.searchTipCaseInsensitive}</li>
|
|
37
73
|
</ul>
|
|
38
74
|
</div>
|
|
39
75
|
<div>
|
|
40
|
-
<h3 class="font-medium text-slate-900 dark:text-slate-100 mb-2"
|
|
76
|
+
<h3 class="font-medium text-slate-900 dark:text-slate-100 mb-2">{ui.advancedFeatures}</h3>
|
|
41
77
|
<ul class="space-y-1">
|
|
42
|
-
<li>•
|
|
43
|
-
<li>•
|
|
44
|
-
<li>•
|
|
78
|
+
<li>• {ui.searchTipRealtime}</li>
|
|
79
|
+
<li>• {ui.searchTipFilter}</li>
|
|
80
|
+
<li>• {ui.searchTipFuzzy}</li>
|
|
45
81
|
</ul>
|
|
46
82
|
</div>
|
|
47
83
|
</div>
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { getCollection } from 'astro:content';
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
5
|
+
import { defaultI18nConfig } from '../../config/i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix } from '../../utils/i18n';
|
|
7
|
+
|
|
8
|
+
// Get i18n config
|
|
9
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
10
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
11
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
12
|
+
const ui = localeConfig.ui;
|
|
13
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
4
14
|
|
|
5
15
|
// 获取所有非草稿的 slides
|
|
6
16
|
const slides = (await getCollection('slides', ({ data }) => !data.draft))
|
|
@@ -20,28 +30,37 @@ function formatDate(date: Date | undefined): string {
|
|
|
20
30
|
}
|
|
21
31
|
---
|
|
22
32
|
|
|
23
|
-
<PageLayout title=
|
|
33
|
+
<PageLayout title={ui.slides} description={ui.slidesList} showSidebar={false} i18nConfig={i18nConfig}>
|
|
34
|
+
<!-- 面包屑导航 -->
|
|
35
|
+
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
36
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
37
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
38
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
39
|
+
</svg>
|
|
40
|
+
<span class="text-slate-900 dark:text-slate-100">{ui.slides}</span>
|
|
41
|
+
</nav>
|
|
42
|
+
|
|
24
43
|
<div class="slides-list-page">
|
|
25
44
|
<header class="mb-8">
|
|
26
45
|
<h1 class="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-2">
|
|
27
|
-
|
|
46
|
+
{ui.slides}
|
|
28
47
|
</h1>
|
|
29
48
|
<p class="text-slate-600 dark:text-slate-400">
|
|
30
|
-
|
|
49
|
+
{ui.slidesList}
|
|
31
50
|
</p>
|
|
32
51
|
</header>
|
|
33
52
|
|
|
34
53
|
{slides.length === 0 ? (
|
|
35
54
|
<div class="text-center py-16 text-slate-500 dark:text-slate-400">
|
|
36
55
|
<div class="text-6xl mb-4">📭</div>
|
|
37
|
-
<p
|
|
38
|
-
<p class="text-sm mt-2"
|
|
56
|
+
<p>{ui.noPostsFound}</p>
|
|
57
|
+
<p class="text-sm mt-2">content/slides/</p>
|
|
39
58
|
</div>
|
|
40
59
|
) : (
|
|
41
60
|
<div class="grid gap-6 md:grid-cols-2">
|
|
42
61
|
{slides.map((slide) => (
|
|
43
62
|
<a
|
|
44
|
-
href={
|
|
63
|
+
href={`${localePrefix}/slides/${slide.id}`}
|
|
45
64
|
class="slide-card group block p-6 bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 hover:border-primary-500 dark:hover:border-primary-500 transition-all hover:shadow-lg hover:-translate-y-1"
|
|
46
65
|
>
|
|
47
66
|
<div class="flex items-start justify-between mb-3">
|
|
@@ -3,6 +3,9 @@ import { getCollection } from 'astro:content';
|
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import PostCard from '@jet-w/astro-blog/components/blog/PostCard.astro';
|
|
5
5
|
import Pagination from '@jet-w/astro-blog/components/ui/Pagination.astro';
|
|
6
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
7
|
+
import { defaultI18nConfig } from '../../config/i18n';
|
|
8
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
6
9
|
|
|
7
10
|
export async function getStaticPaths() {
|
|
8
11
|
const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
@@ -30,8 +33,18 @@ export async function getStaticPaths() {
|
|
|
30
33
|
|
|
31
34
|
const { tagSlug, tagName, tagCount } = Astro.props;
|
|
32
35
|
|
|
36
|
+
// Get i18n config
|
|
37
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
38
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
39
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
40
|
+
const ui = localeConfig.ui;
|
|
41
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
42
|
+
|
|
33
43
|
// 获取所有文章并筛选包含该标签的文章
|
|
34
|
-
const
|
|
44
|
+
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
45
|
+
|
|
46
|
+
// Filter posts by current locale
|
|
47
|
+
const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
|
|
35
48
|
|
|
36
49
|
// 筛选包含该标签的文章
|
|
37
50
|
const filteredPosts = allPosts
|
|
@@ -75,17 +88,18 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
75
88
|
---
|
|
76
89
|
|
|
77
90
|
<PageLayout
|
|
78
|
-
title={
|
|
79
|
-
description={
|
|
91
|
+
title={`${ui.taggedWith}: ${tagName}`}
|
|
92
|
+
description={`${ui.taggedWith} ${tagName}`}
|
|
80
93
|
showSidebar={true}
|
|
94
|
+
i18nConfig={i18nConfig}
|
|
81
95
|
>
|
|
82
96
|
<!-- 面包屑导航 -->
|
|
83
97
|
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
84
|
-
<a href=
|
|
98
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
85
99
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
86
100
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
87
101
|
</svg>
|
|
88
|
-
<a href=
|
|
102
|
+
<a href={`${localePrefix}/tags`} class="hover:text-primary-500 transition-colors">{ui.allTags}</a>
|
|
89
103
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
90
104
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
91
105
|
</svg>
|
|
@@ -105,18 +119,18 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
105
119
|
</h1>
|
|
106
120
|
|
|
107
121
|
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
|
|
108
|
-
|
|
122
|
+
{totalPosts} {ui.posts}
|
|
109
123
|
</p>
|
|
110
124
|
|
|
111
125
|
<!-- 标签统计 -->
|
|
112
126
|
<div class="inline-flex items-center space-x-6 text-sm text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800 px-6 py-3 rounded-lg">
|
|
113
127
|
<span>#{tagName}</span>
|
|
114
128
|
<span>•</span>
|
|
115
|
-
<span>{totalPosts}
|
|
129
|
+
<span>{totalPosts} {ui.posts}</span>
|
|
116
130
|
{totalPages > 1 && (
|
|
117
131
|
<>
|
|
118
132
|
<span>•</span>
|
|
119
|
-
<span
|
|
133
|
+
<span>{totalPages} {ui.page}</span>
|
|
120
134
|
</>
|
|
121
135
|
)}
|
|
122
136
|
</div>
|
|
@@ -138,6 +152,9 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
138
152
|
image: post.data.image
|
|
139
153
|
}}
|
|
140
154
|
layout="horizontal"
|
|
155
|
+
localePrefix={localePrefix}
|
|
156
|
+
locale={localeConfig.locale.dateLocale}
|
|
157
|
+
ui={ui}
|
|
141
158
|
/>
|
|
142
159
|
))}
|
|
143
160
|
</div>
|
|
@@ -145,13 +162,13 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
145
162
|
<div class="text-center py-16">
|
|
146
163
|
<div class="text-6xl mb-4">📝</div>
|
|
147
164
|
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
|
|
148
|
-
|
|
165
|
+
{ui.noPostsFound}
|
|
149
166
|
</h3>
|
|
150
167
|
<p class="text-slate-600 dark:text-slate-400 mb-6">
|
|
151
|
-
|
|
168
|
+
{ui.noPostsFound}
|
|
152
169
|
</p>
|
|
153
|
-
<a href=
|
|
154
|
-
|
|
170
|
+
<a href={`${localePrefix}/tags`} class="btn-secondary">
|
|
171
|
+
{ui.allTags}
|
|
155
172
|
</a>
|
|
156
173
|
</div>
|
|
157
174
|
)}
|
|
@@ -161,7 +178,7 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
161
178
|
<Pagination
|
|
162
179
|
currentPage={currentPage}
|
|
163
180
|
totalPages={totalPages}
|
|
164
|
-
baseUrl={
|
|
181
|
+
baseUrl={`${localePrefix}/tags/${tagSlug}`}
|
|
165
182
|
/>
|
|
166
183
|
)}
|
|
167
184
|
|
|
@@ -169,12 +186,12 @@ const relatedTags = Array.from(relatedTagMap.entries())
|
|
|
169
186
|
{relatedTags.length > 0 && (
|
|
170
187
|
<section class="mt-16 pt-8 border-t border-slate-200 dark:border-slate-700">
|
|
171
188
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-6">
|
|
172
|
-
|
|
189
|
+
{ui.popularTags}
|
|
173
190
|
</h2>
|
|
174
191
|
<div class="flex flex-wrap gap-3">
|
|
175
192
|
{relatedTags.map((relatedTag) => (
|
|
176
193
|
<a
|
|
177
|
-
href={
|
|
194
|
+
href={`${localePrefix}/tags/${relatedTag.slug}`}
|
|
178
195
|
class="inline-flex items-center px-4 py-2 bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-600 rounded-lg hover:border-primary-300 dark:hover:border-primary-600 hover:shadow-md transition-all duration-200 group"
|
|
179
196
|
>
|
|
180
197
|
<span class="font-medium text-slate-900 dark:text-slate-100 group-hover:text-primary-500 transition-colors">
|
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
import { getCollection } from 'astro:content';
|
|
3
3
|
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
4
|
import TagCloud from '@jet-w/astro-blog/components/blog/TagCloud.astro';
|
|
5
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
6
|
+
import { defaultI18nConfig } from '../../config/i18n';
|
|
7
|
+
import { getLocaleFromPath, getLocaleConfig, getLocalePrefix, filterPostsByLocale } from '../../utils/i18n';
|
|
8
|
+
|
|
9
|
+
// Get i18n config
|
|
10
|
+
const i18nConfig = virtualI18nConfig || defaultI18nConfig;
|
|
11
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
12
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
13
|
+
const ui = localeConfig.ui;
|
|
14
|
+
const localePrefix = getLocalePrefix(currentLocale, i18nConfig);
|
|
5
15
|
|
|
6
16
|
// 从文章集合动态获取所有标签
|
|
7
|
-
const
|
|
17
|
+
const allPostsCollection = await getCollection('posts', ({ data }) => !data.draft);
|
|
18
|
+
|
|
19
|
+
// Filter posts by current locale
|
|
20
|
+
const allPosts = filterPostsByLocale(allPostsCollection, currentLocale, i18nConfig);
|
|
8
21
|
|
|
9
22
|
// 统计标签
|
|
10
23
|
const tagMap = new Map<string, { name: string; count: number }>();
|
|
@@ -54,44 +67,54 @@ const sortedGroups = Object.entries(groupedByLetter).sort(([a], [b]) => {
|
|
|
54
67
|
---
|
|
55
68
|
|
|
56
69
|
<PageLayout
|
|
57
|
-
title=
|
|
58
|
-
description=
|
|
70
|
+
title={ui.allTags}
|
|
71
|
+
description={ui.allTags}
|
|
59
72
|
showSidebar={true}
|
|
73
|
+
i18nConfig={i18nConfig}
|
|
60
74
|
>
|
|
75
|
+
<!-- 面包屑导航 -->
|
|
76
|
+
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
77
|
+
<a href={`${localePrefix}/`} class="hover:text-primary-500 transition-colors">{ui.home}</a>
|
|
78
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
79
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
80
|
+
</svg>
|
|
81
|
+
<span class="text-slate-900 dark:text-slate-100">{ui.allTags}</span>
|
|
82
|
+
</nav>
|
|
83
|
+
|
|
61
84
|
<!-- 页面头部 -->
|
|
62
85
|
<div class="text-center mb-12">
|
|
63
86
|
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
64
|
-
|
|
87
|
+
{ui.allTags}
|
|
65
88
|
</h1>
|
|
66
89
|
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
|
|
67
|
-
|
|
90
|
+
{ui.filterByTag}
|
|
68
91
|
</p>
|
|
69
92
|
|
|
70
93
|
<!-- 统计信息 -->
|
|
71
94
|
<div class="inline-flex items-center space-x-6 text-sm text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800 px-6 py-3 rounded-lg">
|
|
72
|
-
<span
|
|
95
|
+
<span>{allTags.length} {ui.tags}</span>
|
|
73
96
|
<span>•</span>
|
|
74
|
-
<span>{totalPosts}
|
|
97
|
+
<span>{totalPosts} {ui.posts}</span>
|
|
75
98
|
</div>
|
|
76
99
|
</div>
|
|
77
100
|
|
|
78
101
|
<!-- 标签云 -->
|
|
79
102
|
<section class="mb-16">
|
|
80
103
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
81
|
-
|
|
104
|
+
{ui.tags}
|
|
82
105
|
</h2>
|
|
83
|
-
<TagCloud tags={allTags} />
|
|
106
|
+
<TagCloud tags={allTags} localePrefix={localePrefix} />
|
|
84
107
|
</section>
|
|
85
108
|
|
|
86
109
|
<!-- 热门标签 -->
|
|
87
110
|
<section class="mb-16">
|
|
88
111
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
89
|
-
|
|
112
|
+
{ui.popularTags}
|
|
90
113
|
</h2>
|
|
91
114
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
92
115
|
{sortedTags.slice(0, 9).map((tag) => (
|
|
93
116
|
<a
|
|
94
|
-
href={
|
|
117
|
+
href={`${localePrefix}/tags/${tag.slug}`}
|
|
95
118
|
class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300"
|
|
96
119
|
>
|
|
97
120
|
<div class="flex items-center justify-between">
|
|
@@ -100,7 +123,7 @@ const sortedGroups = Object.entries(groupedByLetter).sort(([a], [b]) => {
|
|
|
100
123
|
{tag.name}
|
|
101
124
|
</h3>
|
|
102
125
|
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
103
|
-
{tag.count}
|
|
126
|
+
{tag.count} {ui.posts}
|
|
104
127
|
</p>
|
|
105
128
|
</div>
|
|
106
129
|
<div class="text-3xl font-bold text-primary-500 opacity-80">
|
|
@@ -121,12 +144,12 @@ const sortedGroups = Object.entries(groupedByLetter).sort(([a], [b]) => {
|
|
|
121
144
|
<!-- 所有标签 -->
|
|
122
145
|
<section class="mb-16">
|
|
123
146
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
124
|
-
|
|
147
|
+
{ui.allTags}
|
|
125
148
|
</h2>
|
|
126
149
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
127
150
|
{sortedTags.map((tag) => (
|
|
128
151
|
<a
|
|
129
|
-
href={
|
|
152
|
+
href={`${localePrefix}/tags/${tag.slug}`}
|
|
130
153
|
class="flex items-center justify-between p-4 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg hover:shadow-md hover:border-primary-300 dark:hover:border-primary-600 transition-all duration-200 group"
|
|
131
154
|
>
|
|
132
155
|
<span class="font-medium text-slate-900 dark:text-slate-100 group-hover:text-primary-500 transition-colors">
|
|
@@ -143,7 +166,7 @@ const sortedGroups = Object.entries(groupedByLetter).sort(([a], [b]) => {
|
|
|
143
166
|
<!-- 按字母分组 -->
|
|
144
167
|
<section>
|
|
145
168
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
146
|
-
|
|
169
|
+
{ui.tags}
|
|
147
170
|
</h2>
|
|
148
171
|
{sortedGroups.map(([letter, tags]) => (
|
|
149
172
|
<div class="mb-6">
|
|
@@ -153,7 +176,7 @@ const sortedGroups = Object.entries(groupedByLetter).sort(([a], [b]) => {
|
|
|
153
176
|
<div class="flex flex-wrap gap-2">
|
|
154
177
|
{tags.sort((a, b) => b.count - a.count).map((tag) => (
|
|
155
178
|
<a
|
|
156
|
-
href={
|
|
179
|
+
href={`${localePrefix}/tags/${tag.slug}`}
|
|
157
180
|
class="inline-flex items-center px-3 py-1 bg-slate-100 dark:bg-slate-800 hover:bg-primary-100 dark:hover:bg-primary-900/30 text-slate-700 dark:text-slate-300 hover:text-primary-700 dark:hover:text-primary-300 rounded-full transition-colors text-sm"
|
|
158
181
|
>
|
|
159
182
|
{tag.name}
|