@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,604 +1,29 @@
1
- <!--
2
- vitepress/components/Mermaid/Mermaid.vue
3
-
4
- Mermaid 图表组件,用于在页面中渲染 Mermaid 图表
1
+ <!--
2
+ Deprecated local shell. Canonical implementation lives in @tnotesjs/ui as Mermaid.
5
3
  -->
6
-
7
4
  <template>
8
- <div
9
- ref="mermaidRef"
10
- class="mermaidWrapper"
11
- >
12
- <!-- 右上角按钮组(hover 时显示,与 code block 风格一致) -->
13
- <div
14
- v-if="!loading && !error"
15
- class="mermaidActions"
16
- :data-copy-state="copyState"
17
- >
18
- <button
19
- @click="toggleFullscreen"
20
- class="mermaidActionFullscreen"
21
- :title="isFullscreen ? '退出全屏' : '全屏查看图表'"
22
- >
23
- <img
24
- v-if="isFullscreen"
25
- :src="icon__fullscreen_exit"
26
- alt="退出全屏"
27
- />
28
- <img
29
- v-else
30
- :src="icon__fullscreen"
31
- alt="全屏"
32
- />
33
- </button>
34
- <button
35
- @click="copySource"
36
- class="mermaidActionCopy"
37
- :data-copy-state="copyState"
38
- :title="copyTitle"
39
- >
40
- <img :src="copyIcon" alt="复制" />
41
- </button>
42
- </div>
43
-
44
- <!-- 图表容器 -->
45
- <div
46
- class="mermaidContainer"
47
- :class="{ fullscreen: isFullscreen }"
48
- @mousedown="startDrag"
49
- @mousemove="onDrag"
50
- @mouseup="endDrag"
51
- @mouseleave="endDrag"
52
- >
53
- <div v-if="loading" class="mermaidLoading">
54
- <div class="spinner"></div>
55
- <p>加载图表中...</p>
56
- </div>
57
- <div v-else-if="error" class="mermaidError">
58
- <span class="errorIcon">⚠️</span>
59
- <p>{{ error }}</p>
60
- </div>
61
- <div
62
- v-show="!loading && !error"
63
- ref="diagramRef"
64
- :class="['mermaidDiagram', props.class]"
65
- :style="diagramTransform ? { transform: diagramTransform, transformOrigin: 'top left' } : undefined"
66
- @wheel="handleWheel"
67
- ></div>
68
- </div>
69
- </div>
5
+ <Mermaid :id="id" :graph="graph" :center="center" security-level="loose" />
70
6
  </template>
71
7
 
72
- <script setup lang="ts">
73
- import mermaid from 'mermaid'
74
- import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from 'vue'
8
+ <script setup>
9
+ import { Mermaid } from '@tnotesjs/ui'
75
10
 
