Skip to content

Commit 1272bf7

Browse files
committed
feat: hierarchical note sections mirroring Notion structure
1 parent 5630896 commit 1272bf7

196 files changed

Lines changed: 298 additions & 115 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
File renamed without changes.

‎scripts/notion-import.mjs‎

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,13 @@ function yamlString(value) {
7070

7171
const notes = keptNotes(exportDir);
7272

73-
// 原始相对路径(去 id 后) -> slug,用于把 Notion 站内链接改写成本站链接
74-
const slugByCleanRel = new Map(Object.entries(mapping).map(([rel, cfg]) => [rel, cfg.slug]));
73+
// 原始相对路径(去 id 后) -> 本站笔记 id,用于把 Notion 站内链接改写成本站链接
74+
const noteIdByCleanRel = new Map(
75+
Object.entries(mapping).map(([rel, cfg]) => [
76+
rel,
77+
cfg.section ? `${cfg.section}/${cfg.slug}` : cfg.slug,
78+
]),
79+
);
7580

7681
const summary = [];
7782
const problems = [];
@@ -90,6 +95,11 @@ for (const note of notes) {
9095
}
9196

9297
const { slug, tags } = config;
98+
const section = config.section?.trim() ?? '';
99+
// 目录结构即层级:<section>/<slug>.md → /notes/<section>/<slug>/
100+
// section 为空则是顶层笔记
101+
const noteId = section ? `${section}/${slug}` : slug;
102+
const outDir = section ? path.join(NOTES_DIR, section) : NOTES_DIR;
93103
const title = config.title?.trim() || note.title;
94104
const mdDir = path.dirname(note.abs);
95105

@@ -114,7 +124,8 @@ for (const note of notes) {
114124
}
115125

116126
const target = normalizeAssetName(path.basename(decoded));
117-
copiedAssets.push({ source, target: path.join(NOTES_DIR, slug, target) });
127+
// 图片放在 md 同级的 <slug>/ 子目录里,相对引用,Astro 才会接管处理
128+
copiedAssets.push({ source, target: path.join(outDir, slug, target) });
118129
return `![${alt}](./${slug}/${target})`;
119130
});
120131

@@ -127,8 +138,8 @@ for (const note of notes) {
127138

128139
if (decoded.toLowerCase().endsWith('.md')) {
129140
const targetRel = stripNotionId(path.normalize(path.join(path.dirname(note.rel), decoded)));
130-
const targetSlug = slugByCleanRel.get(targetRel);
131-
if (targetSlug) return `[${text}](/notes/${targetSlug}/)`;
141+
const targetId = noteIdByCleanRel.get(targetRel);
142+
if (targetId) return `[${text}](/notes/${targetId}/)`;
132143
return text; // 目标未发布,降级成纯文字
133144
}
134145

@@ -140,8 +151,8 @@ for (const note of notes) {
140151
}
141152

142153
const target = normalizeAssetName(path.basename(decoded));
143-
copiedAssets.push({ source, target: path.join(FILES_DIR, slug, target) });
144-
return `[${text}](/files/${slug}/${target})`;
154+
copiedAssets.push({ source, target: path.join(FILES_DIR, noteId, target) });
155+
return `[${text}](/files/${noteId}/${target})`;
145156
});
146157

147158
body = body.replace(/\n{3,}/g, '\n\n').trim();
@@ -159,21 +170,21 @@ for (const note of notes) {
159170
].join('\n');
160171

161172
if (!dryRun) {
162-
fs.mkdirSync(NOTES_DIR, { recursive: true });
163-
fs.writeFileSync(path.join(NOTES_DIR, `${slug}.md`), `${frontmatter}${body}\n`, 'utf8');
173+
fs.mkdirSync(outDir, { recursive: true });
174+
fs.writeFileSync(path.join(outDir, `${slug}.md`), `${frontmatter}${body}\n`, 'utf8');
164175

165176
for (const asset of copiedAssets) {
166177
fs.mkdirSync(path.dirname(asset.target), { recursive: true });
167178
fs.copyFileSync(asset.source, asset.target);
168179
}
169180
}
170181

171-
summary.push({ slug, title, date: dateInfo.created, assets: copiedAssets.length });
182+
summary.push({ id: noteId, title, date: dateInfo.created, assets: copiedAssets.length });
172183
}
173184

