@jet-w/astro-blog 0.2.0 → 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-HVQKQN6B.js → chunk-6D3XRDNY.js} +1 -1
- package/dist/{chunk-ATRISB7B.js → chunk-A2E2VSAQ.js} +43 -3
- package/dist/{chunk-AZHCNNAC.js → chunk-TJTPX2WP.js} +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +2 -2
- package/dist/{i18n-5H4W145i.d.ts → i18n-PgMCFBw0.d.ts} +20 -0
- package/dist/index.d.ts +21 -3
- package/dist/index.js +20 -3
- package/dist/integration.d.ts +1 -1
- package/dist/integration.js +2 -2
- 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/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/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
|
@@ -35,6 +35,20 @@ var zhCNTranslations = {
|
|
|
35
35
|
searchResults: "\u641C\u7D22\u7ED3\u679C",
|
|
36
36
|
noResults: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u7ED3\u679C",
|
|
37
37
|
searching: "\u641C\u7D22\u4E2D...",
|
|
38
|
+
searchArticles: "\u641C\u7D22\u6587\u7AE0",
|
|
39
|
+
searchInAllArticles: "\u5728\u6240\u6709\u6587\u7AE0\u4E2D\u67E5\u627E\u60A8\u611F\u5174\u8DA3\u7684\u5185\u5BB9",
|
|
40
|
+
searchTips: "\u641C\u7D22\u6280\u5DE7",
|
|
41
|
+
basicSearch: "\u57FA\u7840\u641C\u7D22",
|
|
42
|
+
advancedFeatures: "\u9AD8\u7EA7\u529F\u80FD",
|
|
43
|
+
searchTipKeyword: "\u8F93\u5165\u5173\u952E\u8BCD\u641C\u7D22\u6807\u9898\u548C\u5185\u5BB9",
|
|
44
|
+
searchTipMixedLang: "\u652F\u6301\u4E2D\u82F1\u6587\u6DF7\u5408\u641C\u7D22",
|
|
45
|
+
searchTipCaseInsensitive: "\u81EA\u52A8\u5FFD\u7565\u5927\u5C0F\u5199",
|
|
46
|
+
searchTipRealtime: "\u5B9E\u65F6\u641C\u7D22\u5EFA\u8BAE",
|
|
47
|
+
searchTipFilter: "\u6309\u6807\u7B7E\u548C\u5206\u7C7B\u7B5B\u9009",
|
|
48
|
+
searchTipFuzzy: "\u652F\u6301\u6A21\u7CCA\u5339\u914D",
|
|
49
|
+
// Hero section
|
|
50
|
+
browsePosts: "\u6D4F\u89C8\u6587\u7AE0",
|
|
51
|
+
aboutMe: "\u5173\u4E8E\u6211",
|
|
38
52
|
// Pagination
|
|
39
53
|
previousPage: "\u4E0A\u4E00\u9875",
|
|
40
54
|
nextPage: "\u4E0B\u4E00\u9875",
|
|
@@ -44,7 +58,8 @@ var zhCNTranslations = {
|
|
|
44
58
|
publishedOn: "\u53D1\u5E03\u4E8E",
|
|
45
59
|
updatedOn: "\u66F4\u65B0\u4E8E",
|
|
46
60
|
author: "\u4F5C\u8005",
|
|
47
|
-
tableOfContents: "\u76EE\u5F55",
|
|
61
|
+
tableOfContents: "\u9875\u9762\u76EE\u5F55",
|
|
62
|
+
readingProgress: "\u9605\u8BFB\u8FDB\u5EA6",
|
|
48
63
|
relatedPosts: "\u76F8\u5173\u6587\u7AE0",
|
|
49
64
|
sharePost: "\u5206\u4EAB\u6587\u7AE0",
|
|
50
65
|
previousPost: "\u4E0A\u4E00\u7BC7",
|
|
@@ -73,7 +88,12 @@ var zhCNTranslations = {
|
|
|
73
88
|
slides: "\u6F14\u793A",
|
|
74
89
|
slidesList: "\u6F14\u793A\u5217\u8868",
|
|
75
90
|
// RSS
|
|
76
|
-
rssFeed: "RSS \u8BA2\u9605"
|
|
91
|
+
rssFeed: "RSS \u8BA2\u9605",
|
|
92
|
+
// Quick Navigation
|
|
93
|
+
quickNavigation: "\u5FEB\u901F\u5BFC\u822A",
|
|
94
|
+
timeline: "\u65F6\u95F4\u8F74",
|
|
95
|
+
viewAllTimeline: "\u67E5\u770B\u5168\u90E8\u65F6\u95F4\u8F74",
|
|
96
|
+
postsCount: "\u7BC7"
|
|
77
97
|
};
|
|
78
98
|
var enTranslations = {
|
|
79
99
|
// Navigation
|
|
@@ -111,6 +131,20 @@ var enTranslations = {
|
|
|
111
131
|
searchResults: "Search Results",
|
|
112
132
|
noResults: "No results found",
|
|
113
133
|
searching: "Searching...",
|
|
134
|
+
searchArticles: "Search Articles",
|
|
135
|
+
searchInAllArticles: "Find content that interests you in all articles",
|
|
136
|
+
searchTips: "Search Tips",
|
|
137
|
+
basicSearch: "Basic Search",
|
|
138
|
+
advancedFeatures: "Advanced Features",
|
|
139
|
+
searchTipKeyword: "Enter keywords to search titles and content",
|
|
140
|
+
searchTipMixedLang: "Supports mixed language search",
|
|
141
|
+
searchTipCaseInsensitive: "Case insensitive",
|
|
142
|
+
searchTipRealtime: "Real-time search suggestions",
|
|
143
|
+
searchTipFilter: "Filter by tags and categories",
|
|
144
|
+
searchTipFuzzy: "Supports fuzzy matching",
|
|
145
|
+
// Hero section
|
|
146
|
+
browsePosts: "Browse Posts",
|
|
147
|
+
aboutMe: "About Me",
|
|
114
148
|
// Pagination
|
|
115
149
|
previousPage: "Previous",
|
|
116
150
|
nextPage: "Next",
|
|
@@ -121,6 +155,7 @@ var enTranslations = {
|
|
|
121
155
|
updatedOn: "Updated on",
|
|
122
156
|
author: "Author",
|
|
123
157
|
tableOfContents: "Table of Contents",
|
|
158
|
+
readingProgress: "Reading Progress",
|
|
124
159
|
relatedPosts: "Related Posts",
|
|
125
160
|
sharePost: "Share",
|
|
126
161
|
previousPost: "Previous",
|
|
@@ -149,7 +184,12 @@ var enTranslations = {
|
|
|
149
184
|
slides: "Slides",
|
|
150
185
|
slidesList: "All Slides",
|
|
151
186
|
// RSS
|
|
152
|
-
rssFeed: "RSS Feed"
|
|
187
|
+
rssFeed: "RSS Feed",
|
|
188
|
+
// Quick Navigation
|
|
189
|
+
quickNavigation: "Quick Navigation",
|
|
190
|
+
timeline: "Timeline",
|
|
191
|
+
viewAllTimeline: "View all timeline",
|
|
192
|
+
postsCount: "posts"
|
|
153
193
|
};
|
|
154
194
|
var builtInTranslations = {
|
|
155
195
|
"zh-CN": zhCNTranslations,
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SiteConfig, NavigationItem } from '../types/index.js';
|
|
2
2
|
export { D as DividerConfig, M as ManualConfig, f as MixedConfig, P as PathMatchConfig, e as ScanConfig, b as SidebarConfig, a as SidebarGroup, S as SidebarItem, d as defaultSidebarConfig, c as defineSidebarConfig, s as sidebarConfig } from '../sidebar-Da-W_4Lr.js';
|
|
3
|
-
export { F as FooterConfig, m as FooterLink, I as I18nConfig, o as I18nRoutingConfig, L as Locale, n as LocaleConfig, S as SocialLink, U as UITranslations, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from '../i18n-
|
|
3
|
+
export { F as FooterConfig, m as FooterLink, I as I18nConfig, o as I18nRoutingConfig, L as Locale, n as LocaleConfig, S as SocialLink, U as UITranslations, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from '../i18n-PgMCFBw0.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Default site configuration
|
package/dist/config/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
sidebarConfig,
|
|
17
17
|
siteConfig,
|
|
18
18
|
socialLinks
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-TJTPX2WP.js";
|
|
20
20
|
import {
|
|
21
21
|
builtInTranslations,
|
|
22
22
|
defaultI18nConfig,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
enTranslations,
|
|
26
26
|
getUITranslations,
|
|
27
27
|
zhCNTranslations
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-A2E2VSAQ.js";
|
|
29
29
|
export {
|
|
30
30
|
builtInTranslations,
|
|
31
31
|
defaultFooterConfig,
|
|
@@ -99,6 +99,19 @@ interface UITranslations {
|
|
|
99
99
|
searchResults: string;
|
|
100
100
|
noResults: string;
|
|
101
101
|
searching: string;
|
|
102
|
+
searchArticles: string;
|
|
103
|
+
searchInAllArticles: string;
|
|
104
|
+
searchTips: string;
|
|
105
|
+
basicSearch: string;
|
|
106
|
+
advancedFeatures: string;
|
|
107
|
+
searchTipKeyword: string;
|
|
108
|
+
searchTipMixedLang: string;
|
|
109
|
+
searchTipCaseInsensitive: string;
|
|
110
|
+
searchTipRealtime: string;
|
|
111
|
+
searchTipFilter: string;
|
|
112
|
+
searchTipFuzzy: string;
|
|
113
|
+
browsePosts: string;
|
|
114
|
+
aboutMe: string;
|
|
102
115
|
previousPage: string;
|
|
103
116
|
nextPage: string;
|
|
104
117
|
page: string;
|
|
@@ -107,6 +120,7 @@ interface UITranslations {
|
|
|
107
120
|
updatedOn: string;
|
|
108
121
|
author: string;
|
|
109
122
|
tableOfContents: string;
|
|
123
|
+
readingProgress: string;
|
|
110
124
|
relatedPosts: string;
|
|
111
125
|
sharePost: string;
|
|
112
126
|
previousPost: string;
|
|
@@ -133,6 +147,10 @@ interface UITranslations {
|
|
|
133
147
|
slides: string;
|
|
134
148
|
slidesList: string;
|
|
135
149
|
rssFeed: string;
|
|
150
|
+
quickNavigation: string;
|
|
151
|
+
timeline: string;
|
|
152
|
+
viewAllTimeline: string;
|
|
153
|
+
postsCount: string;
|
|
136
154
|
}
|
|
137
155
|
/**
|
|
138
156
|
* Locale-specific configuration
|
|
@@ -148,6 +166,8 @@ interface LocaleConfig {
|
|
|
148
166
|
sidebar?: Partial<SidebarConfig>;
|
|
149
167
|
/** UI translation overrides */
|
|
150
168
|
ui?: Partial<UITranslations>;
|
|
169
|
+
/** Content path prefix for filtering posts by locale (e.g., 'blog_docs_en' for English) */
|
|
170
|
+
contentPathPrefix?: string;
|
|
151
171
|
}
|
|
152
172
|
/**
|
|
153
173
|
* i18n routing configuration
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { defaultMenu, defaultSEO, defaultSiteConfig, defineMenu, defineSiteConfig, menu, siteConfig } from './config/index.js';
|
|
2
2
|
import { b as SidebarConfig } from './sidebar-Da-W_4Lr.js';
|
|
3
3
|
export { D as DividerConfig, M as ManualConfig, f as MixedConfig, P as PathMatchConfig, e as ScanConfig, a as SidebarGroup, S as SidebarItem, d as defaultSidebarConfig, c as defineSidebarConfig, s as sidebarConfig } from './sidebar-Da-W_4Lr.js';
|
|
4
|
-
import { I as I18nConfig, L as Locale, F as FooterConfig, U as UITranslations, S as SocialLink } from './i18n-
|
|
5
|
-
export { m as FooterLink, o as I18nRoutingConfig, n as LocaleConfig, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from './i18n-
|
|
4
|
+
import { I as I18nConfig, L as Locale, F as FooterConfig, U as UITranslations, S as SocialLink } from './i18n-PgMCFBw0.js';
|
|
5
|
+
export { m as FooterLink, o as I18nRoutingConfig, n as LocaleConfig, j as builtInTranslations, c as defaultFooterConfig, g as defaultI18nConfig, d as defaultIcons, l as defaultLocales, a as defaultSocialLinks, e as defineFooterConfig, h as defineI18nConfig, b as defineSocialLinks, k as enTranslations, f as footerConfig, i as getUITranslations, s as socialLinks, z as zhCNTranslations } from './i18n-PgMCFBw0.js';
|
|
6
6
|
import { SiteConfig, NavigationItem } from './types/index.js';
|
|
7
7
|
export { BlogPost, Category, PostFrontmatter, SEOProps, SearchResult, Tag } from './types/index.js';
|
|
8
8
|
export { AstroBlogIntegrationOptions, default as astroBlog, default as astroBlogIntegration } from './integration.js';
|
|
@@ -117,6 +117,24 @@ declare function isMultiLanguageEnabled(config?: I18nConfig): boolean;
|
|
|
117
117
|
* Returns empty string for default locale if prefixDefaultLocale is false
|
|
118
118
|
*/
|
|
119
119
|
declare function getLocalePrefix(locale: string, config?: I18nConfig): string;
|
|
120
|
+
/**
|
|
121
|
+
* Get content path prefix for a specific locale
|
|
122
|
+
* Returns the contentPathPrefix from locale config, or undefined if not set
|
|
123
|
+
*/
|
|
124
|
+
declare function getContentPathPrefix(locale: string, config?: I18nConfig): string | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Filter posts by locale based on contentPathPrefix
|
|
127
|
+
* If contentPathPrefix is set, only return posts that start with that prefix
|
|
128
|
+
* If not set, return all posts (backward compatible)
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* // If en locale has contentPathPrefix: 'blog_docs_en'
|
|
132
|
+
* filterPostsByLocale(posts, 'en', config)
|
|
133
|
+
* // Returns only posts with id starting with 'blog_docs_en/'
|
|
134
|
+
*/
|
|
135
|
+
declare function filterPostsByLocale<T extends {
|
|
136
|
+
id: string;
|
|
137
|
+
}>(posts: T[], locale: string, config?: I18nConfig): T[];
|
|
120
138
|
|
|
121
139
|
/**
|
|
122
140
|
* Vue Composable for i18n
|
|
@@ -214,4 +232,4 @@ declare function getAstroConfig(options?: {
|
|
|
214
232
|
};
|
|
215
233
|
};
|
|
216
234
|
|
|
217
|
-
export { type AlternateLink, type BlogConfig, FooterConfig, I18N_CONFIG_KEY, I18N_LOCALE_KEY, I18N_TRANSLATIONS_KEY, I18nConfig, type I18nContext, Locale, type MergedLocaleConfig, NavigationItem, SidebarConfig, SiteConfig, SocialLink, UITranslations, type UseI18nReturn, createI18nContext, defineBlogConfig, formatDate, formatDateShort, getAlternateLinks, getAstroConfig, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeLocalePrefix, t, useI18n };
|
|
235
|
+
export { type AlternateLink, type BlogConfig, FooterConfig, I18N_CONFIG_KEY, I18N_LOCALE_KEY, I18N_TRANSLATIONS_KEY, I18nConfig, type I18nContext, Locale, type MergedLocaleConfig, NavigationItem, SidebarConfig, SiteConfig, SocialLink, UITranslations, type UseI18nReturn, createI18nContext, defineBlogConfig, filterPostsByLocale, formatDate, formatDateShort, getAlternateLinks, getAstroConfig, getContentPathPrefix, getLocaleByCode, getLocaleConfig, getLocaleFromPath, getLocalePrefix, getLocalizedPath, getTextDirection, isMultiLanguageEnabled, isRTL, removeLocalePrefix, t, useI18n };
|
package/dist/index.js
CHANGED
|
@@ -16,12 +16,12 @@ 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
25
|
import {
|
|
26
26
|
builtInTranslations,
|
|
27
27
|
defaultI18nConfig,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
enTranslations,
|
|
31
31
|
getUITranslations,
|
|
32
32
|
zhCNTranslations
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-A2E2VSAQ.js";
|
|
34
34
|
|
|
35
35
|
// src/utils/i18n.ts
|
|
36
36
|
function getLocaleFromPath(pathname, config = defaultI18nConfig) {
|
|
@@ -174,6 +174,21 @@ function getLocalePrefix(locale, config = defaultI18nConfig) {
|
|
|
174
174
|
}
|
|
175
175
|
return `/${locale}`;
|
|
176
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
|
+
}
|
|
177
192
|
|
|
178
193
|
// src/utils/useI18n.ts
|
|
179
194
|
import { inject, computed } from "vue";
|
|
@@ -275,11 +290,13 @@ export {
|
|
|
275
290
|
defineSiteConfig,
|
|
276
291
|
defineSocialLinks,
|
|
277
292
|
enTranslations,
|
|
293
|
+
filterPostsByLocale,
|
|
278
294
|
footerConfig,
|
|
279
295
|
formatDate,
|
|
280
296
|
formatDateShort,
|
|
281
297
|
getAlternateLinks,
|
|
282
298
|
getAstroConfig,
|
|
299
|
+
getContentPathPrefix,
|
|
283
300
|
getLocaleByCode,
|
|
284
301
|
getLocaleConfig,
|
|
285
302
|
getLocaleFromPath,
|
package/dist/integration.d.ts
CHANGED
package/dist/integration.js
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>
|