@tnotesjs/core 0.6.0 → 0.7.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.
Files changed (42) hide show
  1. package/config/templates.ts +0 -5
  2. package/dist/{chunk-IHFUHB4J.cjs → chunk-7FEANCT6.cjs} +17 -8
  3. package/dist/chunk-AGVER5MX.cjs +1746 -0
  4. package/dist/{chunk-TJ4527KA.cjs → chunk-BL22KD4V.cjs} +324 -950
  5. package/dist/{chunk-UNFSW5C2.js → chunk-GWG75A5M.js} +14 -5
  6. package/dist/{chunk-DUTS75WQ.js → chunk-XNO3PCEH.js} +374 -1000
  7. package/dist/chunk-ZEQS64PE.js +1746 -0
  8. package/dist/cli/index.cjs +76 -155
  9. package/dist/cli/index.js +10 -89
  10. package/dist/markdown/index.cjs +2 -3
  11. package/dist/markdown/index.d.cts +1 -1
  12. package/dist/markdown/index.d.ts +1 -1
  13. package/dist/markdown/index.js +1 -2
  14. package/dist/{types-CwBWwNVv.d.ts → types-C_d0fFeD.d.ts} +8 -1
  15. package/dist/{types-CpsEy8lA.d.cts → types-MRBGgJTX.d.cts} +8 -1
  16. package/dist/vitepress/config/index.cjs +200 -132
  17. package/dist/vitepress/config/index.js +187 -119
  18. package/dist/workspace/index.cjs +4 -1202
  19. package/dist/workspace/index.d.cts +2 -2
  20. package/dist/workspace/index.d.ts +2 -2
  21. package/dist/workspace/index.js +4 -1202
  22. package/package.json +2 -2
  23. package/services/file-watcher/folderChangeHandler.ts +19 -15
  24. package/services/file-watcher/globalUpdateCoordinator.ts +21 -27
  25. package/services/file-watcher/service.ts +1 -6
  26. package/services/index.ts +0 -1
  27. package/services/note/service.ts +4 -6
  28. package/services/readme/service.ts +11 -18
  29. package/services/reconcileToc.ts +20 -0
  30. package/utils/index.ts +0 -9
  31. package/{services/toc → utils}/moveTocInside.test.ts +1 -1
  32. package/vitepress/components/constants.ts +2 -2
  33. package/vitepress/components/sidebar.data.ts +8 -5
  34. package/vitepress/plugins/sidebarStructurePlugin.ts +242 -145
  35. package/dist/chunk-3R7QSABB.cjs +0 -502
  36. package/dist/chunk-CZXRQPFJ.js +0 -11
  37. package/dist/chunk-HXED7KVT.cjs +0 -11
  38. package/dist/chunk-U6IBLREL.js +0 -502
  39. package/services/toc/index.ts +0 -5
  40. package/services/toc/service.ts +0 -759
  41. package/utils/migrateReadmeToToc.test.ts +0 -111
  42. package/utils/migrateReadmeToToc.ts +0 -135
@@ -4,32 +4,13 @@ import {
4
4
  getDefaultConfig
5
5
  } from "./chunk-UJG3UU5X.js";
6
6
  import {
7
- TOC_INDENT_SPACES,
8
- adjustTocLineIndexAfterSubtreeRemoval,
9
- buildFolderTocLine,
10
- buildSidebarFromTocTree,
11
- buildTocLine,
12
- collectNoteIndexesInSubtree,
13
- findFolderLineIndex,
14
- findTocLineIndex,
7
+ createWorkspace,
15
8
  generateNoteTitle,
16
- getNewNoteReadmeBody,
17
- getPreviousNoteIndexOutsideSubtree,
18
- getPreviousTocNoteIndex,
19
- getTocEntrySubtreeRange,
20
- getTocLineCompleted,
21
- isNoteIndexInSubtree,
22
- parseNodeId,
23
- parseTocLine,
24
- parseTocToTree,
25
- processTocEmptyLines,
26
- renameFolderLine,
27
- resolveNoteFromIndex,
28
- serializeTocTree
29
- } from "./chunk-U6IBLREL.js";
9
+ getNewNoteReadmeBody
10
+ } from "./chunk-ZEQS64PE.js";
30
11
  import {
31
12
  generateAnchor
32
- } from "./chunk-CZXRQPFJ.js";
13
+ } from "./chunk-GWG75A5M.js";
33
14
 
34
15
  // utils/errorHandler.ts
35
16
  var TNotesError = class _TNotesError extends Error {
@@ -476,81 +457,6 @@ function parseReadmeCompletedNotes(content) {
476
457
  };
477
458
  }
478
459
 