76
- import {
77
- icon__check,
78
- icon__clipboard,
79
- icon__fullscreen,
80
- icon__fullscreen_exit,
81
- } from '../../assets/icons'
82
-
83
- const props = defineProps({
11
+ defineProps({
84
12
  graph: {
85
13
  type: String,
86
- required: true,
14
+ required: true
87
15
  },
88
16
  id: {
89
17
  type: String,
90
- required: true,
18
+ required: true
19
+ },
20
+ center: {
21
+ type: Boolean,
22
+ default: false
91
23
  },
92
24
  class: {
93
25
  type: String,
94
- default: 'mermaid',
95
- },
96
- })
97
-
98
- const mermaidRef = ref<HTMLElement | null>(null)
99
- const diagramRef = ref<HTMLElement | null>(null)
100
- const loading = ref(true)
101
- const error = ref<string | null>(null)
102
- const isFullscreen = ref(false)
103
-
104
- // ===================================
105
- // #region 复制源码
106
- // ===================================
107
- type CopyState = 'idle' | 'copied' | 'failed'
108
- const COPY_RESET_DELAY = 1000
109
-
110
- const copyState = ref<CopyState>('idle')
111
- const copyIcon = computed(() =>
112
- copyState.value === 'copied' ? icon__check : icon__clipboard,
113
- )
114
- const copyTitle = computed(() => {
115
- if (copyState.value === 'copied') return '已复制'
116
- if (copyState.value === 'failed') return '复制失败'
117
- return '复制源码'
118
- })
119
-
120
- let copyResetTimer: ReturnType<typeof setTimeout> | null = null
121
-
122
- function copySource() {
123
- const text = decodeURIComponent(props.graph)
124
- navigator.clipboard
125
- .writeText(text)
126
- .then(() => {
127
- copyState.value = 'copied'
128
- })
129
- .catch(() => {
130
- copyState.value = 'failed'
131
- })
132
- .finally(() => {
133
- if (copyResetTimer) clearTimeout(copyResetTimer)
134
- copyResetTimer = setTimeout(() => {
135
- copyState.value = 'idle'
136
- }, COPY_RESET_DELAY)
137
- })
138
- }
139
- // #endregion
140
-
141
- // ===================================
142
- // #region 全屏模式:拖拽平移 + Ctrl+滚轮缩放
143
- // ===================================
144
- const scale = ref(1)
145
- const panX = ref(0)
146
- const panY = ref(0)
147
- const dragging = ref(false)
148
- let dragStartX = 0
149
- let dragStartY = 0
150
- let panStartX = 0
151
- let panStartY = 0
152
-
153
- const MIN_SCALE = 0.1
154
- const MAX_SCALE = 5
155
- const SCALE_STEP = 0.01
156
-
157
- const diagramTransform = computed(() =>
158
- isFullscreen.value
159
- ? `translate(${panX.value}px, ${panY.value}px) scale(${scale.value})`
160
- : undefined,
161
- )
162
-
163
- function startDrag(e: MouseEvent) {
164
- if (!isFullscreen.value) return
165
- dragging.value = true
166
- dragStartX = e.clientX
167
- dragStartY = e.clientY
168
- panStartX = panX.value
169
- panStartY = panY.value
170
- }
171
-
172
- function onDrag(e: MouseEvent) {
173
- if (!dragging.value) return
174
- panX.value = panStartX + (e.clientX - dragStartX)
175
- panY.value = panStartY + (e.clientY - dragStartY)
176
- }
177
-
178
- function endDrag() {
179
- dragging.value = false
180
- }
181
-
182
- function handleWheel(e: WheelEvent) {
183
- if (!isFullscreen.value) return
184
- e.preventDefault()
185
- const delta = e.deltaY > 0 ? -SCALE_STEP : SCALE_STEP
186
- scale.value = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale.value + delta))
187
- }
188
- // #endregion
189
-
190
- // ===================================
191
- // #region 全屏控制
192
- // ===================================
193
- function toggleFullscreen() {
194
- if (!mermaidRef.value) return
195
-
196
- if (!isFullscreen.value) {
197
- if (mermaidRef.value.requestFullscreen) {
198
- mermaidRef.value.requestFullscreen()
199
- }
200
- } else {
201
- if (document.exitFullscreen) {
202
- document.exitFullscreen()
203
- }
26
+ default: 'mermaid'
204
27
  }
205
- }
206
-
207
- function handleFullscreenChange() {
208
- isFullscreen.value = !!document.fullscreenElement
209
- if (!isFullscreen.value) {
210
- scale.value = 1
211
- panX.value = 0
212
- panY.value = 0
213
- }
214
- }
215
- // #endregion
216
-
217
- // ===================================
218
- // #region Mermaid 渲染
219
- // ===================================
220
- // 获取当前主题
221
- const getCurrentTheme = () => {
222
- return document.documentElement.classList.contains('dark')
223
- ? 'dark'
224
- : 'default'
225
- }
226
-
227
- // 初始化 Mermaid
228
- const initMermaid = async () => {
229
- try {
230
- mermaid.initialize({
231
- startOnLoad: false,
232
- theme: getCurrentTheme(),
233
- securityLevel: 'loose',
234
- fontFamily: 'inherit',
235
- })
236
- } catch (err) {
237
- console.error('Mermaid initialization error:', err)
238
- }
239
- }
240
-
241
- // 渲染图表
242
- const renderDiagram = async () => {
243
- // 等待下一个 DOM 更新周期
244
- await nextTick()
245
-
246
- if (!diagramRef.value) {
247
- // 再等待一小段时间确保 DOM 完全渲染
248
- await new Promise((resolve) => setTimeout(resolve, 0))
249
- if (!diagramRef.value) {
250
- // console.warn('diagramRef is still null')
251
- return
252
- }
253
- }
254
-
255
- try {
256
- loading.value = true
257
- error.value = null
258
-
259
- // 每次渲染前都重新初始化主题
260
- mermaid.initialize({
261
- startOnLoad: false,
262
- theme: getCurrentTheme(),
263
- securityLevel: 'loose',
264
- fontFamily: 'inherit',
265
- })
266
-
267
- const { svg, bindFunctions } = await mermaid.render(
268
- props.id,
269
- decodeURIComponent(props.graph)
270
- )
271
-
272
- diagramRef.value.innerHTML = svg
273
-
274
- // 绑定交互函数(如果有)
275
- if (bindFunctions) {
276
- bindFunctions(diagramRef.value)
277
- }
278
- } catch (err) {
279
- const message = err instanceof Error ? err.message : String(err)
280
- error.value = `Failed to render diagram: ${message}`
281
- console.error('Mermaid render error:', err)
282
- } finally {
283
- loading.value = false
284
- }
285
- }
286
-
287
- // 监听主题变化
288
- const observeTheme = () => {
289
- const observer = new MutationObserver((mutations) => {
290
- mutations.forEach((mutation) => {
291
- if (
292
- mutation.type === 'attributes' &&
293
- mutation.attributeName === 'class'
294
- ) {
295
- // 检查是否是主题变化
296
- const isDark = document.documentElement.classList.contains('dark')
297
- const wasDark = mutation.oldValue?.includes('dark')
298
-
299
- // 只有当主题实际发生变化时才重新渲染
300
- if ((isDark && !wasDark) || (!isDark && wasDark)) {
301
- renderDiagram()
302
- }
303
- }
304
- })
305
- })
306
-
307
- observer.observe(document.documentElement, {
308
- attributes: true,
309
- attributeFilter: ['class'],
310
- attributeOldValue: true,
311
- })
312
-
313
- return observer
314
- }
315
-
316
- onMounted(async () => {
317
- await initMermaid()
318
- await renderDiagram()
319
-
320
- // 观察主题变化
321
- const themeObserver = observeTheme()
322
-
323
- // 监听全屏变化
324
- document.addEventListener('fullscreenchange', handleFullscreenChange)
325
-
326
- // 清理函数
327
- onBeforeUnmount(() => {
328
- themeObserver.disconnect()
329
- document.removeEventListener('fullscreenchange', handleFullscreenChange)
330
- })
331
28
  })
332
-
333
- // 当图表内容变化时重新渲染
334
- watch(
335
- () => props.graph,
336
- () => {
337
- renderDiagram()
338
- }
339
- )
340
- // #endregion
341
29
  </script>
342
-
343
- <style scoped lang="scss">
344
- /* ===================================== */
345
- /* #region 容器布局 */
346
- /* ===================================== */
347
- .mermaidWrapper {
348
- position: relative;
349
- margin: 1rem 0;
350
- border: 1px solid var(--vp-c-divider);
351
- border-radius: 8px;
352
- background: var(--vp-c-bg-soft);
353
- overflow: hidden;
354
- }
355
- /* ===================================== */
356
- /* #endregion 容器布局 */
357
- /* ===================================== */
358
-
359
- /* ===================================== */
360
- /* #region 右上角按钮组(与 code block 的 .tn-code-actions 风格一致) */
361
- /* ===================================== */
362
- .mermaidActions {
363
- position: absolute;
364
- top: 8px;
365
- right: 8px;
366
- z-index: 10;
367
- display: flex;
368
- align-items: center;
369
- justify-content: center;
370
- gap: 4px;
371
- padding: 2px;
372
- background-color: color-mix(in srgb, var(--vp-code-block-bg) 92%, transparent);
373
- border: 0.1px solid var(--vp-c-divider);
374
- border-radius: 7px;
375
- opacity: 0;
376
- transition: opacity 0.2s;
377
- }
378
-
379
- .mermaidWrapper:hover .mermaidActions,
380
- .mermaidActions:hover,
381
- .mermaidActions:focus-within,
382
- .mermaidActions[data-copy-state='copied'],
383
- .mermaidActions[data-copy-state='failed'] {
384
- opacity: 1;
385
- }
386
-
387
- .mermaidActionCopy,
388
- .mermaidActionFullscreen {
389
- display: inline-flex;
390
- align-items: center;
391
- justify-content: center;
392
- width: 28px;
393
- height: 28px;
394
- padding: 0;
395
- background: transparent;
396
- border: none;
397
- border-radius: 4px;
398
- cursor: pointer;
399
-
400
- &:hover {
401
- background-color: var(--vp-c-default-soft);
402
- color: var(--vp-c-brand-1);
403
- transform: scale(1.05);
404
- }
405
-
406
- &:active {
407
- transform: scale(0.95);
408
- }
409
-
410
- img {
411
- width: 16px;
412
- height: 16px;
413
- }
414
- }
415
-
416
- /* 与 .tn-code-action-copy 完全一致的复制反馈 */
417
- .mermaidActionCopy {
418
- position: relative;
419
- }
420
-
421
- .mermaidActionCopy[data-copy-state='copied'] {
422
- background-color: var(--vp-c-green-soft);
423
- }
424
-
425
- .mermaidActionCopy[data-copy-state='failed'] {
426
- background-color: var(--vp-c-danger-soft);
427
- }
428
-
429
- .mermaidActionCopy::after {
430
- position: absolute;
431
- top: 50%;
432
- right: calc(100% + 6px);
433
- padding: 4px 7px;
434
- color: var(--vp-c-text-1);
435
- background: var(--vp-c-bg-elv);
436
- border: 1px solid var(--vp-c-divider);
437
- border-radius: 6px;
438
- box-shadow: var(--vp-shadow-2);
439
- content: attr(title);
440
- font-size: 12px;
441
- line-height: 18px;
442
- opacity: 0;
443
- pointer-events: none;
444
- transform: translateY(-50%) translateX(2px);
445
- transition:
446
- opacity 0.16s ease,
447
- transform 0.16s ease;
448
- white-space: nowrap;
449
- }
450
-
451
- .mermaidActionCopy[data-copy-state='copied']::after,
452
- .mermaidActionCopy[data-copy-state='failed']::after {
453
- opacity: 1;
454
- transform: translateY(-50%) translateX(0);
455
- }
456
- /* ===================================== */
457
- /* #endregion 右上角按钮组 */
458
- /* ===================================== */
459
-
460
- /* ===================================== */
461
- /* #region 图表容器 */
462
- /* ===================================== */
463
- .mermaidContainer {
464
- position: relative;
465
- min-height: 200px;
466
- padding: 20px;
467
- background: var(--vp-c-bg);
468
-
469
- &.fullscreen {
470
- position: fixed;
471
- top: 0;
472
- left: 0;
473
- right: 0;
474
- bottom: 0;
475
- z-index: 9999;
476
- background: var(--vp-c-bg);
477
- padding: 40px;
478
- overflow: hidden;
479
- cursor: grab;
480
-
481
- &:active {
482
- cursor: grabbing;
483
- }
484
- }
485
- }
486
- /* ===================================== */
487
- /* #endregion 图表容器 */
488
- /* ===================================== */
489
-
490
- /* ===================================== */
491
- /* #region 加载和错误状态 */
492
- /* ===================================== */
493
- .mermaidLoading {
494
- display: flex;
495
- flex-direction: column;
496
- align-items: center;
497
- gap: 12px;
498
- padding: 2rem;
499
- color: var(--vp-c-text-2);
500
-
501
- p {
502
- margin: 0;
503
- font-size: 14px;
504
- }
505
- }
506
-
507
- .spinner {
508
- width: 40px;
509
- height: 40px;
510
- border: 3px solid var(--vp-c-divider);
511
- border-top-color: var(--vp-c-brand-1);
512
- border-radius: 50%;
513
- animation: spin 0.8s linear infinite;
514
- }
515
-
516
- @keyframes spin {
517
- to {
518
- transform: rotate(360deg);
519
- }
520
- }
521
-
522
- .mermaidError {
523
- display: flex;
524
- flex-direction: column;
525
- align-items: center;
526
- gap: 8px;
527
- padding: 2rem;
528
- color: var(--vp-c-danger-1);
529
-
530
- p {
531
- margin: 0;
532
- font-size: 14px;
533
- text-align: center;
534
- }
535
- }
536
-
537
- .errorIcon {
538
- font-size: 32px;
539
- }
540
- /* ===================================== */
541
- /* #endregion 加载和错误状态 */
542
- /* ===================================== */
543
-
544
- /* ===================================== */
545
- /* #region 图表样式 */
546
- /* ===================================== */
547
- .mermaidDiagram {
548
- :global(svg) {
549
- max-width: 100%;
550
- height: auto;
551
- }
552
-
553
- // 重置 VitePress 全局 line-height,避免穿透 SVG foreignObject 导致
554
- // <p> 实际渲染高度超过 Mermaid 计算值,引发文本截断
555
- :global(svg .label p) {
556
- line-height: 1.5;
557
- }
558
- }
559
- /* ===================================== */
560
- /* #endregion 图表样式 */
561
- /* ===================================== */
562
-
563
- /* ===================================== */
564
- /* #region 暗色主题优化 */
565
- /* ===================================== */
566
- :global(html.dark) .mermaidWrapper {
567
- background: var(--vp-code-block-bg);
568
- border-color: var(--vp-c-divider);
569
- }
570
-
571
- :global(html.dark) .mermaidContainer {
572
- background: var(--vp-code-block-bg);
573
- }
574
- /* ===================================== */
575
- /* #endregion 暗色主题优化 */
576
- /* ===================================== */
577
-
578
- /* ===================================== */
579
- /* #region 响应式设计 */
580
- /* ===================================== */
581
- @media (max-width: 768px) {
582
- .mermaidActionCopy,
583
- .mermaidActionFullscreen {
584
- width: 24px;
585
- height: 24px;
586
-
587
- img {
588
- width: 14px;
589
- height: 14px;
590
- }
591
- }
592
-
593
- .mermaidContainer {
594
- padding: 12px;
595
-
596
- &.fullscreen {
597
- padding: 20px;
598
- }
599
- }
600
- }
601
- /* ===================================== */
602
- /* #endregion 响应式设计 */
603
- /* ===================================== */
604
- </style>