@tnotesjs/core 0.6.1 → 0.8.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 (92) hide show
  1. package/commands/BaseCommand.ts +58 -0
  2. package/commands/build/BuildCommand.ts +25 -0
  3. package/commands/build/PreviewCommand.ts +29 -0
  4. package/commands/build/index.ts +8 -0
  5. package/commands/dev/DevCommand.ts +75 -0
  6. package/commands/dev/index.ts +7 -0
  7. package/commands/git/PullCommand.ts +25 -0
  8. package/commands/git/PushCommand.ts +64 -0
  9. package/commands/git/index.ts +8 -0
  10. package/commands/index.ts +11 -0
  11. package/commands/init-sub-repo/InitSubRepoCommand.ts +206 -0
  12. package/commands/init-sub-repo/index.ts +1 -0
  13. package/commands/misc/HelpCommand.ts +104 -0
  14. package/commands/misc/index.ts +7 -0
  15. package/commands/models.ts +87 -0
  16. package/commands/note/CreateNoteCommand.ts +160 -0
  17. package/commands/note/RenameNoteCommand.ts +147 -0
  18. package/commands/note/UpdateNoteConfigCommand.ts +78 -0
  19. package/commands/note/index.ts +9 -0
  20. package/commands/registry.ts +47 -0
  21. package/commands/update/UpdateCommand.ts +219 -0
  22. package/commands/update/index.ts +7 -0
  23. package/commands/update-completed-count/UpdateCompletedCountCommand.ts +208 -0
  24. package/commands/update-completed-count/index.ts +5 -0
  25. package/config/templates.ts +0 -5
  26. package/dist/{chunk-5F5RJ2HV.cjs → chunk-7FEANCT6.cjs} +16 -7
  27. package/dist/chunk-AGVER5MX.cjs +1746 -0
  28. package/dist/{chunk-TJ4527KA.cjs → chunk-BL22KD4V.cjs} +324 -950
  29. package/dist/{chunk-56PKXCOX.js → chunk-GWG75A5M.js} +13 -4
  30. package/dist/{chunk-DUTS75WQ.js → chunk-XNO3PCEH.js} +374 -1000
  31. package/dist/chunk-ZEQS64PE.js +1746 -0
  32. package/dist/cli/index.cjs +76 -155
  33. package/dist/cli/index.js +10 -89
  34. package/dist/markdown/index.cjs +8 -12
  35. package/dist/markdown/index.d.cts +1 -1
  36. package/dist/markdown/index.d.ts +1 -1
  37. package/dist/markdown/index.js +7 -11
  38. package/dist/{types-CwBWwNVv.d.ts → types-C_d0fFeD.d.ts} +8 -1
  39. package/dist/{types-CpsEy8lA.d.cts → types-MRBGgJTX.d.cts} +8 -1
  40. package/dist/vitepress/config/index.cjs +402 -178
  41. package/dist/vitepress/config/index.js +392 -168
  42. package/dist/workspace/index.cjs +4 -1202
  43. package/dist/workspace/index.d.cts +2 -2
  44. package/dist/workspace/index.d.ts +2 -2
  45. package/dist/workspace/index.js +4 -1202
  46. package/markdown/components.ts +86 -0
  47. package/markdown/index.ts +17 -0
  48. package/markdown/noteFormatter.test.ts +44 -0
  49. package/markdown/noteFormatter.ts +237 -0
  50. package/package.json +7 -3
  51. package/services/file-watcher/folderChangeHandler.ts +19 -15
  52. package/services/file-watcher/globalUpdateCoordinator.ts +21 -27
  53. package/services/file-watcher/service.ts +1 -6
  54. package/services/index.ts +0 -1
  55. package/services/note/service.ts +4 -6
  56. package/services/readme/service.ts +11 -18
  57. package/services/reconcileToc.ts +20 -0
  58. package/utils/index.ts +0 -9
  59. package/{services/toc → utils}/moveTocInside.test.ts +1 -1
  60. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +9 -18
  61. package/vitepress/components/EnWordList/EnWordList.vue +16 -662
  62. package/vitepress/components/Footprints/Footprints.vue +15 -537
  63. package/vitepress/components/Mermaid/Mermaid.vue +13 -588
  64. package/vitepress/components/MindmapPreview/MindmapPreview.vue +12 -434
  65. package/vitepress/components/MindmapPreview/markdown.ts +1 -1
  66. package/vitepress/components/NotesTable/NotesTable.vue +11 -130
  67. package/vitepress/components/constants.ts +2 -2
  68. package/vitepress/components/sidebar.data.ts +8 -5
  69. package/vitepress/configs/markdown.config.ts +170 -26
  70. package/vitepress/plugins/sidebarStructurePlugin.ts +242 -145
  71. package/vitepress/theme/index.ts +9 -13
  72. package/vitepress/theme/styles/base.scss +15 -0
  73. package/workspace/atomic.ts +113 -0
  74. package/workspace/errors.ts +27 -0
  75. package/workspace/index.ts +40 -0
  76. package/workspace/mutationQueue.ts +28 -0
  77. package/workspace/paths.ts +64 -0
  78. package/workspace/reconcile.test.ts +300 -0
  79. package/workspace/reconcile.ts +95 -0
  80. package/workspace/scanner.ts +292 -0
  81. package/workspace/types.ts +224 -0
  82. package/workspace/workspace.test.ts +333 -0
  83. package/workspace/workspace.ts +1020 -0
  84. package/dist/chunk-3R7QSABB.cjs +0 -502
  85. package/dist/chunk-CZXRQPFJ.js +0 -11
  86. package/dist/chunk-HXED7KVT.cjs +0 -11
  87. package/dist/chunk-U6IBLREL.js +0 -502
  88. package/services/toc/index.ts +0 -5
  89. package/services/toc/service.ts +0 -759
  90. package/utils/migrateReadmeToToc.test.ts +0 -111
  91. package/utils/migrateReadmeToToc.ts +0 -135
  92. package/vitepress/components/EnWordList/RightClickMenu.vue +0 -93
