@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
@@ -2,13 +2,17 @@
2
2
  * vitepress/plugins/sidebarStructurePlugin.ts
3
3
  *
4
4
  * VitePress dev middleware - 处理语雀风格侧边栏的目录结构操作。
5
+ * 所有 TOC 写入统一走 Core Workspace(toc.move/createGroup/renameGroup/
6
+ * deleteEntry + notes.create;files→TOC 用 reconcileFromFiles)。
5
7
  */
6
8
 
7
9
  import { scheduleNoteSearchReindex } from './localSearchReindexPlugin'
8
- import { FileWatcherService, NoteService, TocService } from '../../services'
10
+ import { ROOT_DIR_PATH } from '../../config/constants'
11
+ import { FileWatcherService } from '../../services'
12
+ import { createWorkspace } from '../../workspace'
9
13
 
10
-
11
- import type { NoteInfo } from '../../types'
14
+ import type { TocTreeNode } from '../../utils'
15
+ import type { TocEntryRef, KnowledgeBaseSnapshot } from '../../workspace'
12
16
  import type { IncomingMessage, ServerResponse } from 'http'
13
17
  import type { PluginOption } from 'vite'
14
18
 
@@ -22,7 +26,7 @@ interface JsonResponse {
22
26
  deletedNoteIndexes?: string[]
23
27
  }
24
28
 
