@tnotesjs/core 0.1.28 → 0.2.1

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.
Files changed (71) hide show
  1. package/README.md +2 -1
  2. package/dist/{chunk-4KG6RXFS.js → chunk-5QN44ES5.js} +2946 -1533
  3. package/dist/{chunk-AGSL3VZE.js → chunk-5Y5IYS6C.js} +7 -0
  4. package/dist/cli/index.js +343 -40
  5. package/dist/index.js +1 -1
  6. package/dist/vitepress/config/index.js +573 -65
  7. package/package.json +4 -1
  8. package/templates/sub-repo/.gitattributes +30 -0
  9. package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
  10. package/templates/sub-repo/.vitepress/config.mts +12 -0
  11. package/templates/sub-repo/.vitepress/env.d.ts +10 -0
  12. package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
  13. package/templates/sub-repo/.vscode/settings.json +330 -0
  14. package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
  15. package/templates/sub-repo/public/favicon.ico +0 -0
  16. package/templates/sub-repo/public/logo.png +0 -0
  17. package/templates/sub-repo/tsconfig.json +45 -0
  18. package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
  19. package/vitepress/assets/icons/icon__setting_light.svg +4 -0
  20. package/vitepress/assets/icons/index.ts +2 -2
  21. package/vitepress/client/localSearchIndexBridge.ts +24 -0
  22. package/vitepress/components/Discussions/Discussions.vue +1 -1
  23. package/vitepress/components/Footprints/Footprints.vue +90 -25
  24. package/vitepress/components/Layout/CustomSidebar.vue +489 -117
  25. package/vitepress/components/Layout/Layout.vue +5 -0
  26. package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
  27. package/vitepress/components/Layout/SidebarItems.vue +517 -342
  28. package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
  29. package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
  30. package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
  31. package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
  32. package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
  33. package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
  34. package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
  35. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
  36. package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
  37. package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
  38. package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
  39. package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
  40. package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
  41. package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
  42. package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
  43. package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
  44. package/vitepress/components/MarkMap/MarkMap.vue +67 -10
  45. package/vitepress/components/Settings/Settings.vue +78 -74
  46. package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
  47. package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
  48. package/vitepress/components/constants.ts +18 -6
  49. package/vitepress/components/sidebar.data.ts +13 -11
  50. package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
  51. package/vitepress/components/sidebarTreeHelpers.ts +168 -0
  52. package/vitepress/components/utils/vscodePaths.test.ts +41 -0
  53. package/vitepress/components/utils/vscodePaths.ts +52 -0
  54. package/vitepress/config/index.ts +2 -0
  55. package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
  56. package/vitepress/plugins/localSearchReindex.test.ts +46 -0
  57. package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
  58. package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
  59. package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
  60. package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
  61. package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
  62. package/vitepress/theme/index.ts +8 -0
  63. package/vitepress/theme/styles/base.scss +0 -8
  64. package/vitepress/theme/styles/components/markmap.scss +23 -2
  65. package/vitepress/theme/styles/components/swiper.scss +1 -1
  66. package/vitepress/theme/styles/doc-layout.scss +212 -0
  67. package/vitepress/theme/styles/index.scss +3 -0
  68. package/vitepress/theme/styles/layout.scss +60 -10
  69. package/vitepress/assets/icons/icon__mindmap.svg +0 -1
  70. package/vitepress/assets/icons/icon__setting.svg +0 -1
  71. package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
@@ -152,6 +152,12 @@ var ConfigManager = class _ConfigManager {
152
152
  this.config = validatedConfig;
153
153
  return this.config;
154
154
  }
155
+ /**
156
+ * 清除内存中的配置缓存,下次 get/getAll 时重新从磁盘加载
157
+ */
158
+ clearCache() {
159
+ this.config = null;
160
+ }
155
161
  /**
156
162
  * 获取配置项
157
163
  */
@@ -188,6 +194,7 @@ function getConfigManager() {
188
194
  }
189
195
 
190
196
  export {
197
+ getDefaultConfig,
191
198
  ConfigManager,
192
199
  getConfigManager
193
200
  };
