@tnotesjs/core 0.2.1 → 0.3.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/README.md +0 -1
- package/config/ConfigManager.ts +137 -0
- package/config/constants.ts +121 -0
- package/config/defaultConfig.ts +33 -4
- package/config/index.ts +31 -0
- package/config/templates.ts +49 -0
- package/core/GitManager.ts +513 -0
- package/core/NoteIndexCache.ts +200 -0
- package/core/NoteManager.ts +395 -0
- package/core/ProcessManager.ts +180 -0
- package/core/ReadmeGenerator.ts +215 -0
- package/core/TocGenerator.ts +212 -0
- package/core/index.ts +11 -0
- package/dist/{chunk-5Y5IYS6C.js → chunk-UJG3UU5X.js} +21 -5
- package/dist/{chunk-5QN44ES5.js → chunk-XCWFZLER.js} +48 -368
- package/dist/cli/index.js +26 -78
- package/dist/index.js +1 -1
- package/dist/vitepress/config/index.js +2 -2
- package/package.json +5 -2
- package/services/file-watcher/configChangeHandler.ts +64 -0
- package/services/file-watcher/eventScheduler.ts +179 -0
- package/services/file-watcher/folderChangeHandler.ts +325 -0
- package/services/file-watcher/fsWatcherAdapter.ts +128 -0
- package/services/file-watcher/globalUpdateCoordinator.ts +60 -0
- package/services/file-watcher/index.ts +7 -0
- package/services/file-watcher/internal.ts +79 -0
- package/services/file-watcher/readmeChangeHandler.ts +28 -0
- package/services/file-watcher/renameDetector.ts +120 -0
- package/services/file-watcher/service.ts +352 -0
- package/services/file-watcher/watchState.ts +194 -0
- package/services/git/index.ts +7 -0
- package/services/git/service.ts +114 -0
- package/services/index.ts +14 -0
- package/services/init-sub-repo/index.ts +2 -0
- package/services/init-sub-repo/initSubRepoLogic.test.ts +162 -0
- package/services/init-sub-repo/initSubRepoLogic.ts +303 -0
- package/services/init-sub-repo/service.ts +101 -0
- package/services/note/index.ts +7 -0
- package/services/note/service.ts +361 -0
- package/services/readme/index.ts +7 -0
- package/services/readme/service.ts +761 -0
- package/services/toc/index.ts +5 -0
- package/services/toc/moveTocInside.test.ts +73 -0
- package/services/toc/service.ts +759 -0
- package/services/vitepress/index.ts +7 -0
- package/services/vitepress/service.ts +339 -0
- package/types/config.ts +0 -3
- package/types/note.ts +0 -2
- package/utils/errorHandler.ts +174 -0
- package/utils/file.ts +17 -0
- package/utils/genHierarchicalSidebar.ts +69 -0
- package/utils/generateAnchor.ts +24 -0
- package/utils/getChangedIds.ts +35 -0
- package/utils/index.ts +71 -0
- package/utils/logger.ts +231 -0
- package/utils/markdown.ts +75 -0
- package/utils/migrateReadmeToToc.test.ts +111 -0
- package/utils/migrateReadmeToToc.ts +135 -0
- package/utils/parseArgs.ts +90 -0
- package/utils/parseReadmeCompletedNotes.test.ts +90 -0
- package/utils/parseReadmeCompletedNotes.ts +108 -0
- package/utils/portUtils.ts +113 -0
- package/utils/readmeHelpers.ts +190 -0
- package/utils/runCommand.ts +29 -0
- package/utils/tocHelpers.test.ts +278 -0
- package/utils/tocHelpers.ts +855 -0
- package/utils/tocNodeId.test.ts +60 -0
- package/utils/tocNodeId.ts +97 -0
- package/utils/validators.ts +102 -0
- package/vitepress/assets/icons/icon__cursor.svg +4 -0
- package/vitepress/assets/icons/index.ts +1 -0
- package/vitepress/components/Layout/AboutPanel.vue +0 -24
- package/vitepress/components/Layout/DocBeforeControls.vue +6 -14
- package/vitepress/components/Layout/DocFooter.vue +3 -3
- package/vitepress/components/Layout/Layout.vue +0 -30
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +20 -11
- package/vitepress/components/Layout/homeReadme.data.ts +0 -48
- package/vitepress/components/MarkMap/MarkMap.vue +6 -13
- package/vitepress/components/Settings/Settings.vue +76 -73
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +16 -6
- package/vitepress/components/SidebarCard/SidebarCard.vue +21 -27
- package/vitepress/components/composables/useLocalIde.ts +83 -0
- package/vitepress/components/constants.ts +5 -6
- package/vitepress/components/utils/vscodePaths.test.ts +22 -0
- package/vitepress/components/utils/vscodePaths.ts +24 -2
- package/vitepress/theme/styles/doc-layout.scss +4 -4
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/ReadmeGenerator.ts
|
|
3
|
+
*
|
|
4
|
+
* README 生成器 - 负责生成各种 README 内容
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
|
8
|
+
|
|
9
|
+
import { TocGenerator } from './TocGenerator'
|
|
10
|
+
import { ConfigManager } from '../config/ConfigManager'
|
|
11
|
+
import { EOL } from '../config/constants'
|
|
12
|
+
import {
|
|
13
|
+
logger,
|
|
14
|
+
parseNoteLine,
|
|
15
|
+
buildNoteLineMarkdown,
|
|
16
|
+
processEmptyLines,
|
|
17
|
+
} from '../utils'
|
|
18
|
+
import { createAddNumberToTitle } from '../utils'
|
|
19
|
+
|
|
20
|
+
import type { NoteInfo } from '../types'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* README 生成器类
|
|
24
|
+
*/
|
|
25
|
+
export class ReadmeGenerator {
|
|
26
|
+
private tocGenerator: TocGenerator
|
|
27
|
+
private configManager: ConfigManager
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.tocGenerator = new TocGenerator()
|
|
31
|
+
this.configManager = ConfigManager.getInstance()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 更新笔记 README
|
|
36
|
+
* @param noteInfo - 笔记信息
|
|
37
|
+
*/
|
|
38
|
+
updateNoteReadme(noteInfo: NoteInfo): void {
|
|
39
|
+
if (!noteInfo.config) {
|
|
40
|
+
logger.warn(`笔记 ${noteInfo.dirName} 缺少配置文件`)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const content = readFileSync(noteInfo.readmePath, 'utf-8')
|
|
45
|
+
|
|
46
|
+
// 跳过空内容(可能是其他进程写入时的 truncate 中间状态)
|
|
47
|
+
if (content.length === 0) return
|
|
48
|
+
|
|
49
|
+
const lines = content.split(EOL)
|
|
50
|
+
|
|
51
|
+
const repoName = this.configManager.get('repoName')
|
|
52
|
+
this.tocGenerator.updateNoteToc(
|
|
53
|
+
noteInfo.index,
|
|
54
|
+
lines,
|
|
55
|
+
noteInfo.config,
|
|
56
|
+
repoName,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const updatedContent = lines.join(EOL)
|
|
60
|
+
writeFileSync(noteInfo.readmePath, updatedContent, 'utf-8')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 更新首页 README
|
|
65
|
+
* 更新笔记链接的状态标记([x] 或 [ ]),同时更新 TOC 区域
|
|
66
|
+
* @param notes - 笔记信息数组
|
|
67
|
+
* @param homeReadmePath - 首页 README 路径
|
|
68
|
+
*/
|
|
69
|
+
updateHomeReadme(notes: NoteInfo[], homeReadmePath: string): void {
|
|
70
|
+
if (!existsSync(homeReadmePath)) {
|
|
71
|
+
logger.error(`根目录下的 README.md 文件未找到:${homeReadmePath}`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const content = readFileSync(homeReadmePath, 'utf-8')
|
|
76
|
+
const lines = content.split(EOL)
|
|
77
|
+
|
|
78
|
+
// 创建笔记配置映射,以笔记索引为键
|
|
79
|
+
const noteByIndexMap = new Map<string, NoteInfo>()
|
|
80
|
+
for (const note of notes) {
|
|
81
|
+
noteByIndexMap.set(note.index, note)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 获取仓库信息
|
|
85
|
+
const repoOwner = this.configManager.get('author')
|
|
86
|
+
const repoName = this.configManager.get('repoName')
|
|
87
|
+
|
|
88
|
+
// 跟踪已存在的笔记索引和要移除的行
|
|
89
|
+
const existingNoteIndexes = new Set<string>()
|
|
90
|
+
const linesToRemove = new Set<number>()
|
|
91
|
+
|
|
92
|
+
// 更新笔记链接的状态标记
|
|
93
|
+
const titles: string[] = []
|
|
94
|
+
const titlesNotesCount: number[] = []
|
|
95
|
+
let inTocRegion = false
|
|
96
|
+
let currentNoteCount = 0
|
|
97
|
+
|
|
98
|
+
// 标题编号器(用于自动更新二级和三级标题前边儿的编号)
|
|
99
|
+
const addNumberToTitle = createAddNumberToTitle()
|
|
100
|
+
const numberedHeaders = ['## ', '### ']
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < lines.length; i++) {
|
|
103
|
+
const line = lines[i]
|
|
104
|
+
|
|
105
|
+
// 跳过 TOC region (后面会重新生成)
|
|
106
|
+
if (line.includes('<!-- region:toc -->')) {
|
|
107
|
+
inTocRegion = true
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
if (line.includes('<!-- endregion:toc -->')) {
|
|
111
|
+
inTocRegion = false
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
if (inTocRegion) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 使用公共方法解析笔记链接
|
|
119
|
+
const parsed = parseNoteLine(line)
|
|
120
|
+
if (parsed.isMatch && parsed.noteIndex) {
|
|
121
|
+
const note = noteByIndexMap.get(parsed.noteIndex)
|
|
122
|
+
|
|
123
|
+
if (!note) {
|
|
124
|
+
// 笔记不存在,标记为移除
|
|
125
|
+
linesToRemove.add(i)
|
|
126
|
+
logger.warn(`移除不存在的笔记: ${parsed.noteIndex}`)
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
existingNoteIndexes.add(parsed.noteIndex)
|
|
131
|
+
lines[i] = buildNoteLineMarkdown(note, repoOwner, repoName)
|
|
132
|
+
currentNoteCount++
|
|
133
|
+
continue
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 匹配标题: ## xxx 或 ### xxx
|
|
137
|
+
const titleMatch = line.match(/^(#{2,})\s+(.+)$/)
|
|
138
|
+
if (titleMatch) {
|
|
139
|
+
// 检查是否是需要编号的标题(2~3 级)
|
|
140
|
+
const isNumberedHeader = numberedHeaders.some((header) =>
|
|
141
|
+
line.startsWith(header),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if (isNumberedHeader) {
|
|
145
|
+
// 自动添加编号
|
|
146
|
+
const [numberedTitle] = addNumberToTitle(line)
|
|
147
|
+
lines[i] = numberedTitle
|
|
148
|
+
|
|
149
|
+
// 保存上一个标题的笔记数量
|
|
150
|
+
if (titles.length > 0) {
|
|
151
|
+
titlesNotesCount.push(currentNoteCount)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
titles.push(numberedTitle)
|
|
155
|
+
currentNoteCount = 0
|
|
156
|
+
} else {
|
|
157
|
+
// 其他级别的标题,不添加编号
|
|
158
|
+
// 保存上一个标题的笔记数量
|
|
159
|
+
if (titles.length > 0) {
|
|
160
|
+
titlesNotesCount.push(currentNoteCount)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
titles.push(line)
|
|
164
|
+
currentNoteCount = 0
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 移除不存在的笔记(从后往前删除,避免索引问题)
|
|
170
|
+
const sortedLinesToRemove = Array.from(linesToRemove).sort((a, b) => b - a)
|
|
171
|
+
for (const lineIndex of sortedLinesToRemove) {
|
|
172
|
+
lines.splice(lineIndex, 1)
|
|
173
|
+
if (currentNoteCount > 0) {
|
|
174
|
+
currentNoteCount--
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 查找缺失的笔记(在真实目录中存在但 README 中不存在)
|
|
179
|
+
const missingNotes: NoteInfo[] = []
|
|
180
|
+
for (const note of notes) {
|
|
181
|
+
if (!existingNoteIndexes.has(note.index)) {
|
|
182
|
+
missingNotes.push(note)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 将缺失的笔记添加到结尾
|
|
187
|
+
if (missingNotes.length > 0) {
|
|
188
|
+
logger.info(`添加 ${missingNotes.length} 篇缺失的笔记到 README`)
|
|
189
|
+
|
|
190
|
+
// 按笔记索引排序
|
|
191
|
+
missingNotes.sort((a, b) => a.index.localeCompare(b.index))
|
|
192
|
+
|
|
193
|
+
for (const note of missingNotes) {
|
|
194
|
+
const noteLine = buildNoteLineMarkdown(note, repoOwner, repoName)
|
|
195
|
+
lines.push(noteLine)
|
|
196
|
+
currentNoteCount++
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 保存最后一个标题的笔记数量
|
|
201
|
+
if (titles.length > 0) {
|
|
202
|
+
titlesNotesCount.push(currentNoteCount)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 更新 TOC 区域
|
|
206
|
+
this.tocGenerator.updateHomeToc(lines, titles, titlesNotesCount)
|
|
207
|
+
|
|
208
|
+
const processedLines = processEmptyLines(lines)
|
|
209
|
+
|
|
210
|
+
const updatedContent = processedLines.join(EOL)
|
|
211
|
+
writeFileSync(homeReadmePath, updatedContent, 'utf-8')
|
|
212
|
+
|
|
213
|
+
logger.info('已更新首页 README')
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/TocGenerator.ts
|
|
3
|
+
*
|
|
4
|
+
* 目录生成器 - 负责生成各种目录(TOC)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { EOL } from '../config/constants'
|
|
8
|
+
import {
|
|
9
|
+
generateToc as generateTocUtil,
|
|
10
|
+
createAddNumberToTitle,
|
|
11
|
+
} from '../utils'
|
|
12
|
+
|
|
13
|
+
import type { NoteConfig } from '../types'
|
|
14
|
+
|
|
15
|
+
// URL 常量
|
|
16
|
+
const BILIBILI_VIDEO_BASE_URL = 'https://www.bilibili.com/video/'
|
|
17
|
+
const TNOTES_YUQUE_BASE_URL = 'https://www.yuque.com/tdahuyou/tnotes.yuque/'
|
|
18
|
+
|
|
19
|
+
// 目录开始和结束标记
|
|
20
|
+
const NOTES_TOC_START_TAG = '<!-- region:toc -->'
|
|
21
|
+
const NOTES_TOC_END_TAG = '<!-- endregion:toc -->'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 目录生成器类
|
|
25
|
+
*/
|
|
26
|
+
export class TocGenerator {
|
|
27
|
+
/**
|
|
28
|
+
* 更新笔记目录
|
|
29
|
+
* @param noteIndex - 笔记索引
|
|
30
|
+
* @param lines - 笔记内容行数组
|
|
31
|
+
* @param noteConfig - 笔记配置
|
|
32
|
+
* @param repoName - 仓库名称
|
|
33
|
+
*/
|
|
34
|
+
updateNoteToc(
|
|
35
|
+
noteIndex: string,
|
|
36
|
+
lines: string[],
|
|
37
|
+
noteConfig: NoteConfig,
|
|
38
|
+
repoName: string,
|
|
39
|
+
): void {
|
|
40
|
+
let startLineIdx = -1,
|
|
41
|
+
endLineIdx = -1
|
|
42
|
+
lines.forEach((line, idx) => {
|
|
43
|
+
if (line.startsWith(NOTES_TOC_START_TAG)) startLineIdx = idx
|
|
44
|
+
if (line.startsWith(NOTES_TOC_END_TAG)) endLineIdx = idx
|
|
45
|
+
})
|
|
46
|
+
if (startLineIdx === -1 || endLineIdx === -1) return
|
|
47
|
+
|
|
48
|
+
const titles: string[] = []
|
|
49
|
+
const numberedHeaders = ['## ', '### '] // 2~3 级标题需要编号
|
|
50
|
+
const unnumberedHeaders = ['#### ', '##### ', '###### '] // 4~6 级标题不需要编号
|
|
51
|
+
const addNumberToTitle = createAddNumberToTitle()
|
|
52
|
+
|
|
53
|
+
// 代码块检测状态
|
|
54
|
+
let inCodeBlock = false
|
|
55
|
+
let inHtmlComment = false
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < lines.length; i++) {
|
|
58
|
+
const line = lines[i]
|
|
59
|
+
|
|
60
|
+
// 检测代码块边界(``` 或 ~~~)
|
|
61
|
+
if (line.trim().startsWith('```') || line.trim().startsWith('~~~')) {
|
|
62
|
+
inCodeBlock = !inCodeBlock
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 检测 HTML 注释边界
|
|
67
|
+
if (line.trim().startsWith('<!--')) {
|
|
68
|
+
inHtmlComment = true
|
|
69
|
+
}
|
|
70
|
+
if (line.trim().includes('-->')) {
|
|
71
|
+
inHtmlComment = false
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 跳过代码块和 HTML 注释内的内容
|
|
76
|
+
if (inCodeBlock || inHtmlComment) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 检查是否是需要编号的标题(2~3 级)
|
|
81
|
+
const isNumberedHeader = numberedHeaders.some((header) =>
|
|
82
|
+
line.startsWith(header),
|
|
83
|
+
)
|
|
84
|
+
// 检查是否是不需要编号的标题(4~6 级)
|
|
85
|
+
const isUnnumberedHeader = unnumberedHeaders.some((header) =>
|
|
86
|
+
line.startsWith(header),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if (isNumberedHeader) {
|
|
90
|
+
const [numberedTitle] = addNumberToTitle(line)
|
|
91
|
+
titles.push(numberedTitle)
|
|
92
|
+
lines[i] = numberedTitle // 更新原行内容(添加编号)
|
|
93
|
+
} else if (isUnnumberedHeader) {
|
|
94
|
+
// 移除可能存在的旧编号
|
|
95
|
+
const match = line.match(/^(#+)\s*(\d+(\.\d+)*\.\s*)?(.*)/)
|
|
96
|
+
if (match) {
|
|
97
|
+
const headerSymbol = match[1]
|
|
98
|
+
const plainTitle = match[4]
|
|
99
|
+
const cleanTitle = `${headerSymbol} ${plainTitle}`
|
|
100
|
+
titles.push(cleanTitle)
|
|
101
|
+
lines[i] = cleanTitle // 更新原行内容(移除编号)
|
|
102
|
+
} else {
|
|
103
|
+
titles.push(line)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const toc = generateTocUtil(titles)
|
|
109
|
+
const bilibiliTOCItems: string[] = []
|
|
110
|
+
const tnotesTOCItems: string[] = []
|
|
111
|
+
const yuqueTOCItems: string[] = []
|
|
112
|
+
|
|
113
|
+
if (noteConfig) {
|
|
114
|
+
if (noteConfig.bilibili.length > 0) {
|
|
115
|
+
noteConfig.bilibili.forEach((bvid, i) => {
|
|
116
|
+
bilibiliTOCItems.push(
|
|
117
|
+
` - [bilibili.${repoName}.${noteIndex}.${i + 1}](${
|
|
118
|
+
BILIBILI_VIDEO_BASE_URL + bvid
|
|
119
|
+
})`,
|
|
120
|
+
)
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
if (noteConfig.tnotes && noteConfig.tnotes.length > 0) {
|
|
124
|
+
// 生成相关知识库标题和链接列表
|
|
125
|
+
tnotesTOCItems.push(
|
|
126
|
+
`- [📒 TNotes(相关知识库)](https://tnotesjs.github.io/TNotes/)`,
|
|
127
|
+
)
|
|
128
|
+
noteConfig.tnotes.forEach((repoName) => {
|
|
129
|
+
tnotesTOCItems.push(
|
|
130
|
+
` - [TNotes.${repoName}](https://tnotesjs.github.io/TNotes.${repoName}/)`,
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
if (noteConfig.yuque.length > 0) {
|
|
135
|
+
noteConfig.yuque.forEach((slug) => {
|
|
136
|
+
yuqueTOCItems.push(
|
|
137
|
+
` - [TNotes.yuque.${repoName.replace(
|
|
138
|
+
'TNotes.',
|
|
139
|
+
'',
|
|
140
|
+
)}.${noteIndex}](${TNOTES_YUQUE_BASE_URL + slug})`,
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const insertTocItems: string[] = []
|
|
147
|
+
const hasExternalResources =
|
|
148
|
+
bilibiliTOCItems.length > 0 ||
|
|
149
|
+
tnotesTOCItems.length > 0 ||
|
|
150
|
+
yuqueTOCItems.length > 0
|
|
151
|
+
|
|
152
|
+
if (hasExternalResources) {
|
|
153
|
+
insertTocItems.push('::: details 📚 相关资源', '')
|
|
154
|
+
|
|
155
|
+
if (bilibiliTOCItems.length > 0) {
|
|
156
|
+
insertTocItems.push(
|
|
157
|
+
`- [📺 bilibili(笔记视频资源)](https://space.bilibili.com/407241004)`,
|
|
158
|
+
...bilibiliTOCItems,
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (tnotesTOCItems.length > 0) {
|
|
163
|
+
insertTocItems.push(...tnotesTOCItems)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (yuqueTOCItems.length > 0) {
|
|
167
|
+
insertTocItems.push(
|
|
168
|
+
`- [📂 TNotes.yuque(笔记附件资源)](${TNOTES_YUQUE_BASE_URL})`,
|
|
169
|
+
...yuqueTOCItems,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
insertTocItems.push('', ':::', '')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lines.splice(
|
|
177
|
+
startLineIdx + 1,
|
|
178
|
+
endLineIdx - startLineIdx - 1,
|
|
179
|
+
'',
|
|
180
|
+
...insertTocItems,
|
|
181
|
+
...toc.replace(new RegExp(`^${EOL}`), '').split(EOL),
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 更新首页目录
|
|
187
|
+
* @param lines - 首页内容行数组
|
|
188
|
+
* @param titles - 标题数组
|
|
189
|
+
* @param _titlesNotesCount - 每个标题下的笔记数量
|
|
190
|
+
*/
|
|
191
|
+
updateHomeToc(
|
|
192
|
+
lines: string[],
|
|
193
|
+
titles: string[],
|
|
194
|
+
_titlesNotesCount: number[],
|
|
195
|
+
): void {
|
|
196
|
+
let startLineIdx = -1,
|
|
197
|
+
endLineIdx = -1
|
|
198
|
+
lines.forEach((line, idx) => {
|
|
199
|
+
if (line.startsWith(NOTES_TOC_START_TAG)) startLineIdx = idx
|
|
200
|
+
if (line.startsWith(NOTES_TOC_END_TAG)) endLineIdx = idx
|
|
201
|
+
})
|
|
202
|
+
if (startLineIdx === -1 || endLineIdx === -1) return
|
|
203
|
+
|
|
204
|
+
const toc = generateTocUtil(titles)
|
|
205
|
+
|
|
206
|
+
lines.splice(
|
|
207
|
+
startLineIdx + 1,
|
|
208
|
+
endLineIdx - startLineIdx - 1,
|
|
209
|
+
...toc.split(EOL),
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
}
|
package/core/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/index.ts
|
|
3
|
+
*
|
|
4
|
+
* Core 层统一导出
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { NoteManager } from './NoteManager'
|
|
8
|
+
export { NoteIndexCache } from './NoteIndexCache'
|
|
9
|
+
export { ReadmeGenerator } from './ReadmeGenerator'
|
|
10
|
+
export { GitManager } from './GitManager'
|
|
11
|
+
export { ProcessManager } from './ProcessManager'
|
|
@@ -32,10 +32,7 @@ function getDefaultConfig(repoName) {
|
|
|
32
32
|
title: cleanName,
|
|
33
33
|
completed_notes_count: {},
|
|
34
34
|
details: `${name} \u77E5\u8BC6\u5E93`,
|
|
35
|
-
link: `https://tnotesjs.github.io/${name}
|
|
36
|
-
created_at: Date.now(),
|
|
37
|
-
updated_at: Date.now(),
|
|
38
|
-
days_since_birth: 0
|
|
35
|
+
link: `https://tnotesjs.github.io/${name}/`
|
|
39
36
|
},
|
|
40
37
|
// 端口配置(根据仓库名生成)
|
|
41
38
|
port: 8e3,
|
|
@@ -86,11 +83,29 @@ function mergeConfig(target, source) {
|
|
|
86
83
|
}
|
|
87
84
|
return result;
|
|
88
85
|
}
|
|
86
|
+
var DEPRECATED_ROOT_ITEM_FIELDS = [
|
|
87
|
+
"created_at",
|
|
88
|
+
"updated_at",
|
|
89
|
+
"days_since_birth"
|
|
90
|
+
];
|
|
91
|
+
function stripDeprecatedRootItemFields(rootItem) {
|
|
92
|
+
let stripped = false;
|
|
93
|
+
for (const key of DEPRECATED_ROOT_ITEM_FIELDS) {
|
|
94
|
+
if (key in rootItem) {
|
|
95
|
+
delete rootItem[key];
|
|
96
|
+
stripped = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return stripped;
|
|
100
|
+
}
|
|
89
101
|
function validateAndCompleteConfig(config) {
|
|
90
102
|
const repoName = config.repoName || "TNotes.default";
|
|
91
103
|
const defaultConfig = getDefaultConfig(repoName);
|
|
92
104
|
const mergedConfig = mergeConfig(config, defaultConfig);
|
|
93
|
-
const
|
|
105
|
+
const rootItem = mergedConfig.root_item || {};
|
|
106
|
+
const stripped = stripDeprecatedRootItemFields(rootItem);
|
|
107
|
+
mergedConfig.root_item = rootItem;
|
|
108
|
+
const modified = stripped || JSON.stringify(config) !== JSON.stringify(mergedConfig);
|
|
94
109
|
return {
|
|
95
110
|
config: mergedConfig,
|
|
96
111
|
modified
|
|
@@ -195,6 +210,7 @@ function getConfigManager() {
|
|
|
195
210
|
|
|
196
211
|
export {
|
|
197
212
|
getDefaultConfig,
|
|
213
|
+
stripDeprecatedRootItemFields,
|
|
198
214
|
ConfigManager,
|
|
199
215
|
getConfigManager
|
|
200
216
|
};
|