@jet-w/astro-blog 0.1.0
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-FXPGR372.js +0 -0
- package/dist/chunk-GYLSY3OJ.js +173 -0
- package/dist/config/index.d.ts +166 -0
- package/dist/config/index.js +38 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +59 -0
- package/dist/types/index.d.ts +75 -0
- package/dist/types/index.js +1 -0
- package/package.json +84 -0
- package/src/components/EChartsCard.vue +118 -0
- package/src/components/Mermaid.vue +73 -0
- package/src/components/about/ContentCard.astro +27 -0
- package/src/components/about/IconCard.astro +77 -0
- package/src/components/about/SocialLinks.astro +54 -0
- package/src/components/about/TagCard.astro +65 -0
- package/src/components/about/TagGroup.astro +33 -0
- package/src/components/about/TimelineCard.astro +52 -0
- package/src/components/blog/FloatingToc.vue +198 -0
- package/src/components/blog/Hero.astro +147 -0
- package/src/components/blog/NavigationTabs.vue +245 -0
- package/src/components/blog/PostCard.astro +161 -0
- package/src/components/blog/PostNavigation.astro +106 -0
- package/src/components/blog/RelatedPosts.astro +175 -0
- package/src/components/blog/TableOfContents.astro +153 -0
- package/src/components/blog/TagCloud.astro +91 -0
- package/src/components/home/FeaturedPostsSection.astro +54 -0
- package/src/components/home/QuickNavSection.astro +81 -0
- package/src/components/home/RecentPostsSection.astro +52 -0
- package/src/components/home/StatsSection.astro +44 -0
- package/src/components/layout/Footer.astro +103 -0
- package/src/components/layout/Header.astro +68 -0
- package/src/components/layout/Sidebar.astro +594 -0
- package/src/components/media/Bilibili.astro +114 -0
- package/src/components/media/Slides.astro +313 -0
- package/src/components/media/Video.astro +111 -0
- package/src/components/media/VideoPlayer.astro +89 -0
- package/src/components/media/YouTube.astro +92 -0
- package/src/components/pte/StudyCalendar.vue +1348 -0
- package/src/components/ui/Icon.astro +187 -0
- package/src/components/ui/MobileMenu.vue +201 -0
- package/src/components/ui/Pagination.astro +143 -0
- package/src/components/ui/SearchBox.vue +179 -0
- package/src/components/ui/SearchInterface.vue +409 -0
- package/src/components/ui/SidebarToggle.vue +57 -0
- package/src/components/ui/ThemeToggle.vue +90 -0
- package/src/layouts/AboutLayout.astro +18 -0
- package/src/layouts/BaseLayout.astro +362 -0
- package/src/layouts/PageLayout.astro +217 -0
- package/src/layouts/SlidesLayout.astro +320 -0
- package/src/plugins/rehype-clean-containers.mjs +24 -0
- package/src/plugins/rehype-relative-links.mjs +43 -0
- package/src/plugins/rehype-tabs.mjs +116 -0
- package/src/plugins/remark-containers.mjs +407 -0
- package/src/plugins/remark-mermaid.mjs +46 -0
- package/src/styles/global.css +870 -0
- package/src/styles/slides.css +220 -0
- package/src/utils/sidebar.ts +492 -0
- package/templates/default/astro.config.mjs +51 -0
- package/templates/default/content/pages/about.mdx +93 -0
- package/templates/default/content/pages/index.mdx +20 -0
- package/templates/default/content/posts/blog_docs/01-quick-start.md +162 -0
- package/templates/default/content/posts/blog_docs/02-frontmatter.md +277 -0
- package/templates/default/content/posts/blog_docs/03-markdown-basic.md +350 -0
- package/templates/default/content/posts/blog_docs/04-containers.md +331 -0
- package/templates/default/content/posts/blog_docs/05-code-blocks.md +388 -0
- package/templates/default/content/posts/blog_docs/06-mermaid.md +431 -0
- package/templates/default/content/posts/blog_docs/07-video.md +243 -0
- package/templates/default/content/posts/blog_docs/08-latex.md +382 -0
- package/templates/default/content/posts/blog_docs/09-icons.md +326 -0
- package/templates/default/content/posts/blog_docs/10-sidebar.md +445 -0
- package/templates/default/content/posts/blog_docs/11-config.md +334 -0
- package/templates/default/content/posts/blog_docs/12-slides.mdx +552 -0
- package/templates/default/content/posts/blog_docs/README.md +151 -0
- package/templates/default/content/slides/demo.md +146 -0
- package/templates/default/content/slides/docs/basic-demo.md +35 -0
- package/templates/default/content/slides/docs/code-demo.md +62 -0
- package/templates/default/content/slides/docs/echarts-demo.md +139 -0
- package/templates/default/content/slides/docs/fragment-demo.md +35 -0
- package/templates/default/content/slides/docs/math-demo.md +48 -0
- package/templates/default/content/slides/docs/mermaid-demo.md +105 -0
- package/templates/default/content/slides/docs/theme-demo.md +38 -0
- package/templates/default/content/slides/docs/vertical-demo.md +50 -0
- package/templates/default/package.json +31 -0
- package/templates/default/public/favicon-bak.svg +4 -0
- package/templates/default/public/images/avatar.jpg +0 -0
- package/templates/default/public/images/avatar.svg +142 -0
- package/templates/default/public/js/mermaid-container.js +402 -0
- package/templates/default/public/js/mermaid-init.js +131 -0
- package/templates/default/public/js/mermaid-render.js +98 -0
- package/templates/default/public/js/mermaid-simple.js +95 -0
- package/templates/default/public/js/tabs-init.js +86 -0
- package/templates/default/public/media/individual_portfolio/INDIVIDUAL PORTFOLIO.png +0 -0
- package/templates/default/public/slides/plugin/highlight/highlight.js +5 -0
- package/templates/default/public/slides/plugin/highlight/monokai.css +71 -0
- package/templates/default/public/slides/plugin/markdown/markdown.js +7 -0
- package/templates/default/public/slides/plugin/math/math.js +1 -0
- package/templates/default/public/slides/plugin/notes/notes.js +1 -0
- package/templates/default/public/slides/reveal.css +9 -0
- package/templates/default/public/slides/reveal.js +9 -0
- package/templates/default/public/slides/theme/beige.css +366 -0
- package/templates/default/public/slides/theme/black-contrast.css +362 -0
- package/templates/default/public/slides/theme/black.css +359 -0
- package/templates/default/public/slides/theme/blood.css +392 -0
- package/templates/default/public/slides/theme/dracula.css +385 -0
- package/templates/default/public/slides/theme/league.css +368 -0
- package/templates/default/public/slides/theme/moon.css +362 -0
- package/templates/default/public/slides/theme/night.css +360 -0
- package/templates/default/public/slides/theme/serif.css +363 -0
- package/templates/default/public/slides/theme/simple.css +362 -0
- package/templates/default/public/slides/theme/sky.css +370 -0
- package/templates/default/public/slides/theme/solarized.css +363 -0
- package/templates/default/public/slides/theme/white-contrast.css +362 -0
- package/templates/default/public/slides/theme/white.css +359 -0
- package/templates/default/public/slides/theme/white_contrast_compact_verbatim_headers.css +360 -0
- package/templates/default/public/test-complete.html +43 -0
- package/templates/default/public/test-mermaid.html +124 -0
- package/templates/default/src/config/index.ts +114 -0
- package/templates/default/src/content.config.ts +96 -0
- package/templates/default/src/pages/[...slug].astro +27 -0
- package/templates/default/src/pages/archives/[year]/[month]/page/[page].astro +176 -0
- package/templates/default/src/pages/archives/[year]/[month].astro +158 -0
- package/templates/default/src/pages/archives/index.astro +210 -0
- package/templates/default/src/pages/categories/[category]/page/[page].astro +218 -0
- package/templates/default/src/pages/categories/[category].astro +198 -0
- package/templates/default/src/pages/categories/index.astro +190 -0
- package/templates/default/src/pages/container-test.astro +79 -0
- package/templates/default/src/pages/mermaid-direct.html +78 -0
- package/templates/default/src/pages/posts/[...slug].astro +335 -0
- package/templates/default/src/pages/posts/index.astro +541 -0
- package/templates/default/src/pages/posts/page/[page].astro +146 -0
- package/templates/default/src/pages/rss.xml.ts +28 -0
- package/templates/default/src/pages/search-index.json.ts +21 -0
- package/templates/default/src/pages/search.astro +50 -0
- package/templates/default/src/pages/slides/[...slug].astro +54 -0
- package/templates/default/src/pages/slides/index.astro +135 -0
- package/templates/default/src/pages/tags/[tag]/page/[page].astro +211 -0
- package/templates/default/src/pages/tags/[tag].astro +191 -0
- package/templates/default/src/pages/tags/index.astro +167 -0
- package/templates/default/tailwind.config.mjs +78 -0
- package/templates/default/tsconfig.json +9 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from 'astro:content';
|
|
3
|
+
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
|
+
import PostCard from '@jet-w/astro-blog/components/blog/PostCard.astro';
|
|
5
|
+
import Pagination from '@jet-w/astro-blog/components/ui/Pagination.astro';
|
|
6
|
+
|
|
7
|
+
export async function getStaticPaths() {
|
|
8
|
+
const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
9
|
+
|
|
10
|
+
// 从所有文章中收集分类
|
|
11
|
+
const categoryMap = new Map<string, { name: string; count: number }>();
|
|
12
|
+
|
|
13
|
+
allPosts.forEach(post => {
|
|
14
|
+
(post.data.categories || []).forEach(category => {
|
|
15
|
+
const slug = category.toLowerCase().replace(/\s+/g, '-');
|
|
16
|
+
if (categoryMap.has(slug)) {
|
|
17
|
+
categoryMap.get(slug)!.count++;
|
|
18
|
+
} else {
|
|
19
|
+
categoryMap.set(slug, { name: category, count: 1 });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 为每个分类生成路径
|
|
25
|
+
return Array.from(categoryMap.entries()).map(([slug, { name, count }]) => ({
|
|
26
|
+
params: { category: slug },
|
|
27
|
+
props: { categorySlug: slug, categoryName: name, categoryCount: count }
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { categorySlug, categoryName } = Astro.props;
|
|
32
|
+
|
|
33
|
+
// 获取所有文章并筛选包含该分类的文章
|
|
34
|
+
const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
35
|
+
|
|
36
|
+
// 筛选包含该分类的文章
|
|
37
|
+
const filteredPosts = allPosts
|
|
38
|
+
.filter(post =>
|
|
39
|
+
(post.data.categories || []).some(c => c.toLowerCase().replace(/\s+/g, '-') === categorySlug)
|
|
40
|
+
)
|
|
41
|
+
.sort((a, b) => {
|
|
42
|
+
const dateA = a.data.pubDate ? new Date(a.data.pubDate).getTime() : 0;
|
|
43
|
+
const dateB = b.data.pubDate ? new Date(b.data.pubDate).getTime() : 0;
|
|
44
|
+
return dateB - dateA;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 分页逻辑
|
|
48
|
+
const currentPage = 1;
|
|
49
|
+
const postsPerPage = 10;
|
|
50
|
+
const totalPosts = filteredPosts.length;
|
|
51
|
+
const totalPages = Math.ceil(totalPosts / postsPerPage);
|
|
52
|
+
const startIndex = (currentPage - 1) * postsPerPage;
|
|
53
|
+
const endIndex = startIndex + postsPerPage;
|
|
54
|
+
const posts = filteredPosts.slice(startIndex, endIndex);
|
|
55
|
+
|
|
56
|
+
// 获取相关分类(同一篇文章中出现的其他分类)
|
|
57
|
+
const relatedCategoryMap = new Map<string, { name: string; count: number }>();
|
|
58
|
+
filteredPosts.forEach(post => {
|
|
59
|
+
(post.data.categories || []).forEach(cat => {
|
|
60
|
+
const slug = cat.toLowerCase().replace(/\s+/g, '-');
|
|
61
|
+
if (slug !== categorySlug) {
|
|
62
|
+
if (relatedCategoryMap.has(slug)) {
|
|
63
|
+
relatedCategoryMap.get(slug)!.count++;
|
|
64
|
+
} else {
|
|
65
|
+
relatedCategoryMap.set(slug, { name: cat, count: 1 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const relatedCategories = Array.from(relatedCategoryMap.entries())
|
|
72
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
73
|
+
.slice(0, 3)
|
|
74
|
+
.map(([slug, { name, count }]) => ({ slug, name, count }));
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
<PageLayout
|
|
78
|
+
title={`分类: ${categoryName}`}
|
|
79
|
+
description={`浏览所有 ${categoryName} 相关的文章`}
|
|
80
|
+
showSidebar={true}
|
|
81
|
+
>
|
|
82
|
+
<!-- 面包屑导航 -->
|
|
83
|
+
<nav class="flex items-center space-x-2 text-sm text-slate-600 dark:text-slate-400 mb-8">
|
|
84
|
+
<a href="/" class="hover:text-primary-500 transition-colors">首页</a>
|
|
85
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
86
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
87
|
+
</svg>
|
|
88
|
+
<a href="/categories" class="hover:text-primary-500 transition-colors">分类</a>
|
|
89
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
91
|
+
</svg>
|
|
92
|
+
<span class="text-slate-900 dark:text-slate-100">{categoryName}</span>
|
|
93
|
+
</nav>
|
|
94
|
+
|
|
95
|
+
<!-- 页面头部 -->
|
|
96
|
+
<div class="text-center mb-12">
|
|
97
|
+
<div class="inline-flex items-center justify-center w-20 h-20 bg-amber-100 dark:bg-amber-900/30 rounded-full mb-6">
|
|
98
|
+
<svg class="w-10 h-10 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
99
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
100
|
+
</svg>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
104
|
+
{categoryName}
|
|
105
|
+
</h1>
|
|
106
|
+
|
|
107
|
+
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8 max-w-2xl mx-auto">
|
|
108
|
+
浏览 {categoryName} 分类下的所有文章
|
|
109
|
+
</p>
|
|
110
|
+
|
|
111
|
+
<!-- 分类统计 -->
|
|
112
|
+
<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
|
+
<span>{categoryName}</span>
|
|
114
|
+
<span>•</span>
|
|
115
|
+
<span>{totalPosts} 篇文章</span>
|
|
116
|
+
{totalPages > 1 && (
|
|
117
|
+
<>
|
|
118
|
+
<span>•</span>
|
|
119
|
+
<span>共 {totalPages} 页</span>
|
|
120
|
+
</>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- 文章列表 -->
|
|
126
|
+
{posts.length > 0 ? (
|
|
127
|
+
<div class="space-y-8 mb-12">
|
|
128
|
+
{posts.map((post) => (
|
|
129
|
+
<PostCard
|
|
130
|
+
post={{
|
|
131
|
+
slug: post.id.toLowerCase(),
|
|
132
|
+
title: post.data.title,
|
|
133
|
+
description: post.data.description,
|
|
134
|
+
pubDate: post.data.pubDate,
|
|
135
|
+
tags: post.data.tags,
|
|
136
|
+
categories: post.data.categories,
|
|
137
|
+
author: post.data.author,
|
|
138
|
+
image: post.data.image
|
|
139
|
+
}}
|
|
140
|
+
layout="horizontal"
|
|
141
|
+
/>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
144
|
+
) : (
|
|
145
|
+
<div class="text-center py-16">
|
|
146
|
+
<div class="text-6xl mb-4">📂</div>
|
|
147
|
+
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
|
|
148
|
+
暂无相关文章
|
|
149
|
+
</h3>
|
|
150
|
+
<p class="text-slate-600 dark:text-slate-400 mb-6">
|
|
151
|
+
目前还没有 {categoryName} 分类的文章,请查看其他分类。
|
|
152
|
+
</p>
|
|
153
|
+
<a href="/categories" class="btn-secondary">
|
|
154
|
+
浏览所有分类
|
|
155
|
+
</a>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
<!-- 分页导航 -->
|
|
160
|
+
{totalPages > 1 && (
|
|
161
|
+
<Pagination
|
|
162
|
+
currentPage={currentPage}
|
|
163
|
+
totalPages={totalPages}
|
|
164
|
+
baseUrl={`/categories/${categorySlug}`}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
<!-- 相关分类 -->
|
|
169
|
+
{relatedCategories.length > 0 && (
|
|
170
|
+
<section class="mt-16 pt-8 border-t border-slate-200 dark:border-slate-700">
|
|
171
|
+
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-6">
|
|
172
|
+
相关分类
|
|
173
|
+
</h2>
|
|
174
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
175
|
+
{relatedCategories.map((relatedCategory) => (
|
|
176
|
+
<a
|
|
177
|
+
href={`/categories/${relatedCategory.slug}`}
|
|
178
|
+
class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300"
|
|
179
|
+
>
|
|
180
|
+
<div class="flex items-center justify-between">
|
|
181
|
+
<div>
|
|
182
|
+
<h3 class="font-semibold text-slate-900 dark:text-slate-100 group-hover:text-amber-500 transition-colors">
|
|
183
|
+
{relatedCategory.name}
|
|
184
|
+
</h3>
|
|
185
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
186
|
+
{relatedCategory.count} 篇文章
|
|
187
|
+
</p>
|
|
188
|
+
</div>
|
|
189
|
+
<svg class="w-5 h-5 text-slate-400 group-hover:text-amber-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
190
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
191
|
+
</svg>
|
|
192
|
+
</div>
|
|
193
|
+
</a>
|
|
194
|
+
))}
|
|
195
|
+
</div>
|
|
196
|
+
</section>
|
|
197
|
+
)}
|
|
198
|
+
</PageLayout>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from 'astro:content';
|
|
3
|
+
import PageLayout from '@jet-w/astro-blog/layouts/PageLayout.astro';
|
|
4
|
+
|
|
5
|
+
// 从文章集合动态获取所有分类
|
|
6
|
+
const allPosts = await getCollection('posts', ({ data }) => !data.draft);
|
|
7
|
+
|
|
8
|
+
// 统计分类
|
|
9
|
+
const categoryMap = new Map<string, { name: string; count: number }>();
|
|
10
|
+
|
|
11
|
+
allPosts.forEach(post => {
|
|
12
|
+
(post.data.categories || []).forEach(category => {
|
|
13
|
+
const slug = category.toLowerCase().replace(/\s+/g, '-');
|
|
14
|
+
if (categoryMap.has(slug)) {
|
|
15
|
+
categoryMap.get(slug)!.count++;
|
|
16
|
+
} else {
|
|
17
|
+
categoryMap.set(slug, { name: category, count: 1 });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// 转换为数组格式
|
|
23
|
+
const allCategories = Array.from(categoryMap.entries()).map(([slug, { name, count }]) => ({
|
|
24
|
+
name,
|
|
25
|
+
count,
|
|
26
|
+
slug,
|
|
27
|
+
description: `浏览 ${name} 分类下的所有文章`
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
// 按文章数量排序
|
|
31
|
+
const sortedCategories = [...allCategories].sort((a, b) => b.count - a.count);
|
|
32
|
+
const totalPosts = allCategories.reduce((sum, category) => sum + category.count, 0);
|
|
33
|
+
|
|
34
|
+
// 获取图标(根据分类名称返回相应图标路径)
|
|
35
|
+
const getCategoryIcon = (slug: string) => {
|
|
36
|
+
const icons: Record<string, string> = {
|
|
37
|
+
'tech': 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4',
|
|
38
|
+
'frontend': 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4',
|
|
39
|
+
'backend': 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01',
|
|
40
|
+
'llm': 'M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z',
|
|
41
|
+
'ai': 'M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z',
|
|
42
|
+
'tutorial': 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253',
|
|
43
|
+
'tools': 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z',
|
|
44
|
+
'project': 'M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4'
|
|
45
|
+
};
|
|
46
|
+
// 尝试匹配 slug 中的关键词
|
|
47
|
+
for (const [key, path] of Object.entries(icons)) {
|
|
48
|
+
if (slug.includes(key)) {
|
|
49
|
+
return path;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 默认图标
|
|
53
|
+
return 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10';
|
|
54
|
+
};
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
<PageLayout
|
|
58
|
+
title="分类"
|
|
59
|
+
description="按分类浏览文章"
|
|
60
|
+
showSidebar={true}
|
|
61
|
+
>
|
|
62
|
+
<!-- 页面头部 -->
|
|
63
|
+
<div class="text-center mb-12">
|
|
64
|
+
<h1 class="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
|
65
|
+
文章分类
|
|
66
|
+
</h1>
|
|
67
|
+
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
|
|
68
|
+
按主题分类浏览所有文章
|
|
69
|
+
</p>
|
|
70
|
+
|
|
71
|
+
<!-- 统计信息 -->
|
|
72
|
+
<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">
|
|
73
|
+
<span>共 {allCategories.length} 个分类</span>
|
|
74
|
+
<span>•</span>
|
|
75
|
+
<span>{totalPosts} 篇文章</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- 分类网格 -->
|
|
80
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
|
|
81
|
+
{sortedCategories.map((category) => (
|
|
82
|
+
<a
|
|
83
|
+
href={`/categories/${category.slug}`}
|
|
84
|
+
class="group card hover:shadow-lg transform hover:-translate-y-1 transition-all duration-300 h-full"
|
|
85
|
+
>
|
|
86
|
+
<div class="flex flex-col h-full">
|
|
87
|
+
<!-- 图标和标题 -->
|
|
88
|
+
<div class="flex items-start space-x-4 mb-4">
|
|
89
|
+
<div class="flex-shrink-0 w-12 h-12 bg-primary-100 dark:bg-primary-900/30 rounded-lg flex items-center justify-center group-hover:bg-primary-200 dark:group-hover:bg-primary-900/50 transition-colors">
|
|
90
|
+
<svg class="w-6 h-6 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
91
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={getCategoryIcon(category.slug)} />
|
|
92
|
+
</svg>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="flex-1">
|
|
95
|
+
<h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 group-hover:text-primary-500 transition-colors mb-2">
|
|
96
|
+
{category.name}
|
|
97
|
+
</h3>
|
|
98
|
+
<div class="flex items-center text-sm text-slate-500 dark:text-slate-400">
|
|
99
|
+
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
100
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
|
101
|
+
</svg>
|
|
102
|
+
<span>{category.count} 篇文章</span>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- 描述 -->
|
|
108
|
+
<p class="text-slate-600 dark:text-slate-400 text-sm mb-4 flex-1">
|
|
109
|
+
{category.description}
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
<!-- 进度条 -->
|
|
113
|
+
<div class="mt-auto">
|
|
114
|
+
<div class="h-2 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden">
|
|
115
|
+
<div
|
|
116
|
+
class="h-full bg-primary-500 rounded-full transition-all duration-500 group-hover:bg-primary-600"
|
|
117
|
+
style={`width: ${Math.round((category.count / Math.max(...sortedCategories.map(c => c.count))) * 100)}%`}
|
|
118
|
+
></div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</a>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- 热门分类 -->
|
|
127
|
+
{sortedCategories.length > 4 && (
|
|
128
|
+
<section class="mb-16">
|
|
129
|
+
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
130
|
+
热门分类
|
|
131
|
+
</h2>
|
|
132
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
133
|
+
{sortedCategories.slice(0, 4).map((category) => (
|
|
134
|
+
<a
|
|
135
|
+
href={`/categories/${category.slug}`}
|
|
136
|
+
class="group p-6 bg-gradient-to-br from-primary-50 to-secondary-50 dark:from-primary-900/20 dark:to-secondary-900/20 rounded-xl border border-primary-200 dark:border-primary-800 hover:shadow-lg transition-all duration-300"
|
|
137
|
+
>
|
|
138
|
+
<div class="text-center">
|
|
139
|
+
<div class="text-3xl font-bold text-primary-500 mb-2">
|
|
140
|
+
{category.count}
|
|
141
|
+
</div>
|
|
142
|
+
<h3 class="font-semibold text-slate-900 dark:text-slate-100 group-hover:text-primary-500 transition-colors">
|
|
143
|
+
{category.name}
|
|
144
|
+
</h3>
|
|
145
|
+
</div>
|
|
146
|
+
</a>
|
|
147
|
+
))}
|
|
148
|
+
</div>
|
|
149
|
+
</section>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
<!-- 分类列表 -->
|
|
153
|
+
<section>
|
|
154
|
+
<h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-8">
|
|
155
|
+
所有分类
|
|
156
|
+
</h2>
|
|
157
|
+
<div class="space-y-3">
|
|
158
|
+
{sortedCategories.map((category) => (
|
|
159
|
+
<a
|
|
160
|
+
href={`/categories/${category.slug}`}
|
|
161
|
+
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"
|
|
162
|
+
>
|
|
163
|
+
<div class="flex items-center space-x-4">
|
|
164
|
+
<div class="w-10 h-10 bg-primary-100 dark:bg-primary-900/30 rounded-lg flex items-center justify-center group-hover:bg-primary-200 dark:group-hover:bg-primary-900/50 transition-colors">
|
|
165
|
+
<svg class="w-5 h-5 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
166
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={getCategoryIcon(category.slug)} />
|
|
167
|
+
</svg>
|
|
168
|
+
</div>
|
|
169
|
+
<div>
|
|
170
|
+
<h3 class="font-semibold text-slate-900 dark:text-slate-100 group-hover:text-primary-500 transition-colors">
|
|
171
|
+
{category.name}
|
|
172
|
+
</h3>
|
|
173
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
174
|
+
{category.description}
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="flex items-center space-x-3">
|
|
179
|
+
<span class="text-sm px-3 py-1 bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-400 rounded-full">
|
|
180
|
+
{category.count} 篇
|
|
181
|
+
</span>
|
|
182
|
+
<svg class="w-5 h-5 text-slate-400 group-hover:text-primary-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
183
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
184
|
+
</svg>
|
|
185
|
+
</div>
|
|
186
|
+
</a>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
</PageLayout>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
import BaseLayout from '@jet-w/astro-blog/layouts/BaseLayout.astro';
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<BaseLayout title="容器测试页面">
|
|
6
|
+
<div class="container mx-auto px-4 py-8">
|
|
7
|
+
<div class="max-w-4xl mx-auto">
|
|
8
|
+
<h1 class="text-3xl font-bold mb-8">容器效果测试</h1>
|
|
9
|
+
|
|
10
|
+
<div class="prose prose-slate dark:prose-invert max-w-none prose-lg">
|
|
11
|
+
|
|
12
|
+
<h1>测试各种容器类型</h1>
|
|
13
|
+
|
|
14
|
+
<div class="container-tip custom-container" data-container-type="tip">
|
|
15
|
+
<div class="container-title">💡 提示</div>
|
|
16
|
+
<div class="container-content">
|
|
17
|
+
<p>这是一个提示容器,用于显示有用的提示信息。</p>
|
|
18
|
+
<ul>
|
|
19
|
+
<li>支持列表</li>
|
|
20
|
+
<li>支持多行内容</li>
|
|
21
|
+
</ul>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="container-note custom-container" data-container-type="note">
|
|
26
|
+
<div class="container-title">📝 注意</div>
|
|
27
|
+
<div class="container-content">
|
|
28
|
+
<p>这是一个注意容器,用于显示需要注意的信息。可以包含<strong>粗体文字</strong>和<em>斜体文字</em>。</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="container-warning custom-container" data-container-type="warning">
|
|
33
|
+
<div class="container-title">⚠️ 警告</div>
|
|
34
|
+
<div class="container-content">
|
|
35
|
+
<p>这是一个警告容器,用于显示警告信息。</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="container-danger custom-container" data-container-type="danger">
|
|
40
|
+
<div class="container-title">🚨 危险</div>
|
|
41
|
+
<div class="container-content">
|
|
42
|
+
<p>这是一个危险容器,用于显示危险信息。</p>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="container-info custom-container" data-container-type="info">
|
|
47
|
+
<div class="container-title">ℹ️ 信息</div>
|
|
48
|
+
<div class="container-content">
|
|
49
|
+
<p>这是一个信息容器,用于显示一般信息。</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="container-details custom-container" data-container-type="details">
|
|
54
|
+
<div class="container-title">📋 详情</div>
|
|
55
|
+
<div class="container-content">
|
|
56
|
+
<p>这是一个详情容器,用于显示详细信息。</p>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<h2>自定义标题测试</h2>
|
|
61
|
+
|
|
62
|
+
<div class="container-tip custom-container" data-container-type="tip">
|
|
63
|
+
<div class="container-title">自定义提示标题</div>
|
|
64
|
+
<div class="container-content">
|
|
65
|
+
<p>这个容器有自定义的标题。</p>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="container-note custom-container" data-container-type="note">
|
|
70
|
+
<div class="container-title">我的自定义注意事项</div>
|
|
71
|
+
<div class="container-content">
|
|
72
|
+
<p>这个容器也有自定义的标题。</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</BaseLayout>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Direct Mermaid Test</title>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
font-family: Arial, sans-serif;
|
|
9
|
+
max-width: 800px;
|
|
10
|
+
margin: 0 auto;
|
|
11
|
+
padding: 2rem;
|
|
12
|
+
}
|
|
13
|
+
.chart-container {
|
|
14
|
+
margin: 2rem 0;
|
|
15
|
+
padding: 1rem;
|
|
16
|
+
border: 2px solid #ddd;
|
|
17
|
+
border-radius: 8px;
|
|
18
|
+
background: #f9f9f9;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<h1>🧪 Direct Mermaid Test</h1>
|
|
24
|
+
|
|
25
|
+
<p><strong>Status:</strong> This is a direct HTML test to verify Mermaid works in this environment.</p>
|
|
26
|
+
|
|
27
|
+
<div class="chart-container">
|
|
28
|
+
<h3>Chart 1: Simple Flow</h3>
|
|
29
|
+
<div class="mermaid">
|
|
30
|
+
graph TD
|
|
31
|
+
A[Start] --> B[Process]
|
|
32
|
+
B --> C[Decision]
|
|
33
|
+
C -->|Yes| D[Action 1]
|
|
34
|
+
C -->|No| E[Action 2]
|
|
35
|
+
D --> F[End]
|
|
36
|
+
E --> F
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="chart-container">
|
|
41
|
+
<h3>Chart 2: Process Flow</h3>
|
|
42
|
+
<div class="mermaid">
|
|
43
|
+
graph LR
|
|
44
|
+
A[Input] --> B[Validate]
|
|
45
|
+
B --> C[Process]
|
|
46
|
+
C --> D[Output]
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Load Mermaid from CDN -->
|
|
51
|
+
<script src="https://unpkg.com/mermaid@10.9.0/dist/mermaid.min.js"></script>
|
|
52
|
+
<script>
|
|
53
|
+
console.log('🚀 Initializing Mermaid directly...');
|
|
54
|
+
|
|
55
|
+
mermaid.initialize({
|
|
56
|
+
startOnLoad: true,
|
|
57
|
+
theme: 'default',
|
|
58
|
+
securityLevel: 'loose'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log('✅ Mermaid initialized and should render automatically');
|
|
62
|
+
|
|
63
|
+
// Check status after a delay
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
const svgs = document.querySelectorAll('.mermaid svg');
|
|
66
|
+
console.log(`📊 Found ${svgs.length} rendered SVG charts`);
|
|
67
|
+
|
|
68
|
+
if (svgs.length > 0) {
|
|
69
|
+
console.log('🎉 SUCCESS: Mermaid is working!');
|
|
70
|
+
document.body.style.borderTop = '5px solid green';
|
|
71
|
+
} else {
|
|
72
|
+
console.log('❌ FAILED: No SVG charts found');
|
|
73
|
+
document.body.style.borderTop = '5px solid red';
|
|
74
|
+
}
|
|
75
|
+
}, 2000);
|
|
76
|
+
</script>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|