@tnotesjs/core 0.1.2 → 0.1.3

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.

@@ -0,0 +1,993 @@
1
+ import {
2
+ NoteIndexCache,
3
+ RenameNoteCommand,
4
+ UpdateNoteConfigCommand,
5
+ generateAnchor,
6
+ logger
7
+ } from "../../chunk-H2NACWVJ.js";
8
+ import {
9
+ __commonJS,
10
+ __toESM
11
+ } from "../../chunk-UIXF3LPU.js";
12
+
13
+ // node_modules/.pnpm/markdown-it-task-lists@2.1.1/node_modules/markdown-it-task-lists/index.js
14
+ var require_markdown_it_task_lists = __commonJS({
15
+ "node_modules/.pnpm/markdown-it-task-lists@2.1.1/node_modules/markdown-it-task-lists/index.js"(exports, module) {
16
+ var disableCheckboxes = true;
17
+ var useLabelWrapper = false;
18
+ var useLabelAfter = false;
19
+ module.exports = function(md, options) {
20
+ if (options) {
21
+ disableCheckboxes = !options.enabled;
22
+ useLabelWrapper = !!options.label;
23
+ useLabelAfter = !!options.labelAfter;
24
+ }
25
+ md.core.ruler.after("inline", "github-task-lists", function(state) {
26
+ var tokens = state.tokens;
27
+ for (var i = 2; i < tokens.length; i++) {
28
+ if (isTodoItem(tokens, i)) {
29
+ todoify(tokens[i], state.Token);
30
+ attrSet(tokens[i - 2], "class", "task-list-item" + (!disableCheckboxes ? " enabled" : ""));
31
+ attrSet(tokens[parentToken(tokens, i - 2)], "class", "contains-task-list");
32
+ }
33
+ }
34
+ });
35
+ };
36
+ function attrSet(token, name, value) {
37
+ var index = token.attrIndex(name);
38
+ var attr = [name, value];
39
+ if (index < 0) {
40
+ token.attrPush(attr);
41
+ } else {
42
+ token.attrs[index] = attr;
43
+ }
44
+ }
45
+ function parentToken(tokens, index) {
46
+ var targetLevel = tokens[index].level - 1;
47
+ for (var i = index - 1; i >= 0; i--) {
48
+ if (tokens[i].level === targetLevel) {
49
+ return i;
50
+ }
51
+ }
52
+ return -1;
53
+ }
54
+ function isTodoItem(tokens, index) {
55
+ return isInline(tokens[index]) && isParagraph(tokens[index - 1]) && isListItem(tokens[index - 2]) && startsWithTodoMarkdown(tokens[index]);
56
+ }
57
+ function todoify(token, TokenConstructor) {
58
+ token.children.unshift(makeCheckbox(token, TokenConstructor));
59
+ token.children[1].content = token.children[1].content.slice(3);
60
+ token.content = token.content.slice(3);
61
+ if (useLabelWrapper) {
62
+ if (useLabelAfter) {
63
+ token.children.pop();
64
+ var id = "task-item-" + Math.ceil(Math.random() * (1e4 * 1e3) - 1e3);
65
+ token.children[0].content = token.children[0].content.slice(0, -1) + ' id="' + id + '">';
66
+ token.children.push(afterLabel(token.content, id, TokenConstructor));
67
+ } else {
68
+ token.children.unshift(beginLabel(TokenConstructor));
69
+ token.children.push(endLabel(TokenConstructor));
70
+ }
71
+ }
72
+ }
73
+ function makeCheckbox(token, TokenConstructor) {
74
+ var checkbox = new TokenConstructor("html_inline", "", 0);
75
+ var disabledAttr = disableCheckboxes ? ' disabled="" ' : "";
76
+ if (token.content.indexOf("[ ] ") === 0) {
77
+ checkbox.content = '<input class="task-list-item-checkbox"' + disabledAttr + 'type="checkbox">';
78
+ } else if (token.content.indexOf("[x] ") === 0 || token.content.indexOf("[X] ") === 0) {
79
+ checkbox.content = '<input class="task-list-item-checkbox" checked=""' + disabledAttr + 'type="checkbox">';
80
+ }
81
+ return checkbox;
82
+ }
83
+ function beginLabel(TokenConstructor) {
84
+ var token = new TokenConstructor("html_inline", "", 0);
85
+ token.content = "<label>";
86
+ return token;
87
+ }
88
+ function endLabel(TokenConstructor) {
89
+ var token = new TokenConstructor("html_inline", "", 0);
90
+ token.content = "</label>";
91
+ return token;
92
+ }
93
+ function afterLabel(content, id, TokenConstructor) {
94
+ var token = new TokenConstructor("html_inline", "", 0);
95
+ token.content = '<label class="task-list-item-label" for="' + id + '">' + content + "</label>";
96
+ token.attrs = [{ for: id }];
97
+ return token;
98
+ }
99
+ function isInline(token) {
100
+ return token.type === "inline";
101
+ }
102
+ function isParagraph(token) {
103
+ return token.type === "paragraph_open";
104
+ }
105
+ function isListItem(token) {
106
+ return token.type === "list_item_open";
107
+ }
108
+ function startsWithTodoMarkdown(token) {
109
+ return token.content.indexOf("[ ] ") === 0 || token.content.indexOf("[x] ") === 0 || token.content.indexOf("[X] ") === 0;
110
+ }
111
+ }
112
+ });
113
+
114
+ // vitepress/config/index.ts
115
+ import fs2 from "fs";
116
+ import path2 from "path";
117
+ import { defineConfig } from "vitepress";
118
+
119
+ // vitepress/configs/constants.ts
120
+ function getIgnoreList(config) {
121
+ return [...config.ignore_dirs.map((dir) => `**/${dir}/**`)];
122
+ }
123
+ function getGithubPageUrl(config) {
124
+ return "https://" + config.author.toLowerCase() + ".github.io/" + config.repoName + "/";
125
+ }
126
+
127
+ // vitepress/configs/head.config.ts
128
+ function getHeadConfig(config, githubPageUrl) {
129
+ const head = [
130
+ [
131
+ "meta",
132
+ {
133
+ name: "keywords",
134
+ content: config.keywords.join(", ")
135
+ }
136
+ ],
137
+ ["meta", { name: "author", content: config.author }],
138
+ ["link", { rel: "canonical", href: githubPageUrl }],
139
+ ["link", { rel: "icon", href: githubPageUrl + "favicon.ico" }],
140
+ ["link", { rel: "preconnect", href: "https://fonts.googleapis.com" }]
141
+ ];
142
+ return head;
143
+ }
144
+
145
+ // vitepress/configs/markdown.config.ts
146
+ var import_markdown_it_task_lists = __toESM(require_markdown_it_task_lists(), 1);
147
+ import markdownItContainer from "markdown-it-container";
148
+ import mila from "markdown-it-link-attributes";
149
+ import fs from "fs";
150
+ import path from "path";
151
+ function esc(s = "") {
152
+ return s.replace(
153
+ /[&<>"']/g,
154
+ (ch) => ({
155
+ "&": "&amp;",
156
+ "<": "&lt;",
157
+ ">": "&gt;",
158
+ '"': "&quot;",
159
+ "'": "&#39;"
160
+ })[ch]
161
+ );
162
+ }
163
+ var simpleMermaidMarkdown = (md) => {
164
+ const fence = md.renderer.rules.fence ? md.renderer.rules.fence.bind(md.renderer.rules) : () => "";
165
+ md.renderer.rules.fence = (tokens, index, options, env, slf) => {
166
+ const token = tokens[index];
167
+ if (token.info.trim() === "mermaid") {
168
+ try {
169
+ const key = `mermaid-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
170
+ const content = token.content;
171
+ return `<Mermaid id="${key}" graph="${encodeURIComponent(content)}" />`;
172
+ } catch (err) {
173
+ return `<pre>${err}</pre>`;
174
+ }
175
+ }
176
+ if (token.info.trim() === "mmd") {
177
+ tokens[index].info = "mermaid";
178
+ }
179
+ return fence(tokens, index, options, env, slf);
180
+ };
181
+ };
182
+ function configureMarkMapContainer(md) {
183
+ md.use(markdownItContainer, "markmap", {
184
+ marker: "`",
185
+ validate(params) {
186
+ const p = (params || "").trim();
187
+ return p.startsWith("markmap");
188
+ },
189
+ render() {
190
+ return "";
191
+ }
192
+ });
193
+ md.core.ruler.after("block", "tn_replace_markmap_container", (state) => {
194
+ const src = state.env.source || "";
195
+ const lines = src.split("\n");
196
+ const tokens = state.tokens;
197
+ for (let i = 0; i < tokens.length; i++) {
198
+ const t = tokens[i];
199
+ if (t.type === "container_markmap_open") {
200
+ let j = i + 1;
201
+ while (j < tokens.length && tokens[j].type !== "container_markmap_close")
202
+ j++;
203
+ if (j >= tokens.length) continue;
204
+ const open = t;
205
+ const startLine = open.map ? open.map[0] + 1 : null;
206
+ const endLine = open.map ? open.map[1] - 1 : null;
207
+ let params = {};
208
+ if (open.map && typeof open.map[0] === "number") {
209
+ const openLine = (lines[open.map[0]] || "").trim();
210
+ let paramPart = "";
211
+ const braceMatch = openLine.match(/\{([^}]*)\}/);
212
+ if (braceMatch) {
213
+ paramPart = braceMatch[1].trim();
214
+ } else {
215
+ const after = openLine.replace(/^`+\s*/, "");
216
+ if (after.startsWith("markmap")) {
217
+ paramPart = after.slice("markmap".length).trim();
218
+ }
219
+ }
220
+ if (paramPart) {
221
+ const tokenArr = paramPart.match(/"[^"]*"|'[^']*'|\S+/g) || [];
222
+ let startIdx = 0;
223
+ if (tokenArr.length > 0 && /^\d+$/.test(tokenArr[0])) {
224
+ params.initialExpandLevel = Number(tokenArr[0]);
225
+ startIdx = 1;
226
+ }
227
+ for (let k = startIdx; k < tokenArr.length; k++) {
228
+ const pair = tokenArr[k];
229
+ if (!pair) continue;
230
+ const m = pair.match(/^([^=:\s]+)\s*(=|:)\s*(.+)$/);
231
+ if (m) {
232
+ const key = m[1];
233
+ let val = m[3];
234
+ if (/^".*"$/.test(val) && val.length >= 2 || /^'.*'$/.test(val) && val.length >= 2) {
235
+ val = val.slice(1, -1);
236
+ } else if (/^\d+$/.test(val)) {
237
+ val = String(Number(val));
238
+ }
239
+ params[key] = val;
240
+ }
241
+ }
242
+ }
243
+ }
244
+ let content = "";
245
+ if (startLine !== null && endLine !== null) {
246
+ for (let k = startLine; k <= endLine && k < lines.length; k++) {
247
+ content += lines[k] + "\n";
248
+ }
249
+ } else {
250
+ for (let k = i + 1; k < j; k++) {
251
+ content += tokens[k].content || "";
252
+ }
253
+ }
254
+ const firstNonEmptyLine = (content || "").split("\n").find((ln) => ln.trim() !== "") || "";
255
+ const refMatch = firstNonEmptyLine.trim().match(/^<<<\s*(.+)$/);
256
+ if (refMatch) {
257
+ let refRaw = refMatch[1].trim().replace(/^['"]|['"]$/g, "");
258
+ try {
259
+ const env = state.env || {};
260
+ const possibleRel = env.relativePath || env.path || env.filePath || env.file || "";
261
+ let refFullPath = refRaw;
262
+ if (!path.isAbsolute(refRaw)) {
263
+ if (possibleRel) {
264
+ const currentDir = path.dirname(possibleRel);
265
+ refFullPath = path.resolve(process.cwd(), currentDir, refRaw);
266
+ } else {
267
+ refFullPath = path.resolve(process.cwd(), refRaw);
268
+ }
269
+ } else {
270
+ refFullPath = refRaw;
271
+ }
272
+ const fileContent = fs.readFileSync(refFullPath, "utf-8");
273
+ content = fileContent;
274
+ } catch (err) {
275
+ const errorMsg = err instanceof Error ? err.message : String(err);
276
+ content = `Failed to load referenced file: ${esc(
277
+ String(refRaw)
278
+ )}
279
+
280
+ Error: ${esc(errorMsg)}`;
281
+ }
282
+ }
283
+ const encodedContent = encodeURIComponent(content.trim());
284
+ let propsStr = `content="${encodedContent}"`;
285
+ for (const [k, v] of Object.entries(params)) {
286
+ if (typeof v === "number" || /^\d+$/.test(String(v))) {
287
+ propsStr += ` :${k}="${v}"`;
288
+ } else {
289
+ const safe = String(v).replace(/"/g, "&quot;");
290
+ propsStr += ` ${k}="${safe}"`;
291
+ }
292
+ }
293
+ const html = `<MarkMap ${propsStr}></MarkMap>
294
+ `;
295
+ const htmlToken = new state.Token("html_block", "", 0);
296
+ htmlToken.content = html;
297
+ tokens.splice(i, j - i + 1, htmlToken);
298
+ }
299
+ }
300
+ return true;
301
+ });
302
+ }
303
+ function configureSwiperContainer(md) {
304
+ let __tn_swiper_uid = 0;
305
+ let __tn_rules_stack = [];
306
+ md.core.ruler.before("block", "tn_swiper_reset_uid", () => {
307
+ __tn_swiper_uid = 0;
308
+ __tn_rules_stack = [];
309
+ return true;
310
+ });
311
+ md.use(markdownItContainer, "swiper", {
312
+ render: (tokens, idx) => {
313
+ if (tokens[idx].nesting === 1) {
314
+ __tn_rules_stack.push({
315
+ image: md.renderer.rules.image,
316
+ pOpen: md.renderer.rules.paragraph_open,
317
+ pClose: md.renderer.rules.paragraph_close
318
+ });
319
+ md.renderer.rules.paragraph_open = () => "";
320
+ md.renderer.rules.paragraph_close = () => "";
321
+ md.renderer.rules.image = (tokens2, i) => {
322
+ const token = tokens2[i];
323
+ const src = token.attrGet("src") || "";
324
+ const alt = token.content || "";
325
+ const title = alt && alt.trim() ? alt : "img";
326
+ return `<div class="swiper-slide" data-title="${esc(
327
+ title
328
+ )}"><img src="${esc(src)}" alt="${esc(alt)}"></div>`;
329
+ };
330
+ const id = `tn-swiper-${++__tn_swiper_uid}`;
331
+ return `
332
+ <div class="tn-swiper" data-swiper-id="${id}">
333
+ <div class="tn-swiper-tabs"></div>
334
+ <div class="swiper-container">
335
+ <div class="swiper-wrapper">
336
+ `;
337
+ } else {
338
+ const prev = __tn_rules_stack.pop() || {
339
+ image: null,
340
+ pOpen: null,
341
+ pClose: null
342
+ };
343
+ md.renderer.rules.image = prev.image;
344
+ md.renderer.rules.paragraph_open = prev.pOpen;
345
+ md.renderer.rules.paragraph_close = prev.pClose;
346
+ return `
347
+ </div>
348
+ <!-- \u4E0B\u4E00\u9875\u6309\u94AE -->
349
+ <!-- <div class="swiper-button-next"></div> -->
350
+ <!-- \u4E0A\u4E00\u9875\u6309\u94AE -->
351
+ <!-- <div class="swiper-button-prev"></div> -->
352
+ <!-- \u5206\u9875\u5BFC\u822A -->
353
+ <!-- <div class="swiper-pagination"></div> -->
354
+ </div>
355
+ </div>
356
+ `;
357
+ }
358
+ }
359
+ });
360
+ }
361
+ function getMarkdownConfig() {
362
+ const markdown = {
363
+ lineNumbers: true,
364
+ math: true,
365
+ config(md) {
366
+ md.core.ruler.before("normalize", "save-source", (state) => {
367
+ state.env.source = state.src;
368
+ return true;
369
+ });
370
+ simpleMermaidMarkdown(md);
371
+ configureMarkMapContainer(md);
372
+ md.use(import_markdown_it_task_lists.default);
373
+ md.use(mila, {
374
+ attrs: {
375
+ target: "_self",
376
+ rel: "noopener"
377
+ }
378
+ });
379
+ configureSwiperContainer(md);
380
+ },
381
+ anchor: {
382
+ slugify: generateAnchor
383
+ },
384
+ image: {
385
+ lazyLoading: true
386
+ }
387
+ };
388
+ return markdown;
389
+ }
390
+
391
+ // vitepress/configs/theme.config.ts
392
+ function getThemeConfig(config) {
393
+ const themeConfig = {
394
+ docFooter: {
395
+ prev: "\u4E0A\u4E00\u7BC7",
396
+ next: "\u4E0B\u4E00\u7BC7"
397
+ },
398
+ externalLinkIcon: true,
399
+ outline: {
400
+ level: [2, 3],
401
+ label: "\u76EE\u5F55"
402
+ },
403
+ nav: [
404
+ {
405
+ text: "\u{1F440} README",
406
+ link: "/README"
407
+ },
408
+ {
409
+ text: "Menus",
410
+ items: config.menuItems
411
+ }
412
+ ],
413
+ search: {
414
+ // 使用本地搜索(不依赖远程服务器)
415
+ provider: "local",
416
+ options: {
417
+ miniSearch: {
418
+ /**
419
+ * 控制如何对文档进行分词、字段提取等预处理
420
+ * @type {Pick<import('minisearch').Options, 'extractField' | 'tokenize' | 'processTerm'>}
421
+ */
422
+ options: {
423
+ // 自定义分词逻辑
424
+ tokenize: (text, language) => {
425
+ if (language === "zh") {
426
+ return text.match(/[\u4e00-\u9fa5]+|\S+/g) || [];
427
+ }
428
+ return text.split(/\s+/);
429
+ },
430
+ // 将所有词转为小写,确保大小写不敏感匹配
431
+ processTerm: (term) => term.toLowerCase()
432
+ },
433
+ /**
434
+ * 控制搜索时的行为(如模糊匹配、权重)
435
+ * @type {import('minisearch').SearchOptions}
436
+ * @default
437
+ * { fuzzy: 0.2, prefix: true, boost: { title: 4, text: 2, titles: 1 } }
438
+ */
439
+ searchOptions: {
440
+ fuzzy: 0.2,
441
+ // 模糊匹配阈值(0-1),允许拼写错误的阈值(数值越低越严格)
442
+ prefix: true,
443
+ // 是否启用前缀匹配(输入"jav"可匹配"javascript")
444
+ boost: {
445
+ title: 10,
446
+ // 文件名作为 h1 标题,权重最高
447
+ headings: 5,
448
+ // h2 - h6
449
+ text: 3,
450
+ // 正文内容索引
451
+ code: 1
452
+ // 代码块索引权重
453
+ }
454
+ }
455
+ },
456
+ /**
457
+ * 控制哪些 Markdown 内容参与本地搜索引擎索引
458
+ * @param {string} src 当前 Markdown 文件的原始内容(即 .md 文件中的文本)
459
+ * @param {import('vitepress').MarkdownEnv} env 包含当前页面环境信息的对象,比如 frontmatter、路径等
460
+ * @param {import('markdown-it-async')} md 一个 Markdown 渲染器实例,用来将 Markdown 转换为 HTML
461
+ */
462
+ async _render(src, env, md) {
463
+ const filePath = env.relativePath;
464
+ if (filePath.includes("TOC.md")) return "";
465
+ const notesIndex = filePath.indexOf("notes/");
466
+ let folderName = "";
467
+ if (notesIndex !== -1) {
468
+ const pathAfterNotes = filePath.slice(notesIndex + "notes/".length);
469
+ folderName = pathAfterNotes.split("/")[0];
470
+ }
471
+ const titleField = `# ${folderName}
472
+ `;
473
+ const html = md.render(titleField + "\n\n" + src, env);
474
+ return html;
475
+ }
476
+ }
477
+ },
478
+ // sidebar: [...sidebar],
479
+ sidebar: [
480
+ {
481
+ text: "\u{1F440} README",
482
+ link: "/README"
483
+ }
484
+ ],
485
+ socialLinks: config.socialLinks
486
+ };
487
+ return themeConfig;
488
+ }
489
+
490
+ // vitepress/plugins/updateConfigPlugin.ts
491
+ function updateConfigPlugin() {
492
+ let updateCommand;
493
+ return {
494
+ name: "tnotes-update-config",
495
+ configureServer(server) {
496
+ updateCommand = new UpdateNoteConfigCommand();
497
+ server.middlewares.use(async (req, res, next) => {
498
+ if (req.url === "/__tnotes_update_config" && req.method === "POST") {
499
+ let body = "";
500
+ req.on("data", (chunk) => {
501
+ body += chunk.toString();
502
+ });
503
+ req.on("end", async () => {
504
+ try {
505
+ const data = JSON.parse(body);
506
+ const { noteIndex, config } = data;
507
+ if (!noteIndex || !config) {
508
+ res.statusCode = 400;
509
+ res.end("Missing noteIndex or config");
510
+ return;
511
+ }
512
+ await updateCommand.updateConfig({
513
+ noteIndex,
514
+ config: {
515
+ done: config.done,
516
+ enableDiscussions: config.enableDiscussions,
517
+ description: config.description
518
+ }
519
+ });
520
+ res.statusCode = 200;
521
+ res.setHeader("Content-Type", "application/json");
522
+ res.end(JSON.stringify({ success: true }));
523
+ } catch (error) {
524
+ console.error("\u66F4\u65B0\u914D\u7F6E\u5931\u8D25:", error);
525
+ res.statusCode = 500;
526
+ res.end(error instanceof Error ? error.message : String(error));
527
+ }
528
+ });
529
+ } else {
530
+ next();
531
+ }
532
+ });
533
+ }
534
+ };
535
+ }
536
+
537
+ // vitepress/plugins/renameNotePlugin.ts
538
+ function renameNotePlugin() {
539
+ const renameCommand = new RenameNoteCommand();
540
+ return {
541
+ name: "tnotes-rename-note",
542
+ configureServer(server) {
543
+ server.middlewares.use(async (req, res, next) => {
544
+ if (req.url === "/__tnotes_rename_note" && req.method === "POST") {
545
+ let body = "";
546
+ req.on("data", (chunk) => {
547
+ body += chunk.toString();
548
+ });
549
+ req.on("end", async () => {
550
+ try {
551
+ const { noteIndex, newTitle } = JSON.parse(body);
552
+ if (!noteIndex || !newTitle) {
553
+ res.statusCode = 400;
554
+ res.end("Missing noteIndex or newTitle");
555
+ return;
556
+ }
557
+ const startTime = Date.now();
558
+ await renameCommand.renameNote({ noteIndex, newTitle });
559
+ const duration = Date.now() - startTime;
560
+ res.statusCode = 200;
561
+ res.setHeader("Content-Type", "application/json");
562
+ res.end(
563
+ JSON.stringify({
564
+ success: true,
565
+ duration,
566
+ newTitle,
567
+ message: "\u91CD\u547D\u540D\u5B8C\u6210"
568
+ })
569
+ );
570
+ } catch (error) {
571
+ res.statusCode = 500;
572
+ res.end(error instanceof Error ? error.message : "Rename failed");
573
+ }
574
+ });
575
+ } else {
576
+ next();
577
+ }
578
+ });
579
+ }
580
+ };
581
+ }
582
+
583
+ // vitepress/plugins/getNoteByConfigIdPlugin.ts
584
+ function getNoteByConfigIdPlugin() {
585
+ return {
586
+ name: "tnotes-get-note-by-config-id",
587
+ configureServer(server) {
588
+ server.middlewares.use(async (req, res, next) => {
589
+ if (req.url?.startsWith("/__tnotes_get_note?") && req.method === "GET") {
590
+ try {
591
+ const url = new URL(req.url, `http://${req.headers.host}`);
592
+ const configId = url.searchParams.get("configId");
593
+ if (!configId) {
594
+ res.statusCode = 400;
595
+ res.setHeader("Content-Type", "application/json");
596
+ res.end(
597
+ JSON.stringify({
598
+ success: false,
599
+ error: "Missing configId parameter"
600
+ })
601
+ );
602
+ return;
603
+ }
604
+ const noteIndexCache = NoteIndexCache.getInstance();
605
+ if (!noteIndexCache.isInitialized()) {
606
+ res.statusCode = 503;
607
+ res.setHeader("Content-Type", "application/json");
608
+ res.end(
609
+ JSON.stringify({
610
+ success: false,
611
+ error: "Service not initialized"
612
+ })
613
+ );
614
+ return;
615
+ }
616
+ const noteItem = noteIndexCache.getByConfigId(configId);
617
+ if (!noteItem) {
618
+ res.statusCode = 200;
619
+ res.setHeader("Content-Type", "application/json");
620
+ res.end(
621
+ JSON.stringify({
622
+ success: true,
623
+ found: false,
624
+ data: null
625
+ })
626
+ );
627
+ return;
628
+ }
629
+ res.statusCode = 200;
630
+ res.setHeader("Content-Type", "application/json");
631
+ res.end(
632
+ JSON.stringify({
633
+ success: true,
634
+ found: true,
635
+ data: {
636
+ noteIndex: noteItem.noteIndex,
637
+ folderName: noteItem.folderName,
638
+ // 构建笔记的完整 URL(包含 README)
639
+ url: `/notes/${encodeURIComponent(
640
+ noteItem.folderName
641
+ )}/README`
642
+ }
643
+ })
644
+ );
645
+ logger.debug(
646
+ `\u67E5\u8BE2\u7B14\u8BB0: configId=${configId}, noteIndex=${noteItem.noteIndex}`
647
+ );
648
+ } catch (error) {
649
+ logger.error("\u67E5\u8BE2\u7B14\u8BB0\u5931\u8D25:", error);
650
+ res.statusCode = 500;
651
+ res.setHeader("Content-Type", "application/json");
652
+ res.end(
653
+ JSON.stringify({
654
+ success: false,
655
+ error: error instanceof Error ? error.message : String(error)
656
+ })
657
+ );
658
+ }
659
+ } else {
660
+ next();
661
+ }
662
+ });
663
+ }
664
+ };
665
+ }
666
+
667
+ // vitepress/plugins/buildProgressPlugin.ts
668
+ import {
669
+ existsSync,
670
+ readFileSync,
671
+ writeFileSync,
672
+ mkdirSync,
673
+ readdirSync
674
+ } from "fs";
675
+ import { join } from "path";
676
+ var CACHE_DIR = join(process.cwd(), "node_modules", ".tnotes-progress");
677
+ var CACHE_FILE = join(CACHE_DIR, "build-cache.json");
678
+ function getCacheData() {
679
+ try {
680
+ if (existsSync(CACHE_FILE)) {
681
+ return JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
682
+ }
683
+ } catch {
684
+ }
685
+ return { transformCount: 0, chunkCount: 0 };
686
+ }
687
+ function setCacheData(data) {
688
+ try {
689
+ if (!existsSync(CACHE_DIR)) {
690
+ mkdirSync(CACHE_DIR, { recursive: true });
691
+ }
692
+ writeFileSync(CACHE_FILE, JSON.stringify(data), "utf-8");
693
+ } catch {
694
+ }
695
+ }
696
+ function countSourceFiles(srcDir) {
697
+ let count = 0;
698
+ const extensions = /\.(vue|ts|js|jsx|tsx|css|scss|sass|styl|less|md)$/i;
699
+ const scan = (dir) => {
700
+ try {
701
+ const entries = readdirSync(dir, { withFileTypes: true });
702
+ for (const entry of entries) {
703
+ const fullPath = join(dir, entry.name);
704
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
705
+ scan(fullPath);
706
+ } else if (entry.isFile() && extensions.test(entry.name)) {
707
+ count++;
708
+ }
709
+ }
710
+ } catch {
711
+ }
712
+ };
713
+ scan(srcDir);
714
+ return count;
715
+ }
716
+ var globalStartTime = 0;
717
+ var globalTransformCount = 0;
718
+ var globalChunkCount = 0;
719
+ var globalHasError = false;
720
+ var globalIsBuilding = false;
721
+ var globalOutDir = "";
722
+ var globalLastPercent = 0;
723
+ var globalFileCount = 0;
724
+ var globalLastLoggedPercent = -1;
725
+ var isTTY = !!(process.stdout.isTTY && process.stderr.isTTY);
726
+ var isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL);
727
+ var useSingleLineMode = isTTY && !isCI;
728
+ var originalStdoutWrite = null;
729
+ var originalStderrWrite = null;
730
+ function interceptOutput() {
731
+ if (originalStdoutWrite) return;
732
+ originalStdoutWrite = process.stdout.write.bind(process.stdout);
733
+ originalStderrWrite = process.stderr.write.bind(process.stderr);
734
+ const filter = (chunk) => {
735
+ const str = chunk.toString();
736
+ return str.includes("\u{1F528}") || str.includes("\u2705 \u6784\u5EFA\u6210\u529F") || str.includes("\u274C \u6784\u5EFA\u5931\u8D25") || str.includes("\u{1F4C1}") || str.includes("\u{1F4CA}") || str.includes("\u{1F4E6}") || str.includes("\u23F1\uFE0F") || str.includes("\x1B[2K");
737
+ };
738
+ process.stdout.write = ((chunk, encodingOrCallback, callback) => {
739
+ if (filter(chunk)) {
740
+ return originalStdoutWrite(chunk, encodingOrCallback, callback);
741
+ }
742
+ if (typeof encodingOrCallback === "function") encodingOrCallback();
743
+ else if (callback) callback();
744
+ return true;
745
+ });
746
+ process.stderr.write = ((chunk, encodingOrCallback, callback) => {
747
+ const str = chunk.toString();
748
+ if (filter(chunk) || str.toLowerCase().includes("error")) {
749
+ return originalStderrWrite(chunk, encodingOrCallback, callback);
750
+ }
751
+ if (typeof encodingOrCallback === "function") encodingOrCallback();
752
+ else if (callback) callback();
753
+ return true;
754
+ });
755
+ }
756
+ function restoreOutput() {
757
+ if (originalStdoutWrite) {
758
+ process.stdout.write = originalStdoutWrite;
759
+ originalStdoutWrite = null;
760
+ }
761
+ if (originalStderrWrite) {
762
+ process.stderr.write = originalStderrWrite;
763
+ originalStderrWrite = null;
764
+ }
765
+ }
766
+ function renderProgress(percent, transforms, chunks, width, complete, incomplete, isFinal = false) {
767
+ if (!originalStderrWrite) return;
768
+ if (!useSingleLineMode && !isFinal) {
769
+ const currentPercent = Math.floor(percent * 100);
770
+ const currentBucket = Math.floor(currentPercent / 10) * 10;
771
+ if (currentBucket <= globalLastLoggedPercent) {
772
+ return;
773
+ }
774
+ globalLastLoggedPercent = currentBucket;
775
+ }
776
+ const filled = Math.floor(percent * width);
777
+ const empty = width - filled;
778
+ const bar = complete.repeat(filled) + incomplete.repeat(empty);
779
+ const percentStr = (percent * 100).toFixed(0).padStart(3, " ");
780
+ const elapsed = ((Date.now() - globalStartTime) / 1e3).toFixed(1);
781
+ const prefix = useSingleLineMode ? "\r\x1B[2K" : "";
782
+ const ending = isFinal || !useSingleLineMode ? "\n" : "";
783
+ const line = `${prefix}Building [${bar}] ${percentStr}% | Transforms: ${transforms} | Chunks: ${chunks} | Time: ${elapsed}s${ending}`;
784
+ originalStderrWrite(line);
785
+ }
786
+ function buildProgressPlugin(options = {}) {
787
+ const { width = 40, complete = "\u2588", incomplete = "\u2591" } = options;
788
+ const cache = getCacheData();
789
+ const hasCache = cache.transformCount > 0;
790
+ return {
791
+ name: "tnotes-build-progress",
792
+ enforce: "pre",
793
+ apply: "build",
794
+ config(config, { command }) {
795
+ if (command === "build") {
796
+ config.logLevel = "silent";
797
+ if (!globalIsBuilding) {
798
+ globalIsBuilding = true;
799
+ globalStartTime = Date.now();
800
+ globalTransformCount = 0;
801
+ globalChunkCount = 0;
802
+ globalHasError = false;
803
+ globalLastPercent = 0;
804
+ globalLastLoggedPercent = -1;
805
+ globalOutDir = config.build?.outDir || "dist";
806
+ if (!hasCache) {
807
+ globalFileCount = countSourceFiles(process.cwd());
808
+ }
809
+ interceptOutput();
810
+ }
811
+ }
812
+ },
813
+ transform(_code, id) {
814
+ globalTransformCount++;
815
+ if (hasCache) {
816
+ const total = cache.transformCount * 2 + cache.chunkCount * 2;
817
+ globalLastPercent = Math.min(0.9, globalTransformCount / total);
818
+ } else {
819
+ if (!id.includes("node_modules") && globalLastPercent < 0.7) {
820
+ globalLastPercent = Math.min(
821
+ 0.7,
822
+ globalTransformCount / (globalFileCount * 4)
823
+ );
824
+ }
825
+ }
826
+ const transformsStr = hasCache ? `${globalTransformCount}/${cache.transformCount * 2}` : `${globalTransformCount}`;
827
+ const chunksStr = hasCache ? `${globalChunkCount}/${cache.chunkCount * 2}` : `${globalChunkCount}`;
828
+ renderProgress(
829
+ globalLastPercent,
830
+ transformsStr,
831
+ chunksStr,
832
+ width,
833
+ complete,
834
+ incomplete
835
+ );
836
+ return null;
837
+ },
838
+ renderChunk() {
839
+ globalChunkCount++;
840
+ if (hasCache) {
841
+ const total = cache.transformCount * 2 + cache.chunkCount * 2;
842
+ globalLastPercent = Math.min(
843
+ 0.98,
844
+ (globalTransformCount + globalChunkCount) / total
845
+ );
846
+ } else {
847
+ if (globalLastPercent < 0.98) {
848
+ globalLastPercent = Math.min(0.98, globalLastPercent + 3e-3);
849
+ }
850
+ }
851
+ const transformsStr = hasCache ? `${globalTransformCount}/${cache.transformCount * 2}` : `${globalTransformCount}`;
852
+ const chunksStr = hasCache ? `${globalChunkCount}/${cache.chunkCount * 2}` : `${globalChunkCount}`;
853
+ renderProgress(
854
+ globalLastPercent,
855
+ transformsStr,
856
+ chunksStr,
857
+ width,
858
+ complete,
859
+ incomplete
860
+ );
861
+ return null;
862
+ },
863
+ buildEnd(err) {
864
+ if (err) {
865
+ globalHasError = true;
866
+ }
867
+ },
868
+ closeBundle() {
869
+ setTimeout(() => {
870
+ if (!globalIsBuilding) return;
871
+ const elapsed = ((Date.now() - globalStartTime) / 1e3).toFixed(1);
872
+ const totalTransforms = hasCache ? cache.transformCount * 2 : globalTransformCount;
873
+ const totalChunks = hasCache ? cache.chunkCount * 2 : globalChunkCount;
874
+ const transformsStr = `${totalTransforms}/${totalTransforms}`;
875
+ const chunksStr = `${totalChunks}/${totalChunks}`;
876
+ renderProgress(
877
+ 1,
878
+ transformsStr,
879
+ chunksStr,
880
+ width,
881
+ complete,
882
+ incomplete,
883
+ true
884
+ );
885
+ restoreOutput();
886
+ if (!globalHasError) {
887
+ setCacheData({
888
+ transformCount: Math.floor(globalTransformCount / 2),
889
+ chunkCount: Math.floor(globalChunkCount / 2)
890
+ });
891
+ console.log(`\u2705 \u6784\u5EFA\u6210\u529F\uFF01`);
892
+ console.log(` \u{1F4C1} \u8F93\u51FA\u76EE\u5F55: ${globalOutDir}`);
893
+ console.log(` \u23F1\uFE0F \u8017\u65F6: ${elapsed}s`);
894
+ } else {
895
+ console.log(`
896
+ \u274C \u6784\u5EFA\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u9519\u8BEF\u4FE1\u606F`);
897
+ }
898
+ globalIsBuilding = false;
899
+ }, 500);
900
+ }
901
+ };
902
+ }
903
+
904
+ // vitepress/config/index.ts
905
+ function loadTNotesConfig(rootPath) {
906
+ const configPath = path2.resolve(rootPath, ".tnotes.json");
907
+ const configContent = fs2.readFileSync(configPath, "utf-8");
908
+ return JSON.parse(configContent);
909
+ }
910
+ function defineNotesConfig(overrides = {}) {
911
+ const rootPath = process.cwd();
912
+ const config = loadTNotesConfig(rootPath);
913
+ const { repoName } = config;
914
+ const IGNORE_LIST = getIgnoreList(config);
915
+ const GITHUB_PAGE_URL = getGithubPageUrl(config);
916
+ const {
917
+ transformPageData: overrideTransformPageData,
918
+ vite: overrideVite,
919
+ ...restOverrides
920
+ } = overrides;
921
+ return defineConfig({
922
+ appearance: "dark",
923
+ base: "/" + repoName + "/",
924
+ cleanUrls: true,
925
+ description: repoName,
926
+ head: getHeadConfig(config, GITHUB_PAGE_URL),
927
+ ignoreDeadLinks: true,
928
+ lang: "zh-Hans",
929
+ lastUpdated: false,
930
+ markdown: getMarkdownConfig(),
931
+ sitemap: {
932
+ hostname: GITHUB_PAGE_URL,
933
+ lastmodDateOnly: false
934
+ },
935
+ themeConfig: getThemeConfig(config),
936
+ title: repoName,
937
+ srcExclude: IGNORE_LIST,
938
+ vite: {
939
+ plugins: [
940
+ buildProgressPlugin(),
941
+ updateConfigPlugin(),
942
+ renameNotePlugin(),
943
+ getNoteByConfigIdPlugin(),
944
+ ...overrideVite?.plugins || []
945
+ ],
946
+ server: {
947
+ watch: {
948
+ ignored: IGNORE_LIST
949
+ },
950
+ ...overrideVite?.server
951
+ },
952
+ css: {
953
+ preprocessorOptions: {
954
+ scss: {
955
+ silenceDeprecations: ["legacy-js-api"]
956
+ }
957
+ },
958
+ ...overrideVite?.css
959
+ },
960
+ build: {
961
+ chunkSizeWarningLimit: 1e3,
962
+ ...overrideVite?.build
963
+ },
964
+ define: {
965
+ __TNOTES_REPO_NAME__: JSON.stringify(config.repoName),
966
+ __TNOTES_AUTHOR__: JSON.stringify(config.author),
967
+ __TNOTES_IGNORE_DIRS__: JSON.stringify(config.ignore_dirs),
968
+ __TNOTES_ROOT_ITEM__: JSON.stringify(config.root_item),
969
+ ...overrideVite?.define
970
+ }
971
+ },
972
+ transformPageData(pageData, ctx) {
973
+ if (/^notes\/\d{4}/.test(pageData.relativePath)) {
974
+ const fullPath = path2.resolve(rootPath, pageData.relativePath);
975
+ try {
976
+ pageData.frontmatter.rawContent = fs2.readFileSync(fullPath, "utf-8");
977
+ } catch {
978
+ pageData.frontmatter.rawContent = null;
979
+ }
980
+ }
981
+ if (typeof overrideTransformPageData === "function") {
982
+ return overrideTransformPageData(pageData, ctx);
983
+ }
984
+ },
985
+ router: {
986
+ prefetchLinks: false
987
+ },
988
+ ...restOverrides
989
+ });
990
+ }
991
+ export {
992
+ defineNotesConfig
993
+ };