@tnotesjs/core 0.7.0 → 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.
- package/commands/BaseCommand.ts +58 -0
- package/commands/build/BuildCommand.ts +25 -0
- package/commands/build/PreviewCommand.ts +29 -0
- package/commands/build/index.ts +8 -0
- package/commands/dev/DevCommand.ts +75 -0
- package/commands/dev/index.ts +7 -0
- package/commands/git/PullCommand.ts +25 -0
- package/commands/git/PushCommand.ts +64 -0
- package/commands/git/index.ts +8 -0
- package/commands/index.ts +11 -0
- package/commands/init-sub-repo/InitSubRepoCommand.ts +206 -0
- package/commands/init-sub-repo/index.ts +1 -0
- package/commands/misc/HelpCommand.ts +104 -0
- package/commands/misc/index.ts +7 -0
- package/commands/models.ts +87 -0
- package/commands/note/CreateNoteCommand.ts +160 -0
- package/commands/note/RenameNoteCommand.ts +147 -0
- package/commands/note/UpdateNoteConfigCommand.ts +78 -0
- package/commands/note/index.ts +9 -0
- package/commands/registry.ts +47 -0
- package/commands/update/UpdateCommand.ts +219 -0
- package/commands/update/index.ts +7 -0
- package/commands/update-completed-count/UpdateCompletedCountCommand.ts +208 -0
- package/commands/update-completed-count/index.ts +5 -0
- package/dist/markdown/index.cjs +6 -9
- package/dist/markdown/index.js +6 -9
- package/dist/vitepress/config/index.cjs +214 -58
- package/dist/vitepress/config/index.js +208 -52
- package/markdown/components.ts +86 -0
- package/markdown/index.ts +17 -0
- package/markdown/noteFormatter.test.ts +44 -0
- package/markdown/noteFormatter.ts +237 -0
- package/package.json +7 -3
- package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +9 -18
- package/vitepress/components/EnWordList/EnWordList.vue +16 -662
- package/vitepress/components/Footprints/Footprints.vue +15 -537
- package/vitepress/components/Mermaid/Mermaid.vue +13 -588
- package/vitepress/components/MindmapPreview/MindmapPreview.vue +12 -434
- package/vitepress/components/MindmapPreview/markdown.ts +1 -1
- package/vitepress/components/NotesTable/NotesTable.vue +11 -130
- package/vitepress/configs/markdown.config.ts +170 -26
- package/vitepress/theme/index.ts +9 -13
- package/vitepress/theme/styles/base.scss +15 -0
- package/workspace/atomic.ts +113 -0
- package/workspace/errors.ts +27 -0
- package/workspace/index.ts +40 -0
- package/workspace/mutationQueue.ts +28 -0
- package/workspace/paths.ts +64 -0
- package/workspace/reconcile.test.ts +300 -0
- package/workspace/reconcile.ts +95 -0
- package/workspace/scanner.ts +292 -0
- package/workspace/types.ts +224 -0
- package/workspace/workspace.test.ts +333 -0
- package/workspace/workspace.ts +1020 -0
- package/vitepress/components/EnWordList/RightClickMenu.vue +0 -93
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
} from "../../chunk-GWG75A5M.js";
|
|
18
18
|
|
|
19
19
|
// vitepress/config/index.ts
|
|
20
|
-
import
|
|
21
|
-
import
|
|
20
|
+
import fs2 from "fs";
|
|
21
|
+
import path4 from "path";
|
|
22
22
|
import { defineConfig } from "vitepress";
|
|
23
23
|
|
|
24
24
|
// vitepress/config/dependencyOptimization.ts
|
|
@@ -63,11 +63,9 @@ function getHeadConfig(config, githubPageUrl) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// vitepress/configs/markdown.config.ts
|
|
66
|
-
import fs from "fs";
|
|
67
66
|
import markdownItContainer from "markdown-it-container";
|
|
68
67
|
import mila from "markdown-it-link-attributes";
|
|
69
68
|
import markdownItTaskLists from "markdown-it-task-lists";
|
|
70
|
-
import path from "path";
|
|
71
69
|
|
|
72
70
|
// vitepress/components/MindmapPreview/markdown.ts
|
|
73
71
|
function cleanHeadingText(value) {
|
|
@@ -90,19 +88,6 @@ function parseMindmapFence(openLine) {
|
|
|
90
88
|
}
|
|
91
89
|
return options;
|
|
92
90
|
}
|
|
93
|
-
function parseMindmapReference(line) {
|
|
94
|
-
const match = line.trim().match(/^<<<\s+(.+?)\s*$/);
|
|
95
|
-
if (!match) return null;
|
|
96
|
-
let rest = match[1].trim();
|
|
97
|
-
let title;
|
|
98
|
-
const titleMatch = rest.match(/\s+\[([^\]]+)\]\s*$/);
|
|
99
|
-
if (titleMatch) {
|
|
100
|
-
title = cleanHeadingText(titleMatch[1]) || void 0;
|
|
101
|
-
rest = rest.slice(0, titleMatch.index).trim();
|
|
102
|
-
}
|
|
103
|
-
const path6 = rest.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, "$1$2").trim();
|
|
104
|
-
return path6 ? { path: path6, title } : null;
|
|
105
|
-
}
|
|
106
91
|
function normalizeMindmapMarkdown(source, options = {}) {
|
|
107
92
|
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
108
93
|
let existingTitle = "";
|
|
@@ -125,6 +110,79 @@ ${body.join("\n")}
|
|
|
125
110
|
`;
|
|
126
111
|
}
|
|
127
112
|
|
|
113
|
+
// node_modules/.pnpm/@tnotesjs+ui@0.1.1_vue@3.5.31_typescript@5.9.3_/node_modules/@tnotesjs/ui/src/components/Footprints/parse.ts
|
|
114
|
+
function parseFootprintsDatetime(meta) {
|
|
115
|
+
const m = meta.trim().match(
|
|
116
|
+
/^(\d{4})-(\d{1,2})-(\d{1,2})(?:\s+(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?)?$/
|
|
117
|
+
);
|
|
118
|
+
if (!m) return [];
|
|
119
|
+
const parts = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
120
|
+
if (m[4] !== void 0) {
|
|
121
|
+
parts.push(Number(m[4]), Number(m[5]));
|
|
122
|
+
if (m[6] !== void 0) parts.push(Number(m[6]));
|
|
123
|
+
}
|
|
124
|
+
return parts;
|
|
125
|
+
}
|
|
126
|
+
function parseFootprintsSource(source) {
|
|
127
|
+
const normalized = source.replace(/\r\n?/g, "\n");
|
|
128
|
+
const lines = normalized.split("\n");
|
|
129
|
+
const open = lines[0]?.match(/^ {0,3}:{3,}\s*footprints(?:\s+(.*))?$/i);
|
|
130
|
+
const times = parseFootprintsDatetime((open?.[1] ?? "").trim());
|
|
131
|
+
let end = lines.length - 1;
|
|
132
|
+
while (end > 0 && !/^ {0,3}:{3,}\s*$/.test(lines[end])) end -= 1;
|
|
133
|
+
const bodyLines = lines.slice(1, end);
|
|
134
|
+
const paragraphs = [];
|
|
135
|
+
const images = [];
|
|
136
|
+
let otherInfo = "";
|
|
137
|
+
let buf = [];
|
|
138
|
+
let inOther = false;
|
|
139
|
+
const flush = () => {
|
|
140
|
+
const text = buf.join("\n").trim();
|
|
141
|
+
buf = [];
|
|
142
|
+
if (!text) return;
|
|
143
|
+
if (inOther) {
|
|
144
|
+
otherInfo = otherInfo ? `${otherInfo}
|
|
145
|
+
${text}` : text;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const imgOnly = text.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
|
|
149
|
+
if (imgOnly) {
|
|
150
|
+
images.push(imgOnly[2]);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const parts = text.split("\n");
|
|
154
|
+
let allImg = true;
|
|
155
|
+
const blockImgs = [];
|
|
156
|
+
for (const line of parts) {
|
|
157
|
+
const m = line.trim().match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
|
|
158
|
+
if (m) blockImgs.push(m[2]);
|
|
159
|
+
else if (line.trim()) {
|
|
160
|
+
allImg = false;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (allImg && blockImgs.length) {
|
|
165
|
+
images.push(...blockImgs);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
paragraphs.push(text);
|
|
169
|
+
};
|
|
170
|
+
for (const line of bodyLines) {
|
|
171
|
+
if (line.trim() === "---") {
|
|
172
|
+
flush();
|
|
173
|
+
inOther = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (line.trim() === "") {
|
|
177
|
+
flush();
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
buf.push(line);
|
|
181
|
+
}
|
|
182
|
+
flush();
|
|
183
|
+
return { times, paragraphs, images, otherInfo };
|
|
184
|
+
}
|
|
185
|
+
|
|
128
186
|
// vitepress/configs/markdown.config.ts
|
|
129
187
|
function esc(s = "") {
|
|
130
188
|
return s.replace(
|
|
@@ -142,11 +200,14 @@ var simpleMermaidMarkdown = (md) => {
|
|
|
142
200
|
const fence = md.renderer.rules.fence ? md.renderer.rules.fence.bind(md.renderer.rules) : () => "";
|
|
143
201
|
md.renderer.rules.fence = (tokens, index, options, env, slf) => {
|
|
144
202
|
const token = tokens[index];
|
|
145
|
-
|
|
203
|
+
const parts = token.info.trim().split(/\s+/).filter(Boolean);
|
|
204
|
+
if (parts[0] === "mermaid") {
|
|
146
205
|
try {
|
|
206
|
+
const centered = parts.slice(1).some((part) => part.toLowerCase() === "center");
|
|
147
207
|
const key = `mermaid-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
148
208
|
const content = token.content;
|
|
149
|
-
|
|
209
|
+
const centerAttr = centered ? ' :center="true"' : "";
|
|
210
|
+
return `<Mermaid id="${key}" graph="${encodeURIComponent(content)}"${centerAttr} />`;
|
|
150
211
|
} catch (err) {
|
|
151
212
|
return `<pre>${err}</pre>`;
|
|
152
213
|
}
|
|
@@ -164,28 +225,14 @@ function configureMindmapFence(md) {
|
|
|
164
225
|
const info = token.info.trim();
|
|
165
226
|
const fenceOptions = parseMindmapFence(info);
|
|
166
227
|
if (!fenceOptions) return fence(tokens, index, options, env, slf);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const reference = parseMindmapReference(firstNonEmptyLine);
|
|
170
|
-
if (reference) {
|
|
171
|
-
const possibleRel = env?.relativePath || env?.path || env?.filePath || env?.file || "";
|
|
172
|
-
const refFullPath = path.isAbsolute(reference.path) ? reference.path : path.resolve(process.cwd(), possibleRel ? path.dirname(possibleRel) : "", reference.path);
|
|
173
|
-
try {
|
|
174
|
-
content = fs.readFileSync(refFullPath, "utf8");
|
|
175
|
-
} catch (error) {
|
|
176
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
177
|
-
content = `- Failed to load referenced file: ${reference.path}
|
|
178
|
-
- Error: ${message}`;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
content = normalizeMindmapMarkdown(content, {
|
|
182
|
-
title: fenceOptions.title || reference?.title
|
|
228
|
+
const content = normalizeMindmapMarkdown(token.content, {
|
|
229
|
+
title: fenceOptions.title
|
|
183
230
|
});
|
|
184
231
|
const props = [
|
|
185
232
|
`content="${encodeURIComponent(content.trim())}"`,
|
|
186
233
|
fenceOptions.initialExpandLevel === void 0 ? "" : `:initialExpandLevel="${fenceOptions.initialExpandLevel}"`
|
|
187
234
|
].filter(Boolean).join(" ");
|
|
188
|
-
return `<
|
|
235
|
+
return `<Mindmap ${props}></Mindmap>
|
|
189
236
|
`;
|
|
190
237
|
};
|
|
191
238
|
}
|
|
@@ -247,6 +294,114 @@ function configureSwiperContainer(md) {
|
|
|
247
294
|
}
|
|
248
295
|
});
|
|
249
296
|
}
|
|
297
|
+
function escapeHtmlText(s) {
|
|
298
|
+
return s.replace(
|
|
299
|
+
/[&<>"']/g,
|
|
300
|
+
(ch) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ch]
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function extractFootprintsPayloadFromTokens(tokens, idx) {
|
|
304
|
+
const meta = String(tokens[idx].info || "").trim().replace(/^footprints\s*/i, "");
|
|
305
|
+
const times = parseFootprintsDatetime(meta);
|
|
306
|
+
const paragraphs = [];
|
|
307
|
+
const images = [];
|
|
308
|
+
let otherInfo = "";
|
|
309
|
+
let inOther = false;
|
|
310
|
+
for (let i = idx + 1; i < tokens.length; i++) {
|
|
311
|
+
const t = tokens[i];
|
|
312
|
+
if (t.type === "container_footprints_close") break;
|
|
313
|
+
if (t.type !== "inline") continue;
|
|
314
|
+
const childImgs = [];
|
|
315
|
+
if (Array.isArray(t.children)) {
|
|
316
|
+
for (const c of t.children) {
|
|
317
|
+
if (c.type === "image") {
|
|
318
|
+
const src = c.attrGet?.("src") || c.attrs?.find((a) => a[0] === "src")?.[1];
|
|
319
|
+
if (src) childImgs.push(src);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const content = String(t.content || "").trim();
|
|
324
|
+
if (content === "---") {
|
|
325
|
+
inOther = true;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (childImgs.length) {
|
|
329
|
+
const withoutImgs = content.replace(/!\[[^\]]*\]\([^)]+\)/g, "").trim();
|
|
330
|
+
if (!withoutImgs) {
|
|
331
|
+
images.push(...childImgs);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const imgOnly = content.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)$/);
|
|
336
|
+
if (imgOnly) {
|
|
337
|
+
images.push(imgOnly[2]);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (!content) continue;
|
|
341
|
+
if (inOther) otherInfo = otherInfo ? `${otherInfo}
|
|
342
|
+
${content}` : content;
|
|
343
|
+
else paragraphs.push(content);
|
|
344
|
+
}
|
|
345
|
+
return { times, paragraphs, images, otherInfo };
|
|
346
|
+
}
|
|
347
|
+
function configureFootprintsContainer(md) {
|
|
348
|
+
md.use(markdownItContainer, "footprints", {
|
|
349
|
+
validate: (params) => /^footprints(\s|$)/i.test(params.trim()),
|
|
350
|
+
render: (tokens, idx, _opts, env) => {
|
|
351
|
+
if (tokens[idx].nesting !== 1) return "";
|
|
352
|
+
let payload = extractFootprintsPayloadFromTokens(tokens, idx);
|
|
353
|
+
const startLine = tokens[idx].map?.[0] ?? 0;
|
|
354
|
+
let endLine = startLine;
|
|
355
|
+
for (let i = idx + 1; i < tokens.length; i++) {
|
|
356
|
+
if (tokens[i].type === "container_footprints_close") {
|
|
357
|
+
endLine = tokens[i].map?.[0] ?? endLine;
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const raw = String(env?.src ?? env?.source ?? "");
|
|
362
|
+
if (raw && endLine > startLine) {
|
|
363
|
+
const slice = raw.split(/\r?\n/).slice(startLine, endLine + 1).join("\n");
|
|
364
|
+
const fromSource = parseFootprintsSource(
|
|
365
|
+
slice.includes(":::") ? slice : `::: ${tokens[idx].info}
|
|
366
|
+
${slice}
|
|
367
|
+
:::`
|
|
368
|
+
);
|
|
369
|
+
if (!payload.paragraphs.length && fromSource.paragraphs.length) {
|
|
370
|
+
payload = { ...payload, paragraphs: fromSource.paragraphs };
|
|
371
|
+
}
|
|
372
|
+
if (!payload.images.length && fromSource.images.length) {
|
|
373
|
+
payload = { ...payload, images: fromSource.images };
|
|
374
|
+
}
|
|
375
|
+
if (!payload.otherInfo && fromSource.otherInfo) {
|
|
376
|
+
payload = { ...payload, otherInfo: fromSource.otherInfo };
|
|
377
|
+
}
|
|
378
|
+
if (!payload.times.length && fromSource.times.length) {
|
|
379
|
+
payload = { ...payload, times: fromSource.times };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (let i = idx + 1; i < tokens.length; i++) {
|
|
383
|
+
if (tokens[i].type === "container_footprints_close") break;
|
|
384
|
+
tokens[i].hidden = true;
|
|
385
|
+
tokens[i].content = "";
|
|
386
|
+
if (Array.isArray(tokens[i].children)) {
|
|
387
|
+
for (const child of tokens[i].children) {
|
|
388
|
+
child.hidden = true;
|
|
389
|
+
child.content = "";
|
|
390
|
+
}
|
|
391
|
+
tokens[i].children = [];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const enc = (value) => encodeURIComponent(JSON.stringify(value)).replace(/'/g, "%27");
|
|
395
|
+
const bindExpr = (value) => "JSON.parse(decodeURIComponent('" + enc(value) + "'))";
|
|
396
|
+
const imageSlot = payload.images.map((src, i) => {
|
|
397
|
+
return '<img src="' + escapeHtmlText(src) + '" @click="openModal(' + String(i) + ')" />';
|
|
398
|
+
}).join("\n");
|
|
399
|
+
const openTag = '<Footprints :times="' + bindExpr(payload.times) + '" :paragraphs="' + bindExpr(payload.paragraphs) + '" :other-info="' + bindExpr(payload.otherInfo) + '">';
|
|
400
|
+
if (!payload.images.length) return openTag + "</Footprints>\n";
|
|
401
|
+
return openTag + '\n<template #image-list="{ openModal }">\n' + imageSlot + "\n</template>\n</Footprints>\n";
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
250
405
|
function getMarkdownConfig() {
|
|
251
406
|
const markdown = {
|
|
252
407
|
lineNumbers: true,
|
|
@@ -266,6 +421,7 @@ function getMarkdownConfig() {
|
|
|
266
421
|
}
|
|
267
422
|
});
|
|
268
423
|
configureSwiperContainer(md);
|
|
424
|
+
configureFootprintsContainer(md);
|
|
269
425
|
},
|
|
270
426
|
anchor: {
|
|
271
427
|
slugify: generateAnchor
|
|
@@ -753,19 +909,19 @@ function getNoteByConfigIdPlugin() {
|
|
|
753
909
|
}
|
|
754
910
|
|
|
755
911
|
// vitepress/plugins/localSearchReindexPlugin.ts
|
|
756
|
-
import
|
|
912
|
+
import path3 from "path";
|
|
757
913
|
|
|
758
914
|
// vitepress/plugins/localSearchIndexBuilder.ts
|
|
759
915
|
import MiniSearch from "minisearch";
|
|
760
|
-
import
|
|
761
|
-
import
|
|
916
|
+
import fs from "fs/promises";
|
|
917
|
+
import path2 from "path";
|
|
762
918
|
import {
|
|
763
919
|
createMarkdownRenderer,
|
|
764
920
|
resolvePages
|
|
765
921
|
} from "vitepress";
|
|
766
922
|
|
|
767
923
|
// vitepress/plugins/localSearchReindexLogic.ts
|
|
768
|
-
import
|
|
924
|
+
import path from "path";
|
|
769
925
|
function slash(value) {
|
|
770
926
|
return value.replace(/\\/g, "/");
|
|
771
927
|
}
|
|
@@ -781,13 +937,13 @@ function isNoteReadmePath(filePath, ignoreDirNames = []) {
|
|
|
781
937
|
function getDocIdFromFile(input) {
|
|
782
938
|
const pathApi = /^[a-zA-Z]:[\\/]/.test(
|
|
783
939
|
input.absoluteOrRelativeFile || input.srcDir
|
|
784
|
-
) ?
|
|
940
|
+
) ? path.win32 : path;
|
|
785
941
|
const absoluteFile = pathApi.isAbsolute(input.absoluteOrRelativeFile) ? input.absoluteOrRelativeFile : pathApi.join(input.srcDir, input.absoluteOrRelativeFile);
|
|
786
942
|
let relFile = slash(pathApi.relative(input.srcDir, absoluteFile));
|
|
787
943
|
if (input.rewrites?.[relFile]) {
|
|
788
944
|
relFile = input.rewrites[relFile];
|
|
789
945
|
}
|
|
790
|
-
let id = slash(
|
|
946
|
+
let id = slash(path.posix.join(input.base, relFile));
|
|
791
947
|
id = id.replace(/(^|\/)index\.md$/, "$1");
|
|
792
948
|
id = id.replace(/\.md$/, input.cleanUrls ? "" : ".html");
|
|
793
949
|
return id;
|
|
@@ -852,9 +1008,9 @@ async function createLocalSearchMarkdownRenderer(siteConfig) {
|
|
|
852
1008
|
);
|
|
853
1009
|
}
|
|
854
1010
|
async function renderPageHtml(siteConfig, md, absoluteFile) {
|
|
855
|
-
if (!await
|
|
1011
|
+
if (!await fs.stat(absoluteFile).catch(() => null)) return "";
|
|
856
1012
|
const srcDir = siteConfig.srcDir;
|
|
857
|
-
const relativePath = slash2(
|
|
1013
|
+
const relativePath = slash2(path2.relative(srcDir, absoluteFile));
|
|
858
1014
|
const env = {
|
|
859
1015
|
path: absoluteFile,
|
|
860
1016
|
relativePath,
|
|
@@ -862,7 +1018,7 @@ async function renderPageHtml(siteConfig, md, absoluteFile) {
|
|
|
862
1018
|
frontmatter: {}
|
|
863
1019
|
};
|
|
864
1020
|
const options = siteConfig.site.themeConfig?.search?.options ?? {};
|
|
865
|
-
const mdRaw = await
|
|
1021
|
+
const mdRaw = await fs.readFile(absoluteFile, "utf-8");
|
|
866
1022
|
if (options._render) {
|
|
867
1023
|
return await options._render(mdRaw, env, md);
|
|
868
1024
|
}
|
|
@@ -870,7 +1026,7 @@ async function renderPageHtml(siteConfig, md, absoluteFile) {
|
|
|
870
1026
|
return env.frontmatter?.search === false ? "" : html;
|
|
871
1027
|
}
|
|
872
1028
|
async function indexPage(siteConfig, md, page, index) {
|
|
873
|
-
const absoluteFile =
|
|
1029
|
+
const absoluteFile = path2.join(siteConfig.srcDir, page);
|
|
874
1030
|
const fileId = getDocIdFromFile({
|
|
875
1031
|
srcDir: siteConfig.srcDir,
|
|
876
1032
|
base: siteConfig.site.base,
|
|
@@ -1160,7 +1316,7 @@ function localSearchReindexPlugin() {
|
|
|
1160
1316
|
const onWatcherEvent = (absoluteFile, event) => {
|
|
1161
1317
|
if (!isNoteReadmePath(absoluteFile, ignoreDirNames)) return;
|
|
1162
1318
|
scheduleNoteSearchReindex(
|
|
1163
|
-
`watcher:${event}:${
|
|
1319
|
+
`watcher:${event}:${path3.basename(absoluteFile)}`
|
|
1164
1320
|
);
|
|
1165
1321
|
};
|
|
1166
1322
|
server.watcher.on("add", (file) => onWatcherEvent(file, "add"));
|
|
@@ -1314,14 +1470,14 @@ function isNoteInSubtree(snapshot, lineIndex, noteIndex) {
|
|
|
1314
1470
|
}
|
|
1315
1471
|
function folderPathAtLine(snapshot, lineIndex) {
|
|
1316
1472
|
const result = [];
|
|
1317
|
-
const walk = (nodes,
|
|
1473
|
+
const walk = (nodes, path5) => {
|
|
1318
1474
|
for (const node of nodes) {
|
|
1319
1475
|
if (node.kind === "folder") {
|
|
1320
1476
|
if (node.tocLineIndex === lineIndex) {
|
|
1321
|
-
result.push(...[...
|
|
1477
|
+
result.push(...[...path5, node.title]);
|
|
1322
1478
|
return true;
|
|
1323
1479
|
}
|
|
1324
|
-
if (walk(node.children, [...
|
|
1480
|
+
if (walk(node.children, [...path5, node.title])) return true;
|
|
1325
1481
|
}
|
|
1326
1482
|
}
|
|
1327
1483
|
return false;
|
|
@@ -1712,9 +1868,9 @@ function defineNotesConfig(overrides = {}) {
|
|
|
1712
1868
|
},
|
|
1713
1869
|
transformPageData(pageData, ctx) {
|
|
1714
1870
|
if (/^notes\/\d{4}/.test(pageData.relativePath)) {
|
|
1715
|
-
const fullPath =
|
|
1871
|
+
const fullPath = path4.resolve(rootPath, pageData.relativePath);
|
|
1716
1872
|
try {
|
|
1717
|
-
const raw =
|
|
1873
|
+
const raw = fs2.readFileSync(fullPath, "utf-8");
|
|
1718
1874
|
pageData.frontmatter.rawContent = Buffer.from(raw, "utf-8").toString(
|
|
1719
1875
|
"base64"
|
|
1720
1876
|
);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type TNotesComponentKind =
|
|
2
|
+
| 'block-component'
|
|
3
|
+
| 'inline-component'
|
|
4
|
+
| 'container'
|
|
5
|
+
| 'fenced-language'
|
|
6
|
+
|
|
7
|
+
export interface TNotesComponentDescriptor {
|
|
8
|
+
name: string
|
|
9
|
+
aliases?: string[]
|
|
10
|
+
kind: TNotesComponentKind
|
|
11
|
+
editable: 'visual' | 'source-only' | 'placeholder'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Runtime-neutral registry shared by Desk and other TNotes clients. It does not
|
|
16
|
+
* import Vue or VitePress and is therefore safe in Node/Electron workers.
|
|
17
|
+
*/
|
|
18
|
+
export const TNOTES_COMPONENTS: readonly TNotesComponentDescriptor[] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'BilibiliVideo',
|
|
21
|
+
kind: 'block-component',
|
|
22
|
+
editable: 'visual',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'WordList',
|
|
26
|
+
kind: 'block-component',
|
|
27
|
+
editable: 'visual',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Footprints',
|
|
31
|
+
kind: 'block-component',
|
|
32
|
+
editable: 'visual',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'NotesTable',
|
|
36
|
+
kind: 'block-component',
|
|
37
|
+
editable: 'visual',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Tooltip',
|
|
41
|
+
kind: 'inline-component',
|
|
42
|
+
editable: 'visual',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'Mindmap',
|
|
46
|
+
aliases: ['MindmapPreview'],
|
|
47
|
+
kind: 'block-component',
|
|
48
|
+
editable: 'visual',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Mermaid',
|
|
52
|
+
kind: 'block-component',
|
|
53
|
+
editable: 'visual',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Discussions',
|
|
57
|
+
kind: 'block-component',
|
|
58
|
+
editable: 'placeholder',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'SidebarCard',
|
|
62
|
+
kind: 'block-component',
|
|
63
|
+
editable: 'placeholder',
|
|
64
|
+
},
|
|
65
|
+
{ name: 'swiper', kind: 'container', editable: 'visual' },
|
|
66
|
+
{ name: 'code-group', kind: 'container', editable: 'visual' },
|
|
67
|
+
{ name: 'footprints', kind: 'container', editable: 'visual' },
|
|
68
|
+
{ name: 'details', kind: 'container', editable: 'visual' },
|
|
69
|
+
{ name: 'info', kind: 'container', editable: 'visual' },
|
|
70
|
+
{ name: 'tip', kind: 'container', editable: 'visual' },
|
|
71
|
+
{ name: 'warning', kind: 'container', editable: 'visual' },
|
|
72
|
+
{ name: 'danger', kind: 'container', editable: 'visual' },
|
|
73
|
+
{ name: 'mermaid', kind: 'fenced-language', editable: 'visual' },
|
|
74
|
+
{ name: 'mindmap', kind: 'fenced-language', editable: 'visual' },
|
|
75
|
+
] as const
|
|
76
|
+
|
|
77
|
+
export function findTNotesComponent(
|
|
78
|
+
name: string,
|
|
79
|
+
): TNotesComponentDescriptor | undefined {
|
|
80
|
+
const normalized = name.toLowerCase()
|
|
81
|
+
return TNOTES_COMPONENTS.find(
|
|
82
|
+
(component) =>
|
|
83
|
+
component.name.toLowerCase() === normalized ||
|
|
84
|
+
component.aliases?.some((alias) => alias.toLowerCase() === normalized),
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
NOTE_TOC_END_TAG,
|
|
3
|
+
NOTE_TOC_START_TAG,
|
|
4
|
+
formatTNotesNote,
|
|
5
|
+
} from './noteFormatter'
|
|
6
|
+
export type {
|
|
7
|
+
FormatTNotesNoteInput,
|
|
8
|
+
FormatTNotesNoteResult,
|
|
9
|
+
} from './noteFormatter'
|
|
10
|
+
export {
|
|
11
|
+
TNOTES_COMPONENTS,
|
|
12
|
+
findTNotesComponent,
|
|
13
|
+
} from './components'
|
|
14
|
+
export type {
|
|
15
|
+
TNotesComponentDescriptor,
|
|
16
|
+
TNotesComponentKind,
|
|
17
|
+
} from './components'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
findTNotesComponent,
|
|
5
|
+
formatTNotesNote,
|
|
6
|
+
TNOTES_COMPONENTS,
|
|
7
|
+
} from './index'
|
|
8
|
+
|
|
9
|
+
describe('TNotes Markdown contract', () => {
|
|
10
|
+
it('exposes runtime-neutral component descriptors', () => {
|
|
11
|
+
expect(findTNotesComponent('NotesTable')?.name).toBe('NotesTable')
|
|
12
|
+
expect(findTNotesComponent('WordList')?.name).toBe('WordList')
|
|
13
|
+
expect(findTNotesComponent('BilibiliVideo')?.name).toBe('BilibiliVideo')
|
|
14
|
+
expect(findTNotesComponent('N')).toBeUndefined()
|
|
15
|
+
expect(findTNotesComponent('SWIPER')?.kind).toBe('container')
|
|
16
|
+
expect(findTNotesComponent('Mermaid')?.editable).toBe('visual')
|
|
17
|
+
expect(TNOTES_COMPONENTS.some((item) => item.name === 'markmap')).toBe(false)
|
|
18
|
+
expect(TNOTES_COMPONENTS.some((item) => item.name === 'mindmap')).toBe(true)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('restores missing generated markers without interpreting fenced headings', async () => {
|
|
22
|
+
const result = await formatTNotesNote({
|
|
23
|
+
content: '## Heading\n\n~~~md\n### source\n~~~\n',
|
|
24
|
+
noteIndex: '0042',
|
|
25
|
+
title: 'Answer',
|
|
26
|
+
repoOwner: 'tnotesjs',
|
|
27
|
+
repoName: 'TNotes.fixture',
|
|
28
|
+
noteConfig: {
|
|
29
|
+
id: 'answer',
|
|
30
|
+
bilibili: [],
|
|
31
|
+
tnotes: [],
|
|
32
|
+
yuque: [],
|
|
33
|
+
done: false,
|
|
34
|
+
enableDiscussions: false,
|
|
35
|
+
},
|
|
36
|
+
prettier: false,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(result.content).toContain('<!-- region:toc -->')
|
|
40
|
+
expect(result.content).toContain('<!-- endregion:toc -->')
|
|
41
|
+
expect(result.generatedToc).toEqual(['- [1. Heading](#1-heading)'])
|
|
42
|
+
expect(result.content).toContain('~~~md\n### source\n~~~')
|
|
43
|
+
})
|
|
44
|
+
})
|