25
- async function readJsonBody(req: IncomingMessage): Promise<any> {
29
+ async function readJsonBody(req: IncomingMessage): Promise<Record<string, unknown>> {
26
30
  let body = ''
27
31
 
28
32
  await new Promise<void>((resolve, reject) => {
@@ -58,28 +62,8 @@ function normalizeCount(value: unknown): number {
58
62
  return count
59
63
  }
60
64
 
61
- function getCreatedNotePayload(notes: NoteInfo[]) {
62
- return notes.map((note) => ({
63
- index: note.index,
64
- dirName: note.dirName,
65
- link: `/notes/${encodeURIComponent(note.dirName)}/README`,
66
- }))
67
- }
68
-
69
- function buildNoteReadmeLink(note: NoteInfo): string {
70
- return `/notes/${encodeURIComponent(note.dirName)}/README`
71
- }
72
-
73
- function resolveDeleteRedirectUrl(
74
- noteService: NoteService,
75
- previousNoteIndex: string | null,
76
- ): string {
77
- if (!previousNoteIndex) return '/'
78
-
79
- const previousNote = noteService.getNoteByIndex(previousNoteIndex)
80
- if (!previousNote) return '/'
81
-
82
- return buildNoteReadmeLink(previousNote)
65
+ function buildNoteReadmeLink(dirName: string): string {
66
+ return `/notes/${encodeURIComponent(dirName)}/README`
83
67
  }
84
68
 
85
69
  async function withSuspendedWatcher<T>(task: () => Promise<T>): Promise<T> {
@@ -93,38 +77,107 @@ async function withSuspendedWatcher<T>(task: () => Promise<T>): Promise<T> {
93
77
  }
94
78
  }
95
79
 
96
- export function sidebarStructurePlugin(): PluginOption {
97
- const noteService = NoteService.getInstance()
98
- const tocService = TocService.getInstance()
80
+ // ---------------------------------------------------------------------------
81
+ // Snapshot-based mapping helpers (pure; operate on the workspace snapshot).
82
+ // ---------------------------------------------------------------------------
99
83
 
100
- async function createNotes(count: number) {
101
- const notes = []
102
- const usedIndexes = new Set<number>()
84
+ type WalkedRef = {
85
+ tocLineIndex: number
86
+ ref: TocEntryRef
87
+ subtreeIndexes: string[]
88
+ orderIndex: number
89
+ }
103
90
 
104
- for (const note of noteService.getAllNotes()) {
105
- const index = parseInt(note.index, 10)
106
- if (!isNaN(index)) {
107
- usedIndexes.add(index)
91
+ function collectRefs(snapshot: KnowledgeBaseSnapshot): {
92
+ byLine: Map<number, WalkedRef>
93
+ orderedIndexes: string[]
94
+ } {
95
+ const byLine = new Map<number, WalkedRef>()
96
+ const orderedIndexes: string[] = []
97
+ let order = 0
98
+
99
+ const walk = (nodes: TocTreeNode[]): void => {
100
+ for (const node of nodes) {
101
+ if (node.kind === 'folder') {
102
+ walk(node.children)
103
+ continue
108
104
  }
105
+ const subtree: string[] = []
106
+ const collect = (n: TocTreeNode): void => {
107
+ if (n.kind === 'note') subtree.push(n.noteIndex)
108
+ for (const child of n.children) collect(child)
109
+ }
110
+ collect(node)
111
+ byLine.set(node.tocLineIndex, {
112
+ tocLineIndex: node.tocLineIndex,
113
+ ref: { type: 'note', noteUuid: '' },
114
+ subtreeIndexes: subtree,
115
+ orderIndex: order,
116
+ })
117
+ orderedIndexes.push(...subtree)
118
+ order += 1
109
119
  }
120
+ }
121
+ walk(snapshot.toc)
122
+ return { byLine, orderedIndexes }
123
+ }
110
124
 
111
- for (let i = 0; i < count; i++) {
112
- const note = await noteService.createNote({
113
- title: 'new',
114
- usedIndexes,
115
- })
116
- const index = parseInt(note.index, 10)
117
- if (!isNaN(index)) {
118
- usedIndexes.add(index)
125
+ function noteUuidForIndex(snapshot: KnowledgeBaseSnapshot, index: string): string {
126
+ const note = snapshot.notes.find((item) => item.index === index)
127
+ if (!note) throw new Error(`未找到笔记: ${index}`)
128
+ return note.uuid
129
+ }
130
+
131
+ /** TOC 树中位于某 entry 之前的最后一个笔记索引(用于删除后的跳转)。 */
132
+ function previousNoteIndex(
133
+ snapshot: KnowledgeBaseSnapshot,
134
+ entry: WalkedRef | undefined,
135
+ ): string | null {
136
+ if (!entry) return null
137
+ const entries = [...collectRefs(snapshot).byLine.values()].sort(
138
+ (a, b) => a.orderIndex - b.orderIndex,
139
+ )
140
+ let cursor = 0
141
+ for (const item of entries) {
142
+ if (item === entry) return cursor > 0 ? collectRefs(snapshot).orderedIndexes[cursor - 1] : null
143
+ cursor += item.subtreeIndexes.length
144
+ }
145
+ return null
146
+ }
147
+
148
+ function isNoteInSubtree(
149
+ snapshot: KnowledgeBaseSnapshot,
150
+ lineIndex: number,
151
+ noteIndex: string,
152
+ ): boolean {
153
+ const entry = collectRefs(snapshot).byLine.get(lineIndex)
154
+ return entry ? entry.subtreeIndexes.includes(noteIndex) : false
155
+ }
156
+
157
+ function folderPathAtLine(snapshot: KnowledgeBaseSnapshot, lineIndex: number): string[] {
158
+ const result: string[] = []
159
+ const walk = (nodes: TocTreeNode[], path: string[]): boolean => {
160
+ for (const node of nodes) {
161
+ if (node.kind === 'folder') {
162
+ if (node.tocLineIndex === lineIndex) {
163
+ result.push(...[...path, node.title])
164
+ return true
165
+ }
166
+ if (walk(node.children, [...path, node.title])) return true
119
167
  }
120
- notes.push(note)
121
168
  }
122
-
123
- return notes
169
+ return false
124
170
  }
171
+ walk(snapshot.toc, [])
172
+ if (result.length === 0) throw new Error(`未找到目录行: ${lineIndex}`)
173
+ return result
174
+ }
125
175
 
126
- async function refreshTocAndSidebar() {
127
- await tocService.regenerateSidebar()
176
+ export function sidebarStructurePlugin(): PluginOption {
177
+ const workspace = createWorkspace({ rootPath: ROOT_DIR_PATH })
178
+
179
+ async function refreshTocAndSidebar(): Promise<void> {
180
+ await workspace.toc.reconcileFromFiles()
128
181
  }
129
182
 
130
183
  return {
@@ -156,26 +209,33 @@ export function sidebarStructurePlugin(): PluginOption {
156
209
  const data = await readJsonBody(req)
157
210
 
158
211
  const result = await withSuspendedWatcher(async () => {
212
+ const snapshot = await workspace.inspect()
213
+
159
214
  if (pathname === '/__tnotes_sidebar_delete_note') {
160
215
  const noteIndex = String(data.noteIndex || '')
161
216
  if (!noteIndex) throw new Error('Missing noteIndex')
162
217
 
163
- const previousNoteIndex =
164
- await tocService.getPreviousNoteIndexBeforeDelete(noteIndex)
218
+ const refs = collectRefs(snapshot)
219
+ const deletedRef = [...refs.byLine.values()].find((item) =>
220
+ item.subtreeIndexes.includes(noteIndex),
221
+ )
222
+ const previous = previousNoteIndex(snapshot, deletedRef)
165
223
 
166
- await tocService.deleteNoteFromToc(noteIndex)
167
- await noteService.deleteNote(noteIndex)
224
+ await workspace.toc.deleteEntry({
225
+ entry: { type: 'note', noteUuid: noteUuidForIndex(snapshot, noteIndex) },
226
+ expectedSnapshotRevision: snapshot.revision,
227
+ })
168
228
  await refreshTocAndSidebar()
169
229
  scheduleNoteSearchReindex('api:delete-note')
170
230
 
231
+ const redirectDir = previous
232
+ ? snapshot.notes.find((item) => item.index === previous)?.dirName
233
+ : undefined
171
234
  return {
172
235
  success: true,
173
236
  sidebarChanged: true,
174
- redirectUrl: resolveDeleteRedirectUrl(
175
- noteService,
176
- previousNoteIndex,
177
- ),
178
- redirectNoteIndex: previousNoteIndex,
237
+ redirectUrl: redirectDir ? buildNoteReadmeLink(redirectDir) : '/',
238
+ redirectNoteIndex: previous,
179
239
  deletedNoteIndexes: [noteIndex],
180
240
  message: '笔记已删除',
181
241
  }
@@ -188,30 +248,33 @@ export function sidebarStructurePlugin(): PluginOption {
188
248
  }
189
249
 
190
250
  const currentNoteIndex = String(data.currentNoteIndex || '')
191
- const previousNoteIndex =
192
- await tocService.getPreviousNoteIndexBeforeEntryDelete(
193
- tocLineIndex,
194
- )
195
- const inSubtree =
196
- currentNoteIndex &&
197
- (await tocService.isNoteInTocEntrySubtree(
198
- tocLineIndex,
199
- currentNoteIndex,
200
- ))
201
-
202
- const deletedNoteIndexes =
203
- await tocService.deleteTocEntryCascade(tocLineIndex)
251
+ const inSubtree = currentNoteIndex
252
+ ? isNoteInSubtree(snapshot, tocLineIndex, currentNoteIndex)
253
+ : false
254
+ const entry = collectRefs(snapshot).byLine.get(tocLineIndex)
255
+ const previous = previousNoteIndex(snapshot, entry)
256
+
257
+ const preview = await workspace.toc.previewDelete({
258
+ type: 'line',
259
+ tocLineIndex,
260
+ })
261
+ await workspace.toc.deleteEntry({
262
+ entry: { type: 'line', tocLineIndex },
263
+ expectedSnapshotRevision: snapshot.revision,
264
+ })
204
265
  await refreshTocAndSidebar()
205
266
  scheduleNoteSearchReindex('api:delete-entry')
206
267
 
268
+ const redirectDir =
269
+ inSubtree && previous
270
+ ? snapshot.notes.find((item) => item.index === previous)?.dirName
271
+ : undefined
207
272
  return {
208
273
  success: true,
209
274
  sidebarChanged: true,
210
- redirectUrl: inSubtree
211
- ? resolveDeleteRedirectUrl(noteService, previousNoteIndex)
212
- : undefined,
213
- redirectNoteIndex: inSubtree ? previousNoteIndex : null,
214
- deletedNoteIndexes,
275
+ redirectUrl: redirectDir ? buildNoteReadmeLink(redirectDir) : undefined,
276
+ redirectNoteIndex: inSubtree ? previous : null,
277
+ deletedNoteIndexes: preview.notes.map((note) => note.index),
215
278
  message: '已删除',
216
279
  }
217
280
  }
@@ -223,7 +286,11 @@ export function sidebarStructurePlugin(): PluginOption {
223
286
  throw new Error('Missing tocLineIndex')
224
287
  }
225
288
 
226
- await tocService.renameFolderInToc(tocLineIndex, newTitle)
289
+ await workspace.toc.renameGroup({
290
+ folderPath: folderPathAtLine(snapshot, tocLineIndex),
291
+ title: newTitle,
292
+ expectedSnapshotRevision: snapshot.revision,
293
+ })
227
294
  await refreshTocAndSidebar()
228
295
 
229
296
  return {
@@ -234,12 +301,29 @@ export function sidebarStructurePlugin(): PluginOption {
234
301
  }
235
302
 
236
303
  if (pathname === '/__tnotes_sidebar_reorder') {
304
+ const awaitMove = async (
305
+ source: TocEntryRef,
306
+ target: TocEntryRef,
307
+ placement: 'before' | 'after' | 'inside',
308
+ ): Promise<void> => {
309
+ await workspace.toc.move({
310
+ source,
311
+ target,
312
+ placement,
313
+ expectedSnapshotRevision: snapshot.revision,
314
+ })
315
+ }
316
+
237
317
  if (
238
- data.node_uuid &&
318
+ typeof data.node_uuid === 'string' &&
239
319
  data.action === 'prependChild' &&
240
320
  !data.target_uuid
241
321
  ) {
242
- await tocService.prependToRootByNodeId(String(data.node_uuid))
322
+ await awaitMove(
323
+ { type: 'note', noteUuid: data.node_uuid },
324
+ { type: 'line', tocLineIndex: 0 },
325
+ data.placement === 'after' ? 'after' : 'before',
326
+ )
243
327
  await refreshTocAndSidebar()
244
328
  return {
245
329
  success: true,
@@ -249,21 +333,15 @@ export function sidebarStructurePlugin(): PluginOption {
249
333
  }
250
334
 
251
335
  if (
252
- data.node_uuid &&
253
- data.target_uuid &&
336
+ typeof data.node_uuid === 'string' &&
337
+ typeof data.target_uuid === 'string' &&
254
338
  (data.action === 'moveAfter' || data.action === 'prependChild')
255
339
  ) {
256
- if (data.action === 'moveAfter') {
257
- await tocService.moveAfterByNodeId(
258
- String(data.node_uuid),
259
- String(data.target_uuid),
260
- )
261
- } else {
262
- await tocService.prependChildByNodeId(
263
- String(data.node_uuid),
264
- String(data.target_uuid),
265
- )
266
- }
340
+ await awaitMove(
341
+ { type: 'note', noteUuid: data.node_uuid },
342
+ { type: 'note', noteUuid: data.target_uuid },
343
+ data.action === 'prependChild' ? 'inside' : 'after',
344
+ )
267
345
  await refreshTocAndSidebar()
268
346
  return {
269
347
  success: true,
@@ -279,39 +357,44 @@ export function sidebarStructurePlugin(): PluginOption {
279
357
 
280
358
  const placement =
281
359
  data.targetType === 'group' || data.placement === 'inside'
282
- ? 'inside'
360
+ ? ('inside' as const)
283
361
  : data.placement === 'after'
284
- ? 'after'
285
- : 'before'
362
+ ? ('after' as const)
363
+ : ('before' as const)
364
+
365
+ const source: TocEntryRef = { type: 'line', tocLineIndex: dragTocLineIndex }
286
366
 
287
367
  if (
288
368
  data.targetTocLineIndex !== undefined &&
289
369
  data.targetTocLineIndex !== null
290
370
  ) {
291
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
292
- targetTocLineIndex: Number(data.targetTocLineIndex),
371
+ await awaitMove(
372
+ source,
373
+ { type: 'line', tocLineIndex: Number(data.targetTocLineIndex) },
293
374
  placement,
294
- })
375
+ )
295
376
  } else if (
296
377
  Array.isArray(data.targetFolderPath) &&
297
378
  data.targetFolderPath.length > 0
298
379
  ) {
299
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
300
- targetType: 'folder',
301
- targetFolderPath: data.targetFolderPath.map(String),
380
+ await awaitMove(
381
+ source,
382
+ {
383
+ type: 'folder',
384
+ folderPath: data.targetFolderPath.map(String) as string[],
385
+ },
302
386
  placement,
303
- })
387
+ )
304
388
  } else {
305
389
  const targetNoteIndex = String(
306
390
  data.targetNoteIndex || data.targetGroupNoteIndex || '',
307
391
  )
308
392
  if (!targetNoteIndex) throw new Error('Missing targetNoteIndex')
309
-
310
- await tocService.moveTocEntryByLineIndex(dragTocLineIndex, {
311
- targetType: 'note',
312
- targetNoteIndex,
393
+ await awaitMove(
394
+ source,
395
+ { type: 'note', noteUuid: noteUuidForIndex(snapshot, targetNoteIndex) },
313
396
  placement,
314
- })
397
+ )
315
398
  }
316
399
 
317
400
  await refreshTocAndSidebar()
@@ -332,10 +415,17 @@ export function sidebarStructurePlugin(): PluginOption {
332
415
  throw new Error('Missing parentTocLineIndex')
333
416
  }
334
417
 
335
- await tocService.insertFolderUnderParent(
336
- parentTocLineIndex,
418
+ const sibling = collectRefs(snapshot).byLine.get(parentTocLineIndex)
419
+ if (!sibling) throw new Error('未找到目录行')
420
+
421
+ await workspace.toc.createGroup({
337
422
  title,
338
- )
423
+ placement:
424
+ sibling.ref.type === 'note'
425
+ ? { type: 'note', targetNoteUuid: sibling.ref.noteUuid, placement: 'inside' }
426
+ : { type: 'root', placement: 'end' },
427
+ expectedSnapshotRevision: snapshot.revision,
428
+ })
339
429
  await refreshTocAndSidebar()
340
430
 
341
431
  return {
@@ -346,49 +436,56 @@ export function sidebarStructurePlugin(): PluginOption {
346
436
  }
347
437
 
348
438
  const count = normalizeCount(data.count)
349
- const notes = await createNotes(count)
350
-
351
- if (data.targetNoteIndex) {
352
- const placement = data.placement === 'after' ? 'after' : 'before'
353
- await tocService.insertNotesAroundNote(
354
- String(data.targetNoteIndex),
355
- notes,
356
- placement,
357
- )
358
- } else if (
359
- data.parentTocLineIndex !== undefined &&
360
- data.parentTocLineIndex !== null
361
- ) {
362
- await tocService.insertNotesUnderTocLine(
363
- Number(data.parentTocLineIndex),
364
- notes,
365
- )
366
- } else if (
367
- Array.isArray(data.parentFolderPath) &&
368
- data.parentFolderPath.length > 0
369
- ) {
370
- await tocService.insertNotesUnderFolder(
371
- data.parentFolderPath.map(String),
372
- notes,
373
- )
374
- } else if (data.parentNoteIndex) {
375
- await tocService.insertNotesUnderParent(
376
- String(data.parentNoteIndex),
377
- notes,
378
- )
379
- } else {
380
- for (const note of notes) {
381
- await tocService.appendNoteToToc(note.index)
439
+ const targetNoteIndex = String(
440
+ data.targetNoteIndex || data.targetGroupNoteIndex || '',
441
+ )
442
+ const parentLine =
443
+ data.parentTocLineIndex !== undefined && data.parentTocLineIndex !== null
444
+ ? Number(data.parentTocLineIndex)
445
+ : null
446
+
447
+ const created: Array<{ index: string; dirName: string }> = []
448
+ for (let i = 0; i < count; i++) {
449
+ let placement:
450
+ | { type: 'note'; targetNoteUuid: string; placement: 'before' | 'after' | 'inside' }
451
+ | { type: 'root'; placement?: 'start' | 'end' }
452
+ | undefined
453
+ if (targetNoteIndex) {
454
+ placement = {
455
+ type: 'note',
456
+ targetNoteUuid: noteUuidForIndex(snapshot, targetNoteIndex),
457
+ placement: data.placement === 'after' ? 'after' : 'before',
458
+ }
459
+ } else if (parentLine !== null) {
460
+ const sibling = collectRefs(snapshot).byLine.get(parentLine)
461
+ if (sibling && sibling.ref.type === 'note') {
462
+ placement = {
463
+ type: 'note',
464
+ targetNoteUuid: sibling.ref.noteUuid,
465
+ placement: 'inside',
466
+ }
467
+ }
382
468
  }
469
+ const result = await workspace.notes.create({
470
+ title: 'new',
471
+ placement,
472
+ expectedSnapshotRevision: snapshot.revision,
473
+ })
474
+ created.push({
475
+ index: result.value.index,
476
+ dirName: result.value.dirName,
477
+ })
383
478
  }
384
-
385
479
  await refreshTocAndSidebar()
386
480
  scheduleNoteSearchReindex('api:create-notes')
387
481
 
388
482
  return {
389
483
  success: true,
390
484
  sidebarChanged: true,
391
- createdNotes: getCreatedNotePayload(notes),
485
+ createdNotes: created.map((note) => ({
486
+ ...note,
487
+ link: buildNoteReadmeLink(note.dirName),
488
+ })),
392
489
  message: '笔记已创建',
393
490
  }
394
491
  })
@@ -13,23 +13,19 @@
13
13
  */
14
14
 
15
15
  import DefaultTheme from 'vitepress/theme'
16
-
16
+ import { BilibiliVideo, WordList, Mermaid, Mindmap, Footprints } from '@tnotesjs/ui'
17
17
  import { initTnotesSearchIndexHmr } from '../client/localSearchIndexBridge'
18
- import BilibiliOutsidePlayer from '../components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue'
19
18
  import Discussions from '../components/Discussions/Discussions.vue'
20
- import EnWordList from '../components/EnWordList/EnWordList.vue'
21
- import Footprints from '../components/Footprints/Footprints.vue'
22
19
  import { useRenameOverlay } from '../components/Layout/composables/useRenameOverlay'
23
20
  import { redirectAfterRename } from '../components/Layout/composables/useRenameRedirect'
24
21
  import Layout from '../components/Layout/Layout.vue'
25
- import Mermaid from '../components/Mermaid/Mermaid.vue'
26
- import MindmapPreview from '../components/MindmapPreview/MindmapPreview.vue'
27
22
  import NotesTable from '../components/NotesTable/NotesTable.vue'
28
23
  import SidebarCard from '../components/SidebarCard/SidebarCard.vue'
29
24
  import Tooltip from '../components/Tooltip/Tooltip.vue'
30
25
 
31
26
 
32
27
  import type { Theme, EnhanceAppContext } from 'vitepress'
28
+ import '@tnotesjs/ui/styles/tokens.css'
33
29
  import './styles/index.scss'
34
30
 
35
31
  /**
@@ -37,18 +33,18 @@ import './styles/index.scss'
37
33
  */
38
34
  function registerCoreComponents(ctx: EnhanceAppContext) {
39
35
  const { app } = ctx
40
- app.component('BilibiliOutsidePlayer', BilibiliOutsidePlayer)
41
- app.component('B', BilibiliOutsidePlayer)
36
+ app.component('BilibiliVideo', BilibiliVideo)
37
+ // Legacy full tag until knowledge-base migration; short aliases B/E/N/F are removed.
38
+ app.component('BilibiliOutsidePlayer', BilibiliVideo)
39
+ app.component('WordList', WordList)
40
+ app.component('EnWordList', WordList)
42
41
  app.component('Discussions', Discussions)
43
- app.component('EnWordList', EnWordList)
44
- app.component('E', EnWordList)
45
42
  app.component('Footprints', Footprints)
46
- app.component('F', Footprints)
47
43
  app.component('SidebarCard', SidebarCard)
48
- app.component('MindmapPreview', MindmapPreview)
44
+ app.component('Mindmap', Mindmap)
45
+ app.component('MindmapPreview', Mindmap)
49
46
  app.component('Mermaid', Mermaid)
50
47
  app.component('NotesTable', NotesTable)
51
- app.component('N', NotesTable)
52
48
  app.component('Tooltip', Tooltip)
53
49
  }
54
50
 
@@ -13,6 +13,21 @@
13
13
  /* 品牌色 */
14
14
  --vp-c-brand-1: #646cff;
15
15
  --vp-c-brand-2: #747bff;
16
+
17
+ /* Map VitePress tokens onto @tnotesjs/ui --tn-* */
18
+ --tn-c-brand: var(--vp-c-brand-1);
19
+ --tn-c-text: var(--vp-c-text-1);
20
+ --tn-c-text-2: var(--vp-c-text-2);
21
+ --tn-c-bg: var(--vp-c-bg);
22
+ --tn-c-bg-soft: var(--vp-c-bg-soft);
23
+ --tn-c-bg-elv: var(--vp-c-bg-elv);
24
+ --tn-c-divider: var(--vp-c-divider);
25
+ --tn-c-danger: var(--vp-c-danger-1);
26
+ --tn-c-success-soft: var(--vp-c-green-soft);
27
+ --tn-c-danger-soft: var(--vp-c-danger-soft);
28
+ --tn-c-default-soft: var(--vp-c-default-soft);
29
+ --tn-shadow-2: var(--vp-shadow-2);
30
+ --tn-font-mono: var(--vp-font-family-mono);
16
31
  }
17
32
 
18
33
  /* #endregion */