@tnotesjs/core 0.2.2 → 0.4.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/defaultConfig.ts +33 -4
- package/config/index.ts +6 -0
- package/core/NoteIndexCache.ts +7 -1
- package/core/NoteManager.ts +18 -30
- 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 +159 -18
- package/package.json +3 -6
- package/services/index.ts +0 -1
- package/services/init-sub-repo/initSubRepoLogic.ts +0 -1
- package/services/note/service.ts +0 -1
- package/types/config.ts +0 -3
- package/types/note.ts +0 -2
- 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/MindmapPreview/InlineRuns.ts +25 -0
- package/vitepress/components/MindmapPreview/MindmapOutlineNode.vue +62 -0
- package/vitepress/components/MindmapPreview/MindmapPreview.vue +287 -0
- package/vitepress/components/MindmapPreview/compat.test.ts +93 -0
- package/vitepress/components/MindmapPreview/compat.ts +128 -0
- package/vitepress/components/MindmapPreview/expandLevel.test.ts +40 -0
- package/vitepress/components/MindmapPreview/expandLevel.ts +28 -0
- package/vitepress/components/MindmapPreview/legacyCorpus.test.ts +57 -0
- 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/configs/markdown.config.ts +77 -20
- package/vitepress/theme/index.ts +4 -2
- package/vitepress/theme/styles/doc-layout.scss +4 -4
- package/vitepress/theme/styles/index.scss +0 -1
- package/services/timestamp/index.ts +0 -7
- package/services/timestamp/service.ts +0 -465
- package/vitepress/components/MarkMap/MarkMap.vue +0 -625
- package/vitepress/theme/styles/components/markmap.scss +0 -122
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* services/TimestampService.ts
|
|
3
|
-
*
|
|
4
|
-
* 时间戳服务 - 管理笔记的创建时间和更新时间
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { execSync } from 'child_process'
|
|
8
|
-
import {
|
|
9
|
-
existsSync,
|
|
10
|
-
readFileSync,
|
|
11
|
-
writeFileSync,
|
|
12
|
-
readdirSync,
|
|
13
|
-
statSync,
|
|
14
|
-
} from 'fs'
|
|
15
|
-
import { join } from 'path'
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
NOTES_DIR_PATH,
|
|
19
|
-
ROOT_DIR_PATH,
|
|
20
|
-
ROOT_CONFIG_PATH,
|
|
21
|
-
} from '../../config/constants'
|
|
22
|
-
import { NoteManager } from '../../core/NoteManager'
|
|
23
|
-
import { logger } from '../../utils'
|
|
24
|
-
|
|
25
|
-
import type { NoteConfig, TNotesConfig } from '../../types'
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 出生日期(用于计算 days_since_birth)
|
|
29
|
-
* 1999-06-29 作为第 1 天
|
|
30
|
-
*/
|
|
31
|
-
const BIRTH_DATE = new Date('1999-06-29T00:00:00+08:00').getTime()
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 时间戳服务类
|
|
35
|
-
*/
|
|
36
|
-
export class TimestampService {
|
|
37
|
-
private noteManager: NoteManager
|
|
38
|
-
|
|
39
|
-
constructor() {
|
|
40
|
-
this.noteManager = NoteManager.getInstance()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 批量获取所有笔记 README.md 的 git 时间戳
|
|
45
|
-
* 只执行 2 次 git log,构建 filePath → {created_at, updated_at} 的映射
|
|
46
|
-
*
|
|
47
|
-
* - 第 1 次:git log --name-only --format=... --diff-filter=A
|
|
48
|
-
* 获取每个文件的首次提交时间(created_at)
|
|
49
|
-
* - 第 2 次:git log --name-only --format=...
|
|
50
|
-
* 获取每个文件的最后修改时间(updated_at)
|
|
51
|
-
*/
|
|
52
|
-
private buildGitTimestampMap(): Map<
|
|
53
|
-
string,
|
|
54
|
-
{ created_at: number; updated_at: number }
|
|
55
|
-
> {
|
|
56
|
-
const createdMap = new Map<string, number>()
|
|
57
|
-
const updatedMap = new Map<string, number>()
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
// 1) 获取所有文件的首次提交时间(--diff-filter=A 只看 add 事件)
|
|
61
|
-
// --reverse 让最早的 commit 排在前面,同文件多次出现时后面覆盖前面
|
|
62
|
-
// 但我们取首次出现(最早),所以不用 --reverse,直接取最后出现的即可
|
|
63
|
-
const createdRaw = execSync(
|
|
64
|
-
`git log --name-only --format="---COMMIT---%ct" --diff-filter=A -- "notes/*/README.md"`,
|
|
65
|
-
{
|
|
66
|
-
cwd: ROOT_DIR_PATH,
|
|
67
|
-
encoding: 'utf-8',
|
|
68
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
69
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
70
|
-
},
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
let currentTimestamp = 0
|
|
74
|
-
for (const line of createdRaw.split(/\r?\n/)) {
|
|
75
|
-
if (line.startsWith('---COMMIT---')) {
|
|
76
|
-
currentTimestamp = parseInt(line.slice(12)) * 1000
|
|
77
|
-
} else if (line.trim() && currentTimestamp) {
|
|
78
|
-
// git log 从新到旧,后出现的是更早的 commit,持续覆盖即可
|
|
79
|
-
// 最终 map 中保留的就是最早(首次)提交时间
|
|
80
|
-
createdMap.set(line.trim(), currentTimestamp)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// 2) 获取所有文件的最后修改时间
|
|
85
|
-
// git log 从新到旧,只取首次出现(最新 commit)
|
|
86
|
-
const updatedRaw = execSync(
|
|
87
|
-
`git log --name-only --format="---COMMIT---%ct" -- "notes/*/README.md"`,
|
|
88
|
-
{
|
|
89
|
-
cwd: ROOT_DIR_PATH,
|
|
90
|
-
encoding: 'utf-8',
|
|
91
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
92
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
93
|
-
},
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
currentTimestamp = 0
|
|
97
|
-
for (const line of updatedRaw.split(/\r?\n/)) {
|
|
98
|
-
if (line.startsWith('---COMMIT---')) {
|
|
99
|
-
currentTimestamp = parseInt(line.slice(12)) * 1000
|
|
100
|
-
} else if (line.trim() && currentTimestamp) {
|
|
101
|
-
// 只取第一次出现(最新的 commit 时间)
|
|
102
|
-
if (!updatedMap.has(line.trim())) {
|
|
103
|
-
updatedMap.set(line.trim(), currentTimestamp)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
logger.error('批量获取 git 时间戳失败', error)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 合并为统一的 map
|
|
112
|
-
const result = new Map<string, { created_at: number; updated_at: number }>()
|
|
113
|
-
for (const [file, created_at] of createdMap) {
|
|
114
|
-
const updated_at = updatedMap.get(file) ?? created_at
|
|
115
|
-
result.set(file, { created_at, updated_at })
|
|
116
|
-
}
|
|
117
|
-
// 补充只在 updatedMap 中出现的文件(理论上不会,但防御性编程)
|
|
118
|
-
for (const [file, updated_at] of updatedMap) {
|
|
119
|
-
if (!result.has(file)) {
|
|
120
|
-
result.set(file, { created_at: updated_at, updated_at })
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return result
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 从 git 获取文件的创建时间和最后修改时间
|
|
129
|
-
* @param noteDirPath - 笔记目录路径
|
|
130
|
-
* @returns 时间戳对象,包含 created_at 和 updated_at
|
|
131
|
-
*/
|
|
132
|
-
private getGitTimestamps(noteDirPath: string): {
|
|
133
|
-
created_at: number
|
|
134
|
-
updated_at: number
|
|
135
|
-
} | null {
|
|
136
|
-
try {
|
|
137
|
-
const readmePath = join(noteDirPath, 'README.md')
|
|
138
|
-
|
|
139
|
-
// 获取 README.md 的首次提交时间(创建时间)- 兼容 Windows,不依赖 tail/head
|
|
140
|
-
const createdAtOutput = execSync(
|
|
141
|
-
`git log --diff-filter=A --follow --format=%ct -- "${readmePath}"`,
|
|
142
|
-
{
|
|
143
|
-
cwd: ROOT_DIR_PATH,
|
|
144
|
-
encoding: 'utf-8',
|
|
145
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
146
|
-
},
|
|
147
|
-
)
|
|
148
|
-
.split(/\r?\n/)
|
|
149
|
-
.filter(Boolean)
|
|
150
|
-
.pop() // 取最后一条(最早的提交)
|
|
151
|
-
|
|
152
|
-
// 获取 README.md 的最后修改时间(只看 README.md,忽略 .tnotes.json)
|
|
153
|
-
const updatedAtOutput = execSync(
|
|
154
|
-
`git log -1 --format=%ct -- "${readmePath}"`,
|
|
155
|
-
{
|
|
156
|
-
cwd: ROOT_DIR_PATH,
|
|
157
|
-
encoding: 'utf-8',
|
|
158
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
159
|
-
},
|
|
160
|
-
).trim()
|
|
161
|
-
|
|
162
|
-
if (!createdAtOutput || !updatedAtOutput) {
|
|
163
|
-
return null
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
created_at: parseInt(createdAtOutput) * 1000, // 转换为毫秒
|
|
168
|
-
updated_at: parseInt(updatedAtOutput) * 1000,
|
|
169
|
-
}
|
|
170
|
-
} catch (error) {
|
|
171
|
-
// git 命令失败(可能是新文件未提交);便于排查时输出调试信息
|
|
172
|
-
logger.debug?.(`getGitTimestamps failed: ${noteDirPath}`, error)
|
|
173
|
-
return null
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* 修复单个笔记的时间戳
|
|
179
|
-
* @param noteDir - 笔记目录名
|
|
180
|
-
* @param forceUpdate - 是否强制更新(忽略现有值)
|
|
181
|
-
* @param timestampMap - 预构建的时间戳映射(批量模式传入,避免逐个 git log)
|
|
182
|
-
* @returns 是否进行了修复
|
|
183
|
-
*/
|
|
184
|
-
private fixNoteTimestamps(
|
|
185
|
-
noteDir: string,
|
|
186
|
-
forceUpdate: boolean = false,
|
|
187
|
-
timestampMap?: Map<string, { created_at: number; updated_at: number }>,
|
|
188
|
-
): boolean {
|
|
189
|
-
const configPath = join(NOTES_DIR_PATH, noteDir, '.tnotes.json')
|
|
190
|
-
|
|
191
|
-
if (!existsSync(configPath)) {
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
// 读取配置
|
|
197
|
-
const configContent = readFileSync(configPath, 'utf-8')
|
|
198
|
-
const config: NoteConfig = JSON.parse(configContent)
|
|
199
|
-
|
|
200
|
-
// 获取 git 时间戳:优先从批量 map 查,fallback 到逐个 git log
|
|
201
|
-
const readmeRelPath = `notes/${noteDir}/README.md`
|
|
202
|
-
const timestamps = timestampMap
|
|
203
|
-
? (timestampMap.get(readmeRelPath) ?? null)
|
|
204
|
-
: this.getGitTimestamps(join(NOTES_DIR_PATH, noteDir))
|
|
205
|
-
|
|
206
|
-
if (!timestamps) {
|
|
207
|
-
return false
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
let modified = false
|
|
211
|
-
|
|
212
|
-
// 修复 created_at(设置为首次提交时间)
|
|
213
|
-
if (
|
|
214
|
-
forceUpdate ||
|
|
215
|
-
!config.created_at ||
|
|
216
|
-
config.created_at !== timestamps.created_at
|
|
217
|
-
) {
|
|
218
|
-
config.created_at = timestamps.created_at
|
|
219
|
-
modified = true
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 修复 updated_at
|
|
223
|
-
if (forceUpdate) {
|
|
224
|
-
// 强制更新:直接使用 git 时间戳
|
|
225
|
-
if (config.updated_at !== timestamps.updated_at) {
|
|
226
|
-
config.updated_at = timestamps.updated_at
|
|
227
|
-
modified = true
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
// 常规更新:只在 git 时间戳更新时更新,避免覆盖用户手动修改
|
|
231
|
-
if (!config.updated_at) {
|
|
232
|
-
// 如果没有 updated_at,初始化为 git 时间戳
|
|
233
|
-
config.updated_at = timestamps.updated_at
|
|
234
|
-
modified = true
|
|
235
|
-
} else if (timestamps.updated_at > config.updated_at) {
|
|
236
|
-
// 如果 git 显示有更新(README.md 被修改),才更新时间戳
|
|
237
|
-
config.updated_at = timestamps.updated_at
|
|
238
|
-
modified = true
|
|
239
|
-
}
|
|
240
|
-
// 如果 git 时间戳 <= config 时间戳,说明没有新的提交,保持原值
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (modified) {
|
|
244
|
-
// 保持字段顺序写回文件
|
|
245
|
-
this.noteManager.writeNoteConfig(configPath, config)
|
|
246
|
-
return true
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return false
|
|
250
|
-
} catch (error) {
|
|
251
|
-
logger.error(`修复时间戳失败: ${noteDir}`, error)
|
|
252
|
-
return false
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* 修复根配置文件的时间戳
|
|
258
|
-
* @param forceUpdate - 是否强制更新
|
|
259
|
-
* @returns 是否进行了修复
|
|
260
|
-
*/
|
|
261
|
-
private fixRootConfigTimestamps(forceUpdate: boolean = false): boolean {
|
|
262
|
-
try {
|
|
263
|
-
// 读取根配置文件
|
|
264
|
-
const configContent = readFileSync(ROOT_CONFIG_PATH, 'utf-8')
|
|
265
|
-
const config: TNotesConfig = JSON.parse(configContent)
|
|
266
|
-
|
|
267
|
-
// 获取首次提交时间(项目创建时间)
|
|
268
|
-
const createdAtOutput = execSync('git log --reverse --format=%ct', {
|
|
269
|
-
cwd: ROOT_DIR_PATH,
|
|
270
|
-
encoding: 'utf-8',
|
|
271
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
272
|
-
}).trim()
|
|
273
|
-
|
|
274
|
-
// 获取最后提交时间(项目最后更新时间)
|
|
275
|
-
const updatedAtOutput = execSync('git log -1 --format=%ct', {
|
|
276
|
-
cwd: ROOT_DIR_PATH,
|
|
277
|
-
encoding: 'utf-8',
|
|
278
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
279
|
-
}).trim()
|
|
280
|
-
|
|
281
|
-
if (!createdAtOutput || !updatedAtOutput) {
|
|
282
|
-
return false
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const firstTimestamp = createdAtOutput.split('\n')[0]
|
|
286
|
-
const createdAt = parseInt(firstTimestamp) * 1000
|
|
287
|
-
const updatedAt = parseInt(updatedAtOutput) * 1000
|
|
288
|
-
|
|
289
|
-
let modified = false
|
|
290
|
-
|
|
291
|
-
// 修复 created_at
|
|
292
|
-
if (
|
|
293
|
-
forceUpdate ||
|
|
294
|
-
!config.root_item.created_at ||
|
|
295
|
-
config.root_item.created_at !== createdAt
|
|
296
|
-
) {
|
|
297
|
-
config.root_item.created_at = createdAt
|
|
298
|
-
modified = true
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// 修复 updated_at
|
|
302
|
-
if (
|
|
303
|
-
forceUpdate ||
|
|
304
|
-
!config.root_item.updated_at ||
|
|
305
|
-
config.root_item.updated_at !== updatedAt
|
|
306
|
-
) {
|
|
307
|
-
config.root_item.updated_at = updatedAt
|
|
308
|
-
modified = true
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// 重新计算天数(从 1999-06-29 作为第 1 天开始计算)
|
|
312
|
-
if (modified) {
|
|
313
|
-
// 计算从出生日期到当前更新时间的天数
|
|
314
|
-
// 公式:Math.floor((updatedAt - BIRTH_DATE) / (1000 * 60 * 60 * 24)) + 1
|
|
315
|
-
// +1 是因为 1999-06-29 作为第 1 天
|
|
316
|
-
const daysSinceBirth =
|
|
317
|
-
Math.floor((updatedAt - BIRTH_DATE) / (1000 * 60 * 60 * 24)) + 1
|
|
318
|
-
config.root_item.days_since_birth = daysSinceBirth
|
|
319
|
-
|
|
320
|
-
// 写回配置文件
|
|
321
|
-
writeFileSync(
|
|
322
|
-
ROOT_CONFIG_PATH,
|
|
323
|
-
JSON.stringify(config, null, 2) + '\n',
|
|
324
|
-
'utf-8',
|
|
325
|
-
)
|
|
326
|
-
return true
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return false
|
|
330
|
-
} catch (error) {
|
|
331
|
-
logger.error('修复根配置文件时间戳失败', error)
|
|
332
|
-
return false
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* 修复所有笔记的时间戳
|
|
338
|
-
* @param forceUpdate - 是否强制更新(忽略现有值,用于修复历史错误数据)
|
|
339
|
-
* @returns 修复统计信息
|
|
340
|
-
*/
|
|
341
|
-
async fixAllTimestamps(forceUpdate: boolean = false): Promise<{
|
|
342
|
-
fixed: number
|
|
343
|
-
skipped: number
|
|
344
|
-
total: number
|
|
345
|
-
rootConfigFixed: boolean
|
|
346
|
-
}> {
|
|
347
|
-
if (forceUpdate) {
|
|
348
|
-
logger.info('正在强制修复笔记时间戳(使用 git 真实时间)...')
|
|
349
|
-
} else {
|
|
350
|
-
logger.info('正在修复笔记时间戳...')
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// 1. 修复根配置文件的时间戳
|
|
354
|
-
const rootConfigFixed = this.fixRootConfigTimestamps(forceUpdate)
|
|
355
|
-
if (rootConfigFixed) {
|
|
356
|
-
logger.success('✅ 根配置文件时间戳已修复')
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// 2. 批量获取所有笔记的 git 时间戳(只执行 2 次 git log)
|
|
360
|
-
const timestampMap = this.buildGitTimestampMap()
|
|
361
|
-
logger.info(`已从 git 历史中提取 ${timestampMap.size} 个文件的时间戳信息`)
|
|
362
|
-
|
|
363
|
-
// 3. 修复所有笔记的时间戳
|
|
364
|
-
if (!existsSync(NOTES_DIR_PATH)) {
|
|
365
|
-
logger.error('notes 目录不存在')
|
|
366
|
-
return { fixed: 0, skipped: 0, total: 0, rootConfigFixed }
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const noteDirs = readdirSync(NOTES_DIR_PATH)
|
|
370
|
-
.filter((name) => {
|
|
371
|
-
const fullPath = join(NOTES_DIR_PATH, name)
|
|
372
|
-
return statSync(fullPath).isDirectory() && /^\d{4}\./.test(name)
|
|
373
|
-
})
|
|
374
|
-
.sort()
|
|
375
|
-
|
|
376
|
-
let fixedCount = 0
|
|
377
|
-
let skippedCount = 0
|
|
378
|
-
|
|
379
|
-
for (const noteDir of noteDirs) {
|
|
380
|
-
const fixed = this.fixNoteTimestamps(noteDir, forceUpdate, timestampMap)
|
|
381
|
-
if (fixed) {
|
|
382
|
-
fixedCount++
|
|
383
|
-
} else {
|
|
384
|
-
skippedCount++
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (fixedCount > 0) {
|
|
389
|
-
logger.success(`时间戳修复完成: ${fixedCount} 个笔记已更新`)
|
|
390
|
-
} else {
|
|
391
|
-
logger.info('所有笔记时间戳均已正确')
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return {
|
|
395
|
-
fixed: fixedCount,
|
|
396
|
-
skipped: skippedCount,
|
|
397
|
-
total: noteDirs.length,
|
|
398
|
-
rootConfigFixed,
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 更新指定笔记的时间戳为当前时间
|
|
404
|
-
* @param noteDirNames - 笔记目录名数组
|
|
405
|
-
* @returns 更新的笔记数量
|
|
406
|
-
*/
|
|
407
|
-
async updateNotesTimestamp(noteDirNames: string[]): Promise<number> {
|
|
408
|
-
if (noteDirNames.length === 0) {
|
|
409
|
-
return 0
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const now = Date.now()
|
|
413
|
-
let updatedCount = 0
|
|
414
|
-
|
|
415
|
-
for (const noteDir of noteDirNames) {
|
|
416
|
-
const configPath = join(NOTES_DIR_PATH, noteDir, '.tnotes.json')
|
|
417
|
-
|
|
418
|
-
if (!existsSync(configPath)) {
|
|
419
|
-
continue
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
try {
|
|
423
|
-
// 读取配置
|
|
424
|
-
const configContent = readFileSync(configPath, 'utf-8')
|
|
425
|
-
const config: NoteConfig = JSON.parse(configContent)
|
|
426
|
-
|
|
427
|
-
// 更新 updated_at 为当前时间
|
|
428
|
-
config.updated_at = now
|
|
429
|
-
|
|
430
|
-
// 保持字段顺序写回文件
|
|
431
|
-
this.noteManager.writeNoteConfig(configPath, config)
|
|
432
|
-
updatedCount++
|
|
433
|
-
} catch (error) {
|
|
434
|
-
logger.error(`更新时间戳失败: ${noteDir}`, error)
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return updatedCount
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* 获取本次变更中包含 README.md 的笔记列表
|
|
443
|
-
* @param changedFiles - git status 返回的变更文件列表
|
|
444
|
-
* @returns 变更的笔记目录名数组
|
|
445
|
-
*/
|
|
446
|
-
getChangedNotes(changedFiles: string[]): string[] {
|
|
447
|
-
const changedNotes = new Set<string>()
|
|
448
|
-
|
|
449
|
-
for (let file of changedFiles) {
|
|
450
|
-
// 移除 git 添加的引号(git 对包含特殊字符的路径会加引号)
|
|
451
|
-
// 支持两种格式:
|
|
452
|
-
// 1. "notes/xxx/README.md" (完整包裹)
|
|
453
|
-
// 2. notes/xxx/README.md" (只有末尾引号)
|
|
454
|
-
file = file.replace(/^"(.*)"$/, '$1').replace(/"$/, '')
|
|
455
|
-
|
|
456
|
-
// 检查是否是 notes 目录下的 README.md 文件
|
|
457
|
-
const match = file.match(/^notes\/([^/]+)\/README\.md$/)
|
|
458
|
-
if (match) {
|
|
459
|
-
changedNotes.add(match[1])
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return Array.from(changedNotes)
|
|
464
|
-
}
|
|
465
|
-
}
|