@tnotesjs/core 0.2.2 → 0.3.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.
Files changed (35) hide show
  1. package/README.md +0 -1
  2. package/config/defaultConfig.ts +33 -4
  3. package/config/index.ts +6 -0
  4. package/core/NoteIndexCache.ts +7 -1
  5. package/core/NoteManager.ts +18 -30
  6. package/dist/{chunk-5Y5IYS6C.js → chunk-UJG3UU5X.js} +21 -5
  7. package/dist/{chunk-5QN44ES5.js → chunk-XCWFZLER.js} +48 -368
  8. package/dist/cli/index.js +26 -78
  9. package/dist/index.js +1 -1
  10. package/dist/vitepress/config/index.js +2 -2
  11. package/package.json +1 -1
  12. package/services/index.ts +0 -1
  13. package/services/init-sub-repo/initSubRepoLogic.ts +0 -1
  14. package/services/note/service.ts +0 -1
  15. package/types/config.ts +0 -3
  16. package/types/note.ts +0 -2
  17. package/vitepress/assets/icons/icon__cursor.svg +4 -0
  18. package/vitepress/assets/icons/index.ts +1 -0
  19. package/vitepress/components/Layout/AboutPanel.vue +0 -24
  20. package/vitepress/components/Layout/DocBeforeControls.vue +6 -14
  21. package/vitepress/components/Layout/DocFooter.vue +3 -3
  22. package/vitepress/components/Layout/Layout.vue +0 -30
  23. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +20 -11
  24. package/vitepress/components/Layout/homeReadme.data.ts +0 -48
  25. package/vitepress/components/MarkMap/MarkMap.vue +6 -13
  26. package/vitepress/components/Settings/Settings.vue +76 -73
  27. package/vitepress/components/SidebarCard/FolderTreeItems.vue +16 -6
  28. package/vitepress/components/SidebarCard/SidebarCard.vue +21 -27
  29. package/vitepress/components/composables/useLocalIde.ts +83 -0
  30. package/vitepress/components/constants.ts +5 -6
  31. package/vitepress/components/utils/vscodePaths.test.ts +22 -0
  32. package/vitepress/components/utils/vscodePaths.ts +24 -2
  33. package/vitepress/theme/styles/doc-layout.scss +4 -4
  34. package/services/timestamp/index.ts +0 -7
  35. package/services/timestamp/service.ts +0 -465
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-5QN44ES5.js";
44
+ } from "../chunk-XCWFZLER.js";
46
45
  import {
47
46
  ConfigManager,
48
- getConfigManager
49
- } from "../chunk-5Y5IYS6C.js";
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.FIX_TIMESTAMPS, COMMAND_NAMES.HELP]
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. 如果知识库创建时间在这12个月内,之前的月份补0
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(createdAt) {
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
- const isBeforeCreation = targetYear < createdYear || targetYear === createdYear && targetMonth < createdMonth;
841
- if (isBeforeCreation) {
842
- result[key] = 0;
843
- prevCount = 0;
844
- this.logger.info(`\u2713 ${key}: 0 \u7BC7\uFF08\u65E9\u4E8E\u521B\u5EFA\u65F6\u95F4\uFF09`);
845
- } else {
846
- try {
847
- const count = await this.getMonthCompletedCount(
848
- targetYear,
849
- targetMonth,
850
- prevCount
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
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ConfigManager,
3
3
  getConfigManager
4
- } from "./chunk-5Y5IYS6C.js";
4
+ } from "./chunk-UJG3UU5X.js";
5
5
  export {
6
6
  ConfigManager,
7
7
  getConfigManager
@@ -7,10 +7,10 @@ import {
7
7
  UpdateNoteConfigCommand,
8
8
  generateAnchor,
9
9
  logger
10
- } from "../../chunk-5QN44ES5.js";
10
+ } from "../../chunk-XCWFZLER.js";
11
11
  import {
12
12
  ConfigManager
13
- } from "../../chunk-5Y5IYS6C.js";
13
+ } from "../../chunk-UJG3UU5X.js";
14
14
 
15
15
  // vitepress/config/index.ts
16
16
  import fs3 from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tnotesjs/core",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "TNotes 知识库核心框架 —— 基于 VitePress 的笔记管理系统",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.17.1",
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',
@@ -133,7 +133,6 @@ export class NoteService {
133
133
  done: false,
134
134
  category,
135
135
  enableDiscussions,
136
- // created_at / updated_at 由 tn:push 时 fix-timestamps 自动写入
137
136
  }
138
137
  this.noteManager.writeNoteConfig(configPath, config)
139
138
 
package/types/config.ts CHANGED
@@ -54,7 +54,4 @@ export interface RootItem {
54
54
  completed_notes_count: Record<string, number>
55
55
  details: string
56
56
  link: string
57
- created_at?: number
58
- updated_at?: number
59
- days_since_birth?: number
60
57
  }
package/types/note.ts CHANGED
@@ -16,8 +16,6 @@ export interface NoteConfig {
16
16
  category?: string
17
17
  enableDiscussions: boolean
18
18
  description?: string // 笔记简介(一句话描述)
19
- created_at?: number
20
- updated_at?: number
21
19
  }
22
20
 
23
21
  /**
@@ -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="open in vscode"
17
+ :title="openCurrentTitle"
18
18
  target="_blank"
19
19
  >
20
- <img :src="icon__vscode" alt="open in vscode" />
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
 
@@ -4,7 +4,7 @@ vitepress/components/Layout/DocFooter.vue
4
4
 
5
5
  <template>
6
6
  <nav class="custom-doc-footer" v-if="prev || next">
7
- <div class="container">
7
+ <div class="pager-container">
8
8
  <div class="prev">
9
9
  <a v-if="prev" :href="prev.link" class="pager-link prev-link">
10
10
  <span class="desc">上一篇</span>
@@ -147,7 +147,7 @@ const next = computed(() => {
147
147
  padding-top: 32px;
148
148
  }
149
149
 
150
- .container {
150
+ .pager-container {
151
151
  display: flex;
152
152
  gap: 16px;
153
153
  }
@@ -219,7 +219,7 @@ const next = computed(() => {
219
219
 
220
220
  /* 响应式调整 */
221
221
  @media (max-width: 768px) {
222
- .container {
222
+ .pager-container {
223
223
  flex-direction: column;
224
224
  gap: 12px;
225
225
  }
@@ -32,10 +32,6 @@ vitepress/components/Layout/Layout.vue
32
32
  :vscode-notes-dir="vscodeNotesDir"
33
33
  :is-home-readme="isHomeReadme"
34
34
  :current-note-id="currentNoteId"
35
- :created_at="created_at"
36
- :updated_at="updated_at"
37
- :home-readme-created-at="homeReadmeCreatedAt"
38
- :home-readme-updated-at="homeReadmeUpdatedAt"
39
35
  :time-modal-open="timeModalOpen"
40
36
  :all-collapsed="allCollapsed"
41
37
  @open-time-modal="openTimeModal"
@@ -65,8 +61,6 @@ vitepress/components/Layout/Layout.vue
65
61
  v-model:editable-note-status="editableNoteStatus"
66
62
  v-model:editable-discussions-enabled="editableDiscussionsEnabled"
67
63
  v-model:title-error="titleError"
68
- :modal-created-at="modalCreatedAt"
69
- :modal-updated-at="modalUpdatedAt"
70
64
  :modal-github-url="modalGithubUrl"
71
65
  :modal-github-page-url="modalGithubPageUrl"
72
66
  :completion-percentage="completionPercentage"
@@ -116,10 +110,6 @@ vitepress/components/Layout/Layout.vue
116
110
  </AboutModal>
117
111
  </template>
118
112
  <template #doc-footer-before>
119
- <!-- <div class="footer-time-info">
120
- <p title="首次提交时间">首次提交时间:{{ formatDate(created_at) }}</p>
121
- <p title="最近提交时间">最近提交时间:{{ formatDate(updated_at) }}</p>
122
- </div> -->
123
113
  </template>
124
114
  <template #doc-after>
125
115
  <!-- 自定义 DocFooter -->
@@ -335,8 +325,6 @@ function getEmptyNoteConfig() {
335
325
  const isDiscussionsVisible = computed(
336
326
  () => currentNoteConfig.value.enableDiscussions,
337
327
  );
338
- const updated_at = computed(() => currentNoteConfig.value.updated_at);
339
- const created_at = computed(() => currentNoteConfig.value.created_at);
340
328
 
341
329
  // 判断是否为首页 README.md
342
330
  const isHomeReadme = computed(() => vpData.page.value.filePath === "README.md");
@@ -349,10 +337,6 @@ const completionPercentage = computed(() => {
349
337
  return Math.round((doneNotesLen.value / totalNotesLen.value) * 100);
350
338
  });
351
339
 
352
- // 首页 README.md 的时间戳
353
- const homeReadmeCreatedAt = computed(() => readmeData?.created_at);
354
- const homeReadmeUpdatedAt = computed(() => readmeData?.updated_at);
355
-
356
340
  // #region - Composables
357
341
  // 404 重定向
358
342
  const { showNotFound, decodedCurrentPath, initRedirectCheck } =
@@ -509,20 +493,6 @@ const modalGithubPageUrl = computed(() => {
509
493
  return "";
510
494
  });
511
495
 
512
- // modal 中显示的创建时间
513
- const modalCreatedAt = computed(() => {
514
- return modalIsHomeReadme.value
515
- ? homeReadmeCreatedAt.value
516
- : modalNoteConfig.value.created_at;
517
- });
518
-
519
- // modal 中显示的更新时间
520
- const modalUpdatedAt = computed(() => {
521
- return modalIsHomeReadme.value
522
- ? homeReadmeUpdatedAt.value
523
- : modalNoteConfig.value.updated_at;
524
- });
525
-
526
496
  function openTimeModal() {
527
497
  sidebarAboutNoteId.value = null;
528
498
  timeModalOpen.value = true;
@@ -1,40 +1,49 @@
1
1
  /**
2
2
  * vitepress/components/Layout/composables/useVSCodeIntegration.ts
3
3
  *
4
- * VSCode 集成和 GitHub 链接拦截
4
+ * 本地 IDE 集成和 GitHub 链接拦截
5
5
  */
6
6
 
7
7
  import { useData } from 'vitepress'
8
- import { ref } from 'vue'
8
+ import { computed, ref, watch } from 'vue'
9
9
 
10
+ import { useLocalIde } from '../../composables/useLocalIde'
10
11
  import { NOTES_DIR_KEY } from '../../constants'
11
- import {
12
- resolveNoteReadmePath,
13
- toVscodeFileUrl,
14
- } from '../../utils/vscodePaths'
12
+ import { resolveNoteReadmePath, toIdeFileUrl } from '../../utils/vscodePaths'
15
13
 
16
14
  import type { Router } from 'vitepress'
17
15
  import type { Ref, ComputedRef } from 'vue'
18
16
 
19
-
20
17
  /**
21
- * VSCode 集成和 GitHub 链接拦截
18
+ * 本地 IDE 集成和 GitHub 链接拦截
22
19
  */
23
20
  export function useVSCodeIntegration() {
24
21
  const vpData = useData()
25
- const vscodeNotesDir = ref('')
22
+ const { ide } = useLocalIde()
23
+ const noteLocalPath = ref('')
24
+
25
+ const vscodeNotesDir = computed(() => {
26
+ if (!noteLocalPath.value) return ''
27
+ // 显式依赖 ide,确保切换 IDE 后 href 更新
28
+ return toIdeFileUrl(noteLocalPath.value, ide.value)
29
+ })
26
30
 
27
- // 更新 VSCode 笔记目录
31
+ // 更新当前页面对应的本地笔记路径
28
32
  const updateVscodeNoteDir = () => {
29
33
  if (typeof window !== 'undefined') {
30
34
  const notesDir = localStorage.getItem(NOTES_DIR_KEY)
31
35
  const notePath = notesDir
32
36
  ? resolveNoteReadmePath(notesDir, vpData.page.value.relativePath)
33
37
  : null
34
- vscodeNotesDir.value = notePath ? toVscodeFileUrl(notePath) : ''
38
+ noteLocalPath.value = notePath || ''
35
39
  }
36
40
  }
37
41
 
42
+ watch(ide, () => {
43
+ // IDE 切换后重新解析路径(无需刷新页面)
44
+ updateVscodeNoteDir()
45
+ })
46
+
38
47
  // 拦截 home README 中的笔记链接,将 GitHub 链接转换为站点内跳转
39
48
  const interceptHomeReadmeLinks = (
40
49
  isHomeReadme: Ref<boolean> | ComputedRef<boolean>,
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * vitepress/components/Layout/homeReadme.data.ts
3
3
  */
4
- import { execSync } from 'node:child_process'
5
4
  import fs from 'node:fs'
6
5
  import path from 'node:path'
7
6
 
@@ -12,8 +11,6 @@ interface ReadmeData {
12
11
  doneNotesID: string[]
13
12
  doneNotesLen: number
14
13
  totalNotesLen: number
15
- created_at?: number
16
- updated_at?: number
17
14
  }
18
15
 
19
16
  export default {
@@ -28,15 +25,10 @@ export default {
28
25
 
29
26
  watchedFiles.forEach((file) => {
30
27
  if (file.endsWith('README.md')) {
31
- // console.log('file:', file) // => file: README.md
32
-
33
28
  const fileContent = fs.readFileSync(file, 'utf-8')
34
29
  const doneNotesID = getDoneNotesID(fileContent)
35
30
  const doneNotesLen = doneNotesID.length
36
31
 
37
- // 获取 git 仓库的时间戳
38
- const timestamps = getGitTimestamps()
39
-
40
32
  // 计算总笔记数
41
33
  const notesDir = path.join(path.dirname(file), 'notes')
42
34
  const totalNotesLen = getTotalNotesCount(notesDir)
@@ -46,7 +38,6 @@ export default {
46
38
  doneNotesID,
47
39
  doneNotesLen,
48
40
  totalNotesLen,
49
- ...timestamps,
50
41
  }
51
42
  }
52
43
  })
@@ -74,45 +65,6 @@ function getTotalNotesCount(notesDir: string): number {
74
65
  }
75
66
  }
76
67
 
77
- /**
78
- * 获取 git 仓库的时间戳
79
- * @returns 包含 created_at 和 updated_at 的对象
80
- */
81
- function getGitTimestamps(): {
82
- created_at?: number
83
- updated_at?: number
84
- } {
85
- const now = Date.now()
86
- let created_at = now
87
- let updated_at = now
88
-
89
- try {
90
- // 仓库的首次提交时间(最早的提交)
91
- // 先获取所有提交,然后取第一行
92
- const createdStdout = execSync(`git log --reverse --format=%ct`, {
93
- encoding: 'utf-8',
94
- })
95
- const createdTs = createdStdout.toString().trim().split('\n')[0]
96
- if (createdTs) {
97
- created_at = parseInt(createdTs, 10) * 1000
98
- }
99
-
100
- // 仓库的最近一次提交时间
101
- const updatedStdout = execSync(`git log -1 --format=%ct`, {
102
- encoding: 'utf-8',
103
- })
104
- const updatedTs = updatedStdout.toString().trim()
105
- if (updatedTs) {
106
- updated_at = parseInt(updatedTs, 10) * 1000
107
- }
108
-
109
- return { created_at, updated_at }
110
- } catch (error) {
111
- console.error(`获取 git 仓库时间戳失败:`, error)
112
- return { created_at, updated_at }
113
- }
114
- }
115
-
116
68
  /**
117
69
  * 返回已完成的笔记的 ID 列表
118
70
  * @param fileContent 文件内容