@tnotesjs/core 0.1.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.

Potentially problematic release.


This version of @tnotesjs/core might be problematic. Click here for more details.

Files changed (117) hide show
  1. package/README.md +105 -0
  2. package/dist/chunk-K3X5OP3N.js +1532 -0
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +4199 -0
  5. package/dist/index.d.ts +138 -0
  6. package/dist/index.js +9 -0
  7. package/package.json +74 -0
  8. package/types/config.ts +61 -0
  9. package/types/index.ts +11 -0
  10. package/types/note.ts +33 -0
  11. package/vitepress/assets/icons/icon__check.svg +3 -0
  12. package/vitepress/assets/icons/icon__clipboard.svg +8 -0
  13. package/vitepress/assets/icons/icon__close.svg +1 -0
  14. package/vitepress/assets/icons/icon__collapse.svg +1 -0
  15. package/vitepress/assets/icons/icon__confirm.svg +1 -0
  16. package/vitepress/assets/icons/icon__copy.svg +4 -0
  17. package/vitepress/assets/icons/icon__focus.svg +1 -0
  18. package/vitepress/assets/icons/icon__fold.svg +3 -0
  19. package/vitepress/assets/icons/icon__folder.svg +1 -0
  20. package/vitepress/assets/icons/icon__fullscreen.svg +1 -0
  21. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -0
  22. package/vitepress/assets/icons/icon__github.svg +4 -0
  23. package/vitepress/assets/icons/icon__mindmap.svg +1 -0
  24. package/vitepress/assets/icons/icon__next.svg +1 -0
  25. package/vitepress/assets/icons/icon__number_gray.svg +1 -0
  26. package/vitepress/assets/icons/icon__number_purple.svg +1 -0
  27. package/vitepress/assets/icons/icon__prev.svg +1 -0
  28. package/vitepress/assets/icons/icon__restore.svg +1 -0
  29. package/vitepress/assets/icons/icon__rotate.svg +4 -0
  30. package/vitepress/assets/icons/icon__search.svg +1 -0
  31. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -0
  32. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -0
  33. package/vitepress/assets/icons/icon__totop.svg +6 -0
  34. package/vitepress/assets/icons/icon__vscode.svg +6 -0
  35. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -0
  36. package/vitepress/assets/icons/icon__zoom_in.svg +1 -0
  37. package/vitepress/assets/icons/icon__zoom_out.svg +1 -0
  38. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -0
  39. package/vitepress/assets/icons/index.ts +38 -0
  40. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -0
  41. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +373 -0
  42. package/vitepress/components/CodeBlockFullscreen/index.ts +115 -0
  43. package/vitepress/components/CodeBlockFullscreen/styles.css +64 -0
  44. package/vitepress/components/Discussions/Discussions.module.scss +32 -0
  45. package/vitepress/components/Discussions/Discussions.vue +211 -0
  46. package/vitepress/components/EnWordList/EnWordList.module.scss +124 -0
  47. package/vitepress/components/EnWordList/EnWordList.vue +543 -0
  48. package/vitepress/components/EnWordList/RightClickMenu.module.scss +22 -0
  49. package/vitepress/components/EnWordList/RightClickMenu.vue +66 -0
  50. package/vitepress/components/Footprints/Footprints.module.scss +93 -0
  51. package/vitepress/components/Footprints/Footprints.vue +377 -0
  52. package/vitepress/components/Layout/AboutModal.module.scss +233 -0
  53. package/vitepress/components/Layout/AboutModal.vue +105 -0
  54. package/vitepress/components/Layout/AboutPanel.vue +266 -0
  55. package/vitepress/components/Layout/ContentCollapse.vue +603 -0
  56. package/vitepress/components/Layout/CustomSidebar.vue +605 -0
  57. package/vitepress/components/Layout/DocBeforeControls.vue +139 -0
  58. package/vitepress/components/Layout/DocFooter.vue +225 -0
  59. package/vitepress/components/Layout/ImagePreview.module.scss +201 -0
  60. package/vitepress/components/Layout/ImagePreview.vue +281 -0
  61. package/vitepress/components/Layout/Layout.module.scss +661 -0
  62. package/vitepress/components/Layout/Layout.vue +542 -0
  63. package/vitepress/components/Layout/NoteStatus.vue +140 -0
  64. package/vitepress/components/Layout/SidebarItems.vue +263 -0
  65. package/vitepress/components/Layout/SidebarNavBefore.vue +92 -0
  66. package/vitepress/components/Layout/Swiper.vue +167 -0
  67. package/vitepress/components/Layout/ToggleFullContent.module.scss +11 -0
  68. package/vitepress/components/Layout/ToggleFullContent.vue +34 -0
  69. package/vitepress/components/Layout/ToggleSidebar.module.scss +11 -0
  70. package/vitepress/components/Layout/ToggleSidebar.vue +35 -0
  71. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -0
  72. package/vitepress/components/Layout/composables/useNoteConfig.ts +121 -0
  73. package/vitepress/components/Layout/composables/useNoteSave.ts +173 -0
  74. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -0
  75. package/vitepress/components/Layout/composables/useRedirect.ts +110 -0
  76. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -0
  77. package/vitepress/components/Layout/homeReadme.data.ts +124 -0
  78. package/vitepress/components/LoadingPage/LoadingPage.vue +192 -0
  79. package/vitepress/components/MarkMap/MarkMap.module.scss +159 -0
  80. package/vitepress/components/MarkMap/MarkMap.vue +404 -0
  81. package/vitepress/components/Mermaid/Mermaid.module.scss +275 -0
  82. package/vitepress/components/Mermaid/Mermaid.vue +364 -0
  83. package/vitepress/components/NotesTable/NotesTable.module.scss +77 -0
  84. package/vitepress/components/NotesTable/NotesTable.vue +98 -0
  85. package/vitepress/components/NotesTable/README.md +67 -0
  86. package/vitepress/components/Settings/Settings.module.scss +433 -0
  87. package/vitepress/components/Settings/Settings.vue +306 -0
  88. package/vitepress/components/SidebarCard/MindMapView.vue +483 -0
  89. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -0
  90. package/vitepress/components/SidebarCard/SidebarCard.vue +948 -0
  91. package/vitepress/components/Tooltip/Tooltip.vue +70 -0
  92. package/vitepress/components/constants.ts +91 -0
  93. package/vitepress/components/notesConfig.data.ts +73 -0
  94. package/vitepress/components/sidebar.data.ts +59 -0
  95. package/vitepress/components/tnotes-config.data.ts +21 -0
  96. package/vitepress/components/utils.ts +26 -0
  97. package/vitepress/config/index.ts +126 -0
  98. package/vitepress/configs/constants.ts +26 -0
  99. package/vitepress/configs/head.config.ts +25 -0
  100. package/vitepress/configs/index.ts +9 -0
  101. package/vitepress/configs/markdown-it.d.ts +23 -0
  102. package/vitepress/configs/markdown.config.ts +366 -0
  103. package/vitepress/configs/theme.config.ts +108 -0
  104. package/vitepress/plugins/buildProgressPlugin.ts +390 -0
  105. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -0
  106. package/vitepress/plugins/renameNotePlugin.ts +60 -0
  107. package/vitepress/plugins/updateConfigPlugin.ts +63 -0
  108. package/vitepress/theme/index.ts +95 -0
  109. package/vitepress/theme/styles/base.scss +50 -0
  110. package/vitepress/theme/styles/components/404.scss +31 -0
  111. package/vitepress/theme/styles/components/collapse.scss +175 -0
  112. package/vitepress/theme/styles/components/markmap.scss +101 -0
  113. package/vitepress/theme/styles/components/swiper.scss +255 -0
  114. package/vitepress/theme/styles/index.scss +25 -0
  115. package/vitepress/theme/styles/layout.scss +62 -0
  116. package/vitepress/theme/styles/utilities.scss +39 -0
  117. package/vitepress/theme/styles/vitepress-override.scss +25 -0
