@tnotesjs/core 0.2.2 → 0.4.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/README.md +0 -1
- package/config/defaultConfig.ts +33 -4
- package/config/index.ts +6 -0
- package/core/NoteIndexCache.ts +7 -1
- package/core/NoteManager.ts +18 -30
- package/dist/{chunk-5Y5IYS6C.js → chunk-UJG3UU5X.js} +21 -5
- package/dist/{chunk-5QN44ES5.js → chunk-XCWFZLER.js} +48 -368
- package/dist/cli/index.js +26 -78
- package/dist/index.js +1 -1
- package/dist/vitepress/config/index.js +159 -18
- package/package.json +3 -6
- package/services/index.ts +0 -1
- package/services/init-sub-repo/initSubRepoLogic.ts +0 -1
- package/services/note/service.ts +0 -1
- package/types/config.ts +0 -3
- package/types/note.ts +0 -2
- package/vitepress/assets/icons/icon__cursor.svg +4 -0
- package/vitepress/assets/icons/index.ts +1 -0
- package/vitepress/components/Layout/AboutPanel.vue +0 -24
- package/vitepress/components/Layout/DocBeforeControls.vue +6 -14
- package/vitepress/components/Layout/DocFooter.vue +3 -3
- package/vitepress/components/Layout/Layout.vue +0 -30
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +20 -11
- package/vitepress/components/Layout/homeReadme.data.ts +0 -48
- package/vitepress/components/MindmapPreview/InlineRuns.ts +25 -0
- package/vitepress/components/MindmapPreview/MindmapOutlineNode.vue +62 -0
- package/vitepress/components/MindmapPreview/MindmapPreview.vue +287 -0
- package/vitepress/components/MindmapPreview/compat.test.ts +93 -0
- package/vitepress/components/MindmapPreview/compat.ts +128 -0
- package/vitepress/components/MindmapPreview/expandLevel.test.ts +40 -0
- package/vitepress/components/MindmapPreview/expandLevel.ts +28 -0
- package/vitepress/components/MindmapPreview/legacyCorpus.test.ts +57 -0
- package/vitepress/components/Settings/Settings.vue +76 -73
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +16 -6
- package/vitepress/components/SidebarCard/SidebarCard.vue +21 -27
- package/vitepress/components/composables/useLocalIde.ts +83 -0
- package/vitepress/components/constants.ts +5 -6
- package/vitepress/components/utils/vscodePaths.test.ts +22 -0
- package/vitepress/components/utils/vscodePaths.ts +24 -2
- package/vitepress/configs/markdown.config.ts +77 -20
- package/vitepress/theme/index.ts +4 -2
- package/vitepress/theme/styles/doc-layout.scss +4 -4
- package/vitepress/theme/styles/index.scss +0 -1
- package/services/timestamp/index.ts +0 -7
- package/services/timestamp/service.ts +0 -465
- package/vitepress/components/MarkMap/MarkMap.vue +0 -625
- package/vitepress/theme/styles/components/markmap.scss +0 -122
package/dist/cli/index.js
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
ROOT_TOC_PATH,
|
|
20
20
|
ReadmeService,
|
|
21
21
|
RenameNoteCommand,
|
|
22
|
-
TimestampService,
|
|
23
22
|
TocService,
|
|
24
23
|
UpdateNoteConfigCommand,
|
|
25
24
|
VitepressService,
|
|
@@ -42,11 +41,12 @@ import {
|
|
|
42
41
|
runCommand,
|
|
43
42
|
validatePort,
|
|
44
43
|
validateTopic
|
|
45
|
-
} from "../chunk-
|
|
44
|
+
} from "../chunk-XCWFZLER.js";
|
|
46
45
|
import {
|
|
47
46
|
ConfigManager,
|
|
48
|
-
getConfigManager
|
|
49
|
-
|
|
47
|
+
getConfigManager,
|
|
48
|
+
stripDeprecatedRootItemFields
|
|
49
|
+
} from "../chunk-UJG3UU5X.js";
|
|
50
50
|
|
|
51
51
|
// commands/update/UpdateCommand.ts
|
|
52
52
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -121,6 +121,9 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
121
121
|
completed_notes_count: fixed
|
|
122
122
|
};
|
|
123
123
|
delete config.root_item.completed_notes_count_last_month;
|
|
124
|
+
stripDeprecatedRootItemFields(
|
|
125
|
+
config.root_item
|
|
126
|
+
);
|
|
124
127
|
writeFileSync(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
125
128
|
if (!this.quiet) {
|
|
126
129
|
const filledCount = Object.keys(fixed).length - _countsBeforeFix;
|
|
@@ -192,7 +195,6 @@ var PushCommand = class extends BaseCommand {
|
|
|
192
195
|
super("push");
|
|
193
196
|
this.gitManager = new GitManager(ROOT_DIR_PATH);
|
|
194
197
|
this.gitService = new GitService();
|
|
195
|
-
this.timestampService = new TimestampService();
|
|
196
198
|
}
|
|
197
199
|
async run() {
|
|
198
200
|
try {
|
|
@@ -214,18 +216,6 @@ var PushCommand = class extends BaseCommand {
|
|
|
214
216
|
const commitMessage = this.gitService.generateCommitMessage();
|
|
215
217
|
await runCommand("git add -A", ROOT_DIR_PATH);
|
|
216
218
|
await runCommand(`git commit -m "${commitMessage}"`, ROOT_DIR_PATH);
|
|
217
|
-
this.logger.info("\u4FEE\u590D\u7B14\u8BB0\u65F6\u95F4\u6233...");
|
|
218
|
-
await this.timestampService.fixAllTimestamps(false);
|
|
219
|
-
const afterFixStatus = await runCommand(
|
|
220
|
-
"git -c core.quotePath=false status --porcelain",
|
|
221
|
-
ROOT_DIR_PATH
|
|
222
|
-
);
|
|
223
|
-
const hasTimestampChanges = afterFixStatus.split(/\r?\n/).filter(Boolean).some((line) => line.includes(".tnotes.json"));
|
|
224
|
-
if (hasTimestampChanges) {
|
|
225
|
-
this.logger.info("\u5C06\u65F6\u95F4\u6233\u53D8\u66F4\u5408\u5E76\u5230 commit...");
|
|
226
|
-
await runCommand("git add -u", ROOT_DIR_PATH);
|
|
227
|
-
await runCommand("git commit --amend --no-edit", ROOT_DIR_PATH);
|
|
228
|
-
}
|
|
229
219
|
} else {
|
|
230
220
|
this.logger.info(`\u68C0\u6D4B\u5230 ${status.ahead} \u4E2A\u672A\u63A8\u9001\u7684\u63D0\u4EA4\uFF0C\u76F4\u63A5\u63A8\u9001...`);
|
|
231
221
|
}
|
|
@@ -539,37 +529,6 @@ var InitTocCommand = class extends BaseCommand {
|
|
|
539
529
|
}
|
|
540
530
|
};
|
|
541
531
|
|
|
542
|
-
// commands/maintenance/FixTimestampsCommand.ts
|
|
543
|
-
var FixTimestampsCommand = class extends BaseCommand {
|
|
544
|
-
constructor() {
|
|
545
|
-
super("fix-timestamps");
|
|
546
|
-
this.timestampService = new TimestampService();
|
|
547
|
-
}
|
|
548
|
-
async run() {
|
|
549
|
-
this.logger.info("\u5F00\u59CB\u4FEE\u590D\u6240\u6709\u7B14\u8BB0\u7684\u65F6\u95F4\u6233...");
|
|
550
|
-
this.logger.info("\u{1F4CC} \u6B64\u64CD\u4F5C\u4F1A\u5C06\u6240\u6709\u65F6\u95F4\u6233\u66F4\u65B0\u4E3A git \u771F\u5B9E\u65F6\u95F4");
|
|
551
|
-
this.logger.info("");
|
|
552
|
-
const result = await this.timestampService.fixAllTimestamps(true);
|
|
553
|
-
this.logger.info("");
|
|
554
|
-
this.logger.info("\u{1F4CA} \u4FEE\u590D\u7EDF\u8BA1:");
|
|
555
|
-
this.logger.info(
|
|
556
|
-
` - \u6839\u914D\u7F6E\u6587\u4EF6: ${result.rootConfigFixed ? "\u5DF2\u4FEE\u590D" : "\u65E0\u9700\u4FEE\u590D"}`
|
|
557
|
-
);
|
|
558
|
-
this.logger.info(` - \u603B\u7B14\u8BB0\u6570: ${result.total}`);
|
|
559
|
-
this.logger.info(` - \u5DF2\u4FEE\u590D: ${result.fixed}`);
|
|
560
|
-
this.logger.info(` - \u8DF3\u8FC7: ${result.skipped}`);
|
|
561
|
-
this.logger.info("");
|
|
562
|
-
if (result.fixed > 0 || result.rootConfigFixed) {
|
|
563
|
-
this.logger.success(
|
|
564
|
-
`\u2705 \u6210\u529F\u4FEE\u590D ${result.fixed} \u4E2A\u7B14\u8BB0${result.rootConfigFixed ? " + \u6839\u914D\u7F6E\u6587\u4EF6" : ""}\u7684\u65F6\u95F4\u6233\uFF01`
|
|
565
|
-
);
|
|
566
|
-
this.logger.info("\u{1F4A1} \u63D0\u793A: \u8FD0\u884C pnpm tn:push \u63D0\u4EA4\u66F4\u6539");
|
|
567
|
-
} else {
|
|
568
|
-
this.logger.success("\u2705 \u6240\u6709\u65F6\u95F4\u6233\u5747\u5DF2\u6B63\u786E\uFF01");
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
|
|
573
532
|
// commands/misc/HelpCommand.ts
|
|
574
533
|
var COMMAND_CATEGORIES = {
|
|
575
534
|
\u5F00\u53D1\u548C\u6784\u5EFA: [COMMAND_NAMES.DEV, COMMAND_NAMES.BUILD, COMMAND_NAMES.PREVIEW],
|
|
@@ -581,7 +540,7 @@ var COMMAND_CATEGORIES = {
|
|
|
581
540
|
"Git \u64CD\u4F5C": [COMMAND_NAMES.PUSH, COMMAND_NAMES.PULL],
|
|
582
541
|
"\u8FC1\u79FB\uFF08v0.2.x \u8FC7\u6E21\uFF09": [COMMAND_NAMES.INIT_TOC],
|
|
583
542
|
\u521D\u59CB\u5316: [COMMAND_NAMES.INIT_SUB_REPO],
|
|
584
|
-
\u5176\u4ED6: [COMMAND_NAMES.
|
|
543
|
+
\u5176\u4ED6: [COMMAND_NAMES.HELP]
|
|
585
544
|
};
|
|
586
545
|
var COMMAND_OPTIONS_INFO = {
|
|
587
546
|
[COMMAND_OPTIONS.QUIET]: {
|
|
@@ -783,13 +742,14 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
783
742
|
const configContent = readFileSync4(ROOT_CONFIG_PATH, "utf-8");
|
|
784
743
|
const config = JSON.parse(configContent);
|
|
785
744
|
this.logger.info("\u5F00\u59CB\u66F4\u65B0\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u5386\u53F2\u8BB0\u5F55...");
|
|
786
|
-
const completedNotesCountHistory = await this.getCompletedNotesCountHistory(
|
|
787
|
-
config.root_item.created_at ?? Date.now()
|
|
788
|
-
);
|
|
745
|
+
const completedNotesCountHistory = await this.getCompletedNotesCountHistory();
|
|
789
746
|
config.root_item = {
|
|
790
747
|
...config.root_item,
|
|
791
748
|
completed_notes_count: completedNotesCountHistory
|
|
792
749
|
};
|
|
750
|
+
stripDeprecatedRootItemFields(
|
|
751
|
+
config.root_item
|
|
752
|
+
);
|
|
793
753
|
writeFileSync3(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
794
754
|
const duration = Date.now() - startTime;
|
|
795
755
|
const monthKeys = Object.keys(completedNotesCountHistory);
|
|
@@ -812,10 +772,9 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
812
772
|
* 1. 计算最近12个月的范围(当前月份往前推11个月)
|
|
813
773
|
* 2. 遍历这12个月,从 Git 历史中读取 TOC.md(回退 README.md)
|
|
814
774
|
* 3. 解析获取完成笔记数量
|
|
815
|
-
* 4.
|
|
816
|
-
* 5. 返回对象 { '25.01': 0, '25.02': 1, ..., '25.12': 15 }
|
|
775
|
+
* 4. 返回对象 { '25.01': 0, '25.02': 1, ..., '25.12': 15 }
|
|
817
776
|
*/
|
|
818
|
-
async getCompletedNotesCountHistory(
|
|
777
|
+
async getCompletedNotesCountHistory() {
|
|
819
778
|
try {
|
|
820
779
|
const now = /* @__PURE__ */ new Date();
|
|
821
780
|
const currentYear = now.getFullYear();
|
|
@@ -826,9 +785,6 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
826
785
|
firstYear = currentYear - 1;
|
|
827
786
|
firstMonth = 12 + firstMonth;
|
|
828
787
|
}
|
|
829
|
-
const createdDate = new Date(createdAt);
|
|
830
|
-
const createdYear = createdDate.getFullYear();
|
|
831
|
-
const createdMonth = createdDate.getMonth();
|
|
832
788
|
const result = {};
|
|
833
789
|
let prevCount = 0;
|
|
834
790
|
for (let i = 0; i < 12; i++) {
|
|
@@ -837,25 +793,18 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
837
793
|
const yearShort = String(targetYear).slice(-2);
|
|
838
794
|
const monthStr = String(targetMonth + 1).padStart(2, "0");
|
|
839
795
|
const key = `${yearShort}.${monthStr}`;
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
result[key] = count;
|
|
853
|
-
prevCount = count;
|
|
854
|
-
this.logger.info(`\u2713 ${key}: ${count} \u7BC7`);
|
|
855
|
-
} catch (error) {
|
|
856
|
-
result[key] = prevCount;
|
|
857
|
-
this.logger.warn(`${key}: \u65E0\u6570\u636E\uFF0C\u4F7F\u7528 ${prevCount}\uFF08\u4E0A\u6708\u503C\uFF09`);
|
|
858
|
-
}
|
|
796
|
+
try {
|
|
797
|
+
const count = await this.getMonthCompletedCount(
|
|
798
|
+
targetYear,
|
|
799
|
+
targetMonth,
|
|
800
|
+
prevCount
|
|
801
|
+
);
|
|
802
|
+
result[key] = count;
|
|
803
|
+
prevCount = count;
|
|
804
|
+
this.logger.info(`\u2713 ${key}: ${count} \u7BC7`);
|
|
805
|
+
} catch (error) {
|
|
806
|
+
result[key] = prevCount;
|
|
807
|
+
this.logger.warn(`${key}: \u65E0\u6570\u636E\uFF0C\u4F7F\u7528 ${prevCount}\uFF08\u4E0A\u6708\u503C\uFF09`);
|
|
859
808
|
}
|
|
860
809
|
}
|
|
861
810
|
return result;
|
|
@@ -919,7 +868,6 @@ var commandFactories = {
|
|
|
919
868
|
"build": () => new BuildCommand(),
|
|
920
869
|
"create-notes": () => new CreateNoteCommand(),
|
|
921
870
|
"dev": () => new DevCommand(),
|
|
922
|
-
"fix-timestamps": () => new FixTimestampsCommand(),
|
|
923
871
|
"init-sub-repo": () => new InitSubRepoCommand(),
|
|
924
872
|
"init-toc": () => new InitTocCommand(),
|
|
925
873
|
"help": () => new HelpCommand(),
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
UpdateNoteConfigCommand,
|
|
8
8
|
generateAnchor,
|
|
9
9
|
logger
|
|
10
|
-
} from "../../chunk-
|
|
10
|
+
} from "../../chunk-XCWFZLER.js";
|
|
11
11
|
import {
|
|
12
12
|
ConfigManager
|
|
13
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-UJG3UU5X.js";
|
|
14
14
|
|
|
15
15
|
// vitepress/config/index.ts
|
|
16
16
|
import fs3 from "fs";
|
|
@@ -49,6 +49,103 @@ import markdownItContainer from "markdown-it-container";
|
|
|
49
49
|
import mila from "markdown-it-link-attributes";
|
|
50
50
|
import markdownItTaskLists from "markdown-it-task-lists";
|
|
51
51
|
import path from "path";
|
|
52
|
+
|
|
53
|
+
// vitepress/components/MindmapPreview/compat.ts
|
|
54
|
+
function cleanHeadingText(value) {
|
|
55
|
+
return value.trim().replace(/\s+#+\s*$/, "").trim();
|
|
56
|
+
}
|
|
57
|
+
function promoteLegacyRootList(body, rootTitle) {
|
|
58
|
+
const firstContentIndex = body.findIndex((line) => line.trim() !== "");
|
|
59
|
+
if (firstContentIndex < 0) return body;
|
|
60
|
+
const firstItem = body[firstContentIndex].match(/^[-+*]\s+(.+?)\s*$/);
|
|
61
|
+
if (!firstItem || cleanHeadingText(firstItem[1]) !== rootTitle) return body;
|
|
62
|
+
const descendants = body.slice(firstContentIndex + 1);
|
|
63
|
+
if (descendants.some((line) => line.trim() !== "" && !/^\s{2,}/.test(line))) return body;
|
|
64
|
+
return [
|
|
65
|
+
...body.slice(0, firstContentIndex),
|
|
66
|
+
...descendants.map((line) => line.replace(/^ {2}/, ""))
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
function parseMarkmapFence(openLine) {
|
|
70
|
+
const fenceBody = openLine.trim().replace(/^`+\s*/, "");
|
|
71
|
+
const nameMatch = fenceBody.match(/^(mindmap|markmap)(?=\s|\{|\[|$)/);
|
|
72
|
+
if (!nameMatch) return {};
|
|
73
|
+
let rest = fenceBody.slice(nameMatch[1].length).trim();
|
|
74
|
+
const options = {};
|
|
75
|
+
const titleMatch = rest.match(/\[([^\]]+)\]/);
|
|
76
|
+
if (titleMatch) {
|
|
77
|
+
options.title = titleMatch[1].trim();
|
|
78
|
+
rest = `${rest.slice(0, titleMatch.index)} ${rest.slice((titleMatch.index ?? 0) + titleMatch[0].length)}`.trim();
|
|
79
|
+
}
|
|
80
|
+
const braceMatch = rest.match(/\{([^}]*)\}/);
|
|
81
|
+
const paramPart = braceMatch ? braceMatch[1].trim() : rest;
|
|
82
|
+
const tokens = paramPart.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
83
|
+
for (const [index, token] of tokens.entries()) {
|
|
84
|
+
if (/^\d+$/.test(token) && index === 0) {
|
|
85
|
+
options.initialExpandLevel = Number(token);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const pair = token.match(/^([^=:\s]+)\s*(?:=|:)\s*(.+)$/);
|
|
89
|
+
if (!pair || pair[1] !== "initialExpandLevel") continue;
|
|
90
|
+
const value = pair[2].replace(/^['"]|['"]$/g, "");
|
|
91
|
+
if (/^\d+$/.test(value)) options.initialExpandLevel = Number(value);
|
|
92
|
+
}
|
|
93
|
+
return options;
|
|
94
|
+
}
|
|
95
|
+
function parseMindmapReference(line) {
|
|
96
|
+
const match = line.trim().match(/^<<<\s+(.+?)\s*$/);
|
|
97
|
+
if (!match) return null;
|
|
98
|
+
let rest = match[1].trim();
|
|
99
|
+
let title;
|
|
100
|
+
const titleMatch = rest.match(/\s+\[([^\]]+)\]\s*$/);
|
|
101
|
+
if (titleMatch) {
|
|
102
|
+
title = cleanHeadingText(titleMatch[1]) || void 0;
|
|
103
|
+
rest = rest.slice(0, titleMatch.index).trim();
|
|
104
|
+
}
|
|
105
|
+
const path6 = rest.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, "$1$2").trim();
|
|
106
|
+
return path6 ? { path: path6, title } : null;
|
|
107
|
+
}
|
|
108
|
+
function normalizeMindmapMarkdown(source, options = {}) {
|
|
109
|
+
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
110
|
+
let existingTitle = "";
|
|
111
|
+
let rootIndex = -1;
|
|
112
|
+
for (let index = 0; index < lines.length; index++) {
|
|
113
|
+
const match = lines[index].match(/^\s{0,3}#(?!#)\s+(.+?)\s*$/);
|
|
114
|
+
if (!match) continue;
|
|
115
|
+
existingTitle = cleanHeadingText(match[1]);
|
|
116
|
+
rootIndex = index;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
const rootTitle = cleanHeadingText(options.title || existingTitle || options.defaultTitle || "root") || "root";
|
|
120
|
+
const body = [];
|
|
121
|
+
let headingDepth = null;
|
|
122
|
+
for (let index = 0; index < lines.length; index++) {
|
|
123
|
+
if (index === rootIndex) continue;
|
|
124
|
+
const line = lines[index];
|
|
125
|
+
const heading = line.match(/^\s{0,3}(#{2,6})\s+(.+?)\s*$/);
|
|
126
|
+
if (heading) {
|
|
127
|
+
headingDepth = heading[1].length - 2;
|
|
128
|
+
body.push(`${" ".repeat(headingDepth)}- ${cleanHeadingText(heading[2])}`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const listItem = line.match(/^(\s*)([-+*])\s+(.+)$/);
|
|
132
|
+
if (listItem && headingDepth !== null) {
|
|
133
|
+
body.push(`${" ".repeat(headingDepth + 1)}${line}`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
body.push(line);
|
|
137
|
+
}
|
|
138
|
+
const normalizedBody = promoteLegacyRootList(body, rootTitle);
|
|
139
|
+
while (normalizedBody[0]?.trim() === "") normalizedBody.shift();
|
|
140
|
+
while (normalizedBody[normalizedBody.length - 1]?.trim() === "") normalizedBody.pop();
|
|
141
|
+
return normalizedBody.length > 0 ? `# ${rootTitle}
|
|
142
|
+
|
|
143
|
+
${normalizedBody.join("\n")}
|
|
144
|
+
` : `# ${rootTitle}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// vitepress/configs/markdown.config.ts
|
|
52
149
|
function esc(s = "") {
|
|
53
150
|
return s.replace(
|
|
54
151
|
/[&<>"']/g,
|
|
@@ -80,12 +177,11 @@ var simpleMermaidMarkdown = (md) => {
|
|
|
80
177
|
return fence(tokens, index, options, env, slf);
|
|
81
178
|
};
|
|
82
179
|
};
|
|
83
|
-
function
|
|
180
|
+
function configureMindmapContainer(md) {
|
|
84
181
|
md.use(markdownItContainer, "markmap", {
|
|
85
182
|
marker: "`",
|
|
86
183
|
validate(params) {
|
|
87
|
-
|
|
88
|
-
return p.startsWith("markmap");
|
|
184
|
+
return (params || "").trim().startsWith("markmap");
|
|
89
185
|
},
|
|
90
186
|
render() {
|
|
91
187
|
return "";
|
|
@@ -98,26 +194,34 @@ function configureMarkMapContainer(md) {
|
|
|
98
194
|
for (let i = 0; i < tokens.length; i++) {
|
|
99
195
|
const t = tokens[i];
|
|
100
196
|
if (t.type === "container_markmap_open") {
|
|
197
|
+
const containerName = "markmap";
|
|
198
|
+
const closeType = "container_markmap_close";
|
|
101
199
|
let j = i + 1;
|
|
102
|
-
while (j < tokens.length && tokens[j].type !==
|
|
200
|
+
while (j < tokens.length && tokens[j].type !== closeType)
|
|
103
201
|
j++;
|
|
104
202
|
if (j >= tokens.length) continue;
|
|
105
203
|
const open = t;
|
|
106
204
|
const startLine = open.map ? open.map[0] + 1 : null;
|
|
107
205
|
const endLine = open.map ? open.map[1] - 1 : null;
|
|
108
206
|
const params = {};
|
|
207
|
+
let explicitTitle;
|
|
109
208
|
if (open.map && typeof open.map[0] === "number") {
|
|
110
209
|
const openLine = (lines[open.map[0]] || "").trim();
|
|
210
|
+
const fenceOptions = parseMarkmapFence(openLine);
|
|
211
|
+
explicitTitle = fenceOptions.title;
|
|
111
212
|
let paramPart = "";
|
|
112
213
|
const braceMatch = openLine.match(/\{([^}]*)\}/);
|
|
113
214
|
if (braceMatch) {
|
|
114
215
|
paramPart = braceMatch[1].trim();
|
|
115
216
|
} else {
|
|
116
217
|
const after = openLine.replace(/^`+\s*/, "");
|
|
117
|
-
if (after.startsWith(
|
|
118
|
-
paramPart = after.slice(
|
|
218
|
+
if (after.startsWith(containerName)) {
|
|
219
|
+
paramPart = after.slice(containerName.length).trim();
|
|
119
220
|
}
|
|
120
221
|
}
|
|
222
|
+
if (fenceOptions.initialExpandLevel !== void 0) {
|
|
223
|
+
params.initialExpandLevel = fenceOptions.initialExpandLevel;
|
|
224
|
+
}
|
|
121
225
|
if (paramPart) {
|
|
122
226
|
const tokenArr = paramPart.match(/"[^"]*"|'[^']*'|\S+/g) || [];
|
|
123
227
|
let startIdx = 0;
|
|
@@ -153,9 +257,11 @@ function configureMarkMapContainer(md) {
|
|
|
153
257
|
}
|
|
154
258
|
}
|
|
155
259
|
const firstNonEmptyLine = (content || "").split("\n").find((ln) => ln.trim() !== "") || "";
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
260
|
+
const reference = parseMindmapReference(firstNonEmptyLine);
|
|
261
|
+
let referencedTitle;
|
|
262
|
+
if (reference) {
|
|
263
|
+
const refRaw = reference.path;
|
|
264
|
+
referencedTitle = reference.title;
|
|
159
265
|
try {
|
|
160
266
|
const env = state.env || {};
|
|
161
267
|
const possibleRel = env.relativePath || env.path || env.filePath || env.file || "";
|
|
@@ -174,13 +280,13 @@ function configureMarkMapContainer(md) {
|
|
|
174
280
|
content = fileContent;
|
|
175
281
|
} catch (err) {
|
|
176
282
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
177
|
-
content =
|
|
178
|
-
|
|
179
|
-
)}
|
|
180
|
-
|
|
181
|
-
Error: ${esc(errorMsg)}`;
|
|
283
|
+
content = `- Failed to load referenced file: ${esc(String(refRaw))}
|
|
284
|
+
- Error: ${esc(errorMsg)}`;
|
|
182
285
|
}
|
|
183
286
|
}
|
|
287
|
+
content = normalizeMindmapMarkdown(content, {
|
|
288
|
+
title: explicitTitle || referencedTitle
|
|
289
|
+
});
|
|
184
290
|
const encodedContent = encodeURIComponent(content.trim());
|
|
185
291
|
let propsStr = `content="${encodedContent}"`;
|
|
186
292
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -191,7 +297,7 @@ Error: ${esc(errorMsg)}`;
|
|
|
191
297
|
propsStr += ` ${k}="${safe}"`;
|
|
192
298
|
}
|
|
193
299
|
}
|
|
194
|
-
const html = `<
|
|
300
|
+
const html = `<MindmapPreview ${propsStr}></MindmapPreview>
|
|
195
301
|
`;
|
|
196
302
|
const htmlToken = new state.Token("html_block", "", 0);
|
|
197
303
|
htmlToken.content = html;
|
|
@@ -201,6 +307,40 @@ Error: ${esc(errorMsg)}`;
|
|
|
201
307
|
return true;
|
|
202
308
|
});
|
|
203
309
|
}
|
|
310
|
+
function configureMindmapFence(md) {
|
|
311
|
+
const fence = md.renderer.rules.fence ? md.renderer.rules.fence.bind(md.renderer.rules) : () => "";
|
|
312
|
+
md.renderer.rules.fence = (tokens, index, options, env, slf) => {
|
|
313
|
+
const token = tokens[index];
|
|
314
|
+
const info = token.info.trim();
|
|
315
|
+
if (!/^mindmap(?=\s|\{|\[|$)/.test(info)) {
|
|
316
|
+
return fence(tokens, index, options, env, slf);
|
|
317
|
+
}
|
|
318
|
+
const fenceOptions = parseMarkmapFence(info);
|
|
319
|
+
let content = token.content;
|
|
320
|
+
const firstNonEmptyLine = content.split("\n").find((line) => line.trim()) ?? "";
|
|
321
|
+
const reference = parseMindmapReference(firstNonEmptyLine);
|
|
322
|
+
if (reference) {
|
|
323
|
+
const possibleRel = env?.relativePath || env?.path || env?.filePath || env?.file || "";
|
|
324
|
+
const refFullPath = path.isAbsolute(reference.path) ? reference.path : path.resolve(process.cwd(), possibleRel ? path.dirname(possibleRel) : "", reference.path);
|
|
325
|
+
try {
|
|
326
|
+
content = fs.readFileSync(refFullPath, "utf8");
|
|
327
|
+
} catch (error) {
|
|
328
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
329
|
+
content = `- Failed to load referenced file: ${reference.path}
|
|
330
|
+
- Error: ${message}`;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
content = normalizeMindmapMarkdown(content, {
|
|
334
|
+
title: fenceOptions.title || reference?.title
|
|
335
|
+
});
|
|
336
|
+
const props = [
|
|
337
|
+
`content="${encodeURIComponent(content.trim())}"`,
|
|
338
|
+
fenceOptions.initialExpandLevel === void 0 ? "" : `:initialExpandLevel="${fenceOptions.initialExpandLevel}"`
|
|
339
|
+
].filter(Boolean).join(" ");
|
|
340
|
+
return `<MindmapPreview ${props}></MindmapPreview>
|
|
341
|
+
`;
|
|
342
|
+
};
|
|
343
|
+
}
|
|
204
344
|
function configureSwiperContainer(md) {
|
|
205
345
|
let __tn_swiper_uid = 0;
|
|
206
346
|
let __tn_rules_stack = [];
|
|
@@ -269,7 +409,8 @@ function getMarkdownConfig() {
|
|
|
269
409
|
return true;
|
|
270
410
|
});
|
|
271
411
|
simpleMermaidMarkdown(md);
|
|
272
|
-
|
|
412
|
+
configureMindmapContainer(md);
|
|
413
|
+
configureMindmapFence(md);
|
|
273
414
|
md.use(markdownItTaskLists);
|
|
274
415
|
md.use(mila, {
|
|
275
416
|
attrs: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tnotesjs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "TNotes 知识库核心框架 —— 基于 VitePress 的笔记管理系统",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.17.1",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"vue": "^3.5.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"
|
|
52
|
+
"@tnotesjs/mindmap-core": "^0.2.0",
|
|
53
53
|
"echarts": "^6.0.0",
|
|
54
54
|
"github-slugger": "^2.0.0",
|
|
55
55
|
"markdown-it-container": "^4.0.0",
|
|
@@ -57,11 +57,8 @@
|
|
|
57
57
|
"markdown-it-mathjax3": "^4.3.2",
|
|
58
58
|
"markdown-it-task-lists": "^2.1.1",
|
|
59
59
|
"marked": "^15.0.11",
|
|
60
|
-
"minisearch": "^7.2.0",
|
|
61
|
-
"markmap-lib": "^0.18.12",
|
|
62
|
-
"markmap-toolbar": "^0.18.12",
|
|
63
|
-
"markmap-view": "^0.18.12",
|
|
64
60
|
"mermaid": "^11.5.0",
|
|
61
|
+
"minisearch": "^7.2.0",
|
|
65
62
|
"sass-embedded": "^1.90.0",
|
|
66
63
|
"swiper": "^11.2.1",
|
|
67
64
|
"uuid": "^11.1.0",
|
package/services/index.ts
CHANGED
|
@@ -9,7 +9,6 @@ export { GitService } from './git'
|
|
|
9
9
|
export { NoteService } from './note'
|
|
10
10
|
export { ReadmeService } from './readme'
|
|
11
11
|
export { TocService } from './toc'
|
|
12
|
-
export { TimestampService } from './timestamp'
|
|
13
12
|
export { VitepressService } from './vitepress'
|
|
14
13
|
export { InitSubRepoService } from './init-sub-repo'
|
|
15
14
|
export * from './init-sub-repo/initSubRepoLogic'
|
|
@@ -189,7 +189,6 @@ export function buildPackageJsonTemplate(
|
|
|
189
189
|
'tn:build': 'tnotes --build',
|
|
190
190
|
'tn:create-notes': 'tnotes --create-notes',
|
|
191
191
|
'tn:dev': 'tnotes --dev',
|
|
192
|
-
'tn:fix-timestamps': 'tnotes --fix-timestamps',
|
|
193
192
|
'tn:help': 'tnotes --help',
|
|
194
193
|
'tn:init-sub-repo': 'tnotes --init-sub-repo',
|
|
195
194
|
'tn:preview': 'tnotes --preview',
|
package/services/note/service.ts
CHANGED
package/types/config.ts
CHANGED
package/types/note.ts
CHANGED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 128 128">
|
|
2
|
+
<path d="M0 0h128v128H0z" fill="none" />
|
|
3
|
+
<path fill="#656cff" fill-rule="evenodd" d="M117.9 30.289L66.664.713a5.32 5.32 0 0 0-5.323 0L10.09 30.29a4.48 4.48 0 0 0-2.234 3.872v59.663c0 1.6.853 3.077 2.24 3.878l51.24 29.586a5.33 5.33 0 0 0 5.324 0l51.246-29.586a4.48 4.48 0 0 0 2.24-3.878V34.166a4.48 4.48 0 0 0-2.24-3.872zm-3.216 6.272l-49.47 85.681c-.337.576-1.217.341-1.217-.325V65.81a3.15 3.15 0 0 0-1.573-2.72l-48.59-28.055c-.571-.331-.336-1.216.33-1.216h98.94c1.409 0 2.284 1.525 1.58 2.741" />
|
|
4
|
+
</svg>
|
|
@@ -14,6 +14,7 @@ export { default as icon__close } from './icon__close.svg'
|
|
|
14
14
|
export { default as icon__collapse } from './icon__collapse.svg'
|
|
15
15
|
export { default as icon__confirm } from './icon__confirm.svg'
|
|
16
16
|
export { default as icon__copy } from './icon__copy.svg'
|
|
17
|
+
export { default as icon__cursor } from './icon__cursor.svg'
|
|
17
18
|
export { default as icon__focus } from './icon__focus.svg'
|
|
18
19
|
export { default as icon__fold } from './icon__fold.svg'
|
|
19
20
|
export { default as icon__folder } from './icon__folder.svg'
|
|
@@ -107,22 +107,6 @@ vitepress/components/Layout/AboutPanel.vue
|
|
|
107
107
|
</div>
|
|
108
108
|
</div>
|
|
109
109
|
|
|
110
|
-
<!-- 首次提交时间 -->
|
|
111
|
-
<div class="timeLine" title="首次提交时间">
|
|
112
|
-
<div class="timeLabel"><strong>⌛️ 首次提交</strong></div>
|
|
113
|
-
<div class="timeValue">
|
|
114
|
-
{{ formatOptionalDate(modalCreatedAt) }}
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<!-- 最近提交时间 -->
|
|
119
|
-
<div class="timeLine" title="最近提交时间">
|
|
120
|
-
<div class="timeLabel"><strong>⌛️ 最近提交</strong></div>
|
|
121
|
-
<div class="timeValue">
|
|
122
|
-
{{ formatOptionalDate(modalUpdatedAt) }}
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
|
|
126
110
|
<!-- GitHub -->
|
|
127
111
|
<div
|
|
128
112
|
class="timeLine"
|
|
@@ -196,8 +180,6 @@ vitepress/components/Layout/AboutPanel.vue
|
|
|
196
180
|
<script setup lang="ts">
|
|
197
181
|
import { computed } from 'vue'
|
|
198
182
|
|
|
199
|
-
import { formatDate } from '../utils'
|
|
200
|
-
|
|
201
183
|
const props = defineProps<{
|
|
202
184
|
isHomeReadme: boolean
|
|
203
185
|
currentNoteId: string | null
|
|
@@ -207,8 +189,6 @@ const props = defineProps<{
|
|
|
207
189
|
editableNoteStatus: boolean
|
|
208
190
|
editableDiscussionsEnabled: boolean
|
|
209
191
|
titleError: string
|
|
210
|
-
modalCreatedAt: number | undefined
|
|
211
|
-
modalUpdatedAt: number | undefined
|
|
212
192
|
modalGithubUrl: string
|
|
213
193
|
modalGithubPageUrl: string
|
|
214
194
|
completionPercentage: number | null
|
|
@@ -263,10 +243,6 @@ function onDescriptionInput() {
|
|
|
263
243
|
function onConfigChange() {
|
|
264
244
|
emit('configChange')
|
|
265
245
|
}
|
|
266
|
-
|
|
267
|
-
function formatOptionalDate(timestamp: number | undefined) {
|
|
268
|
-
return timestamp === undefined ? '-' : formatDate(timestamp)
|
|
269
|
-
}
|
|
270
246
|
</script>
|
|
271
247
|
|
|
272
248
|
<style scoped lang="scss">
|
|
@@ -14,10 +14,10 @@ vitepress/components/Layout/DocBeforeControls.vue
|
|
|
14
14
|
<div class="vscodeBox pcOnly" v-show="vscodeNotesDir">
|
|
15
15
|
<a
|
|
16
16
|
:href="vscodeNotesDir"
|
|
17
|
-
title="
|
|
17
|
+
:title="openCurrentTitle"
|
|
18
18
|
target="_blank"
|
|
19
19
|
>
|
|
20
|
-
<img :src="
|
|
20
|
+
<img :src="localIdeIcon" :alt="openCurrentTitle" />
|
|
21
21
|
</a>
|
|
22
22
|
</div>
|
|
23
23
|
<div class="contentToggleBox pcOnly">
|
|
@@ -65,13 +65,7 @@ vitepress/components/Layout/DocBeforeControls.vue
|
|
|
65
65
|
</button>
|
|
66
66
|
</div>
|
|
67
67
|
<!-- 单个图标,点击打开 modal,只在有笔记数据的页面显示 -->
|
|
68
|
-
<div
|
|
69
|
-
class="aboutBtn"
|
|
70
|
-
v-show="
|
|
71
|
-
(currentNoteId && created_at && updated_at) ||
|
|
72
|
-
(isHomeReadme && homeReadmeCreatedAt && homeReadmeUpdatedAt)
|
|
73
|
-
"
|
|
74
|
-
>
|
|
68
|
+
<div class="aboutBtn" v-show="currentNoteId || isHomeReadme">
|
|
75
69
|
<button
|
|
76
70
|
class="aboutIconButton"
|
|
77
71
|
@click="$emit('open-time-modal')"
|
|
@@ -93,24 +87,22 @@ import ToggleFullContent from './ToggleFullContent.vue'
|
|
|
93
87
|
import ToggleSidebar from './ToggleSidebar.vue'
|
|
94
88
|
import {
|
|
95
89
|
icon__github,
|
|
96
|
-
icon__vscode,
|
|
97
90
|
icon__fold,
|
|
98
91
|
icon__clipboard,
|
|
99
92
|
} from '../../assets/icons'
|
|
93
|
+
import { useLocalIde } from '../composables/useLocalIde'
|
|
100
94
|
|
|
101
95
|
const props = defineProps<{
|
|
102
96
|
isFullContentMode: boolean
|
|
103
97
|
vscodeNotesDir: string
|
|
104
98
|
isHomeReadme: boolean
|
|
105
99
|
currentNoteId: string | null
|
|
106
|
-
created_at: string | undefined
|
|
107
|
-
updated_at: string | undefined
|
|
108
|
-
homeReadmeCreatedAt: string | undefined
|
|
109
|
-
homeReadmeUpdatedAt: string | undefined
|
|
110
100
|
timeModalOpen: boolean
|
|
111
101
|
allCollapsed: boolean
|
|
112
102
|
}>()
|
|
113
103
|
|
|
104
|
+
const { icon: localIdeIcon, openCurrentTitle } = useLocalIde()
|
|
105
|
+
|
|
114
106
|
const showCopyToast = ref(false)
|
|
115
107
|
let copyToastTimer: ReturnType<typeof setTimeout> | null = null
|
|
116
108
|
|