package/dist/cli/index.js CHANGED
@@ -4,33 +4,52 @@ import {
4
4
  COMMAND_DESCRIPTIONS,
5
5
  COMMAND_NAMES,
6
6
  COMMAND_OPTIONS,
7
+ DEFAULT_DESCRIPTION,
8
+ DEFAULT_PORT,
7
9
  FileWatcherService,
8
10
  GitManager,
9
11
  GitService,
12
+ InitSubRepoService,
10
13
  NoteIndexCache,
11
14
  NoteManager,
12
15
  NoteService,
13
16
  ROOT_CONFIG_PATH,
14
17
  ROOT_DIR_PATH,
18
+ ROOT_README_PATH,
19
+ ROOT_TOC_PATH,
15
20
  ReadmeService,
16
21
  RenameNoteCommand,
17
22
  TimestampService,
23
+ TocService,
18
24
  UpdateNoteConfigCommand,
19
25
  VitepressService,
26
+ buildFolderTocLine,
27
+ buildInitContext,
28
+ buildManualSteps,
29
+ buildTocLine,
20
30
  createLogger,
31
+ defaultDisplayName,
32
+ getInitializedConfigPath,
21
33
  handleError,
34
+ isAlreadyInitialized,
22
35
  logger,
36
+ migrateReadmeToToc,
23
37
  parseArgs,
24
38
  parseReadmeCompletedNotes,
25
- runCommand
26
- } from "../chunk-4KG6RXFS.js";
39
+ parseTocCompletedNotes,
40
+ processTocEmptyLines,
41
+ resolveNoteFromIndex,
42
+ runCommand,
43
+ validatePort,
44
+ validateTopic
45
+ } from "../chunk-5QN44ES5.js";
27
46
  import {
28
- ConfigManager
29
- } from "../chunk-AGSL3VZE.js";
47
+ ConfigManager,
48
+ getConfigManager
49
+ } from "../chunk-5Y5IYS6C.js";
30
50
 
31
51
  // commands/update/UpdateCommand.ts
32
52
  import { existsSync, readFileSync, writeFileSync } from "fs";