479
- // utils/migrateReadmeToToc.ts
480
- var README_TOC_END_TAG = "<!-- endregion:toc -->";
481
- function extractReadmeBodyAfterToc(content) {
482
- const endTagIndex = content.indexOf(README_TOC_END_TAG);
483
- if (endTagIndex === -1) {
484
- throw new Error(
485
- `README.md \u4E2D\u672A\u627E\u5230 ${README_TOC_END_TAG}\uFF0C\u65E0\u6CD5\u6267\u884C init-toc \u8FC1\u79FB`
486
- );
487
- }
488
- const afterTag = content.slice(endTagIndex + README_TOC_END_TAG.length);
489
- return afterTag.split("\n");
490
- }
491
- function migrateReadmeToToc(content) {
492
- const lines = extractReadmeBodyAfterToc(content);
493
- const warnings = [];
494
- const seenIndexes = /* @__PURE__ */ new Set();
495
- const sections = [];
496
- let currentHeading = null;
497
- let currentGroup = [];
498
- const flushGroup = () => {
499
- if (currentGroup.length > 0) {
500
- sections.push({ heading: currentHeading, items: currentGroup });
501
- }
502
- currentGroup = [];
503
- };
504
- for (const line of lines) {
505
- const headingMatch = line.match(/^(#{2,})\s+(.+)$/);
506
- if (headingMatch) {
507
- flushGroup();
508
- currentHeading = headingMatch[2].trim();
509
- continue;
510
- }
511
- const parsed = parseTocLine(line);
512
- if (!parsed.isMatch || !parsed.noteIndex) continue;
513
- if (seenIndexes.has(parsed.noteIndex)) {
514
- warnings.push(`\u8DF3\u8FC7\u91CD\u590D\u7B14\u8BB0\u7D22\u5F15: ${parsed.noteIndex}`);
515
- continue;
516
- }
517
- seenIndexes.add(parsed.noteIndex);
518
- currentGroup.push({
519
- noteIndex: parsed.noteIndex,
520
- completed: parsed.completed
521
- });
522
- }
523
- flushGroup();
524
- const entries = [];
525
- for (const section of sections) {
526
- if (section.heading) {
527
- entries.push({
528
- kind: "folder",
529
- folderTitle: section.heading,
530
- indent: 0
531
- });
532
- for (const item of section.items) {
533
- entries.push({
534
- kind: "note",
535
- noteIndex: item.noteIndex,
536
- indent: 1,
537
- completed: item.completed
538
- });
539
- }
540
- continue;
541
- }
542
- for (const item of section.items) {
543
- entries.push({
544
- kind: "note",
545
- noteIndex: item.noteIndex,
546
- indent: 0,
547
- completed: item.completed
548
- });
549
- }
550
- }
551
- return { entries, warnings };
552
- }
553
-
554
460
  // utils/portUtils.ts
555
461
  import { execSync as execSync2 } from "child_process";
556
462
  function isPortInUse(port2) {
@@ -1213,7 +1119,6 @@ var COMMAND_NAMES = {
1213
1119
  PULL: "pull",
1214
1120
  PUSH: "push",
1215
1121
  RENAME_NOTE: "rename-note",
1216
- INIT_TOC: "init-toc",
1217
1122
  INIT_SUB_REPO: "init-sub-repo",
1218
1123
  UPDATE: "update",
1219
1124
  UPDATE_COMPLETED_COUNT: "update-completed-count",
@@ -1223,7 +1128,6 @@ var COMMAND_DESCRIPTIONS = {
1223
1128
  [COMMAND_NAMES.DEV]: "\u542F\u52A8\u77E5\u8BC6\u5E93\u5F00\u53D1\u670D\u52A1",
1224
1129
  [COMMAND_NAMES.BUILD]: "\u6784\u5EFA\u77E5\u8BC6\u5E93",
1225
1130
  [COMMAND_NAMES.PREVIEW]: "\u9884\u89C8\u6784\u5EFA\u540E\u7684\u77E5\u8BC6\u5E93",
1226
- [COMMAND_NAMES.INIT_TOC]: "\u4ECE README.md \u521D\u59CB\u5316 TOC.md\uFF08v0.2.x \u8FC7\u6E21\u547D\u4EE4\uFF0Cv0.3.x \u79FB\u9664\uFF09",
1227
1131
  [COMMAND_NAMES.INIT_SUB_REPO]: "\u521D\u59CB\u5316 TNotes \u5B50\u77E5\u8BC6\u5E93\uFF08\u4E00\u6B21\u6027\u811A\u624B\u67B6\uFF09",
1228
1132
  [COMMAND_NAMES.UPDATE]: "\u6839\u636E\u7B14\u8BB0\u5185\u5BB9\u66F4\u65B0\u77E5\u8BC6\u5E93",
1229
1133
  [COMMAND_NAMES.UPDATE_COMPLETED_COUNT]: "\u66F4\u65B0\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u5386\u53F2\u8BB0\u5F55\uFF08\u8FD1 1 \u5E74\uFF0C\u6700\u8FD1 12 \u4E2A\u6708\uFF09",
@@ -1385,783 +1289,6 @@ var NoteIndexCache = class _NoteIndexCache {
1385
1289
  }
1386
1290
  };
1387
1291
 
1388
- // services/toc/service.ts
1389
- import {
1390
- existsSync as existsSync2,
1391
- readFileSync as readFileSync3,
1392
- writeFileSync as writeFileSync3,
1393
- promises as fsPromises2
1394
- } from "fs";
1395
-
1396
- // services/note/service.ts
1397
- import { writeFileSync as writeFileSync2, readFileSync as readFileSync2, promises as fsPromises } from "fs";
1398
- import { join as join3 } from "path";
1399
- import { v4 as uuidv4 } from "uuid";
1400
- var NoteService = class _NoteService {
1401
- constructor() {
1402
- this.ignoredConfigPaths = /* @__PURE__ */ new Set();
1403
- this.noteManager = NoteManager.getInstance();
1404
- this.noteIndexCache = NoteIndexCache.getInstance();
1405
- }
1406
- static getInstance() {
1407
- if (!_NoteService.instance) {
1408
- _NoteService.instance = new _NoteService();
1409
- }
1410
- return _NoteService.instance;
1411
- }
1412
- /**
1413
- * 标记配置文件在下次变更时被忽略(防止 API 写入触发文件监听循环)
1414
- * @param configPath - 配置文件路径
1415
- */
1416
- ignoreNextConfigChange(configPath) {
1417
- this.ignoredConfigPaths.add(configPath);
1418
- }
1419
- /**
1420
- * 检查配置文件是否应该被忽略
1421
- * @param configPath - 配置文件路径
1422
- * @returns 是否应该忽略
1423
- */
1424
- shouldIgnoreConfigChange(configPath) {
1425
- if (this.ignoredConfigPaths.has(configPath)) {
1426
- this.ignoredConfigPaths.delete(configPath);
1427
- return true;
1428
- }
1429
- return false;
1430
- }
1431
- /**
1432
- * 获取所有笔记
1433
- * dev 模式下(缓存已初始化)从内存读取,其他模式回退到文件扫描
1434
- * @returns 笔记信息数组
1435
- */
1436
- getAllNotes() {
1437
- if (this.noteIndexCache.isInitialized()) {
1438
- return this.noteIndexCache.toNoteInfoList();
1439
- }
1440
- return this.noteManager.scanNotes();
1441
- }
1442
- /**
1443
- * 获取笔记(通过索引)
1444
- * @param noteIndex - 笔记索引(文件夹前 4 位数字)
1445
- * @returns 笔记信息,未找到时返回 undefined
1446
- */
1447
- getNoteByIndex(noteIndex) {
1448
- return this.noteManager.getNoteByIndex(noteIndex);
1449
- }
1450
- /**
1451
- * 创建新笔记
1452
- * @param options - 创建选项
1453
- * @returns 新创建的笔记信息
1454
- */
1455
- async createNote(options = {}) {
1456
- const {
1457
- title = "new",
1458
- category,
1459
- enableDiscussions = false,
1460
- configId,
1461
- usedIndexes
1462
- } = options;
1463
- const noteIndex = this.generateNextNoteIndex(usedIndexes);
1464
- const dirName = `${noteIndex}. ${title}`;
1465
- const notePath = join3(NOTES_PATH, dirName);
1466
- await ensureDirectory(notePath);
1467
- const readmePath = join3(notePath, "README.md");
1468
- const noteTitle = generateNoteTitle(noteIndex, title, REPO_NOTES_URL);
1469
- const readmeContent = noteTitle + "\n" + getNewNoteReadmeBody();
1470
- writeFileSync2(readmePath, readmeContent, "utf-8");
1471
- const configPath = join3(notePath, ".tnotes.json");
1472
- const config2 = {
1473
- id: configId || uuidv4(),
1474
- // 配置 ID 使用 UUID(跨知识库唯一)
1475
- bilibili: [],
1476
- tnotes: [],
1477
- yuque: [],
1478
- done: false,
1479
- category,
1480
- enableDiscussions
1481
- };
1482
- this.noteManager.writeNoteConfig(configPath, config2);
1483
- const noteInfo = {
1484
- index: noteIndex,
1485
- // 返回的 id 是笔记索引(目录前缀)
1486
- path: notePath,
1487
- dirName,
1488
- readmePath,
1489
- configPath,
1490
- config: config2
1491
- };
1492
- if (this.noteIndexCache.isInitialized()) {
1493
- this.noteIndexCache.add(noteInfo);
1494
- }
1495
- logger.info(`Created new note: ${dirName}`);
1496
- return noteInfo;
1497
- }
1498
- /**
1499
- * 删除笔记文件夹
1500
- * @param noteIndex - 笔记索引
1501
- * @returns 被删除的笔记信息
1502
- */
1503
- async deleteNote(noteIndex) {
1504
- const note = this.getNoteByIndex(noteIndex);
1505
- if (!note) {
1506
- throw new Error(`\u7B14\u8BB0\u672A\u627E\u5230: ${noteIndex}`);
1507
- }
1508
- await fsPromises.rm(note.path, { recursive: true, force: true });
1509
- if (this.noteIndexCache.isInitialized()) {
1510
- this.noteIndexCache.delete(noteIndex);
1511
- }
1512
- logger.info(`Deleted note: ${note.dirName}`);
1513
- return note;
1514
- }
1515
- /**
1516
- * 生成下一个笔记索引(填充空缺)
1517
- * @param usedIndexes - 可选的已使用编号集合,不传则内部扫描
1518
- * @returns 新的笔记索引(4位数字字符串,从 0001 到 9999)
1519
- */
1520
- generateNextNoteIndex(usedIndexes) {
1521
- if (!usedIndexes) {
1522
- const notes = this.getAllNotes();
1523
- usedIndexes = /* @__PURE__ */ new Set();
1524
- for (const note of notes) {
1525
- const id = parseInt(note.index, 10);
1526
- if (!isNaN(id) && id >= 1 && id <= 9999) {
1527
- usedIndexes.add(id);
1528
- }
1529
- }
1530
- }
1531
- if (usedIndexes.size === 0) {
1532
- return "0001";
1533
- }
1534
- for (let i = 1; i <= 9999; i++) {
1535
- if (!usedIndexes.has(i)) {
1536
- return i.toString().padStart(CONSTANTS.NOTE_INDEX_LENGTH, "0");
1537
- }
1538
- }
1539
- throw new Error("\u6240\u6709\u7B14\u8BB0\u7F16\u53F7 (0001-9999) \u5DF2\u88AB\u5360\u7528\uFF0C\u65E0\u6CD5\u521B\u5EFA\u65B0\u7B14\u8BB0");
1540
- }
1541
- /**
1542
- * 更新笔记配置
1543
- * @param noteIndex - 笔记索引
1544
- * @param updates - 配置更新
1545
- */
1546
- async updateNoteConfig(noteIndex, updates) {
1547
- const note = this.getNoteByIndex(noteIndex);
1548
- if (!note || !note.config) {
1549
- throw new Error(`Note not found or no config: ${noteIndex}`);
1550
- }
1551
- const oldConfig = { ...note.config };
1552
- const updatedConfig = {
1553
- ...note.config,
1554
- ...updates
1555
- };
1556
- this.ignoreNextConfigChange(note.configPath);
1557
- this.noteManager.updateNoteConfig(note, updatedConfig);
1558
- this.noteIndexCache.updateConfig(noteIndex, updatedConfig);
1559
- const needsGlobalUpdate = this.checkNeedsGlobalUpdate(
1560
- oldConfig,
1561
- updatedConfig
1562
- );
1563
- if (needsGlobalUpdate) {
1564
- logger.info(`\u68C0\u6D4B\u5230\u5168\u5C40\u5B57\u6BB5\u53D8\u66F4 (${noteIndex})\uFF0C\u6B63\u5728\u589E\u91CF\u66F4\u65B0\u5168\u5C40\u6587\u4EF6...`);
1565
- const tocService = TocService.getInstance();
1566
- await tocService.updateNoteInToc(noteIndex, updates);
1567
- await tocService.regenerateSidebar();
1568
- logger.info(`\u5168\u5C40\u6587\u4EF6\u589E\u91CF\u66F4\u65B0\u5B8C\u6210 (${noteIndex})`);
1569
- } else {
1570
- logger.debug(`\u914D\u7F6E\u66F4\u65B0\u4E0D\u5F71\u54CD\u5168\u5C40\u6587\u4EF6 (${noteIndex})`);
1571
- }
1572
- }
1573
- /**
1574
- * 检查配置更新是否需要触发全局更新
1575
- * @param oldConfig - 旧配置
1576
- * @param newConfig - 新配置
1577
- * @returns 是否需要全局更新
1578
- */
1579
- checkNeedsGlobalUpdate(oldConfig, newConfig) {
1580
- const globalFields = ["done"];
1581
- for (const field of globalFields) {
1582
- if (oldConfig[field] !== newConfig[field]) {
1583
- return true;
1584
- }
1585
- }
1586
- return false;
1587
- }
1588
- /**
1589
- * 修正笔记标题
1590
- * @param noteInfo - 笔记信息
1591
- * @returns 是否进行了修正
1592
- */
1593
- async fixNoteTitle(noteInfo) {
1594
- try {
1595
- const readmeContent = readFileSync2(noteInfo.readmePath, "utf-8");
1596
- if (readmeContent.length === 0) return false;
1597
- const lines = readmeContent.split("\n");
1598
- const match = noteInfo.dirName.match(/^\d{4}\.\s+(.+)$/);
1599
- if (!match) {
1600
- logger.warn(`\u68C0\u6D4B\u5230\u9519\u8BEF\u7684\u7B14\u8BB0\u76EE\u5F55\u540D\u79F0\uFF1A${noteInfo.dirName}`);
1601
- return false;
1602
- }
1603
- const expectedTitle = match[1];
1604
- const expectedH1 = generateNoteTitle(
1605
- noteInfo.index,
1606
- expectedTitle,
1607
- REPO_NOTES_URL
1608
- );
1609
- const firstLine = lines[0].trim();
1610
- if (!firstLine.startsWith("# ")) {
1611
- lines.unshift(expectedH1);
1612
- writeFileSync2(noteInfo.readmePath, lines.join("\n"), "utf-8");
1613
- logger.info(`Added title to: ${noteInfo.dirName}`);
1614
- return true;
1615
- }
1616
- if (firstLine !== expectedH1) {
1617
- lines[0] = expectedH1;
1618
- writeFileSync2(noteInfo.readmePath, lines.join("\n"), "utf-8");
1619
- logger.info(`Fixed title for: ${noteInfo.dirName}`);
1620
- return true;
1621
- }
1622
- return false;
1623
- } catch (error) {
1624
- logger.error(`Failed to fix title for: ${noteInfo.dirName}`, error);
1625
- return false;
1626
- }
1627
- }
1628
- /**
1629
- * 修正所有笔记的标题
1630
- * @param providedNotes - 可选的笔记列表,不传则内部扫描
1631
- * @returns 修正的笔记数量
1632
- */
1633
- async fixAllNoteTitles(providedNotes) {
1634
- const notes = providedNotes ?? this.getAllNotes();
1635
- let fixedCount = 0;
1636
- for (const note of notes) {
1637
- const fixed = await this.fixNoteTitle(note);
1638
- if (fixed) {
1639
- fixedCount++;
1640
- }
1641
- }
1642
- if (fixedCount > 0) {
1643
- logger.info(`Fixed ${fixedCount} note titles`);
1644
- }
1645
- return fixedCount;
1646
- }
1647
- };
1648
-
1649
- // services/toc/service.ts
1650
- var TocService = class _TocService {
1651
- constructor() {
1652
- this.noteManager = NoteManager.getInstance();
1653
- this.configManager = ConfigManager.getInstance();
1654
- this.noteIndexCache = NoteIndexCache.getInstance();
1655
- }
1656
- static getInstance() {
1657
- if (!_TocService.instance) {
1658
- _TocService.instance = new _TocService();
1659
- }
1660
- return _TocService.instance;
1661
- }
1662
- /**
1663
- * 确保 TOC.md 存在
1664
- */
1665
- ensureTocExists() {
1666
- if (!existsSync2(ROOT_TOC_PATH)) {
1667
- throw new Error(
1668
- `TOC.md \u4E0D\u5B58\u5728: ${ROOT_TOC_PATH}\u3002\u8BF7\u5728\u77E5\u8BC6\u5E93\u6839\u76EE\u5F55\u521B\u5EFA TOC.md \u4F5C\u4E3A\u76EE\u5F55\u6570\u636E\u6E90\u3002`
1669
- );
1670
- }
1671
- }
1672
- /**
1673
- * update 主流程:规范化 TOC.md
1674
- */
1675
- async normalizeToc(notes) {
1676
- this.ensureTocExists();
1677
- const content = await fsPromises2.readFile(ROOT_TOC_PATH, "utf-8");
1678
- const lines = content.split("\n");
1679
- const tree = parseTocToTree(lines, notes);
1680
- const existingIndexes = /* @__PURE__ */ new Set();
1681
- const collectNoteIndexes = (nodes) => {
1682
- for (const node of nodes) {
1683
- if (node.kind === "note") {
1684
- existingIndexes.add(node.noteIndex);
1685
- }
1686
- collectNoteIndexes(node.children);
1687
- }
1688
- };
1689
- collectNoteIndexes(tree);
1690
- const missingNotes = notes.filter((n) => !existingIndexes.has(n.index));
1691
- if (missingNotes.length > 0) {
1692
- logger.info(`TOC.md \u8FFD\u52A0 ${missingNotes.length} \u7BC7\u7F3A\u5931\u7B14\u8BB0`);
1693
- missingNotes.sort((a, b) => a.index.localeCompare(b.index));
1694
- for (const note of missingNotes) {
1695
- tree.push({
1696
- kind: "note",
1697
- noteIndex: note.index,
1698
- indent: 0,
1699
- tocLineIndex: 0,
1700
- children: []
1701
- });
1702
- }
1703
- }
1704
- const configByIndex = /* @__PURE__ */ new Map();
1705
- for (const note of notes) {
1706
- configByIndex.set(note.index, {
1707
- done: getTocLineCompleted(note)
1708
- });
1709
- }
1710
- const normalizedLines = serializeTocTree(tree, notes, configByIndex);
1711
- await this.writeTocLines(normalizedLines);
1712
- logger.info("\u5DF2\u89C4\u8303\u5316 TOC.md");
1713
- }
1714
- /**
1715
- * 从 TOC.md 重新生成 sidebar.json
1716
- */
1717
- async regenerateSidebar(notes) {
1718
- this.ensureTocExists();
1719
- const allNotes = notes ?? (this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes());
1720
- const content = readFileSync3(ROOT_TOC_PATH, "utf-8");
1721
- const lines = content.split("\n");
1722
- const tree = parseTocToTree(lines, allNotes);
1723
- const sidebarShowNoteId2 = this.configManager.get("sidebarShowNoteId");
1724
- const hierarchicalSidebar = buildSidebarFromTocTree(tree, allNotes, {
1725
- sidebarShowNoteId: sidebarShowNoteId2,
1726
- sidebarIsCollapsed: true
1727
- });
1728
- const sidebarContent = JSON.stringify(hierarchicalSidebar, null, 2);
1729
- const existingContent = existsSync2(VP_SIDEBAR_PATH) ? readFileSync3(VP_SIDEBAR_PATH, "utf-8") : "";
1730
- if (sidebarContent !== existingContent) {
1731
- writeFileSync3(VP_SIDEBAR_PATH, sidebarContent, "utf-8");
1732
- }
1733
- logger.info("\u5DF2\u66F4\u65B0\u4FA7\u8FB9\u680F\u914D\u7F6E");
1734
- }
1735
- /**
1736
- * 增量更新 TOC.md 中某笔记的完成状态与链接
1737
- */
1738
- async updateNoteInToc(noteIndex, updates) {
1739
- this.ensureTocExists();
1740
- const item = this.noteIndexCache.getByNoteIndex(noteIndex);
1741
- if (!item) {
1742
- logger.warn(`\u5C1D\u8BD5\u66F4\u65B0 TOC \u4E2D\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0: ${noteIndex}`);
1743
- return;
1744
- }
1745
- const notes = this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes();
1746
- const note = resolveNoteFromIndex(noteIndex, notes);
1747
- if (!note) return;
1748
- const mergedConfig = {
1749
- ...note.config,
1750
- ...updates,
1751
- ...item.noteConfig
1752
- };
1753
- const tempNote = {
1754
- ...note,
1755
- dirName: item.folderName,
1756
- config: mergedConfig
1757
- };
1758
- const lines = await this.readTocLines();
1759
- let updated = false;
1760
- for (let i = 0; i < lines.length; i++) {
1761
- const parsed = parseTocLine(lines[i]);
1762
- if (parsed.noteIndex === noteIndex) {
1763
- lines[i] = buildTocLine(
1764
- tempNote,
1765
- parsed.indentLevel,
1766
- getTocLineCompleted(tempNote)
1767
- );
1768
- updated = true;
1769
- }
1770
- }
1771
- if (updated) {
1772
- await this.writeTocLines(lines);
1773
- logger.info(`\u589E\u91CF\u66F4\u65B0 TOC.md \u4E2D\u7684\u7B14\u8BB0: ${noteIndex}`);
1774
- } else {
1775
- logger.warn(`TOC.md \u4E2D\u672A\u627E\u5230\u7B14\u8BB0: ${noteIndex}`);
1776
- }
1777
- }
1778
- /**
1779
- * 在 TOC.md 末尾或父节点下追加笔记
1780
- */
1781
- async appendNoteToToc(noteIndex, options) {
1782
- const item = this.noteIndexCache.getByNoteIndex(noteIndex);
1783
- if (!item) {
1784
- logger.warn(`\u5C1D\u8BD5\u6DFB\u52A0\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0\u5230 TOC: ${noteIndex}`);
1785
- return;
1786
- }
1787
- const notes = this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes();
1788
- const note = resolveNoteFromIndex(noteIndex, notes);
1789
- if (!note) return;
1790
- const tempNote = {
1791
- ...note,
1792
- dirName: item.folderName,
1793
- config: item.noteConfig
1794
- };
1795
- if (!existsSync2(ROOT_TOC_PATH)) {
1796
- await this.writeTocLines([buildTocLine(tempNote, 0, false)]);
1797
- logger.info(`\u521B\u5EFA TOC.md \u5E76\u6DFB\u52A0\u7B14\u8BB0: ${noteIndex}`);
1798
- return;
1799
- }
1800
- const lines = await this.readTocLines();
1801
- if (options?.parentTocLineIndex !== void 0) {
1802
- await this.insertNotesUnderTocLine(options.parentTocLineIndex, [tempNote]);
1803
- return;
1804
- }
1805
- if (options?.parentNoteIndex) {
1806
- const parentIndex = findTocLineIndex(lines, options.parentNoteIndex);
1807
- await this.insertNotesUnderTocLine(parentIndex, [tempNote]);
1808
- return;
1809
- }
1810
- if (options?.parentFolderPath && options.parentFolderPath.length > 0) {
1811
- const folderIndex = findFolderLineIndex(lines, options.parentFolderPath);
1812
- await this.insertNotesUnderTocLine(folderIndex, [tempNote]);
1813
- return;
1814
- }
1815
- lines.push(buildTocLine(tempNote, 0, false));
1816
- await this.writeTocLines(lines);
1817
- logger.info(`\u5728 TOC.md \u672B\u5C3E\u6DFB\u52A0\u7B14\u8BB0: ${noteIndex}`);
1818
- }
1819
- /**
1820
- * 删除前按 TOC 文档顺序获取上一项笔记索引;首项返回 null
1821
- */
1822
- async getPreviousNoteIndexBeforeDelete(noteIndex) {
1823
- if (!existsSync2(ROOT_TOC_PATH)) return null;
1824
- const lines = await this.readTocLines();
1825
- return getPreviousTocNoteIndex(lines, noteIndex);
1826
- }
1827
- /**
1828
- * 从 TOC.md 删除笔记及其缩进子树
1829
- */
1830
- async deleteNoteFromToc(noteIndex) {
1831
- if (!existsSync2(ROOT_TOC_PATH)) return;
1832
- const lines = await this.readTocLines();
1833
- let lineIndex;
1834
- try {
1835
- lineIndex = findTocLineIndex(lines, noteIndex);
1836
- } catch {
1837
- logger.warn(`TOC.md \u4E2D\u672A\u627E\u5230\u7B14\u8BB0: ${noteIndex}`);
1838
- return;
1839
- }
1840
- const { start, end } = getTocEntrySubtreeRange(lines, lineIndex);
1841
- lines.splice(start, end - start);
1842
- await this.writeTocLines(lines);
1843
- logger.info(`\u4ECE TOC.md \u5220\u9664\u7B14\u8BB0\u53CA\u5B50\u6811: ${noteIndex} (${end - start} \u884C)`);
1844
- }
1845
- /**
1846
- * 重命名 TOC.md 中的笔记行(保留位置与缩进)
1847
- */
1848
- async renameNoteInToc(noteIndex, newDirName) {
1849
- if (!existsSync2(ROOT_TOC_PATH)) return;
1850
- const item = this.noteIndexCache.getByNoteIndex(noteIndex);
1851
- const notes = this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes();
1852
- const note = resolveNoteFromIndex(noteIndex, notes);
1853
- if (!note) return;
1854
- const tempNote = {
1855
- ...note,
1856
- dirName: newDirName,
1857
- config: item?.noteConfig ?? note.config
1858
- };
1859
- const lines = await this.readTocLines();
1860
- let updated = false;
1861
- for (let i = 0; i < lines.length; i++) {
1862
- const parsed = parseTocLine(lines[i]);
1863
- if (parsed.noteIndex === noteIndex) {
1864
- lines[i] = buildTocLine(
1865
- tempNote,
1866
- parsed.indentLevel,
1867
- parsed.completed
1868
- );
1869
- updated = true;
1870
- }
1871
- }
1872
- if (updated) {
1873
- await this.writeTocLines(lines);
1874
- logger.info(`TOC.md \u91CD\u547D\u540D\u7B14\u8BB0: ${noteIndex} -> ${newDirName}`);
1875
- }
1876
- }
1877
- /**
1878
- * 移动笔记(含子树)到目标位置
1879
- */
1880
- async moveNoteInToc(noteIndex, target) {
1881
- const lines = await this.readTocLines();
1882
- const sourceIndex = findTocLineIndex(lines, noteIndex);
1883
- await this.moveTocEntryByLineIndex(sourceIndex, target);
1884
- }
1885
- /**
1886
- * 移动任意 TOC 行(分组或笔记)及其子树
1887
- */
1888
- async moveTocEntryByLineIndex(sourceTocLineIndex, target) {
1889
- const lines = await this.readTocLines();
1890
- const sourceParsed = parseTocLine(lines[sourceTocLineIndex]);
1891
- if (!sourceParsed.isMatch) {
1892
- throw new Error(`\u65E0\u6548\u7684 TOC \u884C\u7D22\u5F15: ${sourceTocLineIndex}`);
1893
- }
1894
- const { start, end } = getTocEntrySubtreeRange(lines, sourceTocLineIndex);
1895
- const resolvedTargetIndex = this.resolveMoveTargetLineIndex(lines, target);
1896
- if (resolvedTargetIndex >= start && resolvedTargetIndex < end) {
1897
- throw new Error("\u4E0D\u80FD\u79FB\u52A8\u5230\u81EA\u8EAB\u6216\u5B50\u6811\u5185");
1898
- }
1899
- const movingLines = lines.splice(start, end - start);
1900
- if (movingLines.length === 0) {
1901
- throw new Error(`\u65E0\u6CD5\u79FB\u52A8\u7A7A\u5B50\u6811: \u884C ${sourceTocLineIndex}`);
1902
- }
1903
- const targetLineIndex = adjustTocLineIndexAfterSubtreeRemoval(
1904
- resolvedTargetIndex,
1905
- start,
1906
- end
1907
- );
1908
- const targetParsed = parseTocLine(lines[targetLineIndex]);
1909
- if (!targetParsed.isMatch) {
1910
- throw new Error(`\u65E0\u6548\u7684\u76EE\u6807 TOC \u884C\u7D22\u5F15: ${targetLineIndex}`);
1911
- }
1912
- let insertIndex;
1913
- let newIndent;
1914
- if (target.placement === "inside") {
1915
- insertIndex = targetLineIndex + 1;
1916
- newIndent = targetParsed.indentLevel + 1;
1917
- } else if (target.placement === "before") {
1918
- insertIndex = targetLineIndex;
1919
- newIndent = targetParsed.indentLevel;
1920
- } else {
1921
- insertIndex = getTocEntrySubtreeRange(lines, targetLineIndex).end;
1922
- newIndent = targetParsed.indentLevel;
1923
- }
1924
- const oldBaseIndent = parseTocLine(movingLines[0]).indentLevel;
1925
- const indentDelta = newIndent - oldBaseIndent;
1926
- const adjustedLines = this.adjustSubtreeIndent(movingLines, indentDelta);
1927
- const maxDepth = this.getSidebarMaxDepth();
1928
- if (maxDepth > 0) {
1929
- const maxIndent = this.getMaxSubtreeIndent(adjustedLines);
1930
- const maxAllowedIndent = maxDepth - 1;
1931
- if (maxIndent > maxAllowedIndent) {
1932
- throw new Error(`\u79FB\u52A8\u540E\u5C06\u8D85\u51FA\u76EE\u5F55\u6700\u5927\u5C42\u7EA7\uFF08${maxDepth} \u5C42\uFF09`);
1933
- }
1934
- }
1935
- lines.splice(insertIndex, 0, ...adjustedLines);
1936
- await this.writeTocLines(lines);
1937
- logger.info(
1938
- `TOC.md \u79FB\u52A8\u884C ${sourceTocLineIndex} -> \u884C ${targetLineIndex} (${target.placement})`
1939
- );
1940
- }
1941
- /**
1942
- * 语雀式移动:成为 target 的下一同级
1943
- */
1944
- async moveAfterByNodeId(sourceNodeId, targetNodeId) {
1945
- const lines = await this.readTocLines();
1946
- const sourceIndex = this.resolveNodeIdToTocLineIndex(lines, sourceNodeId);
1947
- const targetIndex = this.resolveNodeIdToTocLineIndex(lines, targetNodeId);
1948
- await this.moveTocEntryByLineIndex(sourceIndex, {
1949
- targetTocLineIndex: targetIndex,
1950
- placement: "after"
1951
- });
1952
- }
1953
- /**
1954
- * 语雀式:prepend 到侧栏列表顶(无 target_uuid,book 即容器)
1955
- */
1956
- async prependToRootByNodeId(sourceNodeId) {
1957
- const lines = await this.readTocLines();
1958
- const sourceIndex = this.resolveNodeIdToTocLineIndex(lines, sourceNodeId);
1959
- let targetLineIndex = 0;
1960
- while (targetLineIndex < lines.length && !parseTocLine(lines[targetLineIndex]).isMatch) {
1961
- targetLineIndex++;
1962
- }
1963
- if (targetLineIndex >= lines.length) {
1964
- throw new Error("TOC.md \u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u79FB\u52A8\u5230\u9876\u90E8");
1965
- }
1966
- await this.moveTocEntryByLineIndex(sourceIndex, {
1967
- targetTocLineIndex: targetLineIndex,
1968
- placement: "before"
1969
- });
1970
- }
1971
- /**
1972
- * 语雀式移动:成为 target 的第一个子项
1973
- */
1974
- async prependChildByNodeId(sourceNodeId, targetNodeId) {
1975
- const lines = await this.readTocLines();
1976
- const sourceIndex = this.resolveNodeIdToTocLineIndex(lines, sourceNodeId);
1977
- const targetIndex = this.resolveNodeIdToTocLineIndex(lines, targetNodeId);
1978
- await this.moveTocEntryByLineIndex(sourceIndex, {
1979
- targetTocLineIndex: targetIndex,
1980
- placement: "inside"
1981
- });
1982
- }
1983
- resolveNodeIdToTocLineIndex(lines, nodeId) {
1984
- const parsed = parseNodeId(nodeId);
1985
- if (parsed.kind === "note" && parsed.noteIndex) {
1986
- return findTocLineIndex(lines, parsed.noteIndex);
1987
- }
1988
- if (parsed.kind === "line" && parsed.tocLineIndex !== void 0) {
1989
- return parsed.tocLineIndex;
1990
- }
1991
- if (parsed.kind === "folder" && parsed.folderPath) {
1992
- return findFolderLineIndex(lines, parsed.folderPath);
1993
- }
1994
- throw new Error(`\u65E0\u6CD5\u89E3\u6790 nodeId: ${nodeId}`);
1995
- }
1996
- /**
1997
- * 重命名 TOC 目录行
1998
- */
1999
- async renameFolderInToc(tocLineIndex, newTitle) {
2000
- const lines = await this.readTocLines();
2001
- const updated = renameFolderLine(lines, tocLineIndex, newTitle);
2002
- await this.writeTocLines(updated);
2003
- logger.info(`TOC.md \u91CD\u547D\u540D\u76EE\u5F55\u884C ${tocLineIndex}: ${newTitle.trim()}`);
2004
- }
2005
- /**
2006
- * 删除子树前获取回退笔记(子树之前的最后一篇笔记)
2007
- */
2008
- async getPreviousNoteIndexBeforeEntryDelete(tocLineIndex) {
2009
- const lines = await this.readTocLines();
2010
- return getPreviousNoteIndexOutsideSubtree(lines, tocLineIndex);
2011
- }
2012
- /**
2013
- * 判断 noteIndex 是否在指定 TOC 行子树内
2014
- */
2015
- async isNoteInTocEntrySubtree(tocLineIndex, noteIndex) {
2016
- const lines = await this.readTocLines();
2017
- return isNoteIndexInSubtree(lines, tocLineIndex, noteIndex);
2018
- }
2019
- /**
2020
- * 级联删除 TOC 子树,并删除子树内所有笔记磁盘目录
2021
- */
2022
- async deleteTocEntryCascade(tocLineIndex) {
2023
- if (!existsSync2(ROOT_TOC_PATH)) return [];
2024
- const lines = await this.readTocLines();
2025
- const noteIndexes = collectNoteIndexesInSubtree(lines, tocLineIndex);
2026
- const { start, end } = getTocEntrySubtreeRange(lines, tocLineIndex);
2027
- lines.splice(start, end - start);
2028
- await this.writeTocLines(lines);
2029
- const noteService = NoteService.getInstance();
2030
- for (const noteIndex of noteIndexes) {
2031
- await noteService.deleteNote(noteIndex);
2032
- }
2033
- logger.info(
2034
- `\u7EA7\u8054\u5220\u9664 TOC \u884C ${tocLineIndex}\uFF08${end - start} \u884C\uFF0C${noteIndexes.length} \u7BC7\u7B14\u8BB0\uFF09`
2035
- );
2036
- return noteIndexes;
2037
- }
2038
- /**
2039
- * 在指定 TOC 行(folder 或 note)子树末尾插入笔记
2040
- */
2041
- async insertNotesUnderTocLine(parentTocLineIndex, notesToInsert) {
2042
- if (notesToInsert.length === 0) return;
2043
- const lines = await this.readTocLines();
2044
- const parentParsed = parseTocLine(lines[parentTocLineIndex]);
2045
- if (!parentParsed.isMatch) {
2046
- throw new Error(`\u65E0\u6548\u7684 TOC \u884C\u7D22\u5F15: ${parentTocLineIndex}`);
2047
- }
2048
- const childIndent = parentParsed.indentLevel + 1;
2049
- const { end } = getTocEntrySubtreeRange(lines, parentTocLineIndex);
2050
- const noteLines = notesToInsert.map(
2051
- (note) => buildTocLine(note, childIndent, getTocLineCompleted(note))
2052
- );
2053
- lines.splice(end, 0, ...noteLines);
2054
- await this.writeTocLines(lines);
2055
- logger.info(
2056
- `\u5728 TOC \u884C ${parentTocLineIndex} \u4E0B\u63D2\u5165 ${notesToInsert.length} \u7BC7\u7B14\u8BB0`
2057
- );
2058
- }
2059
- /**
2060
- * 在指定 TOC 行(folder 或 note)子树末尾插入子目录
2061
- */
2062
- async insertFolderUnderParent(parentTocLineIndex, title) {
2063
- const trimmed = title.trim();
2064
- if (!trimmed) {
2065
- throw new Error("\u76EE\u5F55\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
2066
- }
2067
- const lines = await this.readTocLines();
2068
- const parentParsed = parseTocLine(lines[parentTocLineIndex]);
2069
- if (!parentParsed.isMatch) {
2070
- throw new Error(`\u65E0\u6548\u7684 TOC \u884C\u7D22\u5F15: ${parentTocLineIndex}`);
2071
- }
2072
- const childIndent = parentParsed.indentLevel + 1;
2073
- const { end } = getTocEntrySubtreeRange(lines, parentTocLineIndex);
2074
- lines.splice(end, 0, buildFolderTocLine(trimmed, childIndent));
2075
- await this.writeTocLines(lines);
2076
- logger.info(`\u5728 TOC \u884C ${parentTocLineIndex} \u4E0B\u63D2\u5165\u76EE\u5F55: ${trimmed}`);
2077
- }
2078
- /**
2079
- * 在指定笔记上方或下方插入笔记
2080
- */
2081
- async insertNotesAroundNote(targetNoteIndex, notesToInsert, placement) {
2082
- if (notesToInsert.length === 0) return;
2083
- const lines = await this.readTocLines();
2084
- const targetIndex = findTocLineIndex(lines, targetNoteIndex);
2085
- const targetParsed = parseTocLine(lines[targetIndex]);
2086
- const indent = targetParsed.indentLevel;
2087
- const noteLines = notesToInsert.map(
2088
- (note) => buildTocLine(note, indent, getTocLineCompleted(note))
2089
- );
2090
- const insertIndex = placement === "before" ? targetIndex : getTocEntrySubtreeRange(lines, targetIndex).end;
2091
- lines.splice(insertIndex, 0, ...noteLines);
2092
- await this.writeTocLines(lines);
2093
- logger.info(
2094
- `\u5728\u7B14\u8BB0 ${targetNoteIndex} ${placement === "before" ? "\u4E0A\u65B9" : "\u4E0B\u65B9"}\u63D2\u5165 ${notesToInsert.length} \u7BC7\u7B14\u8BB0`
2095
- );
2096
- }
2097
- /**
2098
- * 在目录子树末尾插入笔记
2099
- */
2100
- async insertNotesUnderFolder(folderPath, notesToInsert) {
2101
- if (notesToInsert.length === 0) return;
2102
- const lines = await this.readTocLines();
2103
- const folderIndex = findFolderLineIndex(lines, folderPath);
2104
- await this.insertNotesUnderTocLine(folderIndex, notesToInsert);
2105
- }
2106
- async insertNotesUnderParent(parentNoteIndex, notesToInsert) {
2107
- if (notesToInsert.length === 0) return;
2108
- const lines = await this.readTocLines();
2109
- const parentIndex = findTocLineIndex(lines, parentNoteIndex);
2110
- await this.insertNotesUnderTocLine(parentIndex, notesToInsert);
2111
- }
2112
- /**
2113
- * 规范化 TOC 并刷新 sidebar
2114
- */
2115
- async refreshTocAndSidebar(notes) {
2116
- const allNotes = notes ?? (this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes());
2117
- await this.normalizeToc(allNotes);
2118
- await this.regenerateSidebar(allNotes);
2119
- }
2120
- resolveMoveTargetLineIndex(lines, target) {
2121
- if ("targetTocLineIndex" in target) {
2122
- return target.targetTocLineIndex;
2123
- }
2124
- if (target.targetType === "folder") {
2125
- return findFolderLineIndex(lines, target.targetFolderPath);
2126
- }
2127
- return findTocLineIndex(lines, target.targetNoteIndex);
2128
- }
2129
- getSidebarMaxDepth() {
2130
- const config2 = this.configManager.getAll();
2131
- const depth = config2.sidebarMaxDepth ?? 0;
2132
- return depth > 0 ? depth : 0;
2133
- }
2134
- getMaxSubtreeIndent(lines) {
2135
- let max = 0;
2136
- for (const line of lines) {
2137
- const parsed = parseTocLine(line);
2138
- if (parsed.isMatch) {
2139
- max = Math.max(max, parsed.indentLevel);
2140
- }
2141
- }
2142
- return max;
2143
- }
2144
- adjustSubtreeIndent(lines, delta) {
2145
- if (delta === 0) return lines;
2146
- return lines.map((line) => {
2147
- const parsed = parseTocLine(line);
2148
- if (!parsed.isMatch) return line;
2149
- const newIndent = Math.max(0, parsed.indentLevel + delta);
2150
- const withoutIndent = line.trimStart();
2151
- return `${" ".repeat(newIndent * TOC_INDENT_SPACES)}${withoutIndent}`;
2152
- });
2153
- }
2154
- async readTocLines() {
2155
- this.ensureTocExists();
2156
- const content = await fsPromises2.readFile(ROOT_TOC_PATH, "utf-8");
2157
- return content.split("\n");
2158
- }
2159
- async writeTocLines(lines) {
2160
- const content = processTocEmptyLines(lines).join("\n");
2161
- await fsPromises2.writeFile(ROOT_TOC_PATH, content, "utf-8");
2162
- }
2163
- };
2164
-
2165
1292
  // services/file-watcher/service.ts
2166
1293
  import { request as httpRequest } from "http";
2167
1294
 
@@ -2317,8 +1444,8 @@ var EventScheduler = class {
2317
1444
  };
2318
1445
 
2319
1446
  // services/file-watcher/folderChangeHandler.ts
2320
- import { existsSync as existsSync3, promises as fsPromises3 } from "fs";
2321
- import { join as join4 } from "path";
1447
+ import { existsSync as existsSync2, promises as fsPromises } from "fs";
1448
+ import { dirname, join as join3 } from "path";
2322
1449
 
2323
1450
  // services/file-watcher/internal.ts
2324
1451
  var WATCH_EVENT_TYPES = {
@@ -2335,6 +1462,17 @@ async function safeExecute(label, fn, logger2) {
2335
1462
  }
2336
1463
  }
2337
1464
 
1465
+ // services/reconcileToc.ts
1466
+ var workspaces = /* @__PURE__ */ new Map();
1467
+ async function reconcileTocFromFiles(repoRoot) {
1468
+ let workspace = workspaces.get(repoRoot);
1469
+ if (!workspace) {
1470
+ workspace = createWorkspace({ rootPath: repoRoot });
1471
+ workspaces.set(repoRoot, workspace);
1472
+ }
1473
+ await workspace.toc.reconcileFromFiles();
1474
+ }
1475
+
2338
1476
  // services/file-watcher/folderChangeHandler.ts
2339
1477
  var RENAME_REVERT_DELAY_MS = 2e3;
2340
1478
  var DELETE_REINIT_DELAY_MS = 1e3;
@@ -2364,7 +1502,7 @@ var FolderChangeHandler = class {
2364
1502
  this.activeTimers.add(timer);
2365
1503
  }
2366
1504
  async handleFolderDeletion(deletedFolderName) {
2367
- const { scheduler, watchState, noteIndexCache, tocService, logger: logger2 } = this.config;
1505
+ const { scheduler, watchState, noteIndexCache, logger: logger2, notesDir } = this.config;
2368
1506
  if (scheduler.getUpdating()) return;
2369
1507
  scheduler.setUpdating(true);
2370
1508
  try {
@@ -2377,8 +1515,7 @@ var FolderChangeHandler = class {
2377
1515
  await safeExecute(
2378
1516
  `\u5220\u9664\u7B14\u8BB0 ${noteIndex}`,
2379
1517
  async () => {
2380
- await tocService.deleteNoteFromToc(noteIndex);
2381
- await tocService.regenerateSidebar();
1518
+ await reconcileTocFromFiles(dirname(notesDir));
2382
1519
  this.config.onNoteStructureChanged?.(`watcher:delete:${noteIndex}`);
2383
1520
  },
2384
1521
  logger2
@@ -2447,7 +1584,7 @@ var FolderChangeHandler = class {
2447
1584
  return { oldNoteIndex, newNoteIndex };
2448
1585
  }
2449
1586
  async handleTitleOnlyRename(noteIndex, newName) {
2450
- const { noteIndexCache, tocService, logger: logger2, notesDir } = this.config;
1587
+ const { noteIndexCache, logger: logger2, notesDir } = this.config;
2451
1588
  logger2.info(`\u7B14\u8BB0\u7D22\u5F15\u672A\u53D8 (${noteIndex})\uFF0C\u53EA\u66F4\u65B0\u6807\u9898`);
2452
1589
  noteIndexCache.updateFolderName(noteIndex, newName);
2453
1590
  await safeExecute(
@@ -2455,8 +1592,11 @@ var FolderChangeHandler = class {
2455
1592
  () => this.rewriteNoteReadmeH1(notesDir, newName, noteIndex),
2456
1593
  logger2
2457
1594
  );
2458
- await tocService.renameNoteInToc(noteIndex, newName);
2459
- await tocService.regenerateSidebar();
1595
+ await safeExecute(
1596
+ `\u6807\u9898\u91CD\u547D\u540D\u540C\u6B65\u76EE\u5F55 ${noteIndex}`,
1597
+ () => reconcileTocFromFiles(dirname(notesDir)),
1598
+ logger2
1599
+ );
2460
1600
  this.config.onNoteStructureChanged?.(`watcher:rename-title:${noteIndex}`);
2461
1601
  logger2.success(`\u6807\u9898\u66F4\u65B0\u5B8C\u6210`);
2462
1602
  }
@@ -2467,11 +1607,11 @@ var FolderChangeHandler = class {
2467
1607
  * 据此推导出新标题文本(去掉 `${noteIndex}. ` 前缀)。
2468
1608
  */
2469
1609
  async rewriteNoteReadmeH1(notesDir, folderName, noteIndex) {
2470
- const readmePath = join4(notesDir, folderName, "README.md");
2471
- if (!existsSync3(readmePath)) return;
1610
+ const readmePath = join3(notesDir, folderName, "README.md");
1611
+ if (!existsSync2(readmePath)) return;
2472
1612
  const newTitle = folderName.replace(/^\d{4}\.\s*/, "").trim();
2473
1613
  if (!newTitle) return;
2474
- const content = await fsPromises3.readFile(readmePath, "utf-8");
1614
+ const content = await fsPromises.readFile(readmePath, "utf-8");
2475
1615
  const lines = content.split("\n");
2476
1616
  let h1Index = -1;
2477
1617
  for (let i = 0; i < lines.length; i++) {
@@ -2489,18 +1629,20 @@ var FolderChangeHandler = class {
2489
1629
  const newH1 = generateNoteTitle(noteIndex, newTitle, REPO_NOTES_URL);
2490
1630
  if (lines[h1Index] === newH1) return;
2491
1631
  lines[h1Index] = newH1;
2492
- await fsPromises3.writeFile(readmePath, lines.join("\n"), "utf-8");
1632
+ await fsPromises.writeFile(readmePath, lines.join("\n"), "utf-8");
2493
1633
  }
2494
1634
  async handleIndexChangedRename(oldNoteIndex, newNoteIndex) {
2495
- const { noteService, noteIndexCache, tocService, logger: logger2 } = this.config;
1635
+ const { noteService, noteIndexCache, logger: logger2, notesDir } = this.config;
2496
1636
  logger2.info(`\u7B14\u8BB0\u7D22\u5F15\u53D8\u66F4: ${oldNoteIndex} \u2192 ${newNoteIndex}`);
2497
- await tocService.deleteNoteFromToc(oldNoteIndex);
1637
+ await safeExecute(
1638
+ `\u7D22\u5F15\u53D8\u66F4\u540C\u6B65\u76EE\u5F55 ${oldNoteIndex}\u2192${newNoteIndex}`,
1639
+ () => reconcileTocFromFiles(dirname(notesDir)),
1640
+ logger2
1641
+ );
2498
1642
  const newNote = noteService.getNoteByIndex(newNoteIndex);
2499
1643
  if (newNote) {
2500
1644
  noteIndexCache.delete(oldNoteIndex);
2501
1645
  noteIndexCache.add(newNote);
2502
- await tocService.appendNoteToToc(newNoteIndex);
2503
- await tocService.regenerateSidebar();
2504
1646
  this.config.onNoteStructureChanged?.(
2505
1647
  `watcher:rename-index:${oldNoteIndex}->${newNoteIndex}`
2506
1648
  );
@@ -2512,11 +1654,11 @@ var FolderChangeHandler = class {
2512
1654
  async revertFolderRename(oldName, newName) {
2513
1655
  const { notesDir, scheduler, watchState, logger: logger2 } = this.config;
2514
1656
  try {
2515
- const oldPath = join4(notesDir, oldName);
2516
- const newPath = join4(notesDir, newName);
2517
- if (existsSync3(newPath)) {
1657
+ const oldPath = join3(notesDir, oldName);
1658
+ const newPath = join3(notesDir, newName);
1659
+ if (existsSync2(newPath)) {
2518
1660
  scheduler.setUpdating(true);
2519
- await fsPromises3.rename(newPath, oldPath);
1661
+ await fsPromises.rename(newPath, oldPath);
2520
1662
  logger2.warn(`\u6587\u4EF6\u5939\u5DF2\u56DE\u9000: ${newName} \u2192 ${oldName}`);
2521
1663
  this.scheduleTimer(() => {
2522
1664
  scheduler.setUpdating(false);
@@ -2539,7 +1681,7 @@ var FolderChangeHandler = class {
2539
1681
 
2540
1682
  // services/file-watcher/fsWatcherAdapter.ts
2541
1683
  import { watch } from "fs";
2542
- import { basename, dirname, join as join5, sep } from "path";
1684
+ import { basename, dirname as dirname2, join as join4, sep } from "path";
2543
1685
  var FsWatcherAdapter = class {
2544
1686
  constructor(config2) {
2545
1687
  this.config = config2;
@@ -2582,7 +1724,7 @@ var FsWatcherAdapter = class {
2582
1724
  if (baseFilename !== "README.md" && baseFilename !== ".tnotes.json") {
2583
1725
  return;
2584
1726
  }
2585
- const fullPath = join5(this.config.notesDir, filename);
1727
+ const fullPath = join4(this.config.notesDir, filename);
2586
1728
  const event = this.buildWatchEvent(fullPath, filename);
2587
1729
  if (!event) {
2588
1730
  return;
@@ -2590,7 +1732,7 @@ var FsWatcherAdapter = class {
2590
1732
  onNoteEvent(event);
2591
1733
  }
2592
1734
  buildWatchEvent(fullPath, filename) {
2593
- const noteDirName = basename(dirname(fullPath));
1735
+ const noteDirName = basename(dirname2(fullPath));
2594
1736
  const noteIndex = NoteManager.extractNoteIndex(noteDirName);
2595
1737
  if (!noteIndex) {
2596
1738
  NoteManager.warnInvalidNoteIndex(noteDirName);
@@ -2602,35 +1744,27 @@ var FsWatcherAdapter = class {
2602
1744
  type: fileType,
2603
1745
  noteIndex,
2604
1746
  noteDirName,
2605
- noteDirPath: dirname(fullPath)
1747
+ noteDirPath: dirname2(fullPath)
2606
1748
  };
2607
1749
  }
2608
1750
  };
2609
1751
 
2610
1752
  // services/file-watcher/globalUpdateCoordinator.ts
1753
+ import { dirname as dirname3 } from "path";
2611
1754
  var GlobalUpdateCoordinator = class {
2612
1755
  constructor(config2) {
2613
- this.config = config2;
2614
- }
2615
- async applyConfigUpdates(changedNoteIndexes) {
2616
- if (changedNoteIndexes.length === 0) return;
2617
- const { tocService, noteIndexCache, logger: logger2 } = this.config;
2618
- logger2.info("\u68C0\u6D4B\u5230\u7B14\u8BB0\u72B6\u6001\u53D8\u5316\uFF0C\u589E\u91CF\u66F4\u65B0\u5168\u5C40\u6587\u4EF6...");
2619
- for (const noteIndex of changedNoteIndexes) {
2620
- await safeExecute(
2621
- `\u589E\u91CF\u66F4\u65B0 ${noteIndex}`,
2622
- async () => {
2623
- const item = noteIndexCache.getByNoteIndex(noteIndex);
2624
- await tocService.updateNoteInToc(
2625
- noteIndex,
2626
- item?.noteConfig || {}
2627
- );
2628
- logger2.info(`\u589E\u91CF\u66F4\u65B0 TOC.md \u4E2D\u7684\u7B14\u8BB0: ${noteIndex}`);
2629
- },
2630
- logger2
2631
- );
2632
- }
2633
- await tocService.regenerateSidebar();
1756
+ this.config = config2;
1757
+ }
1758
+ async applyConfigUpdates(changedNoteIndexes) {
1759
+ if (changedNoteIndexes.length === 0) return;
1760
+ const { logger: logger2, notesDir } = this.config;
1761
+ logger2.info("\u68C0\u6D4B\u5230\u7B14\u8BB0\u72B6\u6001\u53D8\u5316\uFF0C\u540C\u6B65\u5168\u5C40\u6587\u4EF6...");
1762
+ await safeExecute(
1763
+ "\u914D\u7F6E\u53D8\u66F4\u540C\u6B65\u76EE\u5F55",
1764
+ () => reconcileTocFromFiles(dirname3(notesDir)),
1765
+ logger2
1766
+ );
1767
+ logger2.info(`\u5DF2\u540C\u6B65\u914D\u7F6E\u53D8\u66F4\u7684\u7B14\u8BB0: ${changedNoteIndexes.join(", ")}`);
2634
1768
  }
2635
1769
  async updateNoteReadmesOnly(events) {
2636
1770
  const noteIndexesToUpdate = [...new Set(events.map((c) => c.noteIndex))];
@@ -2657,8 +1791,8 @@ var ReadmeChangeHandler = class {
2657
1791
  };
2658
1792
 
2659
1793
  // services/file-watcher/renameDetector.ts
2660
- import { existsSync as existsSync4 } from "fs";
2661
- import { join as join6 } from "path";
1794
+ import { existsSync as existsSync3 } from "fs";
1795
+ import { join as join5 } from "path";
2662
1796
  var FOLDER_RENAME_DETECT_WINDOW_MS = 500;
2663
1797
  var RenameDetector = class {
2664
1798
  constructor(config2) {
@@ -2670,8 +1804,8 @@ var RenameDetector = class {
2670
1804
  }
2671
1805
  handleFsRename(folderName) {
2672
1806
  const { notesDir, dirCache, logger: logger2, onDelete, onRename } = this.config;
2673
- const folderPath = join6(notesDir, folderName);
2674
- const folderExists = existsSync4(folderPath);
1807
+ const folderPath = join5(notesDir, folderName);
1808
+ const folderExists = existsSync3(folderPath);
2675
1809
  const noteIndex = NoteManager.extractNoteIndex(folderName);
2676
1810
  if (!noteIndex) {
2677
1811
  logger2.warn(`\u65E0\u6CD5\u4ECE\u6587\u4EF6\u5939\u540D\u79F0\u63D0\u53D6\u7B14\u8BB0\u7D22\u5F15: ${folderName}`);
@@ -2727,8 +1861,8 @@ var RenameDetector = class {
2727
1861
 
2728
1862
  // services/file-watcher/watchState.ts
2729
1863
  import { createHash } from "crypto";
2730
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
2731
- import { join as join7 } from "path";
1864
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync } from "fs";
1865
+ import { join as join6 } from "path";
2732
1866
  var WatchState = class {
2733
1867
  constructor(config2) {
2734
1868
  this.config = config2;
@@ -2747,8 +1881,8 @@ var WatchState = class {
2747
1881
  */
2748
1882
  getFileHash(filePath) {
2749
1883
  try {
2750
- if (!existsSync5(filePath)) return null;
2751
- const content = readFileSync4(filePath, "utf-8");
1884
+ if (!existsSync4(filePath)) return null;
1885
+ const content = readFileSync2(filePath, "utf-8");
2752
1886
  if (content.length === 0) return null;
2753
1887
  return createHash("md5").update(content).digest("hex");
2754
1888
  } catch {
@@ -2808,8 +1942,8 @@ var WatchState = class {
2808
1942
  * @param noteDirName 笔记目录名称
2809
1943
  */
2810
1944
  clearNoteCaches(noteDirName) {
2811
- const readmePath = join7(this.config.notesDir, noteDirName, "README.md");
2812
- const configPath = join7(this.config.notesDir, noteDirName, ".tnotes.json");
1945
+ const readmePath = join6(this.config.notesDir, noteDirName, "README.md");
1946
+ const configPath = join6(this.config.notesDir, noteDirName, ".tnotes.json");
2813
1947
  this.fileHashes.delete(readmePath);
2814
1948
  this.fileHashes.delete(configPath);
2815
1949
  this.configCache.delete(configPath);
@@ -2842,8 +1976,8 @@ var WatchState = class {
2842
1976
  */
2843
1977
  readConfigSnapshot(configPath) {
2844
1978
  try {
2845
- if (!existsSync5(configPath)) return null;
2846
- const content = readFileSync4(configPath, "utf-8");
1979
+ if (!existsSync4(configPath)) return null;
1980
+ const content = readFileSync2(configPath, "utf-8");
2847
1981
  const config2 = JSON.parse(content);
2848
1982
  return {
2849
1983
  done: Boolean(config2.done),
@@ -2865,13 +1999,13 @@ var WatchState = class {
2865
1999
  const noteDirs = readdirSync2(this.config.notesDir);
2866
2000
  this.clearAll();
2867
2001
  for (const noteDir of noteDirs) {
2868
- const noteDirPath = join7(this.config.notesDir, noteDir);
2002
+ const noteDirPath = join6(this.config.notesDir, noteDir);
2869
2003
  if (!statSync(noteDirPath).isDirectory()) continue;
2870
2004
  this.noteDirCache.add(noteDir);
2871
- const readmePath = join7(noteDirPath, "README.md");
2005
+ const readmePath = join6(noteDirPath, "README.md");
2872
2006
  const readmeHash = this.getFileHash(readmePath);
2873
2007
  if (readmeHash) this.fileHashes.set(readmePath, readmeHash);
2874
- const configPath = join7(noteDirPath, ".tnotes.json");
2008
+ const configPath = join6(noteDirPath, ".tnotes.json");
2875
2009
  const configHash = this.getFileHash(configPath);
2876
2010
  if (configHash) {
2877
2011
  this.fileHashes.set(configPath, configHash);
@@ -2887,16 +2021,267 @@ var WatchState = class {
2887
2021
  }
2888
2022
  };
2889
2023
 
2024
+ // services/note/service.ts
2025
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, promises as fsPromises2 } from "fs";
2026
+ import { join as join7 } from "path";
2027
+ import { v4 as uuidv4 } from "uuid";
2028
+ var NoteService = class _NoteService {
2029
+ constructor() {
2030
+ this.ignoredConfigPaths = /* @__PURE__ */ new Set();
2031
+ this.noteManager = NoteManager.getInstance();
2032
+ this.noteIndexCache = NoteIndexCache.getInstance();
2033
+ }
2034
+ static getInstance() {
2035
+ if (!_NoteService.instance) {
2036
+ _NoteService.instance = new _NoteService();
2037
+ }
2038
+ return _NoteService.instance;
2039
+ }
2040
+ /**
2041
+ * 标记配置文件在下次变更时被忽略(防止 API 写入触发文件监听循环)
2042
+ * @param configPath - 配置文件路径
2043
+ */
2044
+ ignoreNextConfigChange(configPath) {
2045
+ this.ignoredConfigPaths.add(configPath);
2046
+ }
2047
+ /**
2048
+ * 检查配置文件是否应该被忽略
2049
+ * @param configPath - 配置文件路径
2050
+ * @returns 是否应该忽略
2051
+ */
2052
+ shouldIgnoreConfigChange(configPath) {
2053
+ if (this.ignoredConfigPaths.has(configPath)) {
2054
+ this.ignoredConfigPaths.delete(configPath);
2055
+ return true;
2056
+ }
2057
+ return false;
2058
+ }
2059
+ /**
2060
+ * 获取所有笔记
2061
+ * dev 模式下(缓存已初始化)从内存读取,其他模式回退到文件扫描
2062
+ * @returns 笔记信息数组
2063
+ */
2064
+ getAllNotes() {
2065
+ if (this.noteIndexCache.isInitialized()) {
2066
+ return this.noteIndexCache.toNoteInfoList();
2067
+ }
2068
+ return this.noteManager.scanNotes();
2069
+ }
2070
+ /**
2071
+ * 获取笔记(通过索引)
2072
+ * @param noteIndex - 笔记索引(文件夹前 4 位数字)
2073
+ * @returns 笔记信息,未找到时返回 undefined
2074
+ */
2075
+ getNoteByIndex(noteIndex) {
2076
+ return this.noteManager.getNoteByIndex(noteIndex);
2077
+ }
2078
+ /**
2079
+ * 创建新笔记
2080
+ * @param options - 创建选项
2081
+ * @returns 新创建的笔记信息
2082
+ */
2083
+ async createNote(options = {}) {
2084
+ const {
2085
+ title = "new",
2086
+ category,
2087
+ enableDiscussions = false,
2088
+ configId,
2089
+ usedIndexes
2090
+ } = options;
2091
+ const noteIndex = this.generateNextNoteIndex(usedIndexes);
2092
+ const dirName = `${noteIndex}. ${title}`;
2093
+ const notePath = join7(NOTES_PATH, dirName);
2094
+ await ensureDirectory(notePath);
2095
+ const readmePath = join7(notePath, "README.md");
2096
+ const noteTitle = generateNoteTitle(noteIndex, title, REPO_NOTES_URL);
2097
+ const readmeContent = noteTitle + "\n" + getNewNoteReadmeBody();
2098
+ writeFileSync2(readmePath, readmeContent, "utf-8");
2099
+ const configPath = join7(notePath, ".tnotes.json");
2100
+ const config2 = {
2101
+ id: configId || uuidv4(),
2102
+ // 配置 ID 使用 UUID(跨知识库唯一)
2103
+ bilibili: [],
2104
+ tnotes: [],
2105
+ yuque: [],
2106
+ done: false,
2107
+ category,
2108
+ enableDiscussions
2109
+ };
2110
+ this.noteManager.writeNoteConfig(configPath, config2);
2111
+ const noteInfo = {
2112
+ index: noteIndex,
2113
+ // 返回的 id 是笔记索引(目录前缀)
2114
+ path: notePath,
2115
+ dirName,
2116
+ readmePath,
2117
+ configPath,
2118
+ config: config2
2119
+ };
2120
+ if (this.noteIndexCache.isInitialized()) {
2121
+ this.noteIndexCache.add(noteInfo);
2122
+ }
2123
+ logger.info(`Created new note: ${dirName}`);
2124
+ return noteInfo;
2125
+ }
2126
+ /**
2127
+ * 删除笔记文件夹
2128
+ * @param noteIndex - 笔记索引
2129
+ * @returns 被删除的笔记信息
2130
+ */
2131
+ async deleteNote(noteIndex) {
2132
+ const note = this.getNoteByIndex(noteIndex);
2133
+ if (!note) {
2134
+ throw new Error(`\u7B14\u8BB0\u672A\u627E\u5230: ${noteIndex}`);
2135
+ }
2136
+ await fsPromises2.rm(note.path, { recursive: true, force: true });
2137
+ if (this.noteIndexCache.isInitialized()) {
2138
+ this.noteIndexCache.delete(noteIndex);
2139
+ }
2140
+ logger.info(`Deleted note: ${note.dirName}`);
2141
+ return note;
2142
+ }
2143
+ /**
2144
+ * 生成下一个笔记索引(填充空缺)
2145
+ * @param usedIndexes - 可选的已使用编号集合,不传则内部扫描
2146
+ * @returns 新的笔记索引(4位数字字符串,从 0001 到 9999)
2147
+ */
2148
+ generateNextNoteIndex(usedIndexes) {
2149
+ if (!usedIndexes) {
2150
+ const notes = this.getAllNotes();
2151
+ usedIndexes = /* @__PURE__ */ new Set();
2152
+ for (const note of notes) {
2153
+ const id = parseInt(note.index, 10);
2154
+ if (!isNaN(id) && id >= 1 && id <= 9999) {
2155
+ usedIndexes.add(id);
2156
+ }
2157
+ }
2158
+ }
2159
+ if (usedIndexes.size === 0) {
2160
+ return "0001";
2161
+ }
2162
+ for (let i = 1; i <= 9999; i++) {
2163
+ if (!usedIndexes.has(i)) {
2164
+ return i.toString().padStart(CONSTANTS.NOTE_INDEX_LENGTH, "0");
2165
+ }
2166
+ }
2167
+ throw new Error("\u6240\u6709\u7B14\u8BB0\u7F16\u53F7 (0001-9999) \u5DF2\u88AB\u5360\u7528\uFF0C\u65E0\u6CD5\u521B\u5EFA\u65B0\u7B14\u8BB0");
2168
+ }
2169
+ /**
2170
+ * 更新笔记配置
2171
+ * @param noteIndex - 笔记索引
2172
+ * @param updates - 配置更新
2173
+ */
2174
+ async updateNoteConfig(noteIndex, updates) {
2175
+ const note = this.getNoteByIndex(noteIndex);
2176
+ if (!note || !note.config) {
2177
+ throw new Error(`Note not found or no config: ${noteIndex}`);
2178
+ }
2179
+ const oldConfig = { ...note.config };
2180
+ const updatedConfig = {
2181
+ ...note.config,
2182
+ ...updates
2183
+ };
2184
+ this.ignoreNextConfigChange(note.configPath);
2185
+ this.noteManager.updateNoteConfig(note, updatedConfig);
2186
+ this.noteIndexCache.updateConfig(noteIndex, updatedConfig);
2187
+ const needsGlobalUpdate = this.checkNeedsGlobalUpdate(
2188
+ oldConfig,
2189
+ updatedConfig
2190
+ );
2191
+ if (needsGlobalUpdate) {
2192
+ logger.info(`\u68C0\u6D4B\u5230\u5168\u5C40\u5B57\u6BB5\u53D8\u66F4 (${noteIndex})\uFF0C\u6B63\u5728\u589E\u91CF\u66F4\u65B0\u5168\u5C40\u6587\u4EF6...`);
2193
+ await reconcileTocFromFiles(ROOT_DIR_PATH);
2194
+ logger.info(`\u5168\u5C40\u6587\u4EF6\u589E\u91CF\u66F4\u65B0\u5B8C\u6210 (${noteIndex})`);
2195
+ } else {
2196
+ logger.debug(`\u914D\u7F6E\u66F4\u65B0\u4E0D\u5F71\u54CD\u5168\u5C40\u6587\u4EF6 (${noteIndex})`);
2197
+ }
2198
+ }
2199
+ /**
2200
+ * 检查配置更新是否需要触发全局更新
2201
+ * @param oldConfig - 旧配置
2202
+ * @param newConfig - 新配置
2203
+ * @returns 是否需要全局更新
2204
+ */
2205
+ checkNeedsGlobalUpdate(oldConfig, newConfig) {
2206
+ const globalFields = ["done"];
2207
+ for (const field of globalFields) {
2208
+ if (oldConfig[field] !== newConfig[field]) {
2209
+ return true;
2210
+ }
2211
+ }
2212
+ return false;
2213
+ }
2214
+ /**
2215
+ * 修正笔记标题
2216
+ * @param noteInfo - 笔记信息
2217
+ * @returns 是否进行了修正
2218
+ */
2219
+ async fixNoteTitle(noteInfo) {
2220
+ try {
2221
+ const readmeContent = readFileSync3(noteInfo.readmePath, "utf-8");
2222
+ if (readmeContent.length === 0) return false;
2223
+ const lines = readmeContent.split("\n");
2224
+ const match = noteInfo.dirName.match(/^\d{4}\.\s+(.+)$/);
2225
+ if (!match) {
2226
+ logger.warn(`\u68C0\u6D4B\u5230\u9519\u8BEF\u7684\u7B14\u8BB0\u76EE\u5F55\u540D\u79F0\uFF1A${noteInfo.dirName}`);
2227
+ return false;
2228
+ }
2229
+ const expectedTitle = match[1];
2230
+ const expectedH1 = generateNoteTitle(
2231
+ noteInfo.index,
2232
+ expectedTitle,
2233
+ REPO_NOTES_URL
2234
+ );
2235
+ const firstLine = lines[0].trim();
2236
+ if (!firstLine.startsWith("# ")) {
2237
+ lines.unshift(expectedH1);
2238
+ writeFileSync2(noteInfo.readmePath, lines.join("\n"), "utf-8");
2239
+ logger.info(`Added title to: ${noteInfo.dirName}`);
2240
+ return true;
2241
+ }
2242
+ if (firstLine !== expectedH1) {
2243
+ lines[0] = expectedH1;
2244
+ writeFileSync2(noteInfo.readmePath, lines.join("\n"), "utf-8");
2245
+ logger.info(`Fixed title for: ${noteInfo.dirName}`);
2246
+ return true;
2247
+ }
2248
+ return false;
2249
+ } catch (error) {
2250
+ logger.error(`Failed to fix title for: ${noteInfo.dirName}`, error);
2251
+ return false;
2252
+ }
2253
+ }
2254
+ /**
2255
+ * 修正所有笔记的标题
2256
+ * @param providedNotes - 可选的笔记列表,不传则内部扫描
2257
+ * @returns 修正的笔记数量
2258
+ */
2259
+ async fixAllNoteTitles(providedNotes) {
2260
+ const notes = providedNotes ?? this.getAllNotes();
2261
+ let fixedCount = 0;
2262
+ for (const note of notes) {
2263
+ const fixed = await this.fixNoteTitle(note);
2264
+ if (fixed) {
2265
+ fixedCount++;
2266
+ }
2267
+ }
2268
+ if (fixedCount > 0) {
2269
+ logger.info(`Fixed ${fixedCount} note titles`);
2270
+ }
2271
+ return fixedCount;
2272
+ }
2273
+ };
2274
+
2890
2275
  // services/readme/service.ts
2891
2276
  import {
2892
- existsSync as existsSync7,
2893
- readFileSync as readFileSync6,
2894
- writeFileSync as writeFileSync5,
2895
- promises as fsPromises4
2277
+ existsSync as existsSync6,
2278
+ readFileSync as readFileSync5,
2279
+ writeFileSync as writeFileSync4,
2280
+ promises as fsPromises3
2896
2281
  } from "fs";
2897
2282
 
2898
2283
  // core/ReadmeGenerator.ts
2899
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
2284
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
2900
2285
 
2901
2286
  // core/TocGenerator.ts
2902
2287
  var BILIBILI_VIDEO_BASE_URL = "https://www.bilibili.com/video/";
@@ -3062,7 +2447,7 @@ var ReadmeGenerator = class {
3062
2447
  logger.warn(`\u7B14\u8BB0 ${noteInfo.dirName} \u7F3A\u5C11\u914D\u7F6E\u6587\u4EF6`);
3063
2448
  return;
3064
2449
  }
3065
- const content = readFileSync5(noteInfo.readmePath, "utf-8");
2450
+ const content = readFileSync4(noteInfo.readmePath, "utf-8");
3066
2451
  if (content.length === 0) return;
3067
2452
  const lines = content.split(EOL);
3068
2453
  const repoName2 = this.configManager.get("repoName");
@@ -3073,7 +2458,7 @@ var ReadmeGenerator = class {
3073
2458
  repoName2
3074
2459
  );
3075
2460
  const updatedContent = lines.join(EOL);
3076
- writeFileSync4(noteInfo.readmePath, updatedContent, "utf-8");
2461
+ writeFileSync3(noteInfo.readmePath, updatedContent, "utf-8");
3077
2462
  }
3078
2463
  /**
3079
2464
  * 更新首页 README
@@ -3082,11 +2467,11 @@ var ReadmeGenerator = class {
3082
2467
  * @param homeReadmePath - 首页 README 路径
3083
2468
  */
3084
2469
  updateHomeReadme(notes, homeReadmePath) {
3085
- if (!existsSync6(homeReadmePath)) {
2470
+ if (!existsSync5(homeReadmePath)) {
3086
2471
  logger.error(`\u6839\u76EE\u5F55\u4E0B\u7684 README.md \u6587\u4EF6\u672A\u627E\u5230\uFF1A${homeReadmePath}`);
3087
2472
  return;
3088
2473
  }
3089
- const content = readFileSync5(homeReadmePath, "utf-8");
2474
+ const content = readFileSync4(homeReadmePath, "utf-8");
3090
2475
  const lines = content.split(EOL);
3091
2476
  const noteByIndexMap = /* @__PURE__ */ new Map();
3092
2477
  for (const note of notes) {
@@ -3178,7 +2563,7 @@ var ReadmeGenerator = class {
3178
2563
  this.tocGenerator.updateHomeToc(lines, titles, titlesNotesCount);
3179
2564
  const processedLines = processEmptyLines(lines);
3180
2565
  const updatedContent = processedLines.join(EOL);
3181
- writeFileSync4(homeReadmePath, updatedContent, "utf-8");
2566
+ writeFileSync3(homeReadmePath, updatedContent, "utf-8");
3182
2567
  logger.info("\u5DF2\u66F4\u65B0\u9996\u9875 README");
3183
2568
  }
3184
2569
  };
@@ -3190,7 +2575,6 @@ var ReadmeService = class _ReadmeService {
3190
2575
  this.readmeGenerator = new ReadmeGenerator();
3191
2576
  this.configManager = ConfigManager.getInstance();
3192
2577
  this.noteIndexCache = NoteIndexCache.getInstance();
3193
- this.tocService = TocService.getInstance();
3194
2578
  }
3195
2579
  static getInstance() {
3196
2580
  if (!_ReadmeService.instance) {
@@ -3230,8 +2614,7 @@ var ReadmeService = class _ReadmeService {
3230
2614
  await this.updateHomeReadme(notes);
3231
2615
  }
3232
2616
  if (updateSidebar) {
3233
- await this.tocService.normalizeToc(notes);
3234
- await this.tocService.regenerateSidebar(notes);
2617
+ await reconcileTocFromFiles(ROOT_DIR_PATH);
3235
2618
  }
3236
2619
  logger.info("\u77E5\u8BC6\u5E93\u66F4\u65B0\u5B8C\u6210\uFF01");
3237
2620
  }
@@ -3311,11 +2694,11 @@ var ReadmeService = class _ReadmeService {
3311
2694
  * @param notes - 笔记信息数组
3312
2695
  */
3313
2696
  async updateSidebar(notes) {
3314
- if (!existsSync7(ROOT_README_PATH)) {
2697
+ if (!existsSync6(ROOT_README_PATH)) {
3315
2698
  logger.error("\u672A\u627E\u5230\u9996\u9875 README\uFF0C\u65E0\u6CD5\u751F\u6210\u4FA7\u8FB9\u680F");
3316
2699
  return;
3317
2700
  }
3318
- const content = readFileSync6(ROOT_README_PATH, "utf-8");
2701
+ const content = readFileSync5(ROOT_README_PATH, "utf-8");
3319
2702
  const lines = content.split("\n");
3320
2703
  const itemList = [];
3321
2704
  const titles = [];
@@ -3378,7 +2761,7 @@ var ReadmeService = class _ReadmeService {
3378
2761
  titlesNotesCount,
3379
2762
  sidebarIsCollapsed
3380
2763
  );
3381
- writeFileSync5(
2764
+ writeFileSync4(
3382
2765
  VP_SIDEBAR_PATH,
3383
2766
  JSON.stringify(hierarchicalSidebar, null, 2),
3384
2767
  "utf-8"
@@ -3403,7 +2786,7 @@ var ReadmeService = class _ReadmeService {
3403
2786
  logger.warn(`\u5C1D\u8BD5\u66F4\u65B0\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0: ${noteIndex}`);
3404
2787
  return;
3405
2788
  }
3406
- const content = await fsPromises4.readFile(ROOT_README_PATH, "utf-8");
2789
+ const content = await fsPromises3.readFile(ROOT_README_PATH, "utf-8");
3407
2790
  const lines = content.split("\n");
3408
2791
  const repoOwner = this.configManager.get("author");
3409
2792
  const repoName2 = this.configManager.get("repoName");
@@ -3425,7 +2808,7 @@ var ReadmeService = class _ReadmeService {
3425
2808
  }
3426
2809
  }
3427
2810
  if (updated) {
3428
- await fsPromises4.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
2811
+ await fsPromises3.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
3429
2812
  logger.info(`\u589E\u91CF\u66F4\u65B0 README.md \u4E2D\u7684\u7B14\u8BB0: ${noteIndex}`);
3430
2813
  } else {
3431
2814
  logger.warn(`README.md \u4E2D\u672A\u627E\u5230\u7B14\u8BB0: ${noteIndex}`);
@@ -3436,7 +2819,7 @@ var ReadmeService = class _ReadmeService {
3436
2819
  * @param noteIndex - 笔记索引
3437
2820
  */
3438
2821
  async deleteNoteFromReadme(noteIndex) {
3439
- const content = await fsPromises4.readFile(ROOT_README_PATH, "utf-8");
2822
+ const content = await fsPromises3.readFile(ROOT_README_PATH, "utf-8");
3440
2823
  const lines = content.split("\n");
3441
2824
  const linesToRemove = [];
3442
2825
  for (let i = 0; i < lines.length; i++) {
@@ -3449,7 +2832,7 @@ var ReadmeService = class _ReadmeService {
3449
2832
  for (let i = linesToRemove.length - 1; i >= 0; i--) {
3450
2833
  lines.splice(linesToRemove[i], 1);
3451
2834
  }
3452
- await fsPromises4.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
2835
+ await fsPromises3.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
3453
2836
  logger.info(
3454
2837
  `\u4ECE README.md \u4E2D\u5220\u9664\u7B14\u8BB0: ${noteIndex} (${linesToRemove.length} \u5904\u5F15\u7528)`
3455
2838
  );
@@ -3467,7 +2850,7 @@ var ReadmeService = class _ReadmeService {
3467
2850
  logger.warn(`\u5C1D\u8BD5\u6DFB\u52A0\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0: ${noteIndex}`);
3468
2851
  return;
3469
2852
  }
3470
- const content = await fsPromises4.readFile(ROOT_README_PATH, "utf-8");
2853
+ const content = await fsPromises3.readFile(ROOT_README_PATH, "utf-8");
3471
2854
  const lines = content.split("\n");
3472
2855
  const repoOwner = this.configManager.get("author");
3473
2856
  const repoName2 = this.configManager.get("repoName");
@@ -3481,7 +2864,7 @@ var ReadmeService = class _ReadmeService {
3481
2864
  };
3482
2865
  const noteLine = buildNoteLineMarkdown(tempNoteInfo, repoOwner, repoName2);
3483
2866
  lines.push(noteLine);
3484
- await fsPromises4.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
2867
+ await fsPromises3.writeFile(ROOT_README_PATH, lines.join("\n"), "utf-8");
3485
2868
  logger.info(`\u5728 README.md \u672B\u5C3E\u6DFB\u52A0\u7B14\u8BB0: ${noteIndex}`);
3486
2869
  }
3487
2870
  /**
@@ -3604,26 +2987,24 @@ var ReadmeService = class _ReadmeService {
3604
2987
  /**
3605
2988
  * 更新 TOC.md 和 sidebar,不重写每篇笔记 README
3606
2989
  */
3607
- async refreshHomeReadmeAndSidebar(notes) {
3608
- const allNotes = notes ?? (this.noteIndexCache.isInitialized() ? this.noteIndexCache.toNoteInfoList() : this.noteManager.scanNotes());
3609
- await this.tocService.normalizeToc(allNotes);
3610
- await this.tocService.regenerateSidebar(allNotes);
2990
+ async refreshHomeReadmeAndSidebar(_notes) {
2991
+ await reconcileTocFromFiles(ROOT_DIR_PATH);
3611
2992
  }
3612
2993
  /**
3613
2994
  * 重新生成 sidebar.json(基于当前 TOC.md)
3614
2995
  * @param notes - 可选的笔记列表,不传则内部扫描
3615
2996
  */
3616
- async regenerateSidebar(notes) {
3617
- await this.tocService.regenerateSidebar(notes);
2997
+ async regenerateSidebar(_notes) {
2998
+ await reconcileTocFromFiles(ROOT_DIR_PATH);
3618
2999
  logger.info("\u91CD\u65B0\u751F\u6210 sidebar.json");
3619
3000
  }
3620
3001
  async readHomeReadmeLines() {
3621
- const content = await fsPromises4.readFile(ROOT_README_PATH, "utf-8");
3002
+ const content = await fsPromises3.readFile(ROOT_README_PATH, "utf-8");
3622
3003
  return content.split("\n");
3623
3004
  }
3624
3005
  async writeHomeReadmeLines(lines) {
3625
3006
  const content = processEmptyLines(lines).join("\n");
3626
- await fsPromises4.writeFile(ROOT_README_PATH, content, "utf-8");
3007
+ await fsPromises3.writeFile(ROOT_README_PATH, content, "utf-8");
3627
3008
  }
3628
3009
  assertGroupPath(groupPath) {
3629
3010
  if (!Array.isArray(groupPath) || groupPath.length === 0) {
@@ -3731,7 +3112,6 @@ var FileWatcherService = class _FileWatcherService {
3731
3112
  init() {
3732
3113
  this.noteService = NoteService.getInstance();
3733
3114
  this.readmeService = ReadmeService.getInstance();
3734
- this.tocService = TocService.getInstance();
3735
3115
  this.noteIndexCache = NoteIndexCache.getInstance();
3736
3116
  this.watchState = this.initWatchState();
3737
3117
  this.scheduler = this.initScheduler();
@@ -3762,7 +3142,6 @@ var FileWatcherService = class _FileWatcherService {
3762
3142
  scheduler: this.scheduler,
3763
3143
  noteService: this.noteService,
3764
3144
  readmeService: this.readmeService,
3765
- tocService: this.tocService,
3766
3145
  noteIndexCache: this.noteIndexCache,
3767
3146
  logger,
3768
3147
  onRenameSuccess: (payload) => {
@@ -3864,9 +3243,8 @@ var FileWatcherService = class _FileWatcherService {
3864
3243
  }
3865
3244
  initCoordinator() {
3866
3245
  return new GlobalUpdateCoordinator({
3246
+ notesDir: this.notesDir,
3867
3247
  readmeService: this.readmeService,
3868
- tocService: this.tocService,
3869
- noteIndexCache: this.noteIndexCache,
3870
3248
  logger
3871
3249
  });
3872
3250
  }
@@ -4746,12 +4124,12 @@ var VitepressService = class _VitepressService {
4746
4124
  };
4747
4125
 
4748
4126
  // services/init-sub-repo/service.ts
4749
- import { copyFileSync, existsSync as existsSync9, mkdirSync, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
4750
- import { dirname as dirname2, join as join9, resolve as resolve3 } from "path";
4127
+ import { copyFileSync, existsSync as existsSync8, mkdirSync, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
4128
+ import { dirname as dirname4, join as join9, resolve as resolve3 } from "path";
4751
4129
  import { fileURLToPath } from "url";
4752
4130
 
4753
4131
  // services/init-sub-repo/initSubRepoLogic.ts
4754
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
4132
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
4755
4133
  import { join as join8, resolve as resolve2 } from "path";
4756
4134
  var TOPIC_PATTERN = /^[a-z][a-z0-9-]*$/;
4757
4135
  var DEFAULT_PORT = 8e3;
@@ -4944,20 +4322,20 @@ function buildDynamicFiles(ctx) {
4944
4322
  ];
4945
4323
  }
4946
4324
  function isAlreadyInitialized(rootPath2) {
4947
- return existsSync8(resolve2(rootPath2, ".tnotes.json"));
4325
+ return existsSync7(resolve2(rootPath2, ".tnotes.json"));
4948
4326
  }
4949
4327
  function getInitializedConfigPath(rootPath2) {
4950
4328
  return resolve2(rootPath2, ".tnotes.json");
4951
4329
  }
4952
4330
  function resolvePackageJsonMerge(targetPath, template) {
4953
- if (!existsSync8(targetPath)) {
4331
+ if (!existsSync7(targetPath)) {
4954
4332
  return {
4955
4333
  content: `${JSON.stringify(template, null, 2)}
4956
4334
  `,
4957
4335
  action: "created"
4958
4336
  };
4959
4337
  }
4960
- const existing = JSON.parse(readFileSync7(targetPath, "utf-8"));
4338
+ const existing = JSON.parse(readFileSync6(targetPath, "utf-8"));
4961
4339
  const merged = mergePackageJson(existing, template);
4962
4340
  return {
4963
4341
  content: `${JSON.stringify(merged, null, 2)}
@@ -4980,14 +4358,14 @@ function buildManualSteps(ctx) {
4980
4358
 
4981
4359
  // services/init-sub-repo/service.ts
4982
4360
  function getCorePackageRoot(fromModuleUrl) {
4983
- let dir = dirname2(fileURLToPath(fromModuleUrl));
4984
- while (dir !== dirname2(dir)) {
4361
+ let dir = dirname4(fileURLToPath(fromModuleUrl));
4362
+ while (dir !== dirname4(dir)) {
4985
4363
  const pkgPath = join9(dir, "package.json");
4986
- if (existsSync9(pkgPath)) {
4987
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4364
+ if (existsSync8(pkgPath)) {
4365
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
4988
4366
  if (pkg.name === "@tnotesjs/core") return dir;
4989
4367
  }
4990
- dir = dirname2(dir);
4368
+ dir = dirname4(dir);
4991
4369
  }
4992
4370
  throw new Error("\u65E0\u6CD5\u5B9A\u4F4D @tnotesjs/core \u5305\u6839\u76EE\u5F55");
4993
4371
  }
@@ -5009,12 +4387,12 @@ var InitSubRepoService = class _InitSubRepoService {
5009
4387
  const updated = [];
5010
4388
  for (const relativePath of STATIC_TEMPLATE_FILES) {
5011
4389
  const targetPath = resolve3(this.rootPath, relativePath);
5012
- if (existsSync9(targetPath)) {
4390
+ if (existsSync8(targetPath)) {
5013
4391
  skipped.push(relativePath);
5014
4392
  continue;
5015
4393
  }
5016
4394
  const sourcePath = join9(this.templateRoot, relativePath);
5017
- mkdirSync(dirname2(targetPath), { recursive: true });
4395
+ mkdirSync(dirname4(targetPath), { recursive: true });
5018
4396
  copyFileSync(sourcePath, targetPath);
5019
4397
  created.push(relativePath);
5020
4398
  }
@@ -5026,18 +4404,18 @@ var InitSubRepoService = class _InitSubRepoService {
5026
4404
  targetPath,
5027
4405
  packageTemplate
5028
4406
  );
5029
- mkdirSync(dirname2(targetPath), { recursive: true });
5030
- writeFileSync6(targetPath, content, "utf-8");
4407
+ mkdirSync(dirname4(targetPath), { recursive: true });
4408
+ writeFileSync5(targetPath, content, "utf-8");
5031
4409
  if (action === "created") created.push(file.relativePath);
5032
4410
  else updated.push(file.relativePath);
5033
4411
  continue;
5034
4412
  }
5035
- if (existsSync9(targetPath)) {
4413
+ if (existsSync8(targetPath)) {
5036
4414
  skipped.push(file.relativePath);
5037
4415
  continue;
5038
4416
  }
5039
- mkdirSync(dirname2(targetPath), { recursive: true });
5040
- writeFileSync6(targetPath, file.content, "utf-8");
4417
+ mkdirSync(dirname4(targetPath), { recursive: true });
4418
+ writeFileSync5(targetPath, file.content, "utf-8");
5041
4419
  created.push(file.relativePath);
5042
4420
  }
5043
4421
  return { created, skipped, updated };
@@ -5128,13 +4506,12 @@ var UpdateNoteConfigCommand = class extends BaseCommand {
5128
4506
  };
5129
4507
 
5130
4508
  // commands/note/RenameNoteCommand.ts
5131
- import { existsSync as existsSync10, renameSync, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
4509
+ import { existsSync as existsSync9, renameSync, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
5132
4510
  import { join as join10 } from "path";
5133
4511
  var RenameNoteCommand = class extends BaseCommand {
5134
4512
  constructor() {
5135
4513
  super("rename-note");
5136
4514
  this.noteService = NoteService.getInstance();
5137
- this.tocService = TocService.getInstance();
5138
4515
  }
5139
4516
  async run() {
5140
4517
  const noteIndex = process.env.NOTE_ID;
@@ -5165,7 +4542,7 @@ var RenameNoteCommand = class extends BaseCommand {
5165
4542
  }
5166
4543
  const newDirName = `${noteIndex}. ${newTitle.trim()}`;
5167
4544
  const newPath = join10(NOTES_PATH, newDirName);
5168
- if (existsSync10(newPath)) {
4545
+ if (existsSync9(newPath)) {
5169
4546
  throw new Error(`\u76EE\u6807\u6587\u4EF6\u5939\u5DF2\u5B58\u5728: ${newDirName}`);
5170
4547
  }
5171
4548
  const watcher = FileWatcherService.getInstance();
@@ -5185,8 +4562,8 @@ var RenameNoteCommand = class extends BaseCommand {
5185
4562
  try {
5186
4563
  this.logger.info("\u6B63\u5728\u66F4\u65B0\u7B14\u8BB0\u5185\u90E8\u6807\u9898...");
5187
4564
  const readmePath = join10(newPath, "README.md");
5188
- if (existsSync10(readmePath)) {
5189
- const content = readFileSync9(readmePath, "utf-8");
4565
+ if (existsSync9(readmePath)) {
4566
+ const content = readFileSync8(readmePath, "utf-8");
5190
4567
  const lines = content.split("\n");
5191
4568
  let h1Index = -1;
5192
4569
  for (let i = 0; i < lines.length; i++) {
@@ -5202,7 +4579,7 @@ var RenameNoteCommand = class extends BaseCommand {
5202
4579
  REPO_NOTES_URL
5203
4580
  );
5204
4581
  lines[h1Index] = newH1;
5205
- writeFileSync7(readmePath, lines.join("\n"), "utf-8");
4582
+ writeFileSync6(readmePath, lines.join("\n"), "utf-8");
5206
4583
  this.logger.success("\u2705 \u7B14\u8BB0\u6807\u9898\u5DF2\u66F4\u65B0");
5207
4584
  } else {
5208
4585
  this.logger.warn(
@@ -5215,8 +4592,7 @@ var RenameNoteCommand = class extends BaseCommand {
5215
4592
  }
5216
4593
  try {
5217
4594
  this.logger.info("\u6B63\u5728\u66F4\u65B0 TOC.md \u548C sidebar.json...");
5218
- await this.tocService.renameNoteInToc(noteIndex, newDirName);
5219
- await this.tocService.regenerateSidebar();
4595
+ await reconcileTocFromFiles(ROOT_DIR_PATH);
5220
4596
  this.logger.success("\u2705 \u5168\u5C40\u6587\u4EF6\u5DF2\u66F4\u65B0");
5221
4597
  } catch (error) {
5222
4598
  this.logger.warn("\u26A0\uFE0F \u6587\u4EF6\u5939\u91CD\u547D\u540D\u6210\u529F,\u4F46\u66F4\u65B0\u5168\u5C40\u6587\u4EF6\u65F6\u51FA\u9519:", error);
@@ -5226,19 +4602,17 @@ var RenameNoteCommand = class extends BaseCommand {
5226
4602
 
5227
4603
  export {
5228
4604
  ROOT_DIR_PATH,
5229
- ROOT_README_PATH,
5230
4605
  ROOT_TOC_PATH,
5231
4606
  ROOT_CONFIG_PATH,
4607
+ reconcileTocFromFiles,
5232
4608
  handleError,
5233
4609
  logger,
5234
4610
  createLogger,
5235
4611
  parseArgs,
5236
4612
  parseReadmeCompletedNotes,
5237
- migrateReadmeToToc,
5238
4613
  runCommand,
5239
4614
  NoteManager,
5240
4615
  NoteIndexCache,
5241
- TocService,
5242
4616
  NoteService,
5243
4617
  ReadmeService,
5244
4618
  FileWatcherService,