174185
summary.sort((a, b) => a.date.localeCompare(b.date));
175186
for (const s of summary) {
176-
console.log(`${s.date} ${s.slug.padEnd(36)} ${String(s.assets).padStart(3)} 个资源 ${s.title}`);
187+
console.log(`${s.date} ${s.id.padEnd(44)} ${String(s.assets).padStart(3)} 个资源 ${s.title}`);
177188
}
178189

179190
console.log(`\n${dryRun ? '[试运行] ' : ''}共 ${summary.length} 篇`);

‎scripts/notion-mapping.json‎

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,48 @@
11
{
22
"_说明": [
3-
"Notion 原路径 → 目标文件名(slug)、标题、标签 的映射表。",
4-
"slug 决定最终 URL:/notes/<slug>/,必须是 ASCII kebab-case。",
3+
"Notion 原路径 → 章节(section)、目标文件名(slug)、标题、标签 的映射表。",
4+
"section 对应 Notion 里的父页面,决定目录结构和 URL:/notes/<section>/<slug>/。",
5+
"section 留空表示顶层笔记,URL 为 /notes/<slug>/。",
6+
"章节的中文显示名和排序在 src/lib/sections.ts 里定义。",
57
"title 留空表示沿用 Notion 原标题;填了则覆盖(用于原标题过于笼统的情况)。",
68
"这份表由人工审核,导入脚本只按表执行,不自己猜。"
79
],
810

911
"notes": {
10-
"deeplearning 2 limu\\预备知识.md": { "slug": "preliminaries", "title": "", "tags": ["深度学习", "李沐课程"] },
11-
"deeplearning 2 limu\\线性神经网络.md": { "slug": "linear-neural-network", "title": "", "tags": ["深度学习", "李沐课程"] },
12-
"deeplearning 2 limu\\softmax回归.md": { "slug": "softmax-regression", "title": "", "tags": ["深度学习", "李沐课程"] },
13-
"deeplearning 2 limu\\感知机+激活函数.md": { "slug": "perceptron-and-activation", "title": "", "tags": ["深度学习", "李沐课程"] },
14-
"deeplearning 2 limu\\模型选择+过拟合欠拟合+权重衰退.md": { "slug": "model-selection-and-regularization", "title": "", "tags": ["深度学习", "李沐课程"] },
15-
"deeplearning 2 limu\\数值的稳定性+模型初始化和激活函数.md": { "slug": "numerical-stability-and-init", "title": "", "tags": ["深度学习", "李沐课程"] },
16-
"deeplearning 2 limu\\dropout.md": { "slug": "dropout", "title": "Dropout", "tags": ["深度学习", "李沐课程"] },
17-
"deeplearning 2 limu\\卷积神经网络.md": { "slug": "convolutional-neural-network", "title": "", "tags": ["深度学习", "李沐课程"] },
18-
"deeplearning 2 limu\\CV.md": { "slug": "computer-vision", "title": "计算机视觉", "tags": ["深度学习", "李沐课程", "CV"] },
19-
"deeplearning 2 limu\\数据集\\图像分类.md": { "slug": "image-classification-datasets", "title": "图像分类数据集", "tags": ["深度学习", "李沐课程"] },
20-
"deeplearning 2 limu\\环境安装.md": { "slug": "dl-environment-setup", "title": "深度学习环境安装", "tags": ["深度学习", "工具"] },
12+
"deeplearning 2 limu\\预备知识.md": { "section": "deep-learning", "slug": "preliminaries", "title": "", "tags": ["深度学习", "李沐课程"] },
13+
"deeplearning 2 limu\\线性神经网络.md": { "section": "deep-learning", "slug": "linear-neural-network", "title": "", "tags": ["深度学习", "李沐课程"] },
14+
"deeplearning 2 limu\\softmax回归.md": { "section": "deep-learning", "slug": "softmax-regression", "title": "", "tags": ["深度学习", "李沐课程"] },
15+
"deeplearning 2 limu\\感知机+激活函数.md": { "section": "deep-learning", "slug": "perceptron-and-activation", "title": "", "tags": ["深度学习", "李沐课程"] },
16+
"deeplearning 2 limu\\模型选择+过拟合欠拟合+权重衰退.md": { "section": "deep-learning", "slug": "model-selection-and-regularization", "title": "", "tags": ["深度学习", "李沐课程"] },
17+
"deeplearning 2 limu\\数值的稳定性+模型初始化和激活函数.md": { "section": "deep-learning", "slug": "numerical-stability-and-init", "title": "", "tags": ["深度学习", "李沐课程"] },
18+
"deeplearning 2 limu\\dropout.md": { "section": "deep-learning", "slug": "dropout", "title": "Dropout", "tags": ["深度学习", "李沐课程"] },
19+
"deeplearning 2 limu\\卷积神经网络.md": { "section": "deep-learning", "slug": "convolutional-neural-network", "title": "", "tags": ["深度学习", "李沐课程"] },
20+
"deeplearning 2 limu\\CV.md": { "section": "deep-learning", "slug": "computer-vision", "title": "计算机视觉", "tags": ["深度学习", "李沐课程", "CV"] },
21+
"deeplearning 2 limu\\数据集\\图像分类.md": { "section": "deep-learning", "slug": "image-classification-datasets", "title": "图像分类数据集", "tags": ["深度学习", "李沐课程"] },
22+
"deeplearning 2 limu\\环境安装.md": { "section": "deep-learning", "slug": "environment-setup", "title": "深度学习环境安装", "tags": ["深度学习", "工具"] },
2123

22-
"about paper\\how to read paper\\NLP-Transformer\\Transformer.md": { "slug": "transformer", "title": "Transformer", "tags": ["论文精读", "NLP"] },
23-
"about paper\\how to read paper\\NLP-Transformer\\BERT.md": { "slug": "bert", "title": "BERT", "tags": ["论文精读", "NLP"] },
24-
"about paper\\how to read paper\\CV-Transformer\\VIT.md": { "slug": "vit", "title": "ViT", "tags": ["论文精读", "CV"] },
25-
"about paper\\how to read paper\\CV-Transformer\\MAE.md": { "slug": "mae", "title": "MAE", "tags": ["论文精读", "CV"] },
26-
"about paper\\how to read paper\\CV-对比学习\\MOCO(没太看懂对比学习).md": { "slug": "moco", "title": "", "tags": ["论文精读", "CV", "对比学习"] },
27-
"about paper\\how to read paper\\CV-CNN\\ResNet论文精读.md": { "slug": "resnet", "title": "ResNet 论文精读", "tags": ["论文精读", "CV"] },
28-
"ai infra\\Scaling Laws for Neural Language Models.md": { "slug": "scaling-laws", "title": "", "tags": ["论文精读", "AI Infra"] },
29-
"about paper\\how to read paper.md": { "slug": "how-to-read-paper", "title": "如何读论文", "tags": ["方法论"] },
30-
"about paper\\邪修.md": { "slug": "research-shortcuts", "title": "", "tags": ["方法论"] },
31-
"about paper\\科研工具合集.md": { "slug": "research-tools", "title": "", "tags": ["工具"] },
24+
"about paper\\how to read paper.md": { "section": "paper-reading", "slug": "how-to-read-paper", "title": "如何读论文", "tags": ["方法论"] },
25+
"about paper\\邪修.md": { "section": "paper-reading", "slug": "research-shortcuts", "title": "", "tags": ["方法论"] },
26+
"about paper\\科研工具合集.md": { "section": "paper-reading", "slug": "research-tools", "title": "", "tags": ["工具"] },
27+
"about paper\\how to read paper\\NLP-Transformer\\Transformer.md": { "section": "paper-reading", "slug": "transformer", "title": "Transformer", "tags": ["论文精读", "NLP"] },
28+
"about paper\\how to read paper\\NLP-Transformer\\BERT.md": { "section": "paper-reading", "slug": "bert", "title": "BERT", "tags": ["论文精读", "NLP"] },
29+
"about paper\\how to read paper\\CV-Transformer\\VIT.md": { "section": "paper-reading", "slug": "vit", "title": "ViT", "tags": ["论文精读", "CV"] },
30+
"about paper\\how to read paper\\CV-Transformer\\MAE.md": { "section": "paper-reading", "slug": "mae", "title": "MAE", "tags": ["论文精读", "CV"] },
31+
"about paper\\how to read paper\\CV-对比学习\\MOCO(没太看懂对比学习).md": { "section": "paper-reading", "slug": "moco", "title": "", "tags": ["论文精读", "CV", "对比学习"] },
32+
"about paper\\how to read paper\\CV-CNN\\ResNet论文精读.md": { "section": "paper-reading", "slug": "resnet", "title": "ResNet 论文精读", "tags": ["论文精读", "CV"] },
3233

33-
"CS336\\assignment-1.md": { "slug": "cs336-assignment-1", "title": "CS336 Assignment 1", "tags": ["CS336"] },
34-
"CS336\\git.md": { "slug": "git-basics", "title": "Git 常用操作", "tags": ["工具"] },
35-
"CS336\\python\\uv.md": { "slug": "uv", "title": "uv 包管理器", "tags": ["工具", "Python"] },
36-
"CS336\\python\\基础.md": { "slug": "python-basics", "title": "Python 基础", "tags": ["Python", "CS336"] },
34+
"CS336\\assignment-1.md": { "section": "cs336", "slug": "assignment-1", "title": "Assignment 1", "tags": ["CS336"] },
35+
"CS336\\git.md": { "section": "cs336", "slug": "git", "title": "Git 常用操作", "tags": ["工具"] },
36+
"CS336\\python\\uv.md": { "section": "cs336", "slug": "uv", "title": "uv 包管理器", "tags": ["工具", "Python"] },
37+
"CS336\\python\\基础.md": { "section": "cs336", "slug": "python-basics", "title": "Python 基础", "tags": ["Python", "CS336"] },
3738

38-
"kaggle\\autonomous-agent-prediction-beta.md": { "slug": "autonomous-agent-prediction", "title": "Autonomous Agent Prediction", "tags": ["Kaggle"] },
39-
"kaggle\\autonomous-agent-prediction-beta\\环境准备.md": { "slug": "kaggle-env-setup", "title": "Kaggle 环境准备", "tags": ["Kaggle", "工具"] },
39+
"kaggle\\autonomous-agent-prediction-beta.md": { "section": "kaggle", "slug": "autonomous-agent-prediction", "title": "Autonomous Agent Prediction", "tags": ["Kaggle"] },
40+
"kaggle\\autonomous-agent-prediction-beta\\环境准备.md": { "section": "kaggle", "slug": "environment-setup", "title": "比赛环境准备", "tags": ["Kaggle", "工具"] },
4041

41-
"ai infra.md": { "slug": "ai-infra", "title": "AI Infra", "tags": ["AI Infra"] },
42-
"ai infra\\aicluster.md": { "slug": "ai-cluster", "title": "AI Cluster", "tags": ["AI Infra"] },
42+
"ai infra.md": { "section": "ai-infra", "slug": "overview", "title": "AI Infra 概览", "tags": ["AI Infra"] },
43+
"ai infra\\aicluster.md": { "section": "ai-infra", "slug": "ai-cluster", "title": "AI Cluster", "tags": ["AI Infra"] },
44+
"ai infra\\Scaling Laws for Neural Language Models.md": { "section": "ai-infra", "slug": "scaling-laws", "title": "", "tags": ["论文精读", "AI Infra"] },
4345

44-
"各种问题汇总.md": { "slug": "troubleshooting", "title": "", "tags": ["踩坑"] }
46+
"各种问题汇总.md": { "section": "", "slug": "troubleshooting", "title": "", "tags": ["踩坑"] }
4547
}
4648
}