33
- import { resolve } from "path";
34
53
  var UpdateCommand = class extends BaseCommand {
35
54
  constructor() {
36
55
  super("update");
@@ -78,35 +97,36 @@ var UpdateCommand = class extends BaseCommand {
78
97
  }
79
98
  /**
80
99
  * 更新 root_item 配置
81
- * 只更新当前月份的完成笔记数量
100
+ * 更新当前月份的完成笔记数量,并自动补齐从最后已有月份到当前月份之间的所有缺失月份。
82
101
  */
83
102
  async updateRootItem() {
84
103
  try {
85
104
  const configContent = readFileSync(ROOT_CONFIG_PATH, "utf-8");
86
105
  const config = JSON.parse(configContent);
87
- const readmePath = resolve(ROOT_DIR_PATH, "README.md");
88
- if (!existsSync(readmePath)) {
89
- throw new Error("\u6839\u76EE\u5F55 README.md \u4E0D\u5B58\u5728");
106
+ if (!existsSync(ROOT_TOC_PATH)) {
107
+ throw new Error("\u6839\u76EE\u5F55 TOC.md \u4E0D\u5B58\u5728");
90
108
  }
91
- const readmeContent = readFileSync(readmePath, "utf-8");
92
- const { completedCount } = parseReadmeCompletedNotes(readmeContent);
109
+ const tocContent = readFileSync(ROOT_TOC_PATH, "utf-8");
110
+ const { completedCount } = parseTocCompletedNotes(tocContent);
93
111
  const now = /* @__PURE__ */ new Date();
94
112
  const yearShort = String(now.getFullYear()).slice(-2);
95
113
  const monthStr = String(now.getMonth() + 1).padStart(2, "0");
96
114
  const currentKey = `${yearShort}.${monthStr}`;
97
- const completedNotesCount = {
98
- ...config.root_item.completed_notes_count || {},
99
- [currentKey]: completedCount
100
- };
115
+ const existing = { ...config.root_item.completed_notes_count || {} };
116
+ const _countsBeforeFix = Object.keys(existing).length;
117
+ existing[currentKey] = completedCount;
118
+ const fixed = fillMissingMonthGaps(existing, currentKey);
101
119
  config.root_item = {
102
120
  ...config.root_item,
103
- completed_notes_count: completedNotesCount
121
+ completed_notes_count: fixed
104
122
  };
105
123
  delete config.root_item.completed_notes_count_last_month;
106
124
  writeFileSync(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
107
125
  if (!this.quiet) {
126
+ const filledCount = Object.keys(fixed).length - _countsBeforeFix;
127
+ const filledMsg = filledCount > 0 ? `\uFF0C\u8865\u9F50 ${filledCount} \u4E2A\u7F3A\u5931\u6708\u4EFD` : "";
108
128
  this.logger.success(
109
- `root_item \u914D\u7F6E\u5DF2\u66F4\u65B0: ${currentKey} \u6708\u5B8C\u6210 ${completedCount} \u7BC7\u7B14\u8BB0`
129
+ `root_item \u914D\u7F6E\u5DF2\u66F4\u65B0: ${currentKey} \u6708\u5B8C\u6210 ${completedCount} \u7BC7\u7B14\u8BB0${filledMsg}`
110
130
  );
111
131
  }
112
132
  } catch (error) {
@@ -119,6 +139,52 @@ var UpdateCommand = class extends BaseCommand {
119
139
  }
120
140
  }
121
141
  };
142
+ function fillMissingMonthGaps(counts, currentKey) {
143
+ const existingKeys = Object.keys(counts).sort();
144
+ const result = {};
145
+ const keySet = new Set(existingKeys);
146
+ keySet.add(currentKey);
147
+ const allKeys = Array.from(keySet).sort();
148
+ let prevValue = 0;
149
+ let lastKey = null;
150
+ for (const key of allKeys) {
151
+ if (lastKey !== null) {
152
+ const missing = generateMissingMonthKeys(lastKey, key);
153
+ for (const midKey of missing) {
154
+ result[midKey] = prevValue;
155
+ }
156
+ }
157
+ result[key] = counts[key] ?? prevValue;
158
+ prevValue = result[key];
159
+ lastKey = key;
160
+ }
161
+ return result;
162
+ }
163
+ function generateMissingMonthKeys(fromKey, toKey) {
164
+ const parseKey = (key) => {
165
+ const [yy, mm] = key.split(".").map(Number);
166
+ return { year: 2e3 + yy, month: mm };
167
+ };
168
+ const from = parseKey(fromKey);
169
+ const to = parseKey(toKey);
170
+ const missing = [];
171
+ let year = from.year;
172
+ let month = from.month;
173
+ while (true) {
174
+ month++;
175
+ if (month > 12) {
176
+ month = 1;
177
+ year++;
178
+ }
179
+ const shortYear = String(year).slice(-2);
180
+ const paddedMonth = String(month).padStart(2, "0");
181
+ const candidateKey = `${shortYear}.${paddedMonth}`;
182
+ if (candidateKey === toKey) break;
183
+ if (year > to.year || year === to.year && month > to.month) break;
184
+ missing.push(candidateKey);
185
+ }
186
+ return missing;
187
+ }
122
188
 
123
189
  // commands/git/PushCommand.ts
124
190
  var PushCommand = class extends BaseCommand {
@@ -225,14 +291,15 @@ var DevCommand = class extends BaseCommand {
225
291
  this.fileWatcherService = new FileWatcherService();
226
292
  this.noteIndexCache = NoteIndexCache.getInstance();
227
293
  this.noteManager = NoteManager.getInstance();
228
- this.readmeService = ReadmeService.getInstance();
294
+ this.tocService = TocService.getInstance();
229
295
  this.vitepressService = new VitepressService();
230
296
  }
231
297
  async run() {
232
298
  const notes = this.noteManager.scanNotes();
233
299
  this.logger.info(`\u626B\u63CF\u5230 ${notes.length} \u7BC7\u7B14\u8BB0`);
234
300
  this.noteIndexCache.initialize(notes);
235
- await this.readmeService.regenerateSidebar(notes);
301
+ this.tocService.ensureTocExists();
302
+ await this.tocService.regenerateSidebar(notes);
236
303
  const result = await this.vitepressService.startServer();
237
304
  if (result) {
238
305
  const versionInfo = result.version ? `\uFF08v${result.version}\uFF09` : "";
@@ -254,6 +321,224 @@ var DevCommand = class extends BaseCommand {
254
321
  }
255
322
  };
256
323
 
324
+ // commands/init-sub-repo/InitSubRepoCommand.ts
325
+ import { readFileSync as readFileSync2 } from "fs";
326
+ import { join, dirname } from "path";
327
+ import { createInterface } from "readline";
328
+ import { fileURLToPath } from "url";
329
+ import { v4 as uuidv4 } from "uuid";
330
+ function readCoreVersion() {
331
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
332
+ let dir = moduleDir;
333
+ while (dir !== dirname(dir)) {
334
+ const pkgPath = join(dir, "package.json");
335
+ try {
336
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
337
+ if (pkg.name === "@tnotesjs/core" && pkg.version) return pkg.version;
338
+ } catch {
339
+ }
340
+ dir = dirname(dir);
341
+ }
342
+ return "0.1.28";
343
+ }
344
+ var InitSubRepoCommand = class extends BaseCommand {
345
+ constructor() {
346
+ super("init-sub-repo");
347
+ this.rootPath = process.cwd();
348
+ }
349
+ async run() {
350
+ if (isAlreadyInitialized(this.rootPath)) {
351
+ const configPath = getInitializedConfigPath(this.rootPath);
352
+ this.logger.info(
353
+ `\u68C0\u6D4B\u5230 .tnotes.json \u914D\u7F6E\u6587\u4EF6\uFF1A${configPath}\uFF0C\u5F53\u524D\u4ED3\u5E93\u5DF2\u7ECF\u662F\u4E00\u4E2A TNotes \u77E5\u8BC6\u5E93`
354
+ );
355
+ return;
356
+ }
357
+ this.logger.info("\u521D\u59CB\u5316 TNotes \u5B50\u77E5\u8BC6\u5E93");
358
+ this.logger.info("");
359
+ const input = await this.promptForInput();
360
+ const ctx = buildInitContext(input);
361
+ this.logger.info("");
362
+ this.logger.info("\u5373\u5C06\u521B\u5EFA\u4EE5\u4E0B\u77E5\u8BC6\u5E93\uFF1A");
363
+ this.logger.info(` \u4ED3\u5E93\u540D\uFF1A${ctx.repoName}`);
364
+ this.logger.info(` \u5C55\u793A\u540D\uFF1A${ctx.displayName}`);
365
+ this.logger.info(` \u7AEF\u53E3\uFF1A${ctx.port}`);
366
+ this.logger.info(` \u63CF\u8FF0\uFF1A${ctx.description}`);
367
+ this.logger.info("");
368
+ const confirmed = await this.promptConfirm("\u786E\u8BA4\u521D\u59CB\u5316\uFF1F [Y/n] ");
369
+ if (!confirmed) {
370
+ this.logger.info("\u5DF2\u53D6\u6D88\u521D\u59CB\u5316");
371
+ return;
372
+ }
373
+ const service = InitSubRepoService.fromModuleUrl(import.meta.url, this.rootPath);
374
+ const result = service.writeScaffold(input);
375
+ if (result.created.length > 0) {
376
+ this.logger.success(`\u5DF2\u521B\u5EFA ${result.created.length} \u4E2A\u6587\u4EF6\uFF1A`);
377
+ for (const file of result.created) {
378
+ this.logger.info(` + ${file}`);
379
+ }
380
+ }
381
+ if (result.updated.length > 0) {
382
+ this.logger.success(`\u5DF2\u66F4\u65B0 ${result.updated.length} \u4E2A\u6587\u4EF6\uFF1A`);
383
+ for (const file of result.updated) {
384
+ this.logger.info(` ~ ${file}`);
385
+ }
386
+ }
387
+ if (result.skipped.length > 0) {
388
+ this.logger.warn(`\u5DF2\u8DF3\u8FC7 ${result.skipped.length} \u4E2A\u5DF2\u5B58\u5728\u6587\u4EF6\uFF1A`);
389
+ for (const file of result.skipped) {
390
+ this.logger.info(` - ${file}`);
391
+ }
392
+ }
393
+ getConfigManager().clearCache();
394
+ try {
395
+ this.logger.info("");
396
+ this.logger.info("\u6B63\u5728\u89C4\u8303\u5316 TOC.md \u5E76\u751F\u6210 sidebar.json...");
397
+ const noteService = NoteService.getInstance();
398
+ const tocService = TocService.getInstance();
399
+ const notes = noteService.getAllNotes();
400
+ await tocService.normalizeToc(notes);
401
+ await tocService.regenerateSidebar(notes);
402
+ this.logger.success("sidebar.json \u5DF2\u751F\u6210");
403
+ } catch (error) {
404
+ this.logger.warn(
405
+ `\u81EA\u52A8 update \u5931\u8D25\uFF08\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C pnpm tn:update\uFF09\uFF1A${error instanceof Error ? error.message : String(error)}`
406
+ );
407
+ }
408
+ this.logger.info("");
409
+ this.logger.success("\u5B50\u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
410
+ this.logger.info("");
411
+ this.logger.info("\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A");
412
+ for (const [index, step] of buildManualSteps(ctx).entries()) {
413
+ this.logger.info(` ${index + 1}. ${step}`);
414
+ }
415
+ }
416
+ async promptForInput() {
417
+ const topic = await this.promptTopic();
418
+ const displayName = await this.promptDisplayName(topic);
419
+ const port2 = await this.promptPort();
420
+ const description = await this.promptDescription();
421
+ return {
422
+ topic,
423
+ displayName,
424
+ port: port2,
425
+ description,
426
+ repoUuid: uuidv4(),
427
+ noteUuid: uuidv4(),
428
+ coreVersion: readCoreVersion()
429
+ };
430
+ }
431
+ async promptTopic() {
432
+ while (true) {
433
+ const answer = await this.ask("\u8BF7\u8F93\u5165 topic\uFF08\u5982 react\uFF0C\u5C06\u751F\u6210 TNotes.react\uFF09: ");
434
+ const error = validateTopic(answer);
435
+ if (error) {
436
+ this.logger.warn(error);
437
+ continue;
438
+ }
439
+ return answer.trim();
440
+ }
441
+ }
442
+ async promptDisplayName(topic) {
443
+ const fallback = defaultDisplayName(topic);
444
+ const answer = await this.ask(
445
+ `\u8BF7\u8F93\u5165\u5C55\u793A\u540D\uFF08\u7528\u4E8E\u9996\u9875 hero\uFF0C\u9ED8\u8BA4 ${fallback}\uFF09: `
446
+ );
447
+ return answer.trim() || fallback;
448
+ }
449
+ async promptPort() {
450
+ const answer = await this.ask(`\u8BF7\u8F93\u5165 dev \u7AEF\u53E3\uFF08\u9ED8\u8BA4 ${DEFAULT_PORT}\uFF09: `);
451
+ const { port: port2, error } = validatePort(answer);
452
+ if (error) this.logger.warn(error);
453
+ return port2;
454
+ }
455
+ async promptDescription() {
456
+ const answer = await this.ask(
457
+ `\u8BF7\u8F93\u5165\u4ED3\u5E93\u63CF\u8FF0\uFF08\u9ED8\u8BA4\uFF1A${DEFAULT_DESCRIPTION}\uFF09: `
458
+ );
459
+ return answer.trim() || DEFAULT_DESCRIPTION;
460
+ }
461
+ async promptConfirm(question) {
462
+ const answer = await this.ask(question);
463
+ const normalized = answer.trim().toLowerCase();
464
+ return normalized === "" || normalized === "y" || normalized === "yes";
465
+ }
466
+ ask(question) {
467
+ const rl = createInterface({
468
+ input: process.stdin,
469
+ output: process.stdout
470
+ });
471
+ return new Promise((resolve) => {
472
+ rl.question(question, (answer) => {
473
+ rl.close();
474
+ resolve(answer);
475
+ });
476
+ });
477
+ }
478
+ };
479
+
480
+ // commands/init-toc/InitTocCommand.ts
481
+ import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
482
+ var InitTocCommand = class extends BaseCommand {
483
+ constructor() {
484
+ super("init-toc");
485
+ this.noteManager = NoteManager.getInstance();
486
+ this.tocService = TocService.getInstance();
487
+ }
488
+ async run() {
489
+ if (!existsSync2(ROOT_README_PATH)) {
490
+ throw new Error(`README.md \u4E0D\u5B58\u5728: ${ROOT_README_PATH}`);
491
+ }
492
+ const readmeContent = readFileSync3(ROOT_README_PATH, "utf-8");
493
+ const notes = this.noteManager.scanNotes();
494
+ const { entries, warnings } = migrateReadmeToToc(readmeContent);
495
+ for (const warning of warnings) {
496
+ this.logger.warn(warning);
497
+ }
498
+ const lines = [];
499
+ let skipped = 0;
500
+ for (const entry of entries) {
501
+ if (entry.kind === "folder") {
502
+ lines.push(buildFolderTocLine(entry.folderTitle, entry.indent));
503
+ continue;
504
+ }
505
+ const note = resolveNoteFromIndex(entry.noteIndex, notes);
506
+ if (!note) {
507
+ skipped++;
508
+ this.logger.warn(
509
+ `\u8DF3\u8FC7 README \u4E2D\u5B58\u5728\u4F46 notes/ \u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0: ${entry.noteIndex}`
510
+ );
511
+ continue;
512
+ }
513
+ lines.push(buildTocLine(note, entry.indent, entry.completed));
514
+ }
515
+ if (lines.length === 0) {
516
+ throw new Error(
517
+ "\u672A\u80FD\u4ECE README.md \u89E3\u6790\u5230\u4EFB\u4F55\u6709\u6548\u7B14\u8BB0\u6761\u76EE\uFF0CTOC.md \u672A\u5199\u5165"
518
+ );
519
+ }
520
+ const content = processTocEmptyLines(lines).join("\n");
521
+ writeFileSync2(ROOT_TOC_PATH, content, "utf-8");
522
+ await this.tocService.regenerateSidebar(notes);
523
+ this.logger.success(
524
+ `TOC.md \u5DF2\u4ECE README.md \u521D\u59CB\u5316\uFF08${lines.length} \u6761\u7B14\u8BB0${skipped > 0 ? `\uFF0C\u8DF3\u8FC7 ${skipped} \u6761` : ""}\uFF09`
525
+ );
526
+ this.logger.info(`\u8F93\u51FA\u8DEF\u5F84: ${ROOT_TOC_PATH}`);
527
+ this.logger.info(
528
+ "\u8FC7\u6E21\u547D\u4EE4 init-toc \u4EC5\u5728 v0.2.x \u63D0\u4F9B\uFF0Cv0.3.x \u5C06\u79FB\u9664\uFF1B\u540E\u7EED\u8BF7\u4F7F\u7528 TOC.md + pnpm tn:update"
529
+ );
530
+ const listedIndexes = new Set(
531
+ entries.filter((e) => e.kind === "note").map((e) => e.noteIndex)
532
+ );
533
+ const missingInReadme = notes.filter((n) => !listedIndexes.has(n.index));
534
+ if (missingInReadme.length > 0) {
535
+ this.logger.info(
536
+ `README \u4E2D\u672A\u5217\u51FA ${missingInReadme.length} \u7BC7\u7B14\u8BB0\uFF0C\u53EF\u6267\u884C pnpm tn:update \u8FFD\u52A0\u5230 TOC.md`
537
+ );
538
+ }
539
+ }
540
+ };
541
+
257
542
  // commands/maintenance/FixTimestampsCommand.ts
258
543
  var FixTimestampsCommand = class extends BaseCommand {
259
544
  constructor() {
@@ -294,6 +579,8 @@ var COMMAND_CATEGORIES = {
294
579
  COMMAND_NAMES.CREATE_NOTES
295
580
  ],
296
581
  "Git \u64CD\u4F5C": [COMMAND_NAMES.PUSH, COMMAND_NAMES.PULL],
582
+ "\u8FC1\u79FB\uFF08v0.2.x \u8FC7\u6E21\uFF09": [COMMAND_NAMES.INIT_TOC],
583
+ \u521D\u59CB\u5316: [COMMAND_NAMES.INIT_SUB_REPO],
297
584
  \u5176\u4ED6: [COMMAND_NAMES.FIX_TIMESTAMPS, COMMAND_NAMES.HELP]
298
585
  };
299
586
  var COMMAND_OPTIONS_INFO = {
@@ -337,6 +624,9 @@ var HelpCommand = class extends BaseCommand {
337
624
  this.logger.info(" pnpm tn:build");
338
625
  this.logger.info(" pnpm tn:create-notes # \u6279\u91CF\u521B\u5EFA\u7B14\u8BB0");
339
626
  this.logger.info(" pnpm tn:update");
627
+ this.logger.info(
628
+ " pnpm tn:init-toc # v0.1.x \u2192 v0.2.x\uFF1A\u4ECE README.md \u751F\u6210 TOC.md"
629
+ );
340
630
  this.logger.info(
341
631
  " pnpm tn:update-completed-count # \u751F\u6210\u5F53\u524D\u77E5\u8BC6\u5E93\u6700\u8FD1 12 \u4E2A\u6708\u7684\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u7EDF\u8BA1"
342
632
  );
@@ -358,8 +648,8 @@ var HelpCommand = class extends BaseCommand {
358
648
  };
359
649
 
360
650
  // commands/note/CreateNoteCommand.ts
361
- import { createInterface } from "readline";
362
- import { v4 as uuidv4 } from "uuid";
651
+ import { createInterface as createInterface2 } from "readline";
652
+ import { v4 as uuidv42 } from "uuid";
363
653
  var CreateNoteCommand = class extends BaseCommand {
364
654
  constructor() {
365
655
  super("create-notes");
@@ -389,7 +679,7 @@ var CreateNoteCommand = class extends BaseCommand {
389
679
  title = `new`;
390
680
  this.logger.info(`[${i}/${count}] \u521B\u5EFA\u7B14\u8BB0: ${title}`);
391
681
  }
392
- const configId = uuidv4();
682
+ const configId = uuidv42();
393
683
  const note = await this.noteService.createNote({
394
684
  title: title || `new`,
395
685
  enableDiscussions: false,
@@ -433,27 +723,27 @@ var CreateNoteCommand = class extends BaseCommand {
433
723
  * 提示用户输入要创建的笔记数量
434
724
  */
435
725
  async promptForCount() {
436
- const rl = createInterface({
726
+ const rl = createInterface2({
437
727
  input: process.stdin,
438
728
  output: process.stdout
439
729
  });
440
- return new Promise((resolve2) => {
730
+ return new Promise((resolve) => {
441
731
  rl.question("\n\u{1F4DD} \u8BF7\u8F93\u5165\u8981\u521B\u5EFA\u7684\u7B14\u8BB0\u6570\u91CF\uFF08\u9ED8\u8BA4\u4E3A 1\uFF09: ", (answer) => {
442
732
  rl.close();
443
733
  const trimmed = answer.trim();
444
734
  if (!trimmed) {
445
735
  this.logger.info("\u4F7F\u7528\u9ED8\u8BA4\u6570\u91CF: 1");
446
- resolve2(1);
736
+ resolve(1);
447
737
  return;
448
738
  }
449
739
  const num = parseInt(trimmed, 10);
450
740
  if (isNaN(num) || num < 1 || !Number.isInteger(num)) {
451
741
  this.logger.warn(`\u8F93\u5165 "${trimmed}" \u4E0D\u662F\u6709\u6548\u7684\u6B63\u6574\u6570\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: 1`);
452
- resolve2(1);
742
+ resolve(1);
453
743
  return;
454
744
  }
455
745
  this.logger.info(`\u5C06\u521B\u5EFA ${num} \u7BC7\u7B14\u8BB0`);
456
- resolve2(num);
746
+ resolve(num);
457
747
  });
458
748
  });
459
749
  }
@@ -461,14 +751,14 @@ var CreateNoteCommand = class extends BaseCommand {
461
751
  * 提示用户输入笔记标题
462
752
  */
463
753
  async promptForTitle() {
464
- const rl = createInterface({
754
+ const rl = createInterface2({
465
755
  input: process.stdin,
466
756
  output: process.stdout
467
757
  });
468
- return new Promise((resolve2) => {
758
+ return new Promise((resolve) => {
469
759
  rl.question("\u8BF7\u8F93\u5165\u7B14\u8BB0\u6807\u9898: ", (answer) => {
470
760
  rl.close();
471
- resolve2(answer.trim());
761
+ resolve(answer.trim());
472
762
  });
473
763
  });
474
764
  }
@@ -476,7 +766,7 @@ var CreateNoteCommand = class extends BaseCommand {
476
766
 
477
767
  // commands/update-completed-count/UpdateCompletedCountCommand.ts
478
768
  import { execSync } from "child_process";
479
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
769
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
480
770
  var UpdateCompletedCountCommand = class extends BaseCommand {
481
771
  constructor() {
482
772
  super("update-completed-count");
@@ -490,7 +780,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
490
780
  async updateCurrentRepo() {
491
781
  const startTime = Date.now();
492
782
  try {
493
- const configContent = readFileSync2(ROOT_CONFIG_PATH, "utf-8");
783
+ const configContent = readFileSync4(ROOT_CONFIG_PATH, "utf-8");
494
784
  const config = JSON.parse(configContent);
495
785
  this.logger.info("\u5F00\u59CB\u66F4\u65B0\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u5386\u53F2\u8BB0\u5F55...");
496
786
  const completedNotesCountHistory = await this.getCompletedNotesCountHistory(
@@ -500,7 +790,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
500
790
  ...config.root_item,
501
791
  completed_notes_count: completedNotesCountHistory
502
792
  };
503
- writeFileSync2(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
793
+ writeFileSync3(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
504
794
  const duration = Date.now() - startTime;
505
795
  const monthKeys = Object.keys(completedNotesCountHistory);
506
796
  const currentKey = monthKeys[monthKeys.length - 1];
@@ -520,8 +810,8 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
520
810
  *
521
811
  * 逻辑:
522
812
  * 1. 计算最近12个月的范围(当前月份往前推11个月)
523
- * 2. 遍历这12个月,从 Git 历史中读取 README.md
524
- * 3. 解析 README.md 获取完成笔记数量
813
+ * 2. 遍历这12个月,从 Git 历史中读取 TOC.md(回退 README.md
814
+ * 3. 解析获取完成笔记数量
525
815
  * 4. 如果知识库创建时间在这12个月内,之前的月份补0
526
816
  * 5. 返回对象 { '25.01': 0, '25.02': 1, ..., '25.12': 15 }
527
817
  */
@@ -589,26 +879,37 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
589
879
  const monthStr = String(lastDayOfMonth.getMonth() + 1).padStart(2, "0");
590
880
  const dayStr = String(lastDayOfMonth.getDate()).padStart(2, "0");
591
881
  const untilDate = `${yearStr}-${monthStr}-${dayStr} 23:59:59 +0800`;
592
- const commitHash = execSync(
593
- `git log --until="${untilDate}" --format=%H -1 -- README.md`,
882
+ let commitHash = execSync(
883
+ `git log --until="${untilDate}" --format=%H -1 -- TOC.md`,
594
884
  {
595
885
  cwd: ROOT_DIR_PATH,
596
886
  encoding: "utf-8"
597
887
  }
598
888
  ).trim();
889
+ let filePath = "TOC.md";
890
+ if (!commitHash) {
891
+ commitHash = execSync(
892
+ `git log --until="${untilDate}" --format=%H -1 -- README.md`,
893
+ {
894
+ cwd: ROOT_DIR_PATH,
895
+ encoding: "utf-8"
896
+ }
897
+ ).trim();
898
+ filePath = "README.md";
899
+ }
599
900
  if (!commitHash) {
600
901
  return fallbackCount;
601
902
  }
602
- let readmeContent;
903
+ let fileContent;
603
904
  try {
604
- readmeContent = execSync(`git show ${commitHash}:README.md`, {
905
+ fileContent = execSync(`git show ${commitHash}:${filePath}`, {
605
906
  cwd: ROOT_DIR_PATH,
606
907
  encoding: "utf-8"
607
908
  });
608
909
  } catch (error) {
609
910
  return fallbackCount;
610
911
  }
611
- const { completedCount } = parseReadmeCompletedNotes(readmeContent);
912
+ const { completedCount } = filePath === "TOC.md" ? parseTocCompletedNotes(fileContent) : parseReadmeCompletedNotes(fileContent);
612
913
  return completedCount;
613
914
  }
614
915
  };
@@ -619,6 +920,8 @@ var commandFactories = {
619
920
  "create-notes": () => new CreateNoteCommand(),
620
921
  "dev": () => new DevCommand(),
621
922
  "fix-timestamps": () => new FixTimestampsCommand(),
923
+ "init-sub-repo": () => new InitSubRepoCommand(),
924
+ "init-toc": () => new InitTocCommand(),
622
925
  "help": () => new HelpCommand(),
623
926
  "preview": () => new PreviewCommand(),
624
927
  "pull": () => new PullCommand(),
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ConfigManager,
3
3
  getConfigManager
4
- } from "./chunk-AGSL3VZE.js";
4
+ } from "./chunk-5Y5IYS6C.js";
5
5
  export {
6
6
  ConfigManager,
7
7
  getConfigManager