@@ -1,23 +1,24 @@
1
1
  import {
2
2
  FileWatcherService,
3
3
  NoteIndexCache,
4
- NoteService,
4
+ ROOT_DIR_PATH,
5
5
  RenameNoteCommand,
6
- TocService,
7
6
  UpdateNoteConfigCommand,
8
7
  logger
9
- } from "../../chunk-DUTS75WQ.js";
8
+ } from "../../chunk-XNO3PCEH.js";
10
9
  import {
11
10
  ConfigManager
12
11
  } from "../../chunk-UJG3UU5X.js";
13
- import "../../chunk-U6IBLREL.js";
12
+ import {
13
+ createWorkspace
14
+ } from "../../chunk-ZEQS64PE.js";
14
15
  import {
15
16
  generateAnchor
16
- } from "../../chunk-CZXRQPFJ.js";
17
+ } from "../../chunk-GWG75A5M.js";
17
18
 
18
19
  // vitepress/config/index.ts
19
- import fs3 from "fs";
20
- import path5 from "path";
20
+ import fs2 from "fs";
21
+ import path4 from "path";
21
22
  import { defineConfig } from "vitepress";
22
23
 
23
24
  // vitepress/config/dependencyOptimization.ts
@@ -62,11 +63,9 @@ function getHeadConfig(config, githubPageUrl) {
62
63
  }
63
64
 
64
65
  // vitepress/configs/markdown.config.ts
65
- import fs from "fs";
66
66
  import markdownItContainer from "markdown-it-container";
67
67
  import mila from "markdown-it-link-attributes";
68
68
  import markdownItTaskLists from "markdown-it-task-lists";
69
- import path from "path";
70
69
 
71
70
  // vitepress/components/MindmapPreview/markdown.ts
72
71
  function cleanHeadingText(value) {
@@ -89,19 +88,6 @@ function parseMindmapFence(openLine) {
89
88
  }
90
89
  return options;
91
90
  }
92
- function parseMindmapReference(line) {
93
- const match = line.trim().match(/^<<<\s+(.+?)\s*$/);
94
- if (!match) return null;
95
- let rest = match[1].trim();
96
- let title;
97
- const titleMatch = rest.match(/\s+\[([^\]]+)\]\s*$/);
98
- if (titleMatch) {
99
- title = cleanHeadingText(titleMatch[1]) || void 0;
100
- rest = rest.slice(0, titleMatch.index).trim();
101
- }
102
- const path6 = rest.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, "$1$2").trim();
103
- return path6 ? { path: path6, title } : null;
104
- }
105
91
  function normalizeMindmapMarkdown(source, options = {}) {
106
92
  const lines = source.replace(/\r\n?/g, "\n").split("\n");
107
93
  let existingTitle = "";
@@ -124,6 +110,79 @@ ${body.join("\n")}
124
110
  `;
125
111
  }
126
112
 
113
+ // node_modules/.pnpm/@tnotesjs+ui@0.1.1_vue@3.5.31_typescript@5.9.3_/node_modules/@tnotesjs/ui/src/components/Footprints/parse.ts
114
+ function parseFootprintsDatetime(meta) {
115
+ const m = meta.trim().match(
116
+ /^(\d{4})-(\d{1,2})-(\d{1,2})(?:\s+(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?)?$/
117
+ );
118
+ if (!m) return [];
119
+ const parts = [Number(m[1]), Number(m[2]), Number(m[3])];
120
+ if (m[4] !== void 0) {
121
+ parts.push(Number(m[4]), Number(m[5]));
122
+ if (m[6] !== void 0) parts.push(Number(m[6]));
123
+ }
124
+ return parts;
125
+ }
126
+ function parseFootprintsSource(source) {
127
+ const normalized = source.replace(/\r\n?/g, "\n");
128
+ const lines = normalized.split("\n");
129
+ const open = lines[0]?.match(/^ {0,3}:{3,}\s*footprints(?:\s+(.*))?$/i);
130
+ const times = parseFootprintsDatetime((open?.[1] ?? "").trim());
131
+ let end = lines.length - 1;
132
+ while (end > 0 && !/^ {0,3}:{3,}\s*$/.test(lines[end])) end -= 1;
133
+ const bodyLines = lines.slice(1, end);
134
+ const paragraphs = [];
135
+ const images = [];
136
+ let otherInfo = "";
137
+ let buf = [];
138
+ let inOther = false;
139
+ const flush = () => {
140
+ const text = buf.join("\n").trim();
141
+ buf = [];
142
+ if (!text) return;
143
+ if (inOther) {
144
+ otherInfo = otherInfo ? `${otherInfo}
145
+ ${text}` : text;
146
+ return;
147
+ }
148
+ const imgOnly = text.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
149
+ if (imgOnly) {
150
+ images.push(imgOnly[2]);
151
+ return;
152
+ }
153
+ const parts = text.split("\n");
154
+ let allImg = true;
155
+ const blockImgs = [];
156
+ for (const line of parts) {
157
+ const m = line.trim().match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
158
+ if (m) blockImgs.push(m[2]);
159
+ else if (line.trim()) {
160
+ allImg = false;
161
+ break;
162
+ }
163
+ }
164
+ if (allImg && blockImgs.length) {
165
+ images.push(...blockImgs);
166
+ return;
167
+ }
168
+ paragraphs.push(text);
169
+ };
170
+ for (const line of bodyLines) {
171
+ if (line.trim() === "---") {
172
+ flush();
173
+ inOther = true;
174
+ continue;
175
+ }
176
+ if (line.trim() === "") {
177
+ flush();
178
+ continue;
179
+ }
180
+ buf.push(line);
181
+ }
182
+ flush();
183
+ return { times, paragraphs, images, otherInfo };
184
+ }
185
+
127
186
  // vitepress/configs/markdown.config.ts
128
187
  function esc(s = "") {
129
188
  return s.replace(
@@ -141,11 +200,14 @@ var simpleMermaidMarkdown = (md) => {
141
200
  const fence = md.renderer.rules.fence ? md.renderer.rules.fence.bind(md.renderer.rules) : () => "";
142
201
  md.renderer.rules.fence = (tokens, index, options, env, slf) => {
143
202
  const token = tokens[index];
144
- if (token.info.trim() === "mermaid") {
203
+ const parts = token.info.trim().split(/\s+/).filter(Boolean);
204
+ if (parts[0] === "mermaid") {
145
205
  try {
206
+ const centered = parts.slice(1).some((part) => part.toLowerCase() === "center");
146
207
  const key = `mermaid-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
147
208
  const content = token.content;
148
- return `<Mermaid id="${key}" graph="${encodeURIComponent(content)}" />`;
209
+ const centerAttr = centered ? ' :center="true"' : "";
210
+ return `<Mermaid id="${key}" graph="${encodeURIComponent(content)}"${centerAttr} />`;
149
211
  } catch (err) {
150
212
  return `<pre>${err}</pre>`;
151
213
  }