‎src/components/NoteList.astro‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
import type { Note } from '../lib/posts';
3+
import FormattedDate from './FormattedDate.astro';
4+
5+
interface Props {
6+
notes: Note[];
7+
/** 是否显示每篇的 description 摘要 */
8+
showDescription?: boolean;
9+
}
10+
11+
const { notes, showDescription = true } = Astro.props;
12+
---
13+
14+
<ul>
15+
{
16+
notes.map((note) => (
17+
<li class="border-b border-rule py-3">
18+
<div class="flex flex-wrap items-baseline justify-between gap-x-6">
19+
<a href={`/notes/${note.id}/`} class="no-underline hover:underline">
20+
{note.data.title}
21+
{note.data.draft && <span class="ml-2 text-sm text-muted">[草稿]</span>}
22+
</a>
23+
<span class="text-sm text-muted tabular-nums">
24+
<FormattedDate date={note.data.date} />
25+
</span>
26+
</div>
27+
{showDescription && note.data.description && (
28+
<p class="mt-1 text-sm text-muted">{note.data.description}</p>
29+
)}
30+
</li>
31+
))
32+
}
33+
</ul>
Lines changed: 3 additions & 3 deletions
File renamed without changes.
Lines changed: 2 additions & 2 deletions

0 commit comments

Comments
 (0)