@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,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { siteConfig } from '../config/site';
|
|
3
|
+
import '../styles/slides.css';
|
|
4
|
+
|
|
5
|
+
export interface Props {
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
theme?: string;
|
|
9
|
+
transition?: string;
|
|
10
|
+
controls?: boolean;
|
|
11
|
+
progress?: boolean;
|
|
12
|
+
center?: boolean;
|
|
13
|
+
hash?: boolean;
|
|
14
|
+
slideNumber?: boolean;
|
|
15
|
+
showBackButton?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
title,
|
|
20
|
+
description = '',
|
|
21
|
+
theme = 'black',
|
|
22
|
+
transition = 'slide',
|
|
23
|
+
controls = true,
|
|
24
|
+
progress = true,
|
|
25
|
+
center = true,
|
|
26
|
+
hash = true,
|
|
27
|
+
slideNumber = false,
|
|
28
|
+
showBackButton = true,
|
|
29
|
+
} = Astro.props;
|
|
30
|
+
|
|
31
|
+
const fullTitle = `${title} | ${siteConfig.title}`;
|
|
32
|
+
|
|
33
|
+
// Reveal.js 配置
|
|
34
|
+
const revealConfig = JSON.stringify({
|
|
35
|
+
hash,
|
|
36
|
+
controls,
|
|
37
|
+
progress,
|
|
38
|
+
center,
|
|
39
|
+
transition,
|
|
40
|
+
slideNumber,
|
|
41
|
+
// Markdown 配置
|
|
42
|
+
markdown: {
|
|
43
|
+
smartypants: true,
|
|
44
|
+
},
|
|
45
|
+
// 插件配置
|
|
46
|
+
plugins: ['RevealMarkdown', 'RevealHighlight', 'RevealNotes', 'RevealMath'],
|
|
47
|
+
});
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<!DOCTYPE html>
|
|
51
|
+
<html lang="zh-CN">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="UTF-8" />
|
|
54
|
+
<meta name="description" content={description} />
|
|
55
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
56
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
57
|
+
<meta name="generator" content={Astro.generator} />
|
|
58
|
+
<title>{fullTitle}</title>
|
|
59
|
+
|
|
60
|
+
<!-- Open Graph -->
|
|
61
|
+
<meta property="og:type" content="article" />
|
|
62
|
+
<meta property="og:title" content={fullTitle} />
|
|
63
|
+
<meta property="og:description" content={description} />
|
|
64
|
+
|
|
65
|
+
<!-- Reveal.js CSS -->
|
|
66
|
+
<link rel="stylesheet" href="/slides/reveal.css" />
|
|
67
|
+
<link rel="stylesheet" href={`/slides/theme/${theme}.css`} id="theme" />
|
|
68
|
+
|
|
69
|
+
<!-- Reveal.js 代码高亮主题 -->
|
|
70
|
+
<link rel="stylesheet" href="/slides/plugin/highlight/monokai.css" />
|
|
71
|
+
|
|
72
|
+
<!-- KaTeX for math -->
|
|
73
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" />
|
|
74
|
+
|
|
75
|
+
<!-- Mermaid 和 ECharts 在幻灯片中的样式 -->
|
|
76
|
+
<style>
|
|
77
|
+
.reveal .mermaid {
|
|
78
|
+
background: rgba(255, 255, 255, 0.95);
|
|
79
|
+
padding: 1em;
|
|
80
|
+
border-radius: 8px;
|
|
81
|
+
}
|
|
82
|
+
.reveal .echarts-container {
|
|
83
|
+
width: 100%;
|
|
84
|
+
height: 400px;
|
|
85
|
+
margin: 0 auto;
|
|
86
|
+
}
|
|
87
|
+
.reveal pre code.language-mermaid,
|
|
88
|
+
.reveal pre code.language-echarts {
|
|
89
|
+
display: none;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
92
|
+
</head>
|
|
93
|
+
<body>
|
|
94
|
+
<div class="reveal">
|
|
95
|
+
<div class="slides">
|
|
96
|
+
<slot />
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- 返回按钮(仅在独立页面显示,嵌入模式通过 JS 隐藏) -->
|
|
101
|
+
<a href="#" class="slides-back-btn" id="slides-back-btn" title="返回上一级">
|
|
102
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
103
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
104
|
+
</svg>
|
|
105
|
+
</a>
|
|
106
|
+
|
|
107
|
+
<!-- 检测嵌入模式并隐藏返回按钮,设置返回链接为父路径 -->
|
|
108
|
+
<script is:inline>
|
|
109
|
+
(function() {
|
|
110
|
+
var btn = document.getElementById('slides-back-btn');
|
|
111
|
+
if (!btn) return;
|
|
112
|
+
|
|
113
|
+
// 检查是否在 iframe 中或 URL 包含 embed=true
|
|
114
|
+
var isEmbed = window.self !== window.top ||
|
|
115
|
+
window.location.search.indexOf('embed=true') !== -1;
|
|
116
|
+
if (isEmbed) {
|
|
117
|
+
btn.style.display = 'none';
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 设置返回链接为父路径
|
|
122
|
+
var path = window.location.pathname;
|
|
123
|
+
// 移除末尾的斜杠
|
|
124
|
+
if (path.endsWith('/')) {
|
|
125
|
+
path = path.slice(0, -1);
|
|
126
|
+
}
|
|
127
|
+
// 获取父路径
|
|
128
|
+
var parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
129
|
+
// 如果父路径为空,返回首页
|
|
130
|
+
btn.href = parentPath || '/';
|
|
131
|
+
})();
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<!-- Reveal.js -->
|
|
135
|
+
<script is:inline src="/slides/reveal.js"></script>
|
|
136
|
+
<script is:inline src="/slides/plugin/markdown/markdown.js"></script>
|
|
137
|
+
<script is:inline src="/slides/plugin/highlight/highlight.js"></script>
|
|
138
|
+
<script is:inline src="/slides/plugin/notes/notes.js"></script>
|
|
139
|
+
<script is:inline src="/slides/plugin/math/math.js"></script>
|
|
140
|
+
|
|
141
|
+
<!-- Mermaid -->
|
|
142
|
+
<script is:inline src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
143
|
+
|
|
144
|
+
<!-- ECharts -->
|
|
145
|
+
<script is:inline src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
|
146
|
+
|
|
147
|
+
<!-- 初始化 Reveal.js -->
|
|
148
|
+
<script is:inline define:vars={{ revealConfig }}>
|
|
149
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
150
|
+
const config = JSON.parse(revealConfig);
|
|
151
|
+
|
|
152
|
+
Reveal.initialize({
|
|
153
|
+
hash: config.hash,
|
|
154
|
+
controls: config.controls,
|
|
155
|
+
progress: config.progress,
|
|
156
|
+
center: config.center,
|
|
157
|
+
transition: config.transition,
|
|
158
|
+
slideNumber: config.slideNumber,
|
|
159
|
+
|
|
160
|
+
// Markdown 配置
|
|
161
|
+
markdown: config.markdown,
|
|
162
|
+
|
|
163
|
+
// 数学公式配置
|
|
164
|
+
math: {
|
|
165
|
+
mathjax: null,
|
|
166
|
+
katex: 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js',
|
|
167
|
+
katexScript: 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js',
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
// 插件
|
|
171
|
+
plugins: [RevealMarkdown, RevealHighlight, RevealNotes, RevealMath.KaTeX]
|
|
172
|
+
}).then(function() {
|
|
173
|
+
// Reveal.js 初始化完成后,渲染 Mermaid 和 ECharts
|
|
174
|
+
var mermaidContainers = [];
|
|
175
|
+
var echartsInstances = [];
|
|
176
|
+
|
|
177
|
+
// 初始化 Mermaid
|
|
178
|
+
if (typeof mermaid !== 'undefined') {
|
|
179
|
+
mermaid.initialize({
|
|
180
|
+
startOnLoad: false,
|
|
181
|
+
theme: 'default',
|
|
182
|
+
securityLevel: 'loose'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// 查找所有 mermaid 代码块并准备容器
|
|
186
|
+
document.querySelectorAll('pre code.language-mermaid, pre code.mermaid').forEach(function(block, index) {
|
|
187
|
+
var code = block.textContent;
|
|
188
|
+
var container = document.createElement('div');
|
|
189
|
+
container.className = 'mermaid';
|
|
190
|
+
container.id = 'mermaid-slide-' + index;
|
|
191
|
+
container.setAttribute('data-mermaid-code', code);
|
|
192
|
+
container.style.minHeight = '100px';
|
|
193
|
+
|
|
194
|
+
// 替换代码块
|
|
195
|
+
var pre = block.parentElement;
|
|
196
|
+
pre.parentElement.insertBefore(container, pre);
|
|
197
|
+
pre.style.display = 'none';
|
|
198
|
+
|
|
199
|
+
mermaidContainers.push({
|
|
200
|
+
container: container,
|
|
201
|
+
code: code,
|
|
202
|
+
rendered: false
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// 渲染当前可见幻灯片中的 Mermaid
|
|
207
|
+
async function renderVisibleMermaid() {
|
|
208
|
+
var needsLayout = false;
|
|
209
|
+
|
|
210
|
+
mermaidContainers.forEach(function(item) {
|
|
211
|
+
if (item.rendered) return;
|
|
212
|
+
|
|
213
|
+
var section = item.container.closest('section');
|
|
214
|
+
if (section && (section.classList.contains('present') || section.closest('.present'))) {
|
|
215
|
+
item.container.textContent = item.code;
|
|
216
|
+
item.rendered = true;
|
|
217
|
+
needsLayout = true;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// 渲染所有已标记的 Mermaid
|
|
222
|
+
var toRender = mermaidContainers.filter(function(item) {
|
|
223
|
+
return item.rendered && item.container.textContent;
|
|
224
|
+
}).map(function(item) {
|
|
225
|
+
return item.container;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (toRender.length > 0) {
|
|
229
|
+
try {
|
|
230
|
+
await mermaid.run({ nodes: toRender });
|
|
231
|
+
// Mermaid 渲染完成后,通知 Reveal.js 重新计算布局
|
|
232
|
+
if (needsLayout) {
|
|
233
|
+
Reveal.layout();
|
|
234
|
+
}
|
|
235
|
+
} catch(e) {
|
|
236
|
+
console.warn('Mermaid render warning:', e);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 初始渲染 - 使用更长的延迟确保 Reveal.js 完全初始化
|
|
242
|
+
setTimeout(renderVisibleMermaid, 200);
|
|
243
|
+
|
|
244
|
+
// 幻灯片切换时渲染
|
|
245
|
+
Reveal.on('slidechanged', function() {
|
|
246
|
+
setTimeout(renderVisibleMermaid, 50);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 初始化 ECharts
|
|
251
|
+
if (typeof echarts !== 'undefined') {
|
|
252
|
+
document.querySelectorAll('pre code.language-echarts, pre code.echarts').forEach(function(block, index) {
|
|
253
|
+
try {
|
|
254
|
+
var code = block.textContent.trim();
|
|
255
|
+
var option = JSON.parse(code);
|
|
256
|
+
|
|
257
|
+
// 创建 ECharts 容器
|
|
258
|
+
var container = document.createElement('div');
|
|
259
|
+
container.className = 'echarts-container';
|
|
260
|
+
container.id = 'echarts-slide-' + index;
|
|
261
|
+
container.style.height = '400px';
|
|
262
|
+
container.style.width = '100%';
|
|
263
|
+
|
|
264
|
+
// 替换代码块
|
|
265
|
+
var pre = block.parentElement;
|
|
266
|
+
pre.parentElement.insertBefore(container, pre);
|
|
267
|
+
pre.style.display = 'none';
|
|
268
|
+
|
|
269
|
+
echartsInstances.push({
|
|
270
|
+
container: container,
|
|
271
|
+
option: option,
|
|
272
|
+
chart: null,
|
|
273
|
+
initialized: false
|
|
274
|
+
});
|
|
275
|
+
} catch (e) {
|
|
276
|
+
console.error('ECharts parsing error:', e);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 渲染当前可见幻灯片中的 ECharts
|
|
281
|
+
function renderVisibleEcharts() {
|
|
282
|
+
echartsInstances.forEach(function(item) {
|
|
283
|
+
var section = item.container.closest('section');
|
|
284
|
+
var isVisible = section && (section.classList.contains('present') || section.closest('.present'));
|
|
285
|
+
|
|
286
|
+
if (isVisible) {
|
|
287
|
+
if (!item.initialized) {
|
|
288
|
+
item.chart = echarts.init(item.container);
|
|
289
|
+
item.chart.setOption(item.option);
|
|
290
|
+
item.initialized = true;
|
|
291
|
+
} else if (item.chart) {
|
|
292
|
+
item.chart.resize();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 初始渲染
|
|
299
|
+
setTimeout(renderVisibleEcharts, 100);
|
|
300
|
+
|
|
301
|
+
// 幻灯片切换时渲染/调整大小
|
|
302
|
+
Reveal.on('slidechanged', function() {
|
|
303
|
+
setTimeout(renderVisibleEcharts, 50);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// 监听窗口大小变化
|
|
307
|
+
window.addEventListener('resize', function() {
|
|
308
|
+
echartsInstances.forEach(function(item) {
|
|
309
|
+
if (item.chart) {
|
|
310
|
+
item.chart.resize();
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
</script>
|
|
319
|
+
</body>
|
|
320
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
|
|
3
|
+
export function rehypeCleanContainers() {
|
|
4
|
+
return (tree) => {
|
|
5
|
+
visit(tree, 'text', (node, index, parent) => {
|
|
6
|
+
if (node.value && typeof node.value === 'string') {
|
|
7
|
+
// 清理文本中的容器标记残留
|
|
8
|
+
if (node.value.trim() === ':::') {
|
|
9
|
+
// 如果这个文本节点只包含 :::,移除它
|
|
10
|
+
parent.children.splice(index, 1);
|
|
11
|
+
return index;
|
|
12
|
+
}
|
|
13
|
+
if (node.value.includes(':::')) {
|
|
14
|
+
// 清理包含 ::: 的文本
|
|
15
|
+
node.value = node.value.replace(/:::\s*$/, '').replace(/^\s*:::/, '');
|
|
16
|
+
if (node.value.trim() === '') {
|
|
17
|
+
parent.children.splice(index, 1);
|
|
18
|
+
return index;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rehype 插件:修复 Markdown 中的相对链接
|
|
3
|
+
* 将相对链接转换为正确的绝对路径
|
|
4
|
+
*/
|
|
5
|
+
import { visit } from 'unist-util-visit';
|
|
6
|
+
|
|
7
|
+
export function rehypeRelativeLinks(options = {}) {
|
|
8
|
+
return (tree, file) => {
|
|
9
|
+
// 获取当前文件的路径信息
|
|
10
|
+
const filePath = file.history[0] || '';
|
|
11
|
+
|
|
12
|
+
// 从文件路径中提取目录信息
|
|
13
|
+
// 例如:/path/to/content/posts/blog_docs/README.md -> blog_docs
|
|
14
|
+
const match = filePath.match(/content\/posts\/(.+?)\/[^/]+\.(md|mdx)$/i);
|
|
15
|
+
if (!match) return;
|
|
16
|
+
|
|
17
|
+
const dirPath = match[1]; // 例如:blog_docs 或 tech/subfolder
|
|
18
|
+
|
|
19
|
+
visit(tree, 'element', (node) => {
|
|
20
|
+
if (node.tagName === 'a' && node.properties?.href) {
|
|
21
|
+
const href = node.properties.href;
|
|
22
|
+
|
|
23
|
+
// 只处理相对链接(以 ./ 或不以 / # http 开头的链接)
|
|
24
|
+
if (href.startsWith('./') ||
|
|
25
|
+
(!href.startsWith('/') &&
|
|
26
|
+
!href.startsWith('#') &&
|
|
27
|
+
!href.startsWith('http://') &&
|
|
28
|
+
!href.startsWith('https://') &&
|
|
29
|
+
!href.startsWith('mailto:'))) {
|
|
30
|
+
|
|
31
|
+
// 移除 ./ 前缀
|
|
32
|
+
const cleanHref = href.replace(/^\.\//, '');
|
|
33
|
+
|
|
34
|
+
// 构建新的绝对路径
|
|
35
|
+
const newHref = `/posts/${dirPath}/${cleanHref}`;
|
|
36
|
+
node.properties.href = newHref;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default rehypeRelativeLinks;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import { toText } from 'hast-util-to-text';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rehype plugin to transform :::tabs containers into interactive tabs
|
|
6
|
+
*
|
|
7
|
+
* This works on the HTML AST after remark-directive has converted
|
|
8
|
+
* :::tabs into <div> elements
|
|
9
|
+
*/
|
|
10
|
+
export function rehypeTabs() {
|
|
11
|
+
return (tree) => {
|
|
12
|
+
visit(tree, 'element', (node, index, parent) => {
|
|
13
|
+
if (!parent || index === undefined) return;
|
|
14
|
+
|
|
15
|
+
// Look for divs that contain @tab markers (including tabs-wrapper from remark-containers)
|
|
16
|
+
if (node.tagName !== 'div') return;
|
|
17
|
+
|
|
18
|
+
// Check if this is a tabs-wrapper div
|
|
19
|
+
const isTabsWrapper = node.properties?.className?.includes('tabs-wrapper');
|
|
20
|
+
|
|
21
|
+
// Check if this div contains @tab paragraphs
|
|
22
|
+
const children = node.children || [];
|
|
23
|
+
const tabMarkers = [];
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < children.length; i++) {
|
|
26
|
+
const child = children[i];
|
|
27
|
+
if (child.type === 'element' && child.tagName === 'p') {
|
|
28
|
+
const text = getTextContent(child).trim();
|
|
29
|
+
const tabMatch = text.match(/^@tab\s+(.+)$/);
|
|
30
|
+
if (tabMatch) {
|
|
31
|
+
tabMarkers.push({ index: i, title: tabMatch[1].trim() });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If we found @tab markers, transform this into tabs
|
|
37
|
+
if (tabMarkers.length === 0) return;
|
|
38
|
+
|
|
39
|
+
// Generate unique ID
|
|
40
|
+
const tabsId = `tabs-${Math.random().toString(36).substring(2, 11)}`;
|
|
41
|
+
|
|
42
|
+
// Build tabs structure
|
|
43
|
+
const tabsContent = [];
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < tabMarkers.length; i++) {
|
|
46
|
+
const startIdx = tabMarkers[i].index;
|
|
47
|
+
const endIdx = i < tabMarkers.length - 1 ? tabMarkers[i + 1].index : children.length;
|
|
48
|
+
|
|
49
|
+
// Collect content between this @tab and the next (excluding the @tab paragraph itself)
|
|
50
|
+
const content = children.slice(startIdx + 1, endIdx);
|
|
51
|
+
|
|
52
|
+
tabsContent.push({
|
|
53
|
+
title: tabMarkers[i].title,
|
|
54
|
+
content: content
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create new tabs structure
|
|
59
|
+
const tabButtons = tabsContent.map((tab, idx) => ({
|
|
60
|
+
type: 'element',
|
|
61
|
+
tagName: 'button',
|
|
62
|
+
properties: {
|
|
63
|
+
className: idx === 0 ? ['tab-button', 'active'] : ['tab-button'],
|
|
64
|
+
'data-tab-index': idx,
|
|
65
|
+
onclick: `switchTab('${tabsId}', ${idx})`
|
|
66
|
+
},
|
|
67
|
+
children: [{ type: 'text', value: tab.title }]
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
const tabPanels = tabsContent.map((tab, idx) => ({
|
|
71
|
+
type: 'element',
|
|
72
|
+
tagName: 'div',
|
|
73
|
+
properties: {
|
|
74
|
+
className: idx === 0 ? ['tab-panel', 'active'] : ['tab-panel'],
|
|
75
|
+
'data-tab-panel': idx
|
|
76
|
+
},
|
|
77
|
+
children: tab.content
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// Build the tabs container
|
|
81
|
+
const tabsContainer = {
|
|
82
|
+
type: 'element',
|
|
83
|
+
tagName: 'div',
|
|
84
|
+
properties: {
|
|
85
|
+
className: ['custom-tabs'],
|
|
86
|
+
'data-tabs-id': tabsId
|
|
87
|
+
},
|
|
88
|
+
children: [
|
|
89
|
+
{
|
|
90
|
+
type: 'element',
|
|
91
|
+
tagName: 'div',
|
|
92
|
+
properties: { className: ['tabs-header'] },
|
|
93
|
+
children: tabButtons
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: 'element',
|
|
97
|
+
tagName: 'div',
|
|
98
|
+
properties: { className: ['tabs-content'] },
|
|
99
|
+
children: tabPanels
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Replace the original node
|
|
105
|
+
parent.children[index] = tabsContainer;
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getTextContent(node) {
|
|
111
|
+
if (node.type === 'text') return node.value || '';
|
|
112
|
+
if (node.children) {
|
|
113
|
+
return node.children.map(getTextContent).join('');
|
|
114
|
+
}
|
|
115
|
+
return '';
|
|
116
|
+
}
|