@@ -163,28 +225,14 @@ function configureMindmapFence(md) {
163
225
  const info = token.info.trim();
164
226
  const fenceOptions = parseMindmapFence(info);
165
227
  if (!fenceOptions) return fence(tokens, index, options, env, slf);
166
- let content = token.content;
167
- const firstNonEmptyLine = content.split("\n").find((line) => line.trim()) ?? "";
168
- const reference = parseMindmapReference(firstNonEmptyLine);
169
- if (reference) {
170
- const possibleRel = env?.relativePath || env?.path || env?.filePath || env?.file || "";
171
- const refFullPath = path.isAbsolute(reference.path) ? reference.path : path.resolve(process.cwd(), possibleRel ? path.dirname(possibleRel) : "", reference.path);
172
- try {
173
- content = fs.readFileSync(refFullPath, "utf8");
174
- } catch (error) {
175
- const message = error instanceof Error ? error.message : String(error);
176
- content = `- Failed to load referenced file: ${reference.path}
177
- - Error: ${message}`;
178
- }
179
- }
180
- content = normalizeMindmapMarkdown(content, {
181
- title: fenceOptions.title || reference?.title
228
+ const content = normalizeMindmapMarkdown(token.content, {
229
+ title: fenceOptions.title
182
230
  });
183
231
  const props = [
184
232
  `content="${encodeURIComponent(content.trim())}"`,
185
233
  fenceOptions.initialExpandLevel === void 0 ? "" : `:initialExpandLevel="${fenceOptions.initialExpandLevel}"`
186
234
  ].filter(Boolean).join(" ");
187
- return `<MindmapPreview ${props}></MindmapPreview>
235
+ return `<Mindmap ${props}></Mindmap>
188
236
  `;
189
237
  };
190
238
  }
@@ -246,6 +294,114 @@ function configureSwiperContainer(md) {
246
294
  }
247
295
  });
248
296
  }
297
+ function escapeHtmlText(s) {
298
+ return s.replace(
299
+ /[&<>"']/g,
300
+ (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]
301
+ );
302
+ }
303
+ function extractFootprintsPayloadFromTokens(tokens, idx) {
304
+ const meta = String(tokens[idx].info || "").trim().replace(/^footprints\s*/i, "");
305
+ const times = parseFootprintsDatetime(meta);
306
+ const paragraphs = [];
307
+ const images = [];
308
+ let otherInfo = "";
309
+ let inOther = false;
310
+ for (let i = idx + 1; i < tokens.length; i++) {
311
+ const t = tokens[i];
312
+ if (t.type === "container_footprints_close") break;
313
+ if (t.type !== "inline") continue;
314
+ const childImgs = [];
315
+ if (Array.isArray(t.children)) {
316
+ for (const c of t.children) {
317
+ if (c.type === "image") {
318
+ const src = c.attrGet?.("src") || c.attrs?.find((a) => a[0] === "src")?.[1];
319
+ if (src) childImgs.push(src);
320
+ }
321
+ }
322
+ }
323
+ const content = String(t.content || "").trim();
324
+ if (content === "---") {
325
+ inOther = true;
326
+ continue;
327
+ }
328
+ if (childImgs.length) {
329
+ const withoutImgs = content.replace(/!\[[^\]]*\]\([^)]+\)/g, "").trim();
330
+ if (!withoutImgs) {
331
+ images.push(...childImgs);
332
+ continue;
333
+ }
334
+ }
335
+ const imgOnly = content.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
336
+ if (imgOnly) {
337
+ images.push(imgOnly[2]);
338
+ continue;
339
+ }
340
+ if (!content) continue;
341
+ if (inOther) otherInfo = otherInfo ? `${otherInfo}
342
+ ${content}` : content;
343
+ else paragraphs.push(content);
344
+ }
345
+ return { times, paragraphs, images, otherInfo };
346
+ }
347
+ function configureFootprintsContainer(md) {
348
+ md.use(markdownItContainer, "footprints", {
349
+ validate: (params) => /^footprints(\s|$)/i.test(params.trim()),
350
+ render: (tokens, idx, _opts, env) => {
351
+ if (tokens[idx].nesting !== 1) return "";
352
+ let payload = extractFootprintsPayloadFromTokens(tokens, idx);
353
+ const startLine = tokens[idx].map?.[0] ?? 0;
354
+ let endLine = startLine;
355
+ for (let i = idx + 1; i < tokens.length; i++) {
356
+ if (tokens[i].type === "container_footprints_close") {
357
+ endLine = tokens[i].map?.[0] ?? endLine;
358
+ break;
359
+ }
360
+ }
361
+ const raw = String(env?.src ?? env?.source ?? "");
362
+ if (raw && endLine > startLine) {
363
+ const slice = raw.split(/\r?\n/).slice(startLine, endLine + 1).join("\n");
364
+ const fromSource = parseFootprintsSource(
365
+ slice.includes(":::") ? slice : `::: ${tokens[idx].info}
366
+ ${slice}
367
+ :::`
368
+ );
369
+ if (!payload.paragraphs.length && fromSource.paragraphs.length) {
370
+ payload = { ...payload, paragraphs: fromSource.paragraphs };
371
+ }
372
+ if (!payload.images.length && fromSource.images.length) {
373
+ payload = { ...payload, images: fromSource.images };
374
+ }
375
+ if (!payload.otherInfo && fromSource.otherInfo) {
376
+ payload = { ...payload, otherInfo: fromSource.otherInfo };
377
+ }
378
+ if (!payload.times.length && fromSource.times.length) {
379
+ payload = { ...payload, times: fromSource.times };
380
+ }
381
+ }
382
+ for (let i = idx + 1; i < tokens.length; i++) {
383
+ if (tokens[i].type === "container_footprints_close") break;
384
+ tokens[i].hidden = true;
385
+ tokens[i].content = "";
386
+ if (Array.isArray(tokens[i].children)) {
387
+ for (const child of tokens[i].children) {
388
+ child.hidden = true;
389
+ child.content = "";
390
+ }
391
+ tokens[i].children = [];
392
+ }
393
+ }
394
+ const enc = (value) => encodeURIComponent(JSON.stringify(value)).replace(/'/g, "%27");
395
+ const bindExpr = (value) => "JSON.parse(decodeURIComponent('" + enc(value) + "'))";
396
+ const imageSlot = payload.images.map((src, i) => {
397
+ return '<img src="' + escapeHtmlText(src) + '" @click="openModal(' + String(i) + ')" />';
398
+ }).join("\n");
399
+ const openTag = '<Footprints :times="' + bindExpr(payload.times) + '" :paragraphs="' + bindExpr(payload.paragraphs) + '" :other-info="' + bindExpr(payload.otherInfo) + '">';
400
+ if (!payload.images.length) return openTag + "</Footprints>\n";
401
+ return openTag + '\n<template #image-list="{ openModal }">\n' + imageSlot + "\n</template>\n</Footprints>\n";
402
+ }
403
+ });
404
+ }
249
405
  function getMarkdownConfig() {
250
406
  const markdown = {
251
407
  lineNumbers: true,
@@ -265,6 +421,7 @@ function getMarkdownConfig() {
265
421
  }
266
422
  });
267
423
  configureSwiperContainer(md);
424
+ configureFootprintsContainer(md);
268
425
  },
269
426
  anchor: {
270
427
  slugify: generateAnchor
@@ -752,19 +909,19 @@ function getNoteByConfigIdPlugin() {
752
909
  }
753
910
 
754
911
  // vitepress/plugins/localSearchReindexPlugin.ts
755
- import path4 from "path";
912
+ import path3 from "path";
756
913
 
757
914
  // vitepress/plugins/localSearchIndexBuilder.ts
758
915
  import MiniSearch from "minisearch";
759
- import fs2 from "fs/promises";
760
- import path3 from "path";
916
+ import fs from "fs/promises";
917
+ import path2 from "path";
761
918
  import {
762
919
  createMarkdownRenderer,
763
920
  resolvePages
764
921
  } from "vitepress";
765
922
 
766
923
  // vitepress/plugins/localSearchReindexLogic.ts
767
- import path2 from "path";
924
+ import path from "path";
768
925
  function slash(value) {
769
926
  return value.replace(/\\/g, "/");
770
927
  }
@@ -780,13 +937,13 @@ function isNoteReadmePath(filePath, ignoreDirNames = []) {
780
937
  function getDocIdFromFile(input) {
781
938
  const pathApi = /^[a-zA-Z]:[\\/]/.test(
782
939
  input.absoluteOrRelativeFile || input.srcDir
783
- ) ? path2.win32 : path2;
940
+ ) ? path.win32 : path;
784
941
  const absoluteFile = pathApi.isAbsolute(input.absoluteOrRelativeFile) ? input.absoluteOrRelativeFile : pathApi.join(input.srcDir, input.absoluteOrRelativeFile);
785
942
  let relFile = slash(pathApi.relative(input.srcDir, absoluteFile));
786
943
  if (input.rewrites?.[relFile]) {
787
944
  relFile = input.rewrites[relFile];
788
945
  }
789
- let id = slash(path2.posix.join(input.base, relFile));
946
+ let id = slash(path.posix.join(input.base, relFile));
790
947
  id = id.replace(/(^|\/)index\.md$/, "$1");
791
948
  id = id.replace(/\.md$/, input.cleanUrls ? "" : ".html");
792
949
  return id;
@@ -851,9 +1008,9 @@ async function createLocalSearchMarkdownRenderer(siteConfig) {
851
1008
  );
852
1009
  }
853
1010
  async function renderPageHtml(siteConfig, md, absoluteFile) {
854
- if (!await fs2.stat(absoluteFile).catch(() => null)) return "";
1011
+ if (!await fs.stat(absoluteFile).catch(() => null)) return "";
855
1012
  const srcDir = siteConfig.srcDir;
856
- const relativePath = slash2(path3.relative(srcDir, absoluteFile));
1013
+ const relativePath = slash2(path2.relative(srcDir, absoluteFile));
857
1014
  const env = {
858
1015
  path: absoluteFile,
859
1016
  relativePath,
@@ -861,7 +1018,7 @@ async function renderPageHtml(siteConfig, md, absoluteFile) {
861
1018
  frontmatter: {}
862
1019
  };
863
1020
  const options = siteConfig.site.themeConfig?.search?.options ?? {};
864
- const mdRaw = await fs2.readFile(absoluteFile, "utf-8");
1021
+ const mdRaw = await fs.readFile(absoluteFile, "utf-8");
865
1022
  if (options._render) {
866
1023
  return await options._render(mdRaw, env, md);
867
1024
  }
@@ -869,7 +1026,7 @@ async function renderPageHtml(siteConfig, md, absoluteFile) {
869
1026
  return env.frontmatter?.search === false ? "" : html;
870
1027
  }
871
1028
  async function indexPage(siteConfig, md, page, index) {
872
- const absoluteFile = path3.join(siteConfig.srcDir, page);
1029
+ const absoluteFile = path2.join(siteConfig.srcDir, page);
873
1030
  const fileId = getDocIdFromFile({
874
1031
  srcDir: siteConfig.srcDir,
875
1032
  base: siteConfig.site.base,
@@ -1159,7 +1316,7 @@ function localSearchReindexPlugin() {
1159
1316
  const onWatcherEvent = (absoluteFile, event) => {
1160
1317
  if (!isNoteReadmePath(absoluteFile, ignoreDirNames)) return;
1161
1318
  scheduleNoteSearchReindex(
1162
- `watcher:${event}:${path4.basename(absoluteFile)}`
1319
+ `watcher:${event}:${path3.basename(absoluteFile)}`
1163
1320
  );
1164
1321
  };
1165
1322
  server.watcher.on("add", (file) => onWatcherEvent(file, "add"));
@@ -1249,21 +1406,8 @@ function normalizeCount(value) {
1249
1406
  }
1250
1407
  return count;
1251
1408
  }
1252
- function getCreatedNotePayload(notes) {
1253
- return notes.map((note) => ({
1254
- index: note.index,
1255
- dirName: note.dirName,
1256
- link: `/notes/${encodeURIComponent(note.dirName)}/README`
1257
- }));
1258
- }
1259
- function buildNoteReadmeLink(note) {
1260
- return `/notes/${encodeURIComponent(note.dirName)}/README`;
1261
- }
1262
- function resolveDeleteRedirectUrl(noteService, previousNoteIndex) {
1263
- if (!previousNoteIndex) return "/";
1264
- const previousNote = noteService.getNoteByIndex(previousNoteIndex);
1265
- if (!previousNote) return "/";
1266
- return buildNoteReadmeLink(previousNote);
1409
+ function buildNoteReadmeLink(dirName) {
1410
+ return `/notes/${encodeURIComponent(dirName)}/README`;
1267
1411
  }
1268
1412
  async function withSuspendedWatcher(task) {
1269
1413
  const watcher = FileWatcherService.getInstance();
@@ -1274,33 +1418,78 @@ async function withSuspendedWatcher(task) {
1274
1418
  watcher?.unsuspend();
1275
1419
  }
1276
1420
  }
1277
- function sidebarStructurePlugin() {
1278
- const noteService = NoteService.getInstance();
1279
- const tocService = TocService.getInstance();
1280
- async function createNotes(count) {
1281
- const notes = [];
1282
- const usedIndexes = /* @__PURE__ */ new Set();
1283
- for (const note of noteService.getAllNotes()) {
1284
- const index = parseInt(note.index, 10);
1285
- if (!isNaN(index)) {
1286
- usedIndexes.add(index);
1421
+ function collectRefs(snapshot) {
1422
+ const byLine = /* @__PURE__ */ new Map();
1423
+ const orderedIndexes = [];
1424
+ let order = 0;
1425
+ const walk = (nodes) => {
1426
+ for (const node of nodes) {
1427
+ if (node.kind === "folder") {
1428
+ walk(node.children);
1429
+ continue;
1287
1430
  }
1288
- }
1289
- for (let i = 0; i < count; i++) {
1290
- const note = await noteService.createNote({
1291
- title: "new",
1292
- usedIndexes
1431
+ const subtree = [];
1432
+ const collect = (n) => {
1433
+ if (n.kind === "note") subtree.push(n.noteIndex);
1434
+ for (const child of n.children) collect(child);
1435
+ };
1436
+ collect(node);
1437
+ byLine.set(node.tocLineIndex, {
1438
+ tocLineIndex: node.tocLineIndex,
1439
+ ref: { type: "note", noteUuid: "" },
1440
+ subtreeIndexes: subtree,
1441
+ orderIndex: order
1293
1442
  });
1294
- const index = parseInt(note.index, 10);
1295
- if (!isNaN(index)) {
1296
- usedIndexes.add(index);
1297
- }
1298
- notes.push(note);
1443
+ orderedIndexes.push(...subtree);
1444
+ order += 1;
1299
1445
  }
1300
- return notes;
1446
+ };
1447
+ walk(snapshot.toc);
1448
+ return { byLine, orderedIndexes };
1449
+ }
1450
+ function noteUuidForIndex(snapshot, index) {
1451
+ const note = snapshot.notes.find((item) => item.index === index);
1452
+ if (!note) throw new Error(`\u672A\u627E\u5230\u7B14\u8BB0: ${index}`);
1453
+ return note.uuid;
1454
+ }
1455
+ function previousNoteIndex(snapshot, entry) {
1456
+ if (!entry) return null;
1457
+ const entries = [...collectRefs(snapshot).byLine.values()].sort(
1458
+ (a, b) => a.orderIndex - b.orderIndex
1459
+ );
1460
+ let cursor = 0;
1461
+ for (const item of entries) {
1462
+ if (item === entry) return cursor > 0 ? collectRefs(snapshot).orderedIndexes[cursor - 1] : null;
1463
+ cursor += item.subtreeIndexes.length;
1301
1464
  }
1465
+ return null;
1466
+ }
1467
+ function isNoteInSubtree(snapshot, lineIndex, noteIndex) {
1468
+ const entry = collectRefs(snapshot).byLine.get(lineIndex);
1469
+ return entry ? entry.subtreeIndexes.includes(noteIndex) : false;
1470
+ }
1471
+ function folderPathAtLine(snapshot, lineIndex) {
1472
+ const result = [];
1473
+ const walk = (nodes, path5) => {
1474
+ for (const node of nodes) {
1475
+ if (node.kind === "folder") {
1476
+ if (node.tocLineIndex === lineIndex) {
1477
+ result.push(...[...path5, node.title]);
1478
+ return true;
1479
+ }
1480
+ if (walk(node.children, [...path5, node.title])) return true;
1481
+ }
1482
+ }
1483
+ return false;
1484
+ };
1485
+ walk(snapshot.toc, []);
1486
+ if (result.length === 0) throw new Error(`\u672A\u627E\u5230\u76EE\u5F55\u884C: ${lineIndex}`);
1487
+ return result;
1488
+ }
1489
+ function sidebarStructurePlugin() {
1490
+ const workspace = createWorkspace({ rootPath: ROOT_DIR_PATH });
1302
1491
  async function refreshTocAndSidebar() {
1303
- await tocService.regenerateSidebar();
1492
+ await workspace.toc.reconcileFromFiles();
1304
1493
  }
1305
1494
  return {
1306
1495
  name: "tnotes-sidebar-structure",
@@ -1327,22 +1516,27 @@ function sidebarStructurePlugin() {
1327
1516
  try {
1328
1517
  const data = await readJsonBody(req);
1329
1518
  const result = await withSuspendedWatcher(async () => {
1519
+ const snapshot = await workspace.inspect();
1330
1520
  if (pathname === "/__tnotes_sidebar_delete_note") {
1331
1521
  const noteIndex = String(data.noteIndex || "");
1332
1522
  if (!noteIndex) throw new Error("Missing noteIndex");
1333
- const previousNoteIndex = await tocService.getPreviousNoteIndexBeforeDelete(noteIndex);
1334
- await tocService.deleteNoteFromToc(noteIndex);
1335
- await noteService.deleteNote(noteIndex);
1523
+ const refs = collectRefs(snapshot);
1524
+ const deletedRef = [...refs.byLine.values()].find(
1525
+ (item) => item.subtreeIndexes.includes(noteIndex)
1526
+ );
1527
+ const previous = previousNoteIndex(snapshot, deletedRef);
1528
+ await workspace.toc.deleteEntry({
1529
+ entry: { type: "note", noteUuid: noteUuidForIndex(snapshot, noteIndex) },
1530
+ expectedSnapshotRevision: snapshot.revision
1531
+ });
1336
1532
  await refreshTocAndSidebar();
1337
1533
  scheduleNoteSearchReindex("api:delete-note");
1534
+ const redirectDir = previous ? snapshot.notes.find((item) => item.index === previous)?.dirName : void 0;
1338
1535
  return {
1339
1536
  success: true,
1340
1537
  sidebarChanged: true,
1341
- redirectUrl: resolveDeleteRedirectUrl(
1342
- noteService,
1343
- previousNoteIndex
1344
- ),
1345
- redirectNoteIndex: previousNoteIndex,
1538
+ redirectUrl: redirectDir ? buildNoteReadmeLink(redirectDir) : "/",
1539
+ redirectNoteIndex: previous,
1346
1540
  deletedNoteIndexes: [noteIndex],
1347
1541
  message: "\u7B14\u8BB0\u5DF2\u5220\u9664"
1348
1542
  };
@@ -1353,22 +1547,26 @@ function sidebarStructurePlugin() {
1353
1547
  throw new Error("Missing tocLineIndex");
1354
1548
  }
1355
1549
  const currentNoteIndex = String(data.currentNoteIndex || "");
1356
- const previousNoteIndex = await tocService.getPreviousNoteIndexBeforeEntryDelete(
1550
+ const inSubtree = currentNoteIndex ? isNoteInSubtree(snapshot, tocLineIndex, currentNoteIndex) : false;
1551
+ const entry = collectRefs(snapshot).byLine.get(tocLineIndex);
1552
+ const previous = previousNoteIndex(snapshot, entry);
1553
+ const preview = await workspace.toc.previewDelete({
1554
+ type: "line",
1357
1555
  tocLineIndex
1358
- );
1359
- const inSubtree = currentNoteIndex && await tocService.isNoteInTocEntrySubtree(
1360
- tocLineIndex,
1361
- currentNoteIndex
1362
- );
1363
- const deletedNoteIndexes = await tocService.deleteTocEntryCascade(tocLineIndex);
1556
+ });
1557
+ await workspace.toc.deleteEntry({
1558
+ entry: { type: "line", tocLineIndex },
1559
+ expectedSnapshotRevision: snapshot.revision
1560
+ });
1364
1561
  await refreshTocAndSidebar();
1365
1562
  scheduleNoteSearchReindex("api:delete-entry");
1563
+ const redirectDir = inSubtree && previous ? snapshot.notes.find((item) => item.index === previous)?.dirName : void 0;
1366
1564
  return {
1367
1565
  success: true,
1368
1566
  sidebarChanged: true,
1369
- redirectUrl: inSubtree ? resolveDeleteRedirectUrl(noteService, previousNoteIndex) : void 0,
1370
- redirectNoteIndex: inSubtree ? previousNoteIndex : null,
1371
- deletedNoteIndexes,
1567
+ redirectUrl: redirectDir ? buildNoteReadmeLink(redirectDir) : void 0,
1568
+ redirectNoteIndex: inSubtree ? previous : null,
1569
+ deletedNoteIndexes: preview.notes.map((note) => note.index),
1372
1570
  message: "\u5DF2\u5220\u9664"
1373
1571
  };
1374
1572
  }
@@ -1378,7 +1576,11 @@ function sidebarStructurePlugin() {
1378
1576
  if (!Number.isInteger(tocLineIndex) || tocLineIndex < 0) {
1379
1577
  throw new Error("Missing tocLineIndex");
1380
1578
  }
1381
- await tocService.renameFolderInToc(tocLineIndex, newTitle);
1579
+ await workspace.toc.renameGroup({
1580
+ folderPath: folderPathAtLine(snapshot, tocLineIndex),
1581
+ title: newTitle,
1582
+ expectedSnapshotRevision: snapshot.revision
1583
+ });
1382
1584
  await refreshTocAndSidebar();
1383
1585
  return {
1384
1586
  success: true,
@@ -1387,8 +1589,20 @@ function sidebarStructurePlugin() {
1387
1589
  };
1388
1590
  }
1389
1591
  if (pathname === "/__tnotes_sidebar_reorder") {
1390
- if (data.node_uuid && data.action === "prependChild" && !data.target_uuid) {
1391
- await tocService.prependToRootByNodeId(String(data.node_uuid));
1592
+ const awaitMove = async (source2, target, placement2) => {
1593
+ await workspace.toc.move({
1594
+ source: source2,
1595
+ target,
1596
+ placement: placement2,
1597
+ expectedSnapshotRevision: snapshot.revision
1598
+ });
1599
+ };
1600
+ if (typeof data.node_uuid === "string" && data.action === "prependChild" && !data.target_uuid) {
1601
+ await awaitMove(
1602
+ { type: "note", noteUuid: data.node_uuid },
1603
+ { type: "line", tocLineIndex: 0 },
1604
+ data.placement === "after" ? "after" : "before"
1605
+ );
1392
1606
  await refreshTocAndSidebar();
1393
1607
  return {
1394
1608
  success: true,
@@ -1396,18 +1610,12 @@ function sidebarStructurePlugin() {
1396
1610
  message: "\u6392\u5E8F\u5DF2\u66F4\u65B0"
1397
1611
  };
1398
1612
  }
1399
- if (data.node_uuid && data.target_uuid && (data.action === "moveAfter" || data.action === "prependChild")) {
1400
- if (data.action === "moveAfter") {
1401
- await tocService.moveAfterByNodeId(
1402
- String(data.node_uuid),
1403
- String(data.target_uuid)
1404
- );
1405
- } else {
1406
- await tocService.prependChildByNodeId(
1407
- String(data.node_uuid),
1408
- String(data.target_uuid)
1409
- );
1410
- }
1613
+ if (typeof data.node_uuid === "string" && typeof data.target_uuid === "string" && (data.action === "moveAfter" || data.action === "prependChild")) {
1614
+ await awaitMove(
1615
+ { type: "note", noteUuid: data.node_uuid },
1616
+ { type: "note", noteUuid: data.target_uuid },
1617
+ data.action === "prependChild" ? "inside" : "after"
1618
+ );
1411
1619
  await refreshTocAndSidebar();
1412
1620
  return {
1413
1621
  success: true,
@@ -1420,27 +1628,32 @@ function sidebarStructurePlugin() {
1420
1628
  throw new Error("Missing dragTocLineIndex");
1421
1629
  }
1422
1630
  const placement = data.targetType === "group" || data.placement === "inside" ? "inside" : data.placement === "after" ? "after" : "before";
1631
+ const source = { type: "line", tocLineIndex: dragTocLineIndex };
1423
1632
  if (data.targetTocLineIndex !== void 0 && data.targetTocLineIndex !== null) {
1424
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
1425
- targetTocLineIndex: Number(data.targetTocLineIndex),
1633
+ await awaitMove(
1634
+ source,
1635
+ { type: "line", tocLineIndex: Number(data.targetTocLineIndex) },
1426
1636
  placement
1427
- });
1637
+ );
1428
1638
  } else if (Array.isArray(data.targetFolderPath) && data.targetFolderPath.length > 0) {
1429
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
1430
- targetType: "folder",
1431
- targetFolderPath: data.targetFolderPath.map(String),
1639
+ await awaitMove(
1640
+ source,
1641
+ {
1642
+ type: "folder",
1643
+ folderPath: data.targetFolderPath.map(String)
1644
+ },
1432
1645
  placement
1433
- });
1646
+ );
1434
1647
  } else {
1435
- const targetNoteIndex = String(
1648
+ const targetNoteIndex2 = String(
1436
1649
  data.targetNoteIndex || data.targetGroupNoteIndex || ""
1437
1650
  );
1438
- if (!targetNoteIndex) throw new Error("Missing targetNoteIndex");
1439
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
1440
- targetType: "note",
1441
- targetNoteIndex,
1651
+ if (!targetNoteIndex2) throw new Error("Missing targetNoteIndex");
1652
+ await awaitMove(
1653
+ source,
1654
+ { type: "note", noteUuid: noteUuidForIndex(snapshot, targetNoteIndex2) },
1442
1655
  placement
1443
- });
1656
+ );
1444
1657
  }
1445
1658
  await refreshTocAndSidebar();
1446
1659
  return {
@@ -1455,10 +1668,13 @@ function sidebarStructurePlugin() {
1455
1668
  if (!Number.isInteger(parentTocLineIndex) || parentTocLineIndex < 0) {
1456
1669
  throw new Error("Missing parentTocLineIndex");
1457
1670
  }
1458
- await tocService.insertFolderUnderParent(
1459
- parentTocLineIndex,
1460
- title
1461
- );
1671
+ const sibling = collectRefs(snapshot).byLine.get(parentTocLineIndex);
1672
+ if (!sibling) throw new Error("\u672A\u627E\u5230\u76EE\u5F55\u884C");
1673
+ await workspace.toc.createGroup({
1674
+ title,
1675
+ placement: sibling.ref.type === "note" ? { type: "note", targetNoteUuid: sibling.ref.noteUuid, placement: "inside" } : { type: "root", placement: "end" },
1676
+ expectedSnapshotRevision: snapshot.revision
1677
+ });
1462
1678
  await refreshTocAndSidebar();
1463
1679
  return {
1464
1680
  success: true,
@@ -1467,40 +1683,48 @@ function sidebarStructurePlugin() {
1467
1683
  };
1468
1684
  }
1469
1685
  const count = normalizeCount(data.count);
1470
- const notes = await createNotes(count);
1471
- if (data.targetNoteIndex) {
1472
- const placement = data.placement === "after" ? "after" : "before";
1473
- await tocService.insertNotesAroundNote(
1474
- String(data.targetNoteIndex),
1475
- notes,
1476
- placement
1477
- );
1478
- } else if (data.parentTocLineIndex !== void 0 && data.parentTocLineIndex !== null) {
1479
- await tocService.insertNotesUnderTocLine(
1480
- Number(data.parentTocLineIndex),
1481
- notes
1482
- );
1483
- } else if (Array.isArray(data.parentFolderPath) && data.parentFolderPath.length > 0) {
1484
- await tocService.insertNotesUnderFolder(
1485
- data.parentFolderPath.map(String),
1486
- notes
1487
- );
1488
- } else if (data.parentNoteIndex) {
1489
- await tocService.insertNotesUnderParent(
1490
- String(data.parentNoteIndex),
1491
- notes
1492
- );
1493
- } else {
1494
- for (const note of notes) {
1495
- await tocService.appendNoteToToc(note.index);
1686
+ const targetNoteIndex = String(
1687
+ data.targetNoteIndex || data.targetGroupNoteIndex || ""
1688
+ );
1689
+ const parentLine = data.parentTocLineIndex !== void 0 && data.parentTocLineIndex !== null ? Number(data.parentTocLineIndex) : null;
1690
+ const created = [];
1691
+ for (let i = 0; i < count; i++) {
1692
+ let placement;
1693
+ if (targetNoteIndex) {
1694
+ placement = {
1695
+ type: "note",
1696
+ targetNoteUuid: noteUuidForIndex(snapshot, targetNoteIndex),
1697
+ placement: data.placement === "after" ? "after" : "before"
1698
+ };
1699
+ } else if (parentLine !== null) {
1700
+ const sibling = collectRefs(snapshot).byLine.get(parentLine);
1701
+ if (sibling && sibling.ref.type === "note") {
1702
+ placement = {
1703
+ type: "note",
1704
+ targetNoteUuid: sibling.ref.noteUuid,
1705
+ placement: "inside"
1706
+ };
1707
+ }
1496
1708
  }
1709
+ const result2 = await workspace.notes.create({
1710
+ title: "new",
1711
+ placement,
1712
+ expectedSnapshotRevision: snapshot.revision
1713
+ });
1714
+ created.push({
1715
+ index: result2.value.index,
1716
+ dirName: result2.value.dirName
1717
+ });
1497
1718
  }
1498
1719
  await refreshTocAndSidebar();
1499
1720
  scheduleNoteSearchReindex("api:create-notes");
1500
1721
  return {
1501
1722
  success: true,
1502
1723
  sidebarChanged: true,
1503
- createdNotes: getCreatedNotePayload(notes),
1724
+ createdNotes: created.map((note) => ({
1725
+ ...note,
1726
+ link: buildNoteReadmeLink(note.dirName)
1727
+ })),
1504
1728
  message: "\u7B14\u8BB0\u5DF2\u521B\u5EFA"
1505
1729
  };
1506
1730
  });
@@ -1644,9 +1868,9 @@ function defineNotesConfig(overrides = {}) {
1644
1868
  },
1645
1869
  transformPageData(pageData, ctx) {
1646
1870
  if (/^notes\/\d{4}/.test(pageData.relativePath)) {
1647
- const fullPath = path5.resolve(rootPath, pageData.relativePath);
1871
+ const fullPath = path4.resolve(rootPath, pageData.relativePath);
1648
1872
  try {
1649
- const raw = fs3.readFileSync(fullPath, "utf-8");
1873
+ const raw = fs2.readFileSync(fullPath, "utf-8");
1650
1874
  pageData.frontmatter.rawContent = Buffer.from(raw, "utf-8").toString(
1651
1875
  "base64"
1652
1876
  );