@tnotesjs/core 0.1.28 → 0.2.1
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 +2 -1
- package/dist/{chunk-4KG6RXFS.js → chunk-5QN44ES5.js} +2946 -1533
- package/dist/{chunk-AGSL3VZE.js → chunk-5Y5IYS6C.js} +7 -0
- package/dist/cli/index.js +343 -40
- package/dist/index.js +1 -1
- package/dist/vitepress/config/index.js +573 -65
- package/package.json +4 -1
- package/templates/sub-repo/.gitattributes +30 -0
- package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
- package/templates/sub-repo/.vitepress/config.mts +12 -0
- package/templates/sub-repo/.vitepress/env.d.ts +10 -0
- package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
- package/templates/sub-repo/.vscode/settings.json +330 -0
- package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
- package/templates/sub-repo/public/favicon.ico +0 -0
- package/templates/sub-repo/public/logo.png +0 -0
- package/templates/sub-repo/tsconfig.json +45 -0
- package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
- package/vitepress/assets/icons/icon__setting_light.svg +4 -0
- package/vitepress/assets/icons/index.ts +2 -2
- package/vitepress/client/localSearchIndexBridge.ts +24 -0
- package/vitepress/components/Discussions/Discussions.vue +1 -1
- package/vitepress/components/Footprints/Footprints.vue +90 -25
- package/vitepress/components/Layout/CustomSidebar.vue +489 -117
- package/vitepress/components/Layout/Layout.vue +5 -0
- package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
- package/vitepress/components/Layout/SidebarItems.vue +517 -342
- package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
- package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
- package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
- package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
- package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
- package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
- package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
- package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
- package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
- package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
- package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
- package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
- package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
- package/vitepress/components/MarkMap/MarkMap.vue +67 -10
- package/vitepress/components/Settings/Settings.vue +78 -74
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
- package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
- package/vitepress/components/constants.ts +18 -6
- package/vitepress/components/sidebar.data.ts +13 -11
- package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
- package/vitepress/components/sidebarTreeHelpers.ts +168 -0
- package/vitepress/components/utils/vscodePaths.test.ts +41 -0
- package/vitepress/components/utils/vscodePaths.ts +52 -0
- package/vitepress/config/index.ts +2 -0
- package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
- package/vitepress/plugins/localSearchReindex.test.ts +46 -0
- package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
- package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
- package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
- package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
- package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
- package/vitepress/theme/index.ts +8 -0
- package/vitepress/theme/styles/base.scss +0 -8
- package/vitepress/theme/styles/components/markmap.scss +23 -2
- package/vitepress/theme/styles/components/swiper.scss +1 -1
- package/vitepress/theme/styles/doc-layout.scss +212 -0
- package/vitepress/theme/styles/index.scss +3 -0
- package/vitepress/theme/styles/layout.scss +60 -10
- package/vitepress/assets/icons/icon__mindmap.svg +0 -1
- package/vitepress/assets/icons/icon__setting.svg +0 -1
- package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
|
@@ -152,6 +152,12 @@ var ConfigManager = class _ConfigManager {
|
|
|
152
152
|
this.config = validatedConfig;
|
|
153
153
|
return this.config;
|
|
154
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* 清除内存中的配置缓存,下次 get/getAll 时重新从磁盘加载
|
|
157
|
+
*/
|
|
158
|
+
clearCache() {
|
|
159
|
+
this.config = null;
|
|
160
|
+
}
|
|
155
161
|
/**
|
|
156
162
|
* 获取配置项
|
|
157
163
|
*/
|
|
@@ -188,6 +194,7 @@ function getConfigManager() {
|
|
|
188
194
|
}
|
|
189
195
|
|
|
190
196
|
export {
|
|
197
|
+
getDefaultConfig,
|
|
191
198
|
ConfigManager,
|
|
192
199
|
getConfigManager
|
|
193
200
|
};
|
package/dist/cli/index.js
CHANGED
|
@@ -4,33 +4,52 @@ import {
|
|
|
4
4
|
COMMAND_DESCRIPTIONS,
|
|
5
5
|
COMMAND_NAMES,
|
|
6
6
|
COMMAND_OPTIONS,
|
|
7
|
+
DEFAULT_DESCRIPTION,
|
|
8
|
+
DEFAULT_PORT,
|
|
7
9
|
FileWatcherService,
|
|
8
10
|
GitManager,
|
|
9
11
|
GitService,
|
|
12
|
+
InitSubRepoService,
|
|
10
13
|
NoteIndexCache,
|
|
11
14
|
NoteManager,
|
|
12
15
|
NoteService,
|
|
13
16
|
ROOT_CONFIG_PATH,
|
|
14
17
|
ROOT_DIR_PATH,
|
|
18
|
+
ROOT_README_PATH,
|
|
19
|
+
ROOT_TOC_PATH,
|
|
15
20
|
ReadmeService,
|
|
16
21
|
RenameNoteCommand,
|
|
17
22
|
TimestampService,
|
|
23
|
+
TocService,
|
|
18
24
|
UpdateNoteConfigCommand,
|
|
19
25
|
VitepressService,
|
|
26
|
+
buildFolderTocLine,
|
|
27
|
+
buildInitContext,
|
|
28
|
+
buildManualSteps,
|
|
29
|
+
buildTocLine,
|
|
20
30
|
createLogger,
|
|
31
|
+
defaultDisplayName,
|
|
32
|
+
getInitializedConfigPath,
|
|
21
33
|
handleError,
|
|
34
|
+
isAlreadyInitialized,
|
|
22
35
|
logger,
|
|
36
|
+
migrateReadmeToToc,
|
|
23
37
|
parseArgs,
|
|
24
38
|
parseReadmeCompletedNotes,
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
parseTocCompletedNotes,
|
|
40
|
+
processTocEmptyLines,
|
|
41
|
+
resolveNoteFromIndex,
|
|
42
|
+
runCommand,
|
|
43
|
+
validatePort,
|
|
44
|
+
validateTopic
|
|
45
|
+
} from "../chunk-5QN44ES5.js";
|
|
27
46
|
import {
|
|
28
|
-
ConfigManager
|
|
29
|
-
|
|
47
|
+
ConfigManager,
|
|
48
|
+
getConfigManager
|
|
49
|
+
} from "../chunk-5Y5IYS6C.js";
|
|
30
50
|
|
|
31
51
|
// commands/update/UpdateCommand.ts
|
|
32
52
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
33
|
-
import { resolve } from "path";
|
|
34
53
|
var UpdateCommand = class extends BaseCommand {
|
|
35
54
|
constructor() {
|
|
36
55
|
super("update");
|
|
@@ -78,35 +97,36 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
78
97
|
}
|
|
79
98
|
/**
|
|
80
99
|
* 更新 root_item 配置
|
|
81
|
-
*
|
|
100
|
+
* 更新当前月份的完成笔记数量,并自动补齐从最后已有月份到当前月份之间的所有缺失月份。
|
|
82
101
|
*/
|
|
83
102
|
async updateRootItem() {
|
|
84
103
|
try {
|
|
85
104
|
const configContent = readFileSync(ROOT_CONFIG_PATH, "utf-8");
|
|
86
105
|
const config = JSON.parse(configContent);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error("\u6839\u76EE\u5F55 README.md \u4E0D\u5B58\u5728");
|
|
106
|
+
if (!existsSync(ROOT_TOC_PATH)) {
|
|
107
|
+
throw new Error("\u6839\u76EE\u5F55 TOC.md \u4E0D\u5B58\u5728");
|
|
90
108
|
}
|
|
91
|
-
const
|
|
92
|
-
const { completedCount } =
|
|
109
|
+
const tocContent = readFileSync(ROOT_TOC_PATH, "utf-8");
|
|
110
|
+
const { completedCount } = parseTocCompletedNotes(tocContent);
|
|
93
111
|
const now = /* @__PURE__ */ new Date();
|
|
94
112
|
const yearShort = String(now.getFullYear()).slice(-2);
|
|
95
113
|
const monthStr = String(now.getMonth() + 1).padStart(2, "0");
|
|
96
114
|
const currentKey = `${yearShort}.${monthStr}`;
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
115
|
+
const existing = { ...config.root_item.completed_notes_count || {} };
|
|
116
|
+
const _countsBeforeFix = Object.keys(existing).length;
|
|
117
|
+
existing[currentKey] = completedCount;
|
|
118
|
+
const fixed = fillMissingMonthGaps(existing, currentKey);
|
|
101
119
|
config.root_item = {
|
|
102
120
|
...config.root_item,
|
|
103
|
-
completed_notes_count:
|
|
121
|
+
completed_notes_count: fixed
|
|
104
122
|
};
|
|
105
123
|
delete config.root_item.completed_notes_count_last_month;
|
|
106
124
|
writeFileSync(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
107
125
|
if (!this.quiet) {
|
|
126
|
+
const filledCount = Object.keys(fixed).length - _countsBeforeFix;
|
|
127
|
+
const filledMsg = filledCount > 0 ? `\uFF0C\u8865\u9F50 ${filledCount} \u4E2A\u7F3A\u5931\u6708\u4EFD` : "";
|
|
108
128
|
this.logger.success(
|
|
109
|
-
`root_item \u914D\u7F6E\u5DF2\u66F4\u65B0: ${currentKey} \u6708\u5B8C\u6210 ${completedCount} \u7BC7\u7B14\u8BB0`
|
|
129
|
+
`root_item \u914D\u7F6E\u5DF2\u66F4\u65B0: ${currentKey} \u6708\u5B8C\u6210 ${completedCount} \u7BC7\u7B14\u8BB0${filledMsg}`
|
|
110
130
|
);
|
|
111
131
|
}
|
|
112
132
|
} catch (error) {
|
|
@@ -119,6 +139,52 @@ var UpdateCommand = class extends BaseCommand {
|
|
|
119
139
|
}
|
|
120
140
|
}
|
|
121
141
|
};
|
|
142
|
+
function fillMissingMonthGaps(counts, currentKey) {
|
|
143
|
+
const existingKeys = Object.keys(counts).sort();
|
|
144
|
+
const result = {};
|
|
145
|
+
const keySet = new Set(existingKeys);
|
|
146
|
+
keySet.add(currentKey);
|
|
147
|
+
const allKeys = Array.from(keySet).sort();
|
|
148
|
+
let prevValue = 0;
|
|
149
|
+
let lastKey = null;
|
|
150
|
+
for (const key of allKeys) {
|
|
151
|
+
if (lastKey !== null) {
|
|
152
|
+
const missing = generateMissingMonthKeys(lastKey, key);
|
|
153
|
+
for (const midKey of missing) {
|
|
154
|
+
result[midKey] = prevValue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
result[key] = counts[key] ?? prevValue;
|
|
158
|
+
prevValue = result[key];
|
|
159
|
+
lastKey = key;
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
function generateMissingMonthKeys(fromKey, toKey) {
|
|
164
|
+
const parseKey = (key) => {
|
|
165
|
+
const [yy, mm] = key.split(".").map(Number);
|
|
166
|
+
return { year: 2e3 + yy, month: mm };
|
|
167
|
+
};
|
|
168
|
+
const from = parseKey(fromKey);
|
|
169
|
+
const to = parseKey(toKey);
|
|
170
|
+
const missing = [];
|
|
171
|
+
let year = from.year;
|
|
172
|
+
let month = from.month;
|
|
173
|
+
while (true) {
|
|
174
|
+
month++;
|
|
175
|
+
if (month > 12) {
|
|
176
|
+
month = 1;
|
|
177
|
+
year++;
|
|
178
|
+
}
|
|
179
|
+
const shortYear = String(year).slice(-2);
|
|
180
|
+
const paddedMonth = String(month).padStart(2, "0");
|
|
181
|
+
const candidateKey = `${shortYear}.${paddedMonth}`;
|
|
182
|
+
if (candidateKey === toKey) break;
|
|
183
|
+
if (year > to.year || year === to.year && month > to.month) break;
|
|
184
|
+
missing.push(candidateKey);
|
|
185
|
+
}
|
|
186
|
+
return missing;
|
|
187
|
+
}
|
|
122
188
|
|
|
123
189
|
// commands/git/PushCommand.ts
|
|
124
190
|
var PushCommand = class extends BaseCommand {
|
|
@@ -225,14 +291,15 @@ var DevCommand = class extends BaseCommand {
|
|
|
225
291
|
this.fileWatcherService = new FileWatcherService();
|
|
226
292
|
this.noteIndexCache = NoteIndexCache.getInstance();
|
|
227
293
|
this.noteManager = NoteManager.getInstance();
|
|
228
|
-
this.
|
|
294
|
+
this.tocService = TocService.getInstance();
|
|
229
295
|
this.vitepressService = new VitepressService();
|
|
230
296
|
}
|
|
231
297
|
async run() {
|
|
232
298
|
const notes = this.noteManager.scanNotes();
|
|
233
299
|
this.logger.info(`\u626B\u63CF\u5230 ${notes.length} \u7BC7\u7B14\u8BB0`);
|
|
234
300
|
this.noteIndexCache.initialize(notes);
|
|
235
|
-
|
|
301
|
+
this.tocService.ensureTocExists();
|
|
302
|
+
await this.tocService.regenerateSidebar(notes);
|
|
236
303
|
const result = await this.vitepressService.startServer();
|
|
237
304
|
if (result) {
|
|
238
305
|
const versionInfo = result.version ? `\uFF08v${result.version}\uFF09` : "";
|
|
@@ -254,6 +321,224 @@ var DevCommand = class extends BaseCommand {
|
|
|
254
321
|
}
|
|
255
322
|
};
|
|
256
323
|
|
|
324
|
+
// commands/init-sub-repo/InitSubRepoCommand.ts
|
|
325
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
326
|
+
import { join, dirname } from "path";
|
|
327
|
+
import { createInterface } from "readline";
|
|
328
|
+
import { fileURLToPath } from "url";
|
|
329
|
+
import { v4 as uuidv4 } from "uuid";
|
|
330
|
+
function readCoreVersion() {
|
|
331
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
332
|
+
let dir = moduleDir;
|
|
333
|
+
while (dir !== dirname(dir)) {
|
|
334
|
+
const pkgPath = join(dir, "package.json");
|
|
335
|
+
try {
|
|
336
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
337
|
+
if (pkg.name === "@tnotesjs/core" && pkg.version) return pkg.version;
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
dir = dirname(dir);
|
|
341
|
+
}
|
|
342
|
+
return "0.1.28";
|
|
343
|
+
}
|
|
344
|
+
var InitSubRepoCommand = class extends BaseCommand {
|
|
345
|
+
constructor() {
|
|
346
|
+
super("init-sub-repo");
|
|
347
|
+
this.rootPath = process.cwd();
|
|
348
|
+
}
|
|
349
|
+
async run() {
|
|
350
|
+
if (isAlreadyInitialized(this.rootPath)) {
|
|
351
|
+
const configPath = getInitializedConfigPath(this.rootPath);
|
|
352
|
+
this.logger.info(
|
|
353
|
+
`\u68C0\u6D4B\u5230 .tnotes.json \u914D\u7F6E\u6587\u4EF6\uFF1A${configPath}\uFF0C\u5F53\u524D\u4ED3\u5E93\u5DF2\u7ECF\u662F\u4E00\u4E2A TNotes \u77E5\u8BC6\u5E93`
|
|
354
|
+
);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
this.logger.info("\u521D\u59CB\u5316 TNotes \u5B50\u77E5\u8BC6\u5E93");
|
|
358
|
+
this.logger.info("");
|
|
359
|
+
const input = await this.promptForInput();
|
|
360
|
+
const ctx = buildInitContext(input);
|
|
361
|
+
this.logger.info("");
|
|
362
|
+
this.logger.info("\u5373\u5C06\u521B\u5EFA\u4EE5\u4E0B\u77E5\u8BC6\u5E93\uFF1A");
|
|
363
|
+
this.logger.info(` \u4ED3\u5E93\u540D\uFF1A${ctx.repoName}`);
|
|
364
|
+
this.logger.info(` \u5C55\u793A\u540D\uFF1A${ctx.displayName}`);
|
|
365
|
+
this.logger.info(` \u7AEF\u53E3\uFF1A${ctx.port}`);
|
|
366
|
+
this.logger.info(` \u63CF\u8FF0\uFF1A${ctx.description}`);
|
|
367
|
+
this.logger.info("");
|
|
368
|
+
const confirmed = await this.promptConfirm("\u786E\u8BA4\u521D\u59CB\u5316\uFF1F [Y/n] ");
|
|
369
|
+
if (!confirmed) {
|
|
370
|
+
this.logger.info("\u5DF2\u53D6\u6D88\u521D\u59CB\u5316");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const service = InitSubRepoService.fromModuleUrl(import.meta.url, this.rootPath);
|
|
374
|
+
const result = service.writeScaffold(input);
|
|
375
|
+
if (result.created.length > 0) {
|
|
376
|
+
this.logger.success(`\u5DF2\u521B\u5EFA ${result.created.length} \u4E2A\u6587\u4EF6\uFF1A`);
|
|
377
|
+
for (const file of result.created) {
|
|
378
|
+
this.logger.info(` + ${file}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (result.updated.length > 0) {
|
|
382
|
+
this.logger.success(`\u5DF2\u66F4\u65B0 ${result.updated.length} \u4E2A\u6587\u4EF6\uFF1A`);
|
|
383
|
+
for (const file of result.updated) {
|
|
384
|
+
this.logger.info(` ~ ${file}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (result.skipped.length > 0) {
|
|
388
|
+
this.logger.warn(`\u5DF2\u8DF3\u8FC7 ${result.skipped.length} \u4E2A\u5DF2\u5B58\u5728\u6587\u4EF6\uFF1A`);
|
|
389
|
+
for (const file of result.skipped) {
|
|
390
|
+
this.logger.info(` - ${file}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
getConfigManager().clearCache();
|
|
394
|
+
try {
|
|
395
|
+
this.logger.info("");
|
|
396
|
+
this.logger.info("\u6B63\u5728\u89C4\u8303\u5316 TOC.md \u5E76\u751F\u6210 sidebar.json...");
|
|
397
|
+
const noteService = NoteService.getInstance();
|
|
398
|
+
const tocService = TocService.getInstance();
|
|
399
|
+
const notes = noteService.getAllNotes();
|
|
400
|
+
await tocService.normalizeToc(notes);
|
|
401
|
+
await tocService.regenerateSidebar(notes);
|
|
402
|
+
this.logger.success("sidebar.json \u5DF2\u751F\u6210");
|
|
403
|
+
} catch (error) {
|
|
404
|
+
this.logger.warn(
|
|
405
|
+
`\u81EA\u52A8 update \u5931\u8D25\uFF08\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C pnpm tn:update\uFF09\uFF1A${error instanceof Error ? error.message : String(error)}`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
this.logger.info("");
|
|
409
|
+
this.logger.success("\u5B50\u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
410
|
+
this.logger.info("");
|
|
411
|
+
this.logger.info("\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A");
|
|
412
|
+
for (const [index, step] of buildManualSteps(ctx).entries()) {
|
|
413
|
+
this.logger.info(` ${index + 1}. ${step}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async promptForInput() {
|
|
417
|
+
const topic = await this.promptTopic();
|
|
418
|
+
const displayName = await this.promptDisplayName(topic);
|
|
419
|
+
const port2 = await this.promptPort();
|
|
420
|
+
const description = await this.promptDescription();
|
|
421
|
+
return {
|
|
422
|
+
topic,
|
|
423
|
+
displayName,
|
|
424
|
+
port: port2,
|
|
425
|
+
description,
|
|
426
|
+
repoUuid: uuidv4(),
|
|
427
|
+
noteUuid: uuidv4(),
|
|
428
|
+
coreVersion: readCoreVersion()
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
async promptTopic() {
|
|
432
|
+
while (true) {
|
|
433
|
+
const answer = await this.ask("\u8BF7\u8F93\u5165 topic\uFF08\u5982 react\uFF0C\u5C06\u751F\u6210 TNotes.react\uFF09: ");
|
|
434
|
+
const error = validateTopic(answer);
|
|
435
|
+
if (error) {
|
|
436
|
+
this.logger.warn(error);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
return answer.trim();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
async promptDisplayName(topic) {
|
|
443
|
+
const fallback = defaultDisplayName(topic);
|
|
444
|
+
const answer = await this.ask(
|
|
445
|
+
`\u8BF7\u8F93\u5165\u5C55\u793A\u540D\uFF08\u7528\u4E8E\u9996\u9875 hero\uFF0C\u9ED8\u8BA4 ${fallback}\uFF09: `
|
|
446
|
+
);
|
|
447
|
+
return answer.trim() || fallback;
|
|
448
|
+
}
|
|
449
|
+
async promptPort() {
|
|
450
|
+
const answer = await this.ask(`\u8BF7\u8F93\u5165 dev \u7AEF\u53E3\uFF08\u9ED8\u8BA4 ${DEFAULT_PORT}\uFF09: `);
|
|
451
|
+
const { port: port2, error } = validatePort(answer);
|
|
452
|
+
if (error) this.logger.warn(error);
|
|
453
|
+
return port2;
|
|
454
|
+
}
|
|
455
|
+
async promptDescription() {
|
|
456
|
+
const answer = await this.ask(
|
|
457
|
+
`\u8BF7\u8F93\u5165\u4ED3\u5E93\u63CF\u8FF0\uFF08\u9ED8\u8BA4\uFF1A${DEFAULT_DESCRIPTION}\uFF09: `
|
|
458
|
+
);
|
|
459
|
+
return answer.trim() || DEFAULT_DESCRIPTION;
|
|
460
|
+
}
|
|
461
|
+
async promptConfirm(question) {
|
|
462
|
+
const answer = await this.ask(question);
|
|
463
|
+
const normalized = answer.trim().toLowerCase();
|
|
464
|
+
return normalized === "" || normalized === "y" || normalized === "yes";
|
|
465
|
+
}
|
|
466
|
+
ask(question) {
|
|
467
|
+
const rl = createInterface({
|
|
468
|
+
input: process.stdin,
|
|
469
|
+
output: process.stdout
|
|
470
|
+
});
|
|
471
|
+
return new Promise((resolve) => {
|
|
472
|
+
rl.question(question, (answer) => {
|
|
473
|
+
rl.close();
|
|
474
|
+
resolve(answer);
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
// commands/init-toc/InitTocCommand.ts
|
|
481
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
482
|
+
var InitTocCommand = class extends BaseCommand {
|
|
483
|
+
constructor() {
|
|
484
|
+
super("init-toc");
|
|
485
|
+
this.noteManager = NoteManager.getInstance();
|
|
486
|
+
this.tocService = TocService.getInstance();
|
|
487
|
+
}
|
|
488
|
+
async run() {
|
|
489
|
+
if (!existsSync2(ROOT_README_PATH)) {
|
|
490
|
+
throw new Error(`README.md \u4E0D\u5B58\u5728: ${ROOT_README_PATH}`);
|
|
491
|
+
}
|
|
492
|
+
const readmeContent = readFileSync3(ROOT_README_PATH, "utf-8");
|
|
493
|
+
const notes = this.noteManager.scanNotes();
|
|
494
|
+
const { entries, warnings } = migrateReadmeToToc(readmeContent);
|
|
495
|
+
for (const warning of warnings) {
|
|
496
|
+
this.logger.warn(warning);
|
|
497
|
+
}
|
|
498
|
+
const lines = [];
|
|
499
|
+
let skipped = 0;
|
|
500
|
+
for (const entry of entries) {
|
|
501
|
+
if (entry.kind === "folder") {
|
|
502
|
+
lines.push(buildFolderTocLine(entry.folderTitle, entry.indent));
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const note = resolveNoteFromIndex(entry.noteIndex, notes);
|
|
506
|
+
if (!note) {
|
|
507
|
+
skipped++;
|
|
508
|
+
this.logger.warn(
|
|
509
|
+
`\u8DF3\u8FC7 README \u4E2D\u5B58\u5728\u4F46 notes/ \u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728\u7684\u7B14\u8BB0: ${entry.noteIndex}`
|
|
510
|
+
);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
lines.push(buildTocLine(note, entry.indent, entry.completed));
|
|
514
|
+
}
|
|
515
|
+
if (lines.length === 0) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
"\u672A\u80FD\u4ECE README.md \u89E3\u6790\u5230\u4EFB\u4F55\u6709\u6548\u7B14\u8BB0\u6761\u76EE\uFF0CTOC.md \u672A\u5199\u5165"
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
const content = processTocEmptyLines(lines).join("\n");
|
|
521
|
+
writeFileSync2(ROOT_TOC_PATH, content, "utf-8");
|
|
522
|
+
await this.tocService.regenerateSidebar(notes);
|
|
523
|
+
this.logger.success(
|
|
524
|
+
`TOC.md \u5DF2\u4ECE README.md \u521D\u59CB\u5316\uFF08${lines.length} \u6761\u7B14\u8BB0${skipped > 0 ? `\uFF0C\u8DF3\u8FC7 ${skipped} \u6761` : ""}\uFF09`
|
|
525
|
+
);
|
|
526
|
+
this.logger.info(`\u8F93\u51FA\u8DEF\u5F84: ${ROOT_TOC_PATH}`);
|
|
527
|
+
this.logger.info(
|
|
528
|
+
"\u8FC7\u6E21\u547D\u4EE4 init-toc \u4EC5\u5728 v0.2.x \u63D0\u4F9B\uFF0Cv0.3.x \u5C06\u79FB\u9664\uFF1B\u540E\u7EED\u8BF7\u4F7F\u7528 TOC.md + pnpm tn:update"
|
|
529
|
+
);
|
|
530
|
+
const listedIndexes = new Set(
|
|
531
|
+
entries.filter((e) => e.kind === "note").map((e) => e.noteIndex)
|
|
532
|
+
);
|
|
533
|
+
const missingInReadme = notes.filter((n) => !listedIndexes.has(n.index));
|
|
534
|
+
if (missingInReadme.length > 0) {
|
|
535
|
+
this.logger.info(
|
|
536
|
+
`README \u4E2D\u672A\u5217\u51FA ${missingInReadme.length} \u7BC7\u7B14\u8BB0\uFF0C\u53EF\u6267\u884C pnpm tn:update \u8FFD\u52A0\u5230 TOC.md`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
257
542
|
// commands/maintenance/FixTimestampsCommand.ts
|
|
258
543
|
var FixTimestampsCommand = class extends BaseCommand {
|
|
259
544
|
constructor() {
|
|
@@ -294,6 +579,8 @@ var COMMAND_CATEGORIES = {
|
|
|
294
579
|
COMMAND_NAMES.CREATE_NOTES
|
|
295
580
|
],
|
|
296
581
|
"Git \u64CD\u4F5C": [COMMAND_NAMES.PUSH, COMMAND_NAMES.PULL],
|
|
582
|
+
"\u8FC1\u79FB\uFF08v0.2.x \u8FC7\u6E21\uFF09": [COMMAND_NAMES.INIT_TOC],
|
|
583
|
+
\u521D\u59CB\u5316: [COMMAND_NAMES.INIT_SUB_REPO],
|
|
297
584
|
\u5176\u4ED6: [COMMAND_NAMES.FIX_TIMESTAMPS, COMMAND_NAMES.HELP]
|
|
298
585
|
};
|
|
299
586
|
var COMMAND_OPTIONS_INFO = {
|
|
@@ -337,6 +624,9 @@ var HelpCommand = class extends BaseCommand {
|
|
|
337
624
|
this.logger.info(" pnpm tn:build");
|
|
338
625
|
this.logger.info(" pnpm tn:create-notes # \u6279\u91CF\u521B\u5EFA\u7B14\u8BB0");
|
|
339
626
|
this.logger.info(" pnpm tn:update");
|
|
627
|
+
this.logger.info(
|
|
628
|
+
" pnpm tn:init-toc # v0.1.x \u2192 v0.2.x\uFF1A\u4ECE README.md \u751F\u6210 TOC.md"
|
|
629
|
+
);
|
|
340
630
|
this.logger.info(
|
|
341
631
|
" pnpm tn:update-completed-count # \u751F\u6210\u5F53\u524D\u77E5\u8BC6\u5E93\u6700\u8FD1 12 \u4E2A\u6708\u7684\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u7EDF\u8BA1"
|
|
342
632
|
);
|
|
@@ -358,8 +648,8 @@ var HelpCommand = class extends BaseCommand {
|
|
|
358
648
|
};
|
|
359
649
|
|
|
360
650
|
// commands/note/CreateNoteCommand.ts
|
|
361
|
-
import { createInterface } from "readline";
|
|
362
|
-
import { v4 as
|
|
651
|
+
import { createInterface as createInterface2 } from "readline";
|
|
652
|
+
import { v4 as uuidv42 } from "uuid";
|
|
363
653
|
var CreateNoteCommand = class extends BaseCommand {
|
|
364
654
|
constructor() {
|
|
365
655
|
super("create-notes");
|
|
@@ -389,7 +679,7 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
389
679
|
title = `new`;
|
|
390
680
|
this.logger.info(`[${i}/${count}] \u521B\u5EFA\u7B14\u8BB0: ${title}`);
|
|
391
681
|
}
|
|
392
|
-
const configId =
|
|
682
|
+
const configId = uuidv42();
|
|
393
683
|
const note = await this.noteService.createNote({
|
|
394
684
|
title: title || `new`,
|
|
395
685
|
enableDiscussions: false,
|
|
@@ -433,27 +723,27 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
433
723
|
* 提示用户输入要创建的笔记数量
|
|
434
724
|
*/
|
|
435
725
|
async promptForCount() {
|
|
436
|
-
const rl =
|
|
726
|
+
const rl = createInterface2({
|
|
437
727
|
input: process.stdin,
|
|
438
728
|
output: process.stdout
|
|
439
729
|
});
|
|
440
|
-
return new Promise((
|
|
730
|
+
return new Promise((resolve) => {
|
|
441
731
|
rl.question("\n\u{1F4DD} \u8BF7\u8F93\u5165\u8981\u521B\u5EFA\u7684\u7B14\u8BB0\u6570\u91CF\uFF08\u9ED8\u8BA4\u4E3A 1\uFF09: ", (answer) => {
|
|
442
732
|
rl.close();
|
|
443
733
|
const trimmed = answer.trim();
|
|
444
734
|
if (!trimmed) {
|
|
445
735
|
this.logger.info("\u4F7F\u7528\u9ED8\u8BA4\u6570\u91CF: 1");
|
|
446
|
-
|
|
736
|
+
resolve(1);
|
|
447
737
|
return;
|
|
448
738
|
}
|
|
449
739
|
const num = parseInt(trimmed, 10);
|
|
450
740
|
if (isNaN(num) || num < 1 || !Number.isInteger(num)) {
|
|
451
741
|
this.logger.warn(`\u8F93\u5165 "${trimmed}" \u4E0D\u662F\u6709\u6548\u7684\u6B63\u6574\u6570\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C: 1`);
|
|
452
|
-
|
|
742
|
+
resolve(1);
|
|
453
743
|
return;
|
|
454
744
|
}
|
|
455
745
|
this.logger.info(`\u5C06\u521B\u5EFA ${num} \u7BC7\u7B14\u8BB0`);
|
|
456
|
-
|
|
746
|
+
resolve(num);
|
|
457
747
|
});
|
|
458
748
|
});
|
|
459
749
|
}
|
|
@@ -461,14 +751,14 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
461
751
|
* 提示用户输入笔记标题
|
|
462
752
|
*/
|
|
463
753
|
async promptForTitle() {
|
|
464
|
-
const rl =
|
|
754
|
+
const rl = createInterface2({
|
|
465
755
|
input: process.stdin,
|
|
466
756
|
output: process.stdout
|
|
467
757
|
});
|
|
468
|
-
return new Promise((
|
|
758
|
+
return new Promise((resolve) => {
|
|
469
759
|
rl.question("\u8BF7\u8F93\u5165\u7B14\u8BB0\u6807\u9898: ", (answer) => {
|
|
470
760
|
rl.close();
|
|
471
|
-
|
|
761
|
+
resolve(answer.trim());
|
|
472
762
|
});
|
|
473
763
|
});
|
|
474
764
|
}
|
|
@@ -476,7 +766,7 @@ var CreateNoteCommand = class extends BaseCommand {
|
|
|
476
766
|
|
|
477
767
|
// commands/update-completed-count/UpdateCompletedCountCommand.ts
|
|
478
768
|
import { execSync } from "child_process";
|
|
479
|
-
import { readFileSync as
|
|
769
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
480
770
|
var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
481
771
|
constructor() {
|
|
482
772
|
super("update-completed-count");
|
|
@@ -490,7 +780,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
490
780
|
async updateCurrentRepo() {
|
|
491
781
|
const startTime = Date.now();
|
|
492
782
|
try {
|
|
493
|
-
const configContent =
|
|
783
|
+
const configContent = readFileSync4(ROOT_CONFIG_PATH, "utf-8");
|
|
494
784
|
const config = JSON.parse(configContent);
|
|
495
785
|
this.logger.info("\u5F00\u59CB\u66F4\u65B0\u5B8C\u6210\u7B14\u8BB0\u6570\u91CF\u5386\u53F2\u8BB0\u5F55...");
|
|
496
786
|
const completedNotesCountHistory = await this.getCompletedNotesCountHistory(
|
|
@@ -500,7 +790,7 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
500
790
|
...config.root_item,
|
|
501
791
|
completed_notes_count: completedNotesCountHistory
|
|
502
792
|
};
|
|
503
|
-
|
|
793
|
+
writeFileSync3(ROOT_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
504
794
|
const duration = Date.now() - startTime;
|
|
505
795
|
const monthKeys = Object.keys(completedNotesCountHistory);
|
|
506
796
|
const currentKey = monthKeys[monthKeys.length - 1];
|
|
@@ -520,8 +810,8 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
520
810
|
*
|
|
521
811
|
* 逻辑:
|
|
522
812
|
* 1. 计算最近12个月的范围(当前月份往前推11个月)
|
|
523
|
-
* 2. 遍历这12个月,从 Git 历史中读取 README.md
|
|
524
|
-
* 3.
|
|
813
|
+
* 2. 遍历这12个月,从 Git 历史中读取 TOC.md(回退 README.md)
|
|
814
|
+
* 3. 解析获取完成笔记数量
|
|
525
815
|
* 4. 如果知识库创建时间在这12个月内,之前的月份补0
|
|
526
816
|
* 5. 返回对象 { '25.01': 0, '25.02': 1, ..., '25.12': 15 }
|
|
527
817
|
*/
|
|
@@ -589,26 +879,37 @@ var UpdateCompletedCountCommand = class extends BaseCommand {
|
|
|
589
879
|
const monthStr = String(lastDayOfMonth.getMonth() + 1).padStart(2, "0");
|
|
590
880
|
const dayStr = String(lastDayOfMonth.getDate()).padStart(2, "0");
|
|
591
881
|
const untilDate = `${yearStr}-${monthStr}-${dayStr} 23:59:59 +0800`;
|
|
592
|
-
|
|
593
|
-
`git log --until="${untilDate}" --format=%H -1 --
|
|
882
|
+
let commitHash = execSync(
|
|
883
|
+
`git log --until="${untilDate}" --format=%H -1 -- TOC.md`,
|
|
594
884
|
{
|
|
595
885
|
cwd: ROOT_DIR_PATH,
|
|
596
886
|
encoding: "utf-8"
|
|
597
887
|
}
|
|
598
888
|
).trim();
|
|
889
|
+
let filePath = "TOC.md";
|
|
890
|
+
if (!commitHash) {
|
|
891
|
+
commitHash = execSync(
|
|
892
|
+
`git log --until="${untilDate}" --format=%H -1 -- README.md`,
|
|
893
|
+
{
|
|
894
|
+
cwd: ROOT_DIR_PATH,
|
|
895
|
+
encoding: "utf-8"
|
|
896
|
+
}
|
|
897
|
+
).trim();
|
|
898
|
+
filePath = "README.md";
|
|
899
|
+
}
|
|
599
900
|
if (!commitHash) {
|
|
600
901
|
return fallbackCount;
|
|
601
902
|
}
|
|
602
|
-
let
|
|
903
|
+
let fileContent;
|
|
603
904
|
try {
|
|
604
|
-
|
|
905
|
+
fileContent = execSync(`git show ${commitHash}:${filePath}`, {
|
|
605
906
|
cwd: ROOT_DIR_PATH,
|
|
606
907
|
encoding: "utf-8"
|
|
607
908
|
});
|
|
608
909
|
} catch (error) {
|
|
609
910
|
return fallbackCount;
|
|
610
911
|
}
|
|
611
|
-
const { completedCount } = parseReadmeCompletedNotes(
|
|
912
|
+
const { completedCount } = filePath === "TOC.md" ? parseTocCompletedNotes(fileContent) : parseReadmeCompletedNotes(fileContent);
|
|
612
913
|
return completedCount;
|
|
613
914
|
}
|
|
614
915
|
};
|
|
@@ -619,6 +920,8 @@ var commandFactories = {
|
|
|
619
920
|
"create-notes": () => new CreateNoteCommand(),
|
|
620
921
|
"dev": () => new DevCommand(),
|
|
621
922
|
"fix-timestamps": () => new FixTimestampsCommand(),
|
|
923
|
+
"init-sub-repo": () => new InitSubRepoCommand(),
|
|
924
|
+
"init-toc": () => new InitTocCommand(),
|
|
622
925
|
"help": () => new HelpCommand(),
|
|
623
926
|
"preview": () => new PreviewCommand(),
|
|
624
927
|
"pull": () => new PullCommand(),
|