@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.
- package/README.md +105 -0
- package/dist/chunk-K3X5OP3N.js +1532 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +4199 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +9 -0
- package/package.json +74 -0
- package/types/config.ts +61 -0
- package/types/index.ts +11 -0
- package/types/note.ts +33 -0
- package/vitepress/assets/icons/icon__check.svg +3 -0
- package/vitepress/assets/icons/icon__clipboard.svg +8 -0
- package/vitepress/assets/icons/icon__close.svg +1 -0
- package/vitepress/assets/icons/icon__collapse.svg +1 -0
- package/vitepress/assets/icons/icon__confirm.svg +1 -0
- package/vitepress/assets/icons/icon__copy.svg +4 -0
- package/vitepress/assets/icons/icon__focus.svg +1 -0
- package/vitepress/assets/icons/icon__fold.svg +3 -0
- package/vitepress/assets/icons/icon__folder.svg +1 -0
- package/vitepress/assets/icons/icon__fullscreen.svg +1 -0
- package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -0
- package/vitepress/assets/icons/icon__github.svg +4 -0
- package/vitepress/assets/icons/icon__mindmap.svg +1 -0
- package/vitepress/assets/icons/icon__next.svg +1 -0
- package/vitepress/assets/icons/icon__number_gray.svg +1 -0
- package/vitepress/assets/icons/icon__number_purple.svg +1 -0
- package/vitepress/assets/icons/icon__prev.svg +1 -0
- package/vitepress/assets/icons/icon__restore.svg +1 -0
- package/vitepress/assets/icons/icon__rotate.svg +4 -0
- package/vitepress/assets/icons/icon__search.svg +1 -0
- package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -0
- package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -0
- package/vitepress/assets/icons/icon__totop.svg +6 -0
- package/vitepress/assets/icons/icon__vscode.svg +6 -0
- package/vitepress/assets/icons/icon__zoom_fit.svg +1 -0
- package/vitepress/assets/icons/icon__zoom_in.svg +1 -0
- package/vitepress/assets/icons/icon__zoom_out.svg +1 -0
- package/vitepress/assets/icons/icon__zoom_reset.svg +1 -0
- package/vitepress/assets/icons/index.ts +38 -0
- package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -0
- package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +373 -0
- package/vitepress/components/CodeBlockFullscreen/index.ts +115 -0
- package/vitepress/components/CodeBlockFullscreen/styles.css +64 -0
- package/vitepress/components/Discussions/Discussions.module.scss +32 -0
- package/vitepress/components/Discussions/Discussions.vue +211 -0
- package/vitepress/components/EnWordList/EnWordList.module.scss +124 -0
- package/vitepress/components/EnWordList/EnWordList.vue +543 -0
- package/vitepress/components/EnWordList/RightClickMenu.module.scss +22 -0
- package/vitepress/components/EnWordList/RightClickMenu.vue +66 -0
- package/vitepress/components/Footprints/Footprints.module.scss +93 -0
- package/vitepress/components/Footprints/Footprints.vue +377 -0
- package/vitepress/components/Layout/AboutModal.module.scss +233 -0
- package/vitepress/components/Layout/AboutModal.vue +105 -0
- package/vitepress/components/Layout/AboutPanel.vue +266 -0
- package/vitepress/components/Layout/ContentCollapse.vue +603 -0
- package/vitepress/components/Layout/CustomSidebar.vue +605 -0
- package/vitepress/components/Layout/DocBeforeControls.vue +139 -0
- package/vitepress/components/Layout/DocFooter.vue +225 -0
- package/vitepress/components/Layout/ImagePreview.module.scss +201 -0
- package/vitepress/components/Layout/ImagePreview.vue +281 -0
- package/vitepress/components/Layout/Layout.module.scss +661 -0
- package/vitepress/components/Layout/Layout.vue +542 -0
- package/vitepress/components/Layout/NoteStatus.vue +140 -0
- package/vitepress/components/Layout/SidebarItems.vue +263 -0
- package/vitepress/components/Layout/SidebarNavBefore.vue +92 -0
- package/vitepress/components/Layout/Swiper.vue +167 -0
- package/vitepress/components/Layout/ToggleFullContent.module.scss +11 -0
- package/vitepress/components/Layout/ToggleFullContent.vue +34 -0
- package/vitepress/components/Layout/ToggleSidebar.module.scss +11 -0
- package/vitepress/components/Layout/ToggleSidebar.vue +35 -0
- package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -0
- package/vitepress/components/Layout/composables/useNoteConfig.ts +121 -0
- package/vitepress/components/Layout/composables/useNoteSave.ts +173 -0
- package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -0
- package/vitepress/components/Layout/composables/useRedirect.ts +110 -0
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -0
- package/vitepress/components/Layout/homeReadme.data.ts +124 -0
- package/vitepress/components/LoadingPage/LoadingPage.vue +192 -0
- package/vitepress/components/MarkMap/MarkMap.module.scss +159 -0
- package/vitepress/components/MarkMap/MarkMap.vue +404 -0
- package/vitepress/components/Mermaid/Mermaid.module.scss +275 -0
- package/vitepress/components/Mermaid/Mermaid.vue +364 -0
- package/vitepress/components/NotesTable/NotesTable.module.scss +77 -0
- package/vitepress/components/NotesTable/NotesTable.vue +98 -0
- package/vitepress/components/NotesTable/README.md +67 -0
- package/vitepress/components/Settings/Settings.module.scss +433 -0
- package/vitepress/components/Settings/Settings.vue +306 -0
- package/vitepress/components/SidebarCard/MindMapView.vue +483 -0
- package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -0
- package/vitepress/components/SidebarCard/SidebarCard.vue +948 -0
- package/vitepress/components/Tooltip/Tooltip.vue +70 -0
- package/vitepress/components/constants.ts +91 -0
- package/vitepress/components/notesConfig.data.ts +73 -0
- package/vitepress/components/sidebar.data.ts +59 -0
- package/vitepress/components/tnotes-config.data.ts +21 -0
- package/vitepress/components/utils.ts +26 -0
- package/vitepress/config/index.ts +126 -0
- package/vitepress/configs/constants.ts +26 -0
- package/vitepress/configs/head.config.ts +25 -0
- package/vitepress/configs/index.ts +9 -0
- package/vitepress/configs/markdown-it.d.ts +23 -0
- package/vitepress/configs/markdown.config.ts +366 -0
- package/vitepress/configs/theme.config.ts +108 -0
- package/vitepress/plugins/buildProgressPlugin.ts +390 -0
- package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -0
- package/vitepress/plugins/renameNotePlugin.ts +60 -0
- package/vitepress/plugins/updateConfigPlugin.ts +63 -0
- package/vitepress/theme/index.ts +95 -0
- package/vitepress/theme/styles/base.scss +50 -0
- package/vitepress/theme/styles/components/404.scss +31 -0
- package/vitepress/theme/styles/components/collapse.scss +175 -0
- package/vitepress/theme/styles/components/markmap.scss +101 -0
- package/vitepress/theme/styles/components/swiper.scss +255 -0
- package/vitepress/theme/styles/index.scss +25 -0
- package/vitepress/theme/styles/layout.scss +62 -0
- package/vitepress/theme/styles/utilities.scss +39 -0
- 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
|
+
}
|