@jet-w/astro-blog 0.1.6 → 0.2.1
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-6D3XRDNY.js +145 -0
- package/dist/chunk-A2E2VSAQ.js +246 -0
- package/dist/{chunk-GYLSY3OJ.js → chunk-TJTPX2WP.js} +1 -1
- package/dist/config/index.d.ts +3 -47
- package/dist/config/index.js +18 -2
- package/dist/i18n-PgMCFBw0.d.ts +222 -0
- package/dist/index.d.ts +204 -7
- package/dist/index.js +255 -3
- package/dist/integration.d.ts +9 -1
- package/dist/integration.js +2 -1
- package/dist/{sidebar-DNdiCKBw.d.ts → sidebar-Da-W_4Lr.d.ts} +1 -1
- package/dist/utils/sidebar.d.ts +1 -1
- package/package.json +1 -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/Footer.astro +36 -20
- package/src/components/layout/Header.astro +75 -17
- package/src/components/layout/Sidebar.astro +40 -25
- package/src/components/ui/LanguageSwitcher.vue +183 -0
- package/src/components/ui/SearchBox.vue +13 -5
- package/src/components/ui/SearchInterface.vue +49 -25
- package/src/layouts/BaseLayout.astro +77 -52
- package/src/layouts/PageLayout.astro +22 -27
- package/src/layouts/SlidesLayout.astro +14 -2
- 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/rss.xml.ts +18 -6
- 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 +33 -4
- package/templates/default/content/posts/blog_docs_en/01.get-started/01-intro.md +81 -0
- package/templates/default/content/posts/blog_docs_en/01.get-started/02-install.md +137 -0
- package/templates/default/content/posts/blog_docs_en/01.get-started/03-create-post.md +176 -0
- package/templates/default/content/posts/blog_docs_en/01.get-started/04-structure.md +173 -0
- package/templates/default/content/posts/blog_docs_en/01.get-started/05-deploy.md +208 -0
- package/templates/default/content/posts/blog_docs_en/01.get-started/README.md +52 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/02-containers.md +245 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/03-code-blocks.md +207 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/03-mermaid.md +194 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/04-icons.md +229 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/06-latex.md +233 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/07-video.md +184 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/08-slides.md +359 -0
- package/templates/default/content/posts/blog_docs_en/02.guide/README.md +213 -0
- package/templates/default/content/posts/blog_docs_en/03.config/01-site.md +208 -0
- package/templates/default/content/posts/blog_docs_en/03.config/02-sidebar.md +240 -0
- package/templates/default/content/posts/blog_docs_en/03.config/03-i18n.md +349 -0
- package/templates/default/content/posts/blog_docs_en/03.config/README.md +85 -0
- package/templates/default/content/posts/blog_docs_en/README.md +79 -0
- 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-lock.json +9667 -0
- package/templates/default/package.json +1 -1
- package/templates/default/src/config/footer.ts +14 -11
- package/templates/default/src/config/locales/en/footer.ts +17 -0
- package/templates/default/src/config/locales/en/index.ts +20 -0
- package/templates/default/src/config/locales/en/menu.ts +14 -0
- package/templates/default/src/config/locales/en/sidebar.ts +34 -0
- package/templates/default/src/config/locales/en/site.ts +7 -0
- package/templates/default/src/config/locales/en/ui.ts +29 -0
- package/templates/default/src/config/locales/index.ts +7 -0
- package/templates/default/src/config/locales/zh-CN/footer.ts +17 -0
- package/templates/default/src/config/locales/zh-CN/index.ts +20 -0
- package/templates/default/src/config/locales/zh-CN/menu.ts +14 -0
- package/templates/default/src/config/locales/zh-CN/sidebar.ts +34 -0
- package/templates/default/src/config/locales/zh-CN/site.ts +7 -0
- package/templates/default/src/config/locales/zh-CN/ui.ts +29 -0
- package/templates/default/src/config/sidebar.ts +10 -12
- package/templates/default/src/config/site.ts +2 -2
- package/templates/default/src/content.config.ts +15 -3
- package/templates/default/src/env.d.ts +7 -0
- package/dist/chunk-MQXPSOYB.js +0 -124
- 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-slides.mdx +0 -552
- package/templates/default/content/posts/blog_docs/README.md +0 -151
package/dist/index.js
CHANGED
|
@@ -16,12 +16,237 @@ import {
|
|
|
16
16
|
sidebarConfig,
|
|
17
17
|
siteConfig,
|
|
18
18
|
socialLinks
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TJTPX2WP.js";
|
|
20
20
|
import "./chunk-FXPGR372.js";
|
|
21
21
|
import {
|
|
22
22
|
astroBlogIntegration,
|
|
23
23
|
integration_default
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-6D3XRDNY.js";
|
|
25
|
+
import {
|
|
26
|
+
builtInTranslations,
|
|
27
|
+
defaultI18nConfig,
|
|
28
|
+
defaultLocales,
|
|
29
|
+
defineI18nConfig,
|
|
30
|
+
enTranslations,
|
|
31
|
+
getUITranslations,
|
|
32
|
+
zhCNTranslations
|
|
33
|
+
} from "./chunk-A2E2VSAQ.js";
|
|
34
|
+
|
|
35
|
+
// src/utils/i18n.ts
|
|
36
|
+
function getLocaleFromPath(pathname, config = defaultI18nConfig) {
|
|
37
|
+
const segments = pathname.replace(/^\//, "").split("/");
|
|
38
|
+
const firstSegment = segments[0];
|
|
39
|
+
const matchedLocale = config.locales.find(
|
|
40
|
+
(locale) => locale.code === firstSegment
|
|
41
|
+
);
|
|
42
|
+
if (matchedLocale) {
|
|
43
|
+
return matchedLocale.code;
|
|
44
|
+
}
|
|
45
|
+
return config.defaultLocale;
|
|
46
|
+
}
|
|
47
|
+
function getLocaleByCode(code, config = defaultI18nConfig) {
|
|
48
|
+
return config.locales.find((locale) => locale.code === code);
|
|
49
|
+
}
|
|
50
|
+
function removeLocalePrefix(pathname, config = defaultI18nConfig) {
|
|
51
|
+
const locale = getLocaleFromPath(pathname, config);
|
|
52
|
+
if (locale === config.defaultLocale && !config.routing.prefixDefaultLocale) {
|
|
53
|
+
return pathname;
|
|
54
|
+
}
|
|
55
|
+
const prefix = `/${locale}`;
|
|
56
|
+
if (pathname.startsWith(prefix)) {
|
|
57
|
+
const rest = pathname.slice(prefix.length);
|
|
58
|
+
return rest || "/";
|
|
59
|
+
}
|
|
60
|
+
return pathname;
|
|
61
|
+
}
|
|
62
|
+
function getLocalizedPath(pathname, targetLocale, config = defaultI18nConfig) {
|
|
63
|
+
const basePath = removeLocalePrefix(pathname, config);
|
|
64
|
+
if (targetLocale === config.defaultLocale && !config.routing.prefixDefaultLocale) {
|
|
65
|
+
return basePath;
|
|
66
|
+
}
|
|
67
|
+
if (basePath === "/") {
|
|
68
|
+
return `/${targetLocale}`;
|
|
69
|
+
}
|
|
70
|
+
return `/${targetLocale}${basePath}`;
|
|
71
|
+
}
|
|
72
|
+
function getAlternateLinks(pathname, baseUrl, config = defaultI18nConfig) {
|
|
73
|
+
const links = [];
|
|
74
|
+
for (const locale of config.locales) {
|
|
75
|
+
const localizedPath = getLocalizedPath(pathname, locale.code, config);
|
|
76
|
+
links.push({
|
|
77
|
+
locale: locale.code,
|
|
78
|
+
url: `${baseUrl.replace(/\/$/, "")}${localizedPath}`,
|
|
79
|
+
hreflang: locale.htmlLang
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const defaultPath = getLocalizedPath(
|
|
83
|
+
pathname,
|
|
84
|
+
config.defaultLocale,
|
|
85
|
+
config
|
|
86
|
+
);
|
|
87
|
+
links.push({
|
|
88
|
+
locale: "x-default",
|
|
89
|
+
url: `${baseUrl.replace(/\/$/, "")}${defaultPath}`,
|
|
90
|
+
hreflang: "x-default"
|
|
91
|
+
});
|
|
92
|
+
return links;
|
|
93
|
+
}
|
|
94
|
+
function deepMerge(base, override) {
|
|
95
|
+
const result = { ...base };
|
|
96
|
+
for (const key in override) {
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(override, key)) {
|
|
98
|
+
const overrideValue = override[key];
|
|
99
|
+
const baseValue = base[key];
|
|
100
|
+
if (typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(overrideValue) && typeof baseValue === "object" && baseValue !== null && !Array.isArray(baseValue)) {
|
|
101
|
+
result[key] = deepMerge(baseValue, overrideValue);
|
|
102
|
+
} else if (overrideValue !== void 0) {
|
|
103
|
+
result[key] = overrideValue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function getLocaleConfig(locale, config = defaultI18nConfig) {
|
|
110
|
+
const localeData = getLocaleByCode(locale, config);
|
|
111
|
+
const localeOverrides = config.localeConfigs[locale] || {};
|
|
112
|
+
const localeInfo = localeData || {
|
|
113
|
+
code: locale,
|
|
114
|
+
name: locale,
|
|
115
|
+
htmlLang: locale,
|
|
116
|
+
dateLocale: locale,
|
|
117
|
+
direction: "ltr"
|
|
118
|
+
};
|
|
119
|
+
const site = deepMerge(defaultSiteConfig, localeOverrides.site || {});
|
|
120
|
+
const menu2 = localeOverrides.menu || defaultMenu;
|
|
121
|
+
const footer = deepMerge(defaultFooterConfig, localeOverrides.footer || {});
|
|
122
|
+
const sidebar = localeOverrides.sidebar ? {
|
|
123
|
+
...sidebarConfig,
|
|
124
|
+
...localeOverrides.sidebar,
|
|
125
|
+
// Use locale-specific groups if provided, otherwise keep default
|
|
126
|
+
groups: localeOverrides.sidebar.groups || sidebarConfig.groups
|
|
127
|
+
} : sidebarConfig;
|
|
128
|
+
const ui = getUITranslations(locale, config);
|
|
129
|
+
return {
|
|
130
|
+
locale: localeInfo,
|
|
131
|
+
site,
|
|
132
|
+
menu: menu2,
|
|
133
|
+
footer,
|
|
134
|
+
sidebar,
|
|
135
|
+
ui
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function t(key, locale, config) {
|
|
139
|
+
const translations = getUITranslations(locale, config);
|
|
140
|
+
return translations[key] || key;
|
|
141
|
+
}
|
|
142
|
+
function formatDate(date, locale, options) {
|
|
143
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
144
|
+
const defaultOptions = {
|
|
145
|
+
year: "numeric",
|
|
146
|
+
month: "long",
|
|
147
|
+
day: "numeric"
|
|
148
|
+
};
|
|
149
|
+
return new Intl.DateTimeFormat(locale, options || defaultOptions).format(
|
|
150
|
+
dateObj
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function formatDateShort(date, locale) {
|
|
154
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
155
|
+
return new Intl.DateTimeFormat(locale, {
|
|
156
|
+
year: "numeric",
|
|
157
|
+
month: "numeric",
|
|
158
|
+
day: "numeric"
|
|
159
|
+
}).format(dateObj);
|
|
160
|
+
}
|
|
161
|
+
function isRTL(locale, config = defaultI18nConfig) {
|
|
162
|
+
const localeData = getLocaleByCode(locale, config);
|
|
163
|
+
return localeData?.direction === "rtl";
|
|
164
|
+
}
|
|
165
|
+
function getTextDirection(locale, config = defaultI18nConfig) {
|
|
166
|
+
return isRTL(locale, config) ? "rtl" : "ltr";
|
|
167
|
+
}
|
|
168
|
+
function isMultiLanguageEnabled(config = defaultI18nConfig) {
|
|
169
|
+
return config.locales.length > 1;
|
|
170
|
+
}
|
|
171
|
+
function getLocalePrefix(locale, config = defaultI18nConfig) {
|
|
172
|
+
if (locale === config.defaultLocale && !config.routing.prefixDefaultLocale) {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
return `/${locale}`;
|
|
176
|
+
}
|
|
177
|
+
function getContentPathPrefix(locale, config = defaultI18nConfig) {
|
|
178
|
+
const localeConfig = config.localeConfigs[locale];
|
|
179
|
+
return localeConfig?.contentPathPrefix;
|
|
180
|
+
}
|
|
181
|
+
function filterPostsByLocale(posts, locale, config = defaultI18nConfig) {
|
|
182
|
+
const contentPathPrefix = getContentPathPrefix(locale, config);
|
|
183
|
+
if (!contentPathPrefix) {
|
|
184
|
+
return posts;
|
|
185
|
+
}
|
|
186
|
+
return posts.filter((post) => {
|
|
187
|
+
const postPath = post.id.toLowerCase();
|
|
188
|
+
const prefix = contentPathPrefix.toLowerCase();
|
|
189
|
+
return postPath.startsWith(prefix + "/") || postPath === prefix;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/utils/useI18n.ts
|
|
194
|
+
import { inject, computed } from "vue";
|
|
195
|
+
var I18N_LOCALE_KEY = /* @__PURE__ */ Symbol("i18n-locale");
|
|
196
|
+
var I18N_CONFIG_KEY = /* @__PURE__ */ Symbol("i18n-config");
|
|
197
|
+
var I18N_TRANSLATIONS_KEY = /* @__PURE__ */ Symbol("i18n-translations");
|
|
198
|
+
function useI18n() {
|
|
199
|
+
const injectedLocale = inject(I18N_LOCALE_KEY, "zh-CN");
|
|
200
|
+
const injectedTranslations = inject(
|
|
201
|
+
I18N_TRANSLATIONS_KEY,
|
|
202
|
+
void 0
|
|
203
|
+
);
|
|
204
|
+
const injectedConfig = inject(I18N_CONFIG_KEY, void 0);
|
|
205
|
+
const locale = computed(() => injectedLocale);
|
|
206
|
+
const translations = computed(() => {
|
|
207
|
+
if (injectedTranslations) {
|
|
208
|
+
return injectedTranslations;
|
|
209
|
+
}
|
|
210
|
+
return getUITranslations(injectedLocale, injectedConfig);
|
|
211
|
+
});
|
|
212
|
+
function t2(key) {
|
|
213
|
+
return translations.value[key] || key;
|
|
214
|
+
}
|
|
215
|
+
function formatDate2(date, options) {
|
|
216
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
217
|
+
const defaultOptions = {
|
|
218
|
+
year: "numeric",
|
|
219
|
+
month: "long",
|
|
220
|
+
day: "numeric"
|
|
221
|
+
};
|
|
222
|
+
return new Intl.DateTimeFormat(
|
|
223
|
+
locale.value,
|
|
224
|
+
options || defaultOptions
|
|
225
|
+
).format(dateObj);
|
|
226
|
+
}
|
|
227
|
+
function formatDateShort2(date) {
|
|
228
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
229
|
+
return new Intl.DateTimeFormat(locale.value, {
|
|
230
|
+
year: "numeric",
|
|
231
|
+
month: "numeric",
|
|
232
|
+
day: "numeric"
|
|
233
|
+
}).format(dateObj);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
locale,
|
|
237
|
+
t: t2,
|
|
238
|
+
formatDate: formatDate2,
|
|
239
|
+
formatDateShort: formatDateShort2,
|
|
240
|
+
translations
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function createI18nContext(locale, config) {
|
|
244
|
+
return {
|
|
245
|
+
locale,
|
|
246
|
+
translations: getUITranslations(locale, config),
|
|
247
|
+
config
|
|
248
|
+
};
|
|
249
|
+
}
|
|
25
250
|
|
|
26
251
|
// src/index.ts
|
|
27
252
|
function defineBlogConfig(config) {
|
|
@@ -41,10 +266,17 @@ function getAstroConfig(options) {
|
|
|
41
266
|
};
|
|
42
267
|
}
|
|
43
268
|
export {
|
|
269
|
+
I18N_CONFIG_KEY,
|
|
270
|
+
I18N_LOCALE_KEY,
|
|
271
|
+
I18N_TRANSLATIONS_KEY,
|
|
44
272
|
integration_default as astroBlog,
|
|
45
273
|
astroBlogIntegration,
|
|
274
|
+
builtInTranslations,
|
|
275
|
+
createI18nContext,
|
|
46
276
|
defaultFooterConfig,
|
|
277
|
+
defaultI18nConfig,
|
|
47
278
|
defaultIcons,
|
|
279
|
+
defaultLocales,
|
|
48
280
|
defaultMenu,
|
|
49
281
|
defaultSEO,
|
|
50
282
|
defaultSidebarConfig,
|
|
@@ -52,14 +284,34 @@ export {
|
|
|
52
284
|
defaultSocialLinks,
|
|
53
285
|
defineBlogConfig,
|
|
54
286
|
defineFooterConfig,
|
|
287
|
+
defineI18nConfig,
|
|
55
288
|
defineMenu,
|
|
56
289
|
defineSidebarConfig,
|
|
57
290
|
defineSiteConfig,
|
|
58
291
|
defineSocialLinks,
|
|
292
|
+
enTranslations,
|
|
293
|
+
filterPostsByLocale,
|
|
59
294
|
footerConfig,
|
|
295
|
+
formatDate,
|
|
296
|
+
formatDateShort,
|
|
297
|
+
getAlternateLinks,
|
|
60
298
|
getAstroConfig,
|
|
299
|
+
getContentPathPrefix,
|
|
300
|
+
getLocaleByCode,
|
|
301
|
+
getLocaleConfig,
|
|
302
|
+
getLocaleFromPath,
|
|
303
|
+
getLocalePrefix,
|
|
304
|
+
getLocalizedPath,
|
|
305
|
+
getTextDirection,
|
|
306
|
+
getUITranslations,
|
|
307
|
+
isMultiLanguageEnabled,
|
|
308
|
+
isRTL,
|
|
61
309
|
menu,
|
|
310
|
+
removeLocalePrefix,
|
|
62
311
|
sidebarConfig,
|
|
63
312
|
siteConfig,
|
|
64
|
-
socialLinks
|
|
313
|
+
socialLinks,
|
|
314
|
+
t,
|
|
315
|
+
useI18n,
|
|
316
|
+
zhCNTranslations
|
|
65
317
|
};
|
package/dist/integration.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { AstroIntegration } from 'astro';
|
|
2
|
+
import { I as I18nConfig } from './i18n-PgMCFBw0.js';
|
|
3
|
+
import './types/index.js';
|
|
4
|
+
import './sidebar-Da-W_4Lr.js';
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* @jet-w/astro-blog Integration
|
|
5
8
|
*
|
|
6
9
|
* This integration injects the blog pages into your Astro project
|
|
10
|
+
* with multi-language support.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
interface AstroBlogIntegrationOptions {
|
|
@@ -19,7 +23,11 @@ interface AstroBlogIntegrationOptions {
|
|
|
19
23
|
search?: boolean;
|
|
20
24
|
rss?: boolean;
|
|
21
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* i18n configuration for multi-language support
|
|
28
|
+
*/
|
|
29
|
+
i18n?: I18nConfig;
|
|
22
30
|
}
|
|
23
31
|
declare function astroBlogIntegration(options?: AstroBlogIntegrationOptions): AstroIntegration;
|
|
24
32
|
|
|
25
|
-
export { type AstroBlogIntegrationOptions, astroBlogIntegration, astroBlogIntegration as default };
|
|
33
|
+
export { type AstroBlogIntegrationOptions, I18nConfig, astroBlogIntegration, astroBlogIntegration as default };
|
package/dist/integration.js
CHANGED
|
@@ -89,4 +89,4 @@ declare const sidebarConfig: SidebarConfig;
|
|
|
89
89
|
declare function defineSidebarConfig(config: Partial<SidebarConfig>): SidebarConfig;
|
|
90
90
|
declare const defaultSidebarConfig: SidebarConfig;
|
|
91
91
|
|
|
92
|
-
export { type DividerConfig as D, type ManualConfig as M, type PathMatchConfig as P, type
|
|
92
|
+
export { type DividerConfig as D, type ManualConfig as M, type PathMatchConfig as P, type SidebarItem as S, type SidebarGroup as a, type SidebarConfig as b, defineSidebarConfig as c, defaultSidebarConfig as d, type ScanConfig as e, type MixedConfig as f, sidebarConfig as s };
|
package/dist/utils/sidebar.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<button
|
|
10
10
|
class="p-3 bg-white dark:bg-slate-800 rounded-full shadow-lg border border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors"
|
|
11
11
|
:class="{ 'bg-primary-50 dark:bg-primary-900/30': showToc }"
|
|
12
|
-
aria-label="
|
|
12
|
+
:aria-label="tocTitle"
|
|
13
13
|
>
|
|
14
14
|
<svg class="w-5 h-5 text-slate-600 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
15
15
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<svg class="w-4 h-4 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
36
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
|
37
37
|
</svg>
|
|
38
|
-
|
|
38
|
+
{{ tocTitle }}
|
|
39
39
|
</h3>
|
|
40
40
|
</div>
|
|
41
41
|
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<!-- 进度条 -->
|
|
67
67
|
<div class="px-4 py-2 border-t border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/50">
|
|
68
68
|
<div class="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400 mb-1">
|
|
69
|
-
<span
|
|
69
|
+
<span>{{ progressTitle }}</span>
|
|
70
70
|
<span>{{ Math.round(progress * 100) }}%</span>
|
|
71
71
|
</div>
|
|
72
72
|
<div class="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-1.5">
|
|
@@ -91,6 +91,14 @@ interface Heading {
|
|
|
91
91
|
level: number
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
const props = withDefaults(defineProps<{
|
|
95
|
+
tocTitle?: string
|
|
96
|
+
progressTitle?: string
|
|
97
|
+
}>(), {
|
|
98
|
+
tocTitle: 'Table of Contents',
|
|
99
|
+
progressTitle: 'Reading Progress',
|
|
100
|
+
})
|
|
101
|
+
|
|
94
102
|
const showToc = ref(false)
|
|
95
103
|
const headings = ref<Heading[]>([])
|
|
96
104
|
const activeId = ref('')
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { siteConfig } from '@jet-w/astro-blog/config';
|
|
3
|
+
import type { I18nConfig } from '../../config/i18n';
|
|
4
|
+
import { defaultI18nConfig } from '../../config/i18n';
|
|
5
|
+
import { i18nConfig as virtualI18nConfig } from 'virtual:astro-blog-i18n';
|
|
6
|
+
import { getLocaleFromPath, getLocaleConfig } from '../../utils/i18n';
|
|
7
|
+
|
|
8
|
+
export interface Props {
|
|
9
|
+
i18nConfig?: I18nConfig;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { i18nConfig = virtualI18nConfig || defaultI18nConfig } = Astro.props;
|
|
13
|
+
|
|
14
|
+
// Get current locale from URL
|
|
15
|
+
const currentLocale = getLocaleFromPath(Astro.url.pathname, i18nConfig);
|
|
16
|
+
const localeConfig = getLocaleConfig(currentLocale, i18nConfig);
|
|
17
|
+
const ui = localeConfig.ui;
|
|
3
18
|
---
|
|
4
19
|
|
|
5
20
|
<section class="py-16 mb-16">
|
|
@@ -89,7 +104,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
|
|
|
89
104
|
href="/posts"
|
|
90
105
|
class="btn-primary inline-flex items-center space-x-2"
|
|
91
106
|
>
|
|
92
|
-
<span
|
|
107
|
+
<span>{ui.browsePosts}</span>
|
|
93
108
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
94
109
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
95
110
|
</svg>
|
|
@@ -99,7 +114,7 @@ import { siteConfig } from '@jet-w/astro-blog/config';
|
|
|
99
114
|
href="/about"
|
|
100
115
|
class="btn-secondary inline-flex items-center space-x-2"
|
|
101
116
|
>
|
|
102
|
-
<span
|
|
117
|
+
<span>{ui.aboutMe}</span>
|
|
103
118
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
104
119
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
105
120
|
</svg>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<a
|
|
27
27
|
v-for="tag in tags"
|
|
28
28
|
:key="tag.name"
|
|
29
|
-
:href="
|
|
29
|
+
:href="`${props.localePrefix}/tags/${encodeTag(tag.name)}`"
|
|
30
30
|
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm transition-colors"
|
|
31
31
|
:class="getTagColorClass(tag.count)"
|
|
32
32
|
>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
<a
|
|
41
41
|
v-for="archive in archives"
|
|
42
42
|
:key="archive.key"
|
|
43
|
-
:href="
|
|
43
|
+
:href="`${props.localePrefix}/archives/${archive.year}/${archive.month}`"
|
|
44
44
|
class="flex items-center justify-between px-3 py-2 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
|
|
45
45
|
>
|
|
46
46
|
<div class="flex items-center gap-3">
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
49
49
|
</svg>
|
|
50
50
|
<span class="text-slate-700 dark:text-slate-300 group-hover:text-primary-600 dark:group-hover:text-primary-400">
|
|
51
|
-
{{ archive.year
|
|
51
|
+
{{ formatArchiveDate(archive.year, archive.month) }}
|
|
52
52
|
</span>
|
|
53
53
|
</div>
|
|
54
54
|
<span class="text-xs text-slate-400 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-full">
|
|
55
|
-
{{ archive.count }}
|
|
55
|
+
{{ archive.count }} {{ props.ui?.postsCount || 'posts' }}
|
|
56
56
|
</span>
|
|
57
57
|
</a>
|
|
58
58
|
</div>
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<a
|
|
63
63
|
v-for="category in categories"
|
|
64
64
|
:key="category.name"
|
|
65
|
-
:href="
|
|
65
|
+
:href="`${props.localePrefix}/categories/${encodeCategory(category.name)}`"
|
|
66
66
|
class="flex items-center justify-between px-3 py-2 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
|
|
67
67
|
>
|
|
68
68
|
<div class="flex items-center gap-3">
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
</span>
|
|
75
75
|
</div>
|
|
76
76
|
<span class="text-xs text-slate-400 bg-slate-100 dark:bg-slate-700 px-2 py-0.5 rounded-full">
|
|
77
|
-
{{ category.count }}
|
|
77
|
+
{{ category.count }} {{ props.ui?.postsCount || 'posts' }}
|
|
78
78
|
</span>
|
|
79
79
|
</a>
|
|
80
80
|
</div>
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
<div v-for="item in timeline" :key="item.slug" class="relative pl-10">
|
|
87
87
|
<div class="absolute left-2.5 w-3 h-3 rounded-full bg-primary-500 border-2 border-white dark:border-slate-800"></div>
|
|
88
88
|
<a
|
|
89
|
-
:href="
|
|
89
|
+
:href="`${props.localePrefix}/posts/${item.slug}`"
|
|
90
90
|
class="block p-3 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors group"
|
|
91
91
|
>
|
|
92
92
|
<div class="text-xs text-slate-400 mb-1">{{ formatDate(item.pubDate) }}</div>
|
|
@@ -97,10 +97,10 @@
|
|
|
97
97
|
</div>
|
|
98
98
|
</div>
|
|
99
99
|
<a
|
|
100
|
-
href="/archives"
|
|
100
|
+
:href="`${props.localePrefix}/archives`"
|
|
101
101
|
class="mt-4 flex items-center justify-center gap-2 py-2 text-sm text-primary-500 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
102
102
|
>
|
|
103
|
-
<span
|
|
103
|
+
<span>{{ props.ui?.viewAllTimeline || 'View all timeline' }}</span>
|
|
104
104
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
105
105
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
106
106
|
</svg>
|
|
@@ -136,14 +136,37 @@ interface TimelineItem {
|
|
|
136
136
|
pubDate: string
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
interface UIProps {
|
|
140
|
+
tags?: string
|
|
141
|
+
archives?: string
|
|
142
|
+
categories?: string
|
|
143
|
+
timeline?: string
|
|
144
|
+
viewAllTimeline?: string
|
|
145
|
+
postsCount?: string
|
|
146
|
+
}
|
|
147
|
+
|
|
139
148
|
interface Props {
|
|
140
149
|
tags: TagItem[]
|
|
141
150
|
archives: ArchiveItem[]
|
|
142
151
|
categories: CategoryItem[]
|
|
143
152
|
timeline: TimelineItem[]
|
|
153
|
+
localePrefix?: string
|
|
154
|
+
dateLocale?: string
|
|
155
|
+
ui?: UIProps
|
|
144
156
|
}
|
|
145
157
|
|
|
146
|
-
const props = defineProps<Props>()
|
|
158
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
159
|
+
localePrefix: '',
|
|
160
|
+
dateLocale: 'en',
|
|
161
|
+
ui: () => ({
|
|
162
|
+
tags: 'Tags',
|
|
163
|
+
archives: 'Archives',
|
|
164
|
+
categories: 'Categories',
|
|
165
|
+
timeline: 'Timeline',
|
|
166
|
+
viewAllTimeline: 'View all timeline',
|
|
167
|
+
postsCount: 'posts',
|
|
168
|
+
})
|
|
169
|
+
})
|
|
147
170
|
|
|
148
171
|
const activeTab = ref('tags')
|
|
149
172
|
|
|
@@ -201,10 +224,10 @@ const TimelineIcon = {
|
|
|
201
224
|
}
|
|
202
225
|
|
|
203
226
|
const tabs = computed(() => [
|
|
204
|
-
{ id: 'tags', name: '
|
|
205
|
-
{ id: 'archives', name: '
|
|
206
|
-
{ id: 'categories', name: '
|
|
207
|
-
{ id: 'timeline', name: '
|
|
227
|
+
{ id: 'tags', name: props.ui?.tags || 'Tags', count: props.tags.length, icon: TagIcon },
|
|
228
|
+
{ id: 'archives', name: props.ui?.archives || 'Archives', count: props.archives.length, icon: ArchiveIcon },
|
|
229
|
+
{ id: 'categories', name: props.ui?.categories || 'Categories', count: props.categories.length, icon: CategoryIcon },
|
|
230
|
+
{ id: 'timeline', name: props.ui?.timeline || 'Timeline', count: props.timeline.length, icon: TimelineIcon },
|
|
208
231
|
])
|
|
209
232
|
|
|
210
233
|
const encodeTag = (tag: string) => {
|
|
@@ -227,12 +250,20 @@ const getTagColorClass = (count: number) => {
|
|
|
227
250
|
|
|
228
251
|
const formatDate = (dateStr: string) => {
|
|
229
252
|
const date = new Date(dateStr)
|
|
230
|
-
return new Intl.DateTimeFormat(
|
|
253
|
+
return new Intl.DateTimeFormat(props.dateLocale, {
|
|
231
254
|
year: 'numeric',
|
|
232
255
|
month: '2-digit',
|
|
233
256
|
day: '2-digit'
|
|
234
257
|
}).format(date)
|
|
235
258
|
}
|
|
259
|
+
|
|
260
|
+
const formatArchiveDate = (year: string, month: string) => {
|
|
261
|
+
const date = new Date(parseInt(year), parseInt(month) - 1, 1)
|
|
262
|
+
return new Intl.DateTimeFormat(props.dateLocale, {
|
|
263
|
+
year: 'numeric',
|
|
264
|
+
month: 'long'
|
|
265
|
+
}).format(date)
|
|
266
|
+
}
|
|
236
267
|
</script>
|
|
237
268
|
|
|
238
269
|
<style scoped>
|
|
@@ -13,12 +13,25 @@ export interface Props {
|
|
|
13
13
|
};
|
|
14
14
|
featured?: boolean;
|
|
15
15
|
layout?: 'vertical' | 'horizontal';
|
|
16
|
+
localePrefix?: string;
|
|
17
|
+
locale?: string;
|
|
18
|
+
ui?: {
|
|
19
|
+
readMore?: string;
|
|
20
|
+
minuteRead?: string;
|
|
21
|
+
};
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
post,
|
|
26
|
+
featured = false,
|
|
27
|
+
layout = 'vertical',
|
|
28
|
+
localePrefix = '',
|
|
29
|
+
locale = 'zh-CN',
|
|
30
|
+
ui = { readMore: '阅读更多', minuteRead: '分钟' }
|
|
31
|
+
} = Astro.props;
|
|
19
32
|
|
|
20
33
|
const formattedDate = post.pubDate
|
|
21
|
-
? new Intl.DateTimeFormat(
|
|
34
|
+
? new Intl.DateTimeFormat(locale, {
|
|
22
35
|
year: 'numeric',
|
|
23
36
|
month: 'long',
|
|
24
37
|
day: 'numeric'
|
|
@@ -31,12 +44,17 @@ const isHorizontal = layout === 'horizontal';
|
|
|
31
44
|
const tagToSlug = (tag: string) => tag.toLowerCase().replace(/\s+/g, '-');
|
|
32
45
|
// 将分类名转换为 slug 格式
|
|
33
46
|
const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+/g, '-');
|
|
47
|
+
|
|
48
|
+
// Build URLs with locale prefix
|
|
49
|
+
const postUrl = `${localePrefix}/posts/${post.slug}`;
|
|
50
|
+
const tagUrl = (tag: string) => `${localePrefix}/tags/${tagToSlug(tag)}`;
|
|
51
|
+
const categoryUrl = (category: string) => `${localePrefix}/categories/${categoryToSlug(category)}`;
|
|
34
52
|
---
|
|
35
53
|
|
|
36
54
|
<article class={`group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300 ${isHorizontal ? 'flex gap-6 items-center' : 'block'}`}>
|
|
37
55
|
<!-- 文章图片 -->
|
|
38
56
|
{post.image && (
|
|
39
|
-
<a href={
|
|
57
|
+
<a href={postUrl} class={`block overflow-hidden rounded-lg ${
|
|
40
58
|
isHorizontal ? 'w-48 h-32 flex-shrink-0' : 'w-full h-48 mb-4'
|
|
41
59
|
}`}>
|
|
42
60
|
<img
|
|
@@ -55,7 +73,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
|
|
|
55
73
|
<div class="flex flex-wrap gap-2 mb-2">
|
|
56
74
|
{post.categories.slice(0, 2).map((category) => (
|
|
57
75
|
<a
|
|
58
|
-
href={
|
|
76
|
+
href={categoryUrl(category)}
|
|
59
77
|
class="text-xs px-2 py-1 bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 rounded-full hover:bg-amber-200 dark:hover:bg-amber-900/50 transition-colors"
|
|
60
78
|
onclick="event.stopPropagation();"
|
|
61
79
|
>
|
|
@@ -70,7 +88,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
|
|
|
70
88
|
<div class="flex flex-wrap gap-2 mb-3">
|
|
71
89
|
{post.tags.slice(0, 3).map((tag) => (
|
|
72
90
|
<a
|
|
73
|
-
href={
|
|
91
|
+
href={tagUrl(tag)}
|
|
74
92
|
class="text-xs px-2 py-1 bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 rounded-full hover:bg-primary-200 dark:hover:bg-primary-900/50 transition-colors"
|
|
75
93
|
onclick="event.stopPropagation();"
|
|
76
94
|
>
|
|
@@ -86,7 +104,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
|
|
|
86
104
|
)}
|
|
87
105
|
|
|
88
106
|
<!-- 标题 -->
|
|
89
|
-
<a href={
|
|
107
|
+
<a href={postUrl}>
|
|
90
108
|
<h3 class={`font-bold text-slate-900 dark:text-slate-100 hover:text-primary-500 transition-colors line-clamp-2 mb-3 ${
|
|
91
109
|
featured ? 'text-xl' : isHorizontal ? 'text-lg' : 'text-lg'
|
|
92
110
|
}`}>
|
|
@@ -95,7 +113,7 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
|
|
|
95
113
|
</a>
|
|
96
114
|
|
|
97
115
|
<!-- 描述 -->
|
|
98
|
-
<a href={
|
|
116
|
+
<a href={postUrl}>
|
|
99
117
|
<p class={`text-slate-600 dark:text-slate-400 line-clamp-3 mb-4 hover:text-slate-700 dark:hover:text-slate-300 transition-colors ${
|
|
100
118
|
isHorizontal ? 'text-sm' : 'text-base'
|
|
101
119
|
}`}>
|
|
@@ -129,13 +147,13 @@ const categoryToSlug = (category: string) => category.toLowerCase().replace(/\s+
|
|
|
129
147
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
130
148
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
131
149
|
</svg>
|
|
132
|
-
<span>{post.readingTime}
|
|
150
|
+
<span>{post.readingTime} {ui.minuteRead}</span>
|
|
133
151
|
</span>
|
|
134
152
|
)}
|
|
135
153
|
</div>
|
|
136
154
|
|
|
137
|
-
<a href={
|
|
138
|
-
<span class="text-sm font-medium"
|
|
155
|
+
<a href={postUrl} class="flex items-center text-primary-500 hover:text-primary-600 transition-colors">
|
|
156
|
+
<span class="text-sm font-medium">{ui.readMore}</span>
|
|
139
157
|
<svg class="w-4 h-4 ml-1 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
140
158
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
141
159
|
</svg>
|