@@ -0,0 +1,390 @@
1
+ /**
2
+ * .vitepress/tnotes/vitepress/plugins/buildProgressPlugin.ts
3
+ *
4
+ * 构建进度插件 - 仅在 build 模式下显示真实进度
5
+ *
6
+ * 基于 vite-plugin-progress 源码简化实现
7
+ * https://github.com/jeddygong/vite-plugin-progress
8
+ */
9
+
10
+ import type { Plugin } from 'vite'
11
+ import {
12
+ existsSync,
13
+ readFileSync,
14
+ writeFileSync,
15
+ mkdirSync,
16
+ readdirSync,
17
+ } from 'fs'
18
+ import { join } from 'path'
19
+
20
+ /** 缓存目录和文件路径 */
21
+ const CACHE_DIR = join(process.cwd(), 'node_modules', '.tnotes-progress')
22
+ const CACHE_FILE = join(CACHE_DIR, 'build-cache.json')
23
+
24
+ /** 缓存数据结构 */
25
+ interface CacheData {
26
+ transformCount: number
27
+ chunkCount: number
28
+ }
29
+
30
+ /** 插件配置选项 */
31
+ interface BuildProgressOptions {
32
+ width?: number
33
+ complete?: string
34
+ incomplete?: string
35
+ }
36
+
37
+ /**
38
+ * 读取缓存数据
39
+ */
40
+ function getCacheData(): CacheData {
41
+ try {
42
+ if (existsSync(CACHE_FILE)) {
43
+ return JSON.parse(readFileSync(CACHE_FILE, 'utf-8'))
44
+ }
45
+ } catch {
46
+ // 忽略错误
47
+ }
48
+ return { transformCount: 0, chunkCount: 0 }
49
+ }
50
+
51
+ /**
52
+ * 写入缓存数据
53
+ */
54
+ function setCacheData(data: CacheData): void {
55
+ try {
56
+ if (!existsSync(CACHE_DIR)) {
57
+ mkdirSync(CACHE_DIR, { recursive: true })
58
+ }
59
+ writeFileSync(CACHE_FILE, JSON.stringify(data), 'utf-8')
60
+ } catch {
61
+ // 忽略错误
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 扫描源目录文件数量
67
+ */
68
+ function countSourceFiles(srcDir: string): number {
69
+ let count = 0
70
+ const extensions = /\.(vue|ts|js|jsx|tsx|css|scss|sass|styl|less|md)$/i
71
+
72
+ const scan = (dir: string) => {
73
+ try {
74
+ const entries = readdirSync(dir, { withFileTypes: true })
75
+ for (const entry of entries) {
76
+ const fullPath = join(dir, entry.name)
77
+ if (
78
+ entry.isDirectory() &&
79
+ !entry.name.startsWith('.') &&
80
+ entry.name !== 'node_modules'
81
+ ) {
82
+ scan(fullPath)
83
+ } else if (entry.isFile() && extensions.test(entry.name)) {
84
+ count++
85
+ }
86
+ }
87
+ } catch {
88
+ // 忽略错误
89
+ }
90
+ }
91
+
92
+ scan(srcDir)
93
+ return count
94
+ }
95
+
96
+ // ============ 全局状态 ============
97
+ let globalStartTime = 0
98
+ let globalTransformCount = 0
99
+ let globalChunkCount = 0
100
+ let globalHasError = false
101
+ let globalIsBuilding = false
102
+ let globalOutDir = ''
103
+ let globalLastPercent = 0
104
+ let globalFileCount = 0
105
+ let globalLastLoggedPercent = -1 // 非 TTY 环境下上次输出的百分比区间,初始为 -1 以便第一次输出
106
+
107
+ /** 检测是否支持单行刷新(交互式终端) */
108
+ const isTTY = !!(process.stdout.isTTY && process.stderr.isTTY)
109
+
110
+ /** 检测是否在 CI 环境中运行 */
111
+ const isCI = !!(
112
+ process.env.CI ||
113
+ process.env.GITHUB_ACTIONS ||
114
+ process.env.GITLAB_CI ||
115
+ process.env.CIRCLECI ||
116
+ process.env.TRAVIS ||
117
+ process.env.JENKINS_URL
118
+ )
119
+
120
+ /** 是否使用单行刷新模式(TTY 且非 CI) */
121
+ const useSingleLineMode = isTTY && !isCI
122
+
123
+ // 保存原始输出函数
124
+ let originalStdoutWrite: typeof process.stdout.write | null = null
125
+ let originalStderrWrite: typeof process.stderr.write | null = null
126
+
127
+ /**
128
+ * 拦截输出
129
+ */
130
+ function interceptOutput() {
131
+ if (originalStdoutWrite) return
132
+
133
+ originalStdoutWrite = process.stdout.write.bind(process.stdout)
134
+ originalStderrWrite = process.stderr.write.bind(process.stderr)
135
+
136
+ const filter = (chunk: string | Uint8Array): boolean => {
137
+ const str = chunk.toString()
138
+ // 只允许我们的输出
139
+ return (
140
+ str.includes('🔨') ||
141
+ str.includes('✅ 构建成功') ||
142
+ str.includes('❌ 构建失败') ||
143
+ str.includes('📁') ||
144
+ str.includes('📊') ||
145
+ str.includes('📦') ||
146
+ str.includes('⏱️') ||
147
+ str.includes('\x1b[2K') // 允许清屏指令
148
+ )
149
+ }
150
+
151
+ process.stdout.write = ((
152
+ chunk: string | Uint8Array,
153
+ encodingOrCallback?: BufferEncoding | ((err?: Error) => void),
154
+ callback?: (err?: Error) => void,
155
+ ): boolean => {
156
+ if (filter(chunk)) {
157
+ return originalStdoutWrite!(chunk, encodingOrCallback as any, callback)
158
+ }
159
+ if (typeof encodingOrCallback === 'function') encodingOrCallback()
160
+ else if (callback) callback()
161
+ return true
162
+ }) as typeof process.stdout.write
163
+
164
+ process.stderr.write = ((
165
+ chunk: string | Uint8Array,
166
+ encodingOrCallback?: BufferEncoding | ((err?: Error) => void),
167
+ callback?: (err?: Error) => void,
168
+ ): boolean => {
169
+ const str = chunk.toString()
170
+ // 允许进度条和真正的错误
171
+ if (filter(chunk) || str.toLowerCase().includes('error')) {
172
+ return originalStderrWrite!(chunk, encodingOrCallback as any, callback)
173
+ }
174
+ if (typeof encodingOrCallback === 'function') encodingOrCallback()
175
+ else if (callback) callback()
176
+ return true
177
+ }) as typeof process.stderr.write
178
+ }
179
+
180
+ /**
181
+ * 恢复输出
182
+ */
183
+ function restoreOutput() {
184
+ if (originalStdoutWrite) {
185
+ process.stdout.write = originalStdoutWrite
186
+ originalStdoutWrite = null
187
+ }
188
+ if (originalStderrWrite) {
189
+ process.stderr.write = originalStderrWrite
190
+ originalStderrWrite = null
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 渲染进度条
196
+ */
197
+ function renderProgress(
198
+ percent: number,
199
+ transforms: string,
200
+ chunks: string,
201
+ width: number,
202
+ complete: string,
203
+ incomplete: string,
204
+ isFinal: boolean = false,
205
+ ) {
206
+ if (!originalStderrWrite) return
207
+
208
+ // 非单行模式下,只在 10% 间隔输出进度,避免日志过多
209
+ if (!useSingleLineMode && !isFinal) {
210
+ const currentPercent = Math.floor(percent * 100)
211
+ const currentBucket = Math.floor(currentPercent / 10) * 10
212
+ if (currentBucket <= globalLastLoggedPercent) {
213
+ return
214
+ }
215
+ globalLastLoggedPercent = currentBucket
216
+ }
217
+
218
+ const filled = Math.floor(percent * width)
219
+ const empty = width - filled
220
+ const bar = complete.repeat(filled) + incomplete.repeat(empty)
221
+ const percentStr = (percent * 100).toFixed(0).padStart(3, ' ')
222
+ const elapsed = ((Date.now() - globalStartTime) / 1000).toFixed(1)
223
+
224
+ // 格式: Building [...] 100% | Transforms: x/y | Chunks: x/y | Time: xs
225
+ // 单行模式使用回车覆盖,否则直接换行
226
+ const prefix = useSingleLineMode ? '\r\x1b[2K' : ''
227
+ const ending = isFinal || !useSingleLineMode ? '\n' : ''
228
+ const line = `${prefix}Building [${bar}] ${percentStr}% | Transforms: ${transforms} | Chunks: ${chunks} | Time: ${elapsed}s${ending}`
229
+ originalStderrWrite(line)
230
+ }
231
+
232
+ /**
233
+ * 创建构建进度插件
234
+ */
235
+ export function buildProgressPlugin(
236
+ options: BuildProgressOptions = {},
237
+ ): Plugin {
238
+ const { width = 40, complete = '█', incomplete = '░' } = options
239
+
240
+ const cache = getCacheData()
241
+ const hasCache = cache.transformCount > 0
242
+
243
+ return {
244
+ name: 'tnotes-build-progress',
245
+ enforce: 'pre',
246
+ apply: 'build',
247
+
248
+ config(config, { command }) {
249
+ if (command === 'build') {
250
+ config.logLevel = 'silent'
251
+
252
+ if (!globalIsBuilding) {
253
+ globalIsBuilding = true
254
+ globalStartTime = Date.now()
255
+ globalTransformCount = 0
256
+ globalChunkCount = 0
257
+ globalHasError = false
258
+ globalLastPercent = 0
259
+ globalLastLoggedPercent = -1
260
+ globalOutDir = config.build?.outDir || 'dist'
261
+
262
+ if (!hasCache) {
263
+ globalFileCount = countSourceFiles(process.cwd())
264
+ }
265
+
266
+ interceptOutput()
267
+ }
268
+ }
269
+ },
270
+
271
+ transform(_code, id) {
272
+ globalTransformCount++
273
+
274
+ if (hasCache) {
275
+ const total = cache.transformCount * 2 + cache.chunkCount * 2
276
+ globalLastPercent = Math.min(0.9, globalTransformCount / total)
277
+ } else {
278
+ if (!id.includes('node_modules') && globalLastPercent < 0.7) {
279
+ globalLastPercent = Math.min(
280
+ 0.7,
281
+ globalTransformCount / (globalFileCount * 4),
282
+ )
283
+ }
284
+ }
285
+
286
+ const transformsStr = hasCache
287
+ ? `${globalTransformCount}/${cache.transformCount * 2}`
288
+ : `${globalTransformCount}`
289
+ const chunksStr = hasCache
290
+ ? `${globalChunkCount}/${cache.chunkCount * 2}`
291
+ : `${globalChunkCount}`
292
+
293
+ renderProgress(
294
+ globalLastPercent,
295
+ transformsStr,
296
+ chunksStr,
297
+ width,
298
+ complete,
299
+ incomplete,
300
+ )
301
+
302
+ return null
303
+ },
304
+
305
+ renderChunk() {
306
+ globalChunkCount++
307
+
308
+ if (hasCache) {
309
+ const total = cache.transformCount * 2 + cache.chunkCount * 2
310
+ globalLastPercent = Math.min(
311
+ 0.98,
312
+ (globalTransformCount + globalChunkCount) / total,
313
+ )
314
+ } else {
315
+ if (globalLastPercent < 0.98) {
316
+ globalLastPercent = Math.min(0.98, globalLastPercent + 0.003)
317
+ }
318
+ }
319
+
320
+ const transformsStr = hasCache
321
+ ? `${globalTransformCount}/${cache.transformCount * 2}`
322
+ : `${globalTransformCount}`
323
+ const chunksStr = hasCache
324
+ ? `${globalChunkCount}/${cache.chunkCount * 2}`
325
+ : `${globalChunkCount}`
326
+
327
+ renderProgress(
328
+ globalLastPercent,
329
+ transformsStr,
330
+ chunksStr,
331
+ width,
332
+ complete,
333
+ incomplete,
334
+ )
335
+
336
+ return null
337
+ },
338
+
339
+ buildEnd(err) {
340
+ if (err) {
341
+ globalHasError = true
342
+ }
343
+ },
344
+
345
+ closeBundle() {
346
+ // 延迟检测是否是最后一次 closeBundle
347
+ setTimeout(() => {
348
+ if (!globalIsBuilding) return
349
+
350
+ const elapsed = ((Date.now() - globalStartTime) / 1000).toFixed(1)
351
+
352
+ // 显示 100%,带换行
353
+ // 100% 时应该显示总数/总数,而不是实际计数/总数
354
+ const totalTransforms = hasCache
355
+ ? cache.transformCount * 2
356
+ : globalTransformCount
357
+ const totalChunks = hasCache ? cache.chunkCount * 2 : globalChunkCount
358
+ const transformsStr = `${totalTransforms}/${totalTransforms}`
359
+ const chunksStr = `${totalChunks}/${totalChunks}`
360
+
361
+ renderProgress(
362
+ 1,
363
+ transformsStr,
364
+ chunksStr,
365
+ width,
366
+ complete,
367
+ incomplete,
368
+ true,
369
+ )
370
+
371
+ restoreOutput()
372
+
373
+ if (!globalHasError) {
374
+ setCacheData({
375
+ transformCount: Math.floor(globalTransformCount / 2),
376
+ chunkCount: Math.floor(globalChunkCount / 2),
377
+ })
378
+
379
+ console.log(`✅ 构建成功!`)
380
+ console.log(` 📁 输出目录: ${globalOutDir}`)
381
+ console.log(` ⏱️ 耗时: ${elapsed}s`)
382
+ } else {
383
+ console.log(`\n❌ 构建失败,请检查错误信息`)
384
+ }
385
+
386
+ globalIsBuilding = false
387
+ }, 500)
388
+ },
389
+ }
390
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * .vitepress/tnotes/vitepress/plugins/getNoteByConfigIdPlugin.ts
3
+ *
4
+ * VitePress 插件 - 根据 configId 查询笔记信息
5
+ */
6
+ import type { PluginOption } from 'vite'
7
+ import { NoteIndexCache } from '../../core'
8
+ import { logger } from '../../utils'
9
+
10
+ export function getNoteByConfigIdPlugin(): PluginOption {
11
+ return {
12
+ name: 'tnotes-get-note-by-config-id',
13
+
14
+ configureServer(server) {
15
+ // 添加中间件处理查询请求
16
+ server.middlewares.use(async (req, res, next) => {
17
+ if (
18
+ req.url?.startsWith('/__tnotes_get_note?') &&
19
+ req.method === 'GET'
20
+ ) {
21
+ try {
22
+ // 解析查询参数
23
+ const url = new URL(req.url, `http://${req.headers.host}`)
24
+ const configId = url.searchParams.get('configId')
25
+
26
+ if (!configId) {
27
+ res.statusCode = 400
28
+ res.setHeader('Content-Type', 'application/json')
29
+ res.end(
30
+ JSON.stringify({
31
+ success: false,
32
+ error: 'Missing configId parameter',
33
+ }),
34
+ )
35
+ return
36
+ }
37
+
38
+ // 确保笔记索引缓存已初始化
39
+ const noteIndexCache = NoteIndexCache.getInstance()
40
+ if (!noteIndexCache.isInitialized()) {
41
+ res.statusCode = 503
42
+ res.setHeader('Content-Type', 'application/json')
43
+ res.end(
44
+ JSON.stringify({
45
+ success: false,
46
+ error: 'Service not initialized',
47
+ }),
48
+ )
49
+ return
50
+ }
51
+
52
+ // 从索引缓存中查询笔记
53
+ const noteItem = noteIndexCache.getByConfigId(configId)
54
+
55
+ if (!noteItem) {
56
+ // 笔记不存在或已被删除
57
+ res.statusCode = 200
58
+ res.setHeader('Content-Type', 'application/json')
59
+ res.end(
60
+ JSON.stringify({
61
+ success: true,
62
+ found: false,
63
+ data: null,
64
+ }),
65
+ )
66
+ return
67
+ }
68
+
69
+ // 返回笔记信息
70
+ res.statusCode = 200
71
+ res.setHeader('Content-Type', 'application/json')
72
+ res.end(
73
+ JSON.stringify({
74
+ success: true,
75
+ found: true,
76
+ data: {
77
+ noteIndex: noteItem.noteIndex,
78
+ folderName: noteItem.folderName,
79
+ // 构建笔记的完整 URL(包含 README)
80
+ url: `/notes/${encodeURIComponent(
81
+ noteItem.folderName,
82
+ )}/README`,
83
+ },
84
+ }),
85
+ )
86
+
87
+ logger.debug(
88
+ `查询笔记: configId=${configId}, noteIndex=${noteItem.noteIndex}`,
89
+ )
90
+ } catch (error) {
91
+ logger.error('查询笔记失败:', error)
92
+ res.statusCode = 500
93
+ res.setHeader('Content-Type', 'application/json')
94
+ res.end(
95
+ JSON.stringify({
96
+ success: false,
97
+ error: error instanceof Error ? error.message : String(error),
98
+ }),
99
+ )
100
+ }
101
+ } else {
102
+ next()
103
+ }
104
+ })
105
+ },
106
+ }
107
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * .vitepress/tnotes/vitepress/plugins/renameNotePlugin.ts
3
+ *
4
+ * Vite 插件 - 处理笔记重命名请求
5
+ */
6
+ import type { PluginOption } from 'vite'
7
+ import { RenameNoteCommand } from '../../commands/note/RenameNoteCommand'
8
+
9
+ export function renameNotePlugin(): PluginOption {
10
+ const renameCommand = new RenameNoteCommand()
11
+
12
+ return {
13
+ name: 'tnotes-rename-note',
14
+ configureServer(server) {
15
+ server.middlewares.use(async (req, res, next) => {
16
+ // 只处理 POST 请求到 /__tnotes_rename_note
17
+ if (req.url === '/__tnotes_rename_note' && req.method === 'POST') {
18
+ let body = ''
19
+
20
+ req.on('data', (chunk) => {
21
+ body += chunk.toString()
22
+ })
23
+
24
+ req.on('end', async () => {
25
+ try {
26
+ const { noteIndex, newTitle } = JSON.parse(body)
27
+
28
+ if (!noteIndex || !newTitle) {
29
+ res.statusCode = 400
30
+ res.end('Missing noteIndex or newTitle')
31
+ return
32
+ }
33
+
34
+ // 执行重命名
35
+ const startTime = Date.now()
36
+ await renameCommand.renameNote({ noteIndex, newTitle })
37
+ const duration = Date.now() - startTime
38
+
39
+ res.statusCode = 200
40
+ res.setHeader('Content-Type', 'application/json')
41
+ res.end(
42
+ JSON.stringify({
43
+ success: true,
44
+ duration,
45
+ newTitle,
46
+ message: '重命名完成',
47
+ })
48
+ )
49
+ } catch (error) {
50
+ res.statusCode = 500
51
+ res.end(error instanceof Error ? error.message : 'Rename failed')
52
+ }
53
+ })
54
+ } else {
55
+ next()
56
+ }
57
+ })
58
+ },
59
+ }
60
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * .vitepress/tnotes/vitepress/plugins/updateConfigPlugin.ts
3
+ *
4
+ * VitePress 插件 - 处理笔记配置更新请求
5
+ */
6
+ import type { PluginOption } from 'vite'
7
+ import { UpdateNoteConfigCommand } from '../../commands/note/UpdateNoteConfigCommand'
8
+
9
+ export function updateConfigPlugin(): PluginOption {
10
+ let updateCommand: UpdateNoteConfigCommand
11
+
12
+ return {
13
+ name: 'tnotes-update-config',
14
+
15
+ configureServer(server) {
16
+ updateCommand = new UpdateNoteConfigCommand()
17
+
18
+ // 添加中间件处理配置更新请求
19
+ server.middlewares.use(async (req, res, next) => {
20
+ if (req.url === '/__tnotes_update_config' && req.method === 'POST') {
21
+ let body = ''
22
+
23
+ req.on('data', (chunk) => {
24
+ body += chunk.toString()
25
+ })
26
+
27
+ req.on('end', async () => {
28
+ try {
29
+ const data = JSON.parse(body)
30
+ const { noteIndex, config } = data
31
+
32
+ if (!noteIndex || !config) {
33
+ res.statusCode = 400
34
+ res.end('Missing noteIndex or config')
35
+ return
36
+ }
37
+
38
+ // 调用命令更新配置
39
+ await updateCommand.updateConfig({
40
+ noteIndex,
41
+ config: {
42
+ done: config.done,
43
+ enableDiscussions: config.enableDiscussions,
44
+ description: config.description,
45
+ },
46
+ })
47
+
48
+ res.statusCode = 200
49
+ res.setHeader('Content-Type', 'application/json')
50
+ res.end(JSON.stringify({ success: true }))
51
+ } catch (error) {
52
+ console.error('更新配置失败:', error)
53
+ res.statusCode = 500
54
+ res.end(error instanceof Error ? error.message : String(error))
55
+ }
56
+ })
57
+ } else {
58
+ next()
59
+ }
60
+ })
61
+ },
62
+ }
63
+ }