@tnotesjs/core 0.1.15 → 0.1.16

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 (115) hide show
  1. package/README.md +87 -87
  2. package/dist/{chunk-FVKQPBPP.js → chunk-4MO7F7Q4.js} +2005 -1769
  3. package/dist/cli/index.js +1 -1
  4. package/dist/vitepress/config/index.js +212 -4
  5. package/package.json +1 -1
  6. package/types/config.ts +61 -61
  7. package/types/index.ts +11 -11
  8. package/types/note.ts +33 -33
  9. package/vitepress/assets/icons/icon__check.svg +3 -3
  10. package/vitepress/assets/icons/icon__clipboard.svg +7 -7
  11. package/vitepress/assets/icons/icon__close.svg +1 -1
  12. package/vitepress/assets/icons/icon__collapse.svg +1 -1
  13. package/vitepress/assets/icons/icon__confirm.svg +1 -1
  14. package/vitepress/assets/icons/icon__copy.svg +4 -4
  15. package/vitepress/assets/icons/icon__focus.svg +1 -1
  16. package/vitepress/assets/icons/icon__fold.svg +3 -3
  17. package/vitepress/assets/icons/icon__folder.svg +1 -1
  18. package/vitepress/assets/icons/icon__fullscreen.svg +1 -1
  19. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -1
  20. package/vitepress/assets/icons/icon__github.svg +3 -3
  21. package/vitepress/assets/icons/icon__mindmap.svg +1 -1
  22. package/vitepress/assets/icons/icon__next.svg +1 -1
  23. package/vitepress/assets/icons/icon__number_gray.svg +1 -1
  24. package/vitepress/assets/icons/icon__number_purple.svg +1 -1
  25. package/vitepress/assets/icons/icon__prev.svg +1 -1
  26. package/vitepress/assets/icons/icon__restore.svg +1 -1
  27. package/vitepress/assets/icons/icon__rotate.svg +4 -4
  28. package/vitepress/assets/icons/icon__search.svg +1 -1
  29. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -1
  30. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -1
  31. package/vitepress/assets/icons/icon__totop.svg +5 -5
  32. package/vitepress/assets/icons/icon__vscode.svg +5 -5
  33. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -1
  34. package/vitepress/assets/icons/icon__zoom_in.svg +1 -1
  35. package/vitepress/assets/icons/icon__zoom_out.svg +1 -1
  36. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -1
  37. package/vitepress/assets/icons/index.ts +39 -39
  38. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -20
  39. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +373 -373
  40. package/vitepress/components/CodeBlockFullscreen/index.ts +115 -115
  41. package/vitepress/components/CodeBlockFullscreen/styles.css +64 -64
  42. package/vitepress/components/Discussions/Discussions.module.scss +32 -32
  43. package/vitepress/components/Discussions/Discussions.vue +211 -211
  44. package/vitepress/components/EnWordList/EnWordList.module.scss +124 -124
  45. package/vitepress/components/EnWordList/EnWordList.vue +543 -543
  46. package/vitepress/components/EnWordList/RightClickMenu.module.scss +22 -22
  47. package/vitepress/components/EnWordList/RightClickMenu.vue +66 -66
  48. package/vitepress/components/Footprints/Footprints.module.scss +93 -93
  49. package/vitepress/components/Footprints/Footprints.vue +377 -377
  50. package/vitepress/components/Layout/AboutModal.module.scss +233 -233
  51. package/vitepress/components/Layout/AboutModal.vue +105 -105
  52. package/vitepress/components/Layout/AboutPanel.vue +270 -270
  53. package/vitepress/components/Layout/ContentCollapse.vue +603 -603
  54. package/vitepress/components/Layout/CustomSidebar.vue +878 -608
  55. package/vitepress/components/Layout/DocBeforeControls.vue +149 -149
  56. package/vitepress/components/Layout/DocFooter.vue +233 -233
  57. package/vitepress/components/Layout/ImagePreview.module.scss +201 -201
  58. package/vitepress/components/Layout/ImagePreview.vue +281 -281
  59. package/vitepress/components/Layout/Layout.module.scss +661 -661
  60. package/vitepress/components/Layout/Layout.vue +618 -559
  61. package/vitepress/components/Layout/NoteStatus.vue +140 -140
  62. package/vitepress/components/Layout/SidebarItems.vue +1083 -268
  63. package/vitepress/components/Layout/SidebarNavBefore.vue +171 -108
  64. package/vitepress/components/Layout/Swiper.vue +167 -167
  65. package/vitepress/components/Layout/ToggleFullContent.module.scss +11 -11
  66. package/vitepress/components/Layout/ToggleFullContent.vue +35 -35
  67. package/vitepress/components/Layout/ToggleSidebar.module.scss +11 -11
  68. package/vitepress/components/Layout/ToggleSidebar.vue +36 -36
  69. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -88
  70. package/vitepress/components/Layout/composables/useNoteConfig.ts +119 -119
  71. package/vitepress/components/Layout/composables/useNoteSave.ts +178 -178
  72. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -85
  73. package/vitepress/components/Layout/composables/useRedirect.ts +113 -113
  74. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -85
  75. package/vitepress/components/Layout/homeReadme.data.ts +124 -124
  76. package/vitepress/components/MarkMap/MarkMap.module.scss +159 -159
  77. package/vitepress/components/MarkMap/MarkMap.vue +405 -405
  78. package/vitepress/components/Mermaid/Mermaid.module.scss +275 -275
  79. package/vitepress/components/Mermaid/Mermaid.vue +365 -365
  80. package/vitepress/components/NotesTable/NotesTable.module.scss +77 -77
  81. package/vitepress/components/NotesTable/NotesTable.vue +98 -98
  82. package/vitepress/components/NotesTable/README.md +67 -67
  83. package/vitepress/components/Settings/Settings.module.scss +375 -433
  84. package/vitepress/components/Settings/Settings.vue +376 -306
  85. package/vitepress/components/SidebarCard/MindMapView.vue +484 -484
  86. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -108
  87. package/vitepress/components/SidebarCard/SidebarCard.vue +946 -946
  88. package/vitepress/components/Tooltip/Tooltip.vue +70 -70
  89. package/vitepress/components/constants.ts +109 -91
  90. package/vitepress/components/notesConfig.data.ts +73 -73
  91. package/vitepress/components/sidebar.data.ts +59 -59
  92. package/vitepress/components/tnotes-config.data.ts +21 -21
  93. package/vitepress/components/utils.ts +26 -26
  94. package/vitepress/config/index.ts +169 -167
  95. package/vitepress/configs/constants.ts +26 -26
  96. package/vitepress/configs/head.config.ts +25 -25
  97. package/vitepress/configs/index.ts +9 -9
  98. package/vitepress/configs/markdown-it.d.ts +23 -23
  99. package/vitepress/configs/markdown.config.ts +366 -366
  100. package/vitepress/configs/theme.config.ts +108 -108
  101. package/vitepress/plugins/buildProgressPlugin.ts +434 -434
  102. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -107
  103. package/vitepress/plugins/renameNotePlugin.ts +68 -68
  104. package/vitepress/plugins/sidebarStructurePlugin.ts +260 -0
  105. package/vitepress/plugins/updateConfigPlugin.ts +63 -63
  106. package/vitepress/theme/index.ts +137 -137
  107. package/vitepress/theme/styles/base.scss +50 -50
  108. package/vitepress/theme/styles/components/404.scss +31 -31
  109. package/vitepress/theme/styles/components/collapse.scss +175 -175
  110. package/vitepress/theme/styles/components/markmap.scss +101 -101
  111. package/vitepress/theme/styles/components/swiper.scss +255 -255
  112. package/vitepress/theme/styles/index.scss +25 -25
  113. package/vitepress/theme/styles/layout.scss +70 -62
  114. package/vitepress/theme/styles/utilities.scss +39 -39
  115. package/vitepress/theme/styles/vitepress-override.scss +25 -25
@@ -1,307 +1,377 @@
1
- <template>
2
- <div :class="$style.settingsWrapper">
3
- <!-- 本地路径配置 -->
4
- <div :class="$style.settingItem">
5
- <div :class="$style.itemHeader">
6
- <div :class="$style.itemTitle">
7
- <span :class="$style.itemIcon">📁</span>
8
- <span :class="$style.itemName">本地知识库路径</span>
9
- </div>
10
- <span :class="$style.statusBadge" v-if="path">✓</span>
11
- </div>
12
-
13
- <div :class="$style.helpText">
14
- 适用于 PC 桌面环境,需要本地安装 VS Code
15
- 编辑器。配置后可在侧边栏快速打开笔记。
16
- </div>
17
-
18
- <div :class="$style.inputGroup">
19
- <input
20
- v-model="path"
21
- type="text"
22
- placeholder="例如: C:\notes 或 /Users/username/notes"
23
- :class="$style.input"
24
- />
25
- <button
26
- v-if="path"
27
- @click="clearPath"
28
- :class="$style.clearBtn"
29
- title="清空"
30
- >
31
-
32
- </button>
33
- </div>
34
- </div>
35
-
36
- <!-- 内容宽度配置 -->
37
- <div :class="$style.settingItem">
38
- <div :class="$style.itemHeader">
39
- <div :class="$style.itemTitle">
40
- <span :class="$style.itemIcon">📏</span>
41
- <span :class="$style.itemName">内容区宽度</span>
42
- </div>
43
- <span :class="$style.currentValue">{{ contentWidth }}</span>
44
- </div>
45
-
46
- <div :class="$style.helpText">
47
- 调整文章内容区域的最大宽度(全屏模式下不限制宽度)。
48
- </div>
49
-
50
- <select
51
- v-model="contentWidth"
52
- @change="applyContentWidth"
53
- :class="$style.select"
54
- >
55
- <option value="688px">标准 (688px)</option>
56
- <option value="755px">较大 (755px)</option>
57
- </select>
58
- </div>
59
-
60
- <!-- 侧边栏配置 -->
61
- <div :class="$style.settingItem">
62
- <div :class="$style.itemHeader">
63
- <div :class="$style.itemTitle">
64
- <span :class="$style.itemIcon">📑</span>
65
- <span :class="$style.itemName">侧边栏配置</span>
66
- </div>
67
- <span :class="$style.statusBadge" v-if="showNoteId">显示编号</span>
68
- </div>
69
-
70
- <div :class="$style.helpText">
71
- 控制侧边栏中是否显示笔记编号(如 "0001. ")。
72
- </div>
73
-
74
- <div :class="$style.field">
75
- <label :class="$style.checkboxLabel">
76
- <input
77
- v-model="showNoteId"
78
- type="checkbox"
79
- :class="$style.checkbox"
80
- />
81
- <span>显示笔记编号</span>
82
- </label>
83
- <div :class="$style.fieldHelp">
84
- 勾选后,侧边栏将显示完整的笔记标题(包含编号),例如 "0001. TNotes
85
- 简介"
86
- </div>
87
- </div>
88
- </div>
89
-
90
- <!-- MarkMap 配置 -->
91
- <div :class="$style.settingItem">
92
- <div :class="$style.itemHeader">
93
- <div :class="$style.itemTitle">
94
- <span :class="$style.itemIcon">💡</span>
95
- <span :class="$style.itemName">MarkMap 思维导图</span>
96
- </div>
97
- </div>
98
-
99
- <div :class="$style.helpText">配置思维导图的默认显示效果。</div>
100
-
101
- <div :class="$style.field">
102
- <div :class="$style.fieldLabel">
103
- <span>分支主题</span>
104
- </div>
105
- <div :class="$style.fieldHelp">选择思维导图分支的配色方案</div>
106
- <select v-model="markmapTheme" :class="$style.select">
107
- <option value="default">默认主题</option>
108
- <option value="colorful">多彩主题</option>
109
- <option value="dark">深色主题</option>
110
- </select>
111
- </div>
112
-
113
- <div :class="$style.field">
114
- <div :class="$style.fieldLabel">
115
- <span>展开层级</span>
116
- </div>
117
- <div :class="$style.fieldHelp">
118
- 设置思维导图初始展开的层级深度(1-100)
119
- </div>
120
- <div :class="$style.inputWithUnit">
121
- <input
122
- v-model.number="markmapExpandLevel"
123
- type="number"
124
- min="1"
125
- max="100"
126
- :class="$style.input"
127
- />
128
- <span :class="$style.unit">层</span>
129
- </div>
130
- </div>
131
- </div>
132
-
133
- <!-- 操作栏 -->
134
- <div :class="$style.actionBar">
135
- <button
136
- @click="save"
137
- :class="[$style.saveBtn, { [$style.disabled]: !hasChanges }]"
138
- :disabled="!hasChanges"
139
- >
140
- {{ saveText }}
141
- </button>
142
- <button v-if="hasChanges" @click="reset" :class="$style.resetBtn">
143
- 重置
144
- </button>
145
- </div>
146
-
147
- <!-- 保存成功提示 -->
148
- <Transition name="toast">
149
- <div v-if="showSuccessToast" :class="$style.toast">✓ 保存成功</div>
150
- </Transition>
151
- </div>
152
- </template>
153
-
154
- <script setup lang="ts">
1
+ <template>
2
+ <div :class="$style.settingsWrapper">
3
+ <section :class="$style.settingsSection">
4
+ <div :class="$style.settingItem">
5
+ <div :class="$style.settingMeta">
6
+ <div :class="$style.labelLine">
7
+ <span :class="$style.itemName">本地知识库路径</span>
8
+ <span :class="$style.infoWrap">
9
+ <span :class="$style.infoIcon">?</span>
10
+ <span :class="$style.tooltip">
11
+ 适用于 PC 桌面环境,需要本地安装 VS Code。配置后可从页面快速打开本地笔记目录。
12
+ </span>
13
+ </span>
14
+ </div>
15
+ <span v-if="path" :class="$style.statusText">已配置</span>
16
+ </div>
17
+
18
+ <div :class="$style.inputGroup">
19
+ <input
20
+ v-model="path"
21
+ type="text"
22
+ placeholder="/Users/username/notes"
23
+ :class="$style.input"
24
+ />
25
+ <button
26
+ v-if="path"
27
+ @click="clearPath"
28
+ :class="$style.clearBtn"
29
+ title="清空"
30
+ type="button"
31
+ >
32
+ ×
33
+ </button>
34
+ </div>
35
+ </div>
36
+
37
+ <div :class="$style.settingItem">
38
+ <div :class="$style.settingMeta">
39
+ <div :class="$style.labelLine">
40
+ <span :class="$style.itemName">内容区宽度</span>
41
+ <span :class="$style.infoWrap">
42
+ <span :class="$style.infoIcon">?</span>
43
+ <span :class="$style.tooltip">
44
+ 调整文章内容区域的最大宽度。全屏模式下不会限制宽度。
45
+ </span>
46
+ </span>
47
+ </div>
48
+ <span :class="$style.statusText">{{ contentWidth }}</span>
49
+ </div>
50
+
51
+ <select v-model="contentWidth" :class="$style.select">
52
+ <option value="688px">标准</option>
53
+ <option value="755px">较大</option>
54
+ </select>
55
+ </div>
56
+
57
+ <div :class="$style.settingItem">
58
+ <div :class="$style.settingMeta">
59
+ <div :class="$style.labelLine">
60
+ <span :class="$style.itemName">侧边栏目录</span>
61
+ <span :class="$style.infoWrap">
62
+ <span :class="$style.infoIcon">?</span>
63
+ <span :class="$style.tooltip">
64
+ 控制侧边栏笔记编号显示,以及目录行距密度。保存后会刷新页面以应用侧边栏配置。
65
+ </span>
66
+ </span>
67
+ </div>
68
+ <span v-if="showNoteId" :class="$style.statusText">显示编号</span>
69
+ </div>
70
+
71
+ <div :class="$style.fieldStack">
72
+ <label :class="$style.checkRow">
73
+ <input
74
+ v-model="showNoteId"
75
+ type="checkbox"
76
+ :class="$style.checkbox"
77
+ />
78
+ <span>显示笔记编号</span>
79
+ </label>
80
+
81
+ <div :class="$style.controlRow">
82
+ <span :class="$style.controlLabel">目录风格</span>
83
+ <div :class="$style.segmented">
84
+ <label
85
+ v-for="option in sidebarDensityOptions"
86
+ :key="option.value"
87
+ :class="[
88
+ $style.segment,
89
+ { [$style.activeSegment]: sidebarDensity === option.value },
90
+ ]"
91
+ >
92
+ <input
93
+ v-model="sidebarDensity"
94
+ type="radio"
95
+ name="sidebar-density"
96
+ :value="option.value"
97
+ />
98
+ <span>{{ option.label }}</span>
99
+ </label>
100
+ </div>
101
+ </div>
102
+
103
+ <div :class="$style.prefixGrid">
104
+ <label :class="$style.field">
105
+ <span :class="$style.controlLabel">已完成笔记的前缀</span>
106
+ <input
107
+ v-model="donePrefix"
108
+ type="text"
109
+ placeholder="可留空"
110
+ :class="[$style.input, $style.prefixInput]"
111
+ />
112
+ </label>
113
+
114
+ <label :class="$style.field">
115
+ <span :class="$style.controlLabel">未完成笔记前缀</span>
116
+ <input
117
+ v-model="undonePrefix"
118
+ type="text"
119
+ placeholder="可留空"
120
+ :class="[$style.input, $style.prefixInput]"
121
+ />
122
+ </label>
123
+ </div>
124
+ </div>
125
+ </div>
126
+
127
+ <div :class="$style.settingItem">
128
+ <div :class="$style.settingMeta">
129
+ <div :class="$style.labelLine">
130
+ <span :class="$style.itemName">MarkMap 思维导图</span>
131
+ <span :class="$style.infoWrap">
132
+ <span :class="$style.infoIcon">?</span>
133
+ <span :class="$style.tooltip">
134
+ 配置 MarkMap 的默认分支配色和初始展开层级。
135
+ </span>
136
+ </span>
137
+ </div>
138
+ </div>
139
+
140
+ <div :class="$style.inlineGrid">
141
+ <label :class="$style.field">
142
+ <span :class="$style.controlLabel">分支主题</span>
143
+ <select v-model="markmapTheme" :class="$style.select">
144
+ <option value="default">默认</option>
145
+ <option value="colorful">多彩</option>
146
+ <option value="dark">深色</option>
147
+ </select>
148
+ </label>
149
+
150
+ <label :class="$style.field">
151
+ <span :class="$style.controlLabel">展开层级</span>
152
+ <div :class="$style.inputWithUnit">
153
+ <input
154
+ v-model.number="markmapExpandLevel"
155
+ type="number"
156
+ min="1"
157
+ max="100"
158
+ :class="$style.input"
159
+ />
160
+ <span :class="$style.unit">层</span>
161
+ </div>
162
+ </label>
163
+ </div>
164
+ </div>
165
+ </section>
166
+
167
+ <div :class="$style.actionBar">
168
+ <button v-if="hasChanges" @click="reset" :class="$style.resetBtn">
169
+ 重置
170
+ </button>
171
+ <button
172
+ @click="save"
173
+ :class="[$style.saveBtn, { [$style.disabled]: !hasChanges }]"
174
+ :disabled="!hasChanges"
175
+ >
176
+ {{ saveText }}
177
+ </button>
178
+ </div>
179
+
180
+ <Transition name="toast">
181
+ <div v-if="showSuccessToast" :class="$style.toast">保存成功</div>
182
+ </Transition>
183
+ </div>
184
+ </template>
185
+
186
+ <script setup lang="ts">
155
187
  import { ref, computed, onMounted } from 'vue'
156
-
157
- import {
158
- NOTES_DIR_KEY,
159
- MARKMAP_THEME_KEY,
160
- MARKMAP_EXPAND_LEVEL_KEY,
161
- SIDEBAR_SHOW_NOTE_ID_KEY,
162
- } from '../constants'
163
- // @ts-expect-error - VitePress Data Loader
164
- import { data as tnotesConfig } from '../tnotes-config.data'
165
-
166
- const CONTENT_WIDTH_KEY = 'tnotes-content-width'
167
-
168
- // ===================================
169
- // #region 响应式数据
170
- // ===================================
171
- const path = ref('')
172
- const originalPath = ref('')
173
- const markmapTheme = ref('default')
174
- const originalMarkmapTheme = ref('default')
175
- const markmapExpandLevel = ref(5)
176
- const originalMarkmapExpandLevel = ref(5)
177
- const contentWidth = ref('688px')
178
- const showNoteId = ref(false)
179
- const originalShowNoteId = ref(false)
180
- const showSuccessToast = ref(false)
181
- // #endregion
182
-
183
- // ===================================
184
- // #region 计算属性
185
- // ===================================
186
- const hasChanges = computed(
187
- () =>
188
- path.value !== originalPath.value ||
189
- markmapTheme.value !== originalMarkmapTheme.value ||
190
- markmapExpandLevel.value !== originalMarkmapExpandLevel.value ||
191
- showNoteId.value !== originalShowNoteId.value
192
- )
193
-
194
- const saveText = computed(() => {
195
- if (!hasChanges.value) return '无更改'
196
- return '保存配置'
197
- })
198
- // #endregion
199
-
200
- // ===================================
201
- // #region 生命周期
202
- // ===================================
203
- onMounted(() => {
204
- if (typeof window !== 'undefined') {
205
- const savedPath = localStorage.getItem(NOTES_DIR_KEY) || ''
206
- path.value = savedPath
207
- originalPath.value = savedPath
208
-
209
- const savedTheme = localStorage.getItem(MARKMAP_THEME_KEY) || 'default'
210
- markmapTheme.value = savedTheme
211
- originalMarkmapTheme.value = savedTheme
212
-
213
- const savedLevel = localStorage.getItem(MARKMAP_EXPAND_LEVEL_KEY) || '5'
214
- markmapExpandLevel.value = parseInt(savedLevel)
215
- originalMarkmapExpandLevel.value = parseInt(savedLevel)
216
-
217
- const savedWidth = localStorage.getItem(CONTENT_WIDTH_KEY) || '688px'
218
- contentWidth.value = savedWidth
219
- applyContentWidth()
220
-
221
- // 读取侧边栏显示笔记 ID 配置
222
- // 优先使用 localStorage 中的用户自定义配置,否则使用配置文件中的默认值
223
- const savedShowNoteId = localStorage.getItem(SIDEBAR_SHOW_NOTE_ID_KEY)
224
- if (savedShowNoteId !== null) {
225
- showNoteId.value = savedShowNoteId === 'true'
226
- } else {
227
- showNoteId.value = tnotesConfig?.sidebarShowNoteId ?? false
228
- }
229
- originalShowNoteId.value = showNoteId.value
230
- }
231
- })
232
- // #endregion
233
-
234
- // ===================================
235
- // #region 事件处理
236
- // ===================================
237
- function handlePathChange() {
238
- // 可以在这里添加路径格式验证
239
- }
240
-
241
- function clearPath() {
242
- path.value = ''
243
- }
244
-
245
- function save() {
246
- if (!hasChanges.value) return
247
-
248
- try {
249
- // 保存前先记录是否需要刷新页面
250
- const needReload = showNoteId.value !== originalShowNoteId.value
251
-
252
- localStorage.setItem(NOTES_DIR_KEY, path.value)
253
- localStorage.setItem(MARKMAP_THEME_KEY, markmapTheme.value)
254
- localStorage.setItem(
255
- MARKMAP_EXPAND_LEVEL_KEY,
256
- markmapExpandLevel.value.toString()
257
- )
258
- localStorage.setItem(SIDEBAR_SHOW_NOTE_ID_KEY, showNoteId.value.toString())
259
-
260
- console.log('💾 [Settings] Saved showNoteId:', showNoteId.value)
261
-
262
- originalPath.value = path.value
263
- originalMarkmapTheme.value = markmapTheme.value
264
- originalMarkmapExpandLevel.value = markmapExpandLevel.value
265
- originalShowNoteId.value = showNoteId.value
266
-
267
- // 显示成功提示
268
- showSuccessToast.value = true
269
- setTimeout(() => {
270
- showSuccessToast.value = false
271
- }, 3000)
272
-
273
- // 触发页面刷新以应用侧边栏配置变化
274
- if (needReload) {
275
- console.log(
276
- '🔄 [Settings] Reloading page to apply sidebar config change...'
277
- )
278
- setTimeout(() => {
279
- window.location.reload()
280
- }, 500)
281
- }
282
- } catch (error) {
283
- console.error('保存配置失败:', error)
284
- alert('保存失败,请检查浏览器设置')
285
- }
286
- }
287
-
288
- function reset() {
289
- path.value = originalPath.value
290
- markmapTheme.value = originalMarkmapTheme.value
291
- markmapExpandLevel.value = originalMarkmapExpandLevel.value
292
- showNoteId.value = originalShowNoteId.value
293
- }
294
-
295
- // 应用内容宽度(通过 CSS 变量)
296
- function applyContentWidth() {
297
- if (typeof document === 'undefined') return
298
- document.documentElement.style.setProperty(
299
- '--tn-content-width',
300
- contentWidth.value
301
- )
302
- localStorage.setItem(CONTENT_WIDTH_KEY, contentWidth.value)
303
- }
304
- // #endregion
305
- </script>
306
-
307
- <style module src="./Settings.module.scss"></style>
188
+
189
+ import {
190
+ NOTES_DIR_KEY,
191
+ MARKMAP_THEME_KEY,
192
+ MARKMAP_EXPAND_LEVEL_KEY,
193
+ SIDEBAR_SHOW_NOTE_ID_KEY,
194
+ SIDEBAR_DENSITY_KEY,
195
+ SIDEBAR_DONE_PREFIX_KEY,
196
+ SIDEBAR_UNDONE_PREFIX_KEY,
197
+ } from '../constants'
198
+ // @ts-expect-error - VitePress Data Loader
199
+ import { data as tnotesConfig } from '../tnotes-config.data'
200
+
201
+ type SidebarDensity = 'compact' | 'default' | 'loose'
202
+
203
+ const CONTENT_WIDTH_KEY = 'tnotes-content-width'
204
+ const DEFAULT_SIDEBAR_DENSITY: SidebarDensity = 'default'
205
+ const DEFAULT_DONE_PREFIX = ''
206
+ const DEFAULT_UNDONE_PREFIX = ''
207
+ const sidebarDensityOptions: Array<{
208
+ label: string
209
+ value: SidebarDensity
210
+ }> = [
211
+ { label: '紧凑', value: 'compact' },
212
+ { label: '默认', value: 'default' },
213
+ { label: '宽松', value: 'loose' },
214
+ ]
215
+
216
+ const path = ref('')
217
+ const originalPath = ref('')
218
+ const markmapTheme = ref('default')
219
+ const originalMarkmapTheme = ref('default')
220
+ const markmapExpandLevel = ref(5)
221
+ const originalMarkmapExpandLevel = ref(5)
222
+ const contentWidth = ref('688px')
223
+ const originalContentWidth = ref('688px')
224
+ const showNoteId = ref(false)
225
+ const originalShowNoteId = ref(false)
226
+ const sidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
227
+ const originalSidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
228
+ const donePrefix = ref(DEFAULT_DONE_PREFIX)
229
+ const originalDonePrefix = ref(DEFAULT_DONE_PREFIX)
230
+ const undonePrefix = ref(DEFAULT_UNDONE_PREFIX)
231
+ const originalUndonePrefix = ref(DEFAULT_UNDONE_PREFIX)
232
+ const showSuccessToast = ref(false)
233
+
234
+ const hasChanges = computed(
235
+ () =>
236
+ path.value !== originalPath.value ||
237
+ markmapTheme.value !== originalMarkmapTheme.value ||
238
+ markmapExpandLevel.value !== originalMarkmapExpandLevel.value ||
239
+ contentWidth.value !== originalContentWidth.value ||
240
+ showNoteId.value !== originalShowNoteId.value ||
241
+ sidebarDensity.value !== originalSidebarDensity.value ||
242
+ donePrefix.value !== originalDonePrefix.value ||
243
+ undonePrefix.value !== originalUndonePrefix.value,
244
+ )
245
+
246
+ const saveText = computed(() => {
247
+ if (!hasChanges.value) return '无更改'
248
+ return '保存'
249
+ })
250
+
251
+ onMounted(() => {
252
+ if (typeof window === 'undefined') return
253
+
254
+ const savedPath = localStorage.getItem(NOTES_DIR_KEY) || ''
255
+ path.value = savedPath
256
+ originalPath.value = savedPath
257
+
258
+ const savedTheme = localStorage.getItem(MARKMAP_THEME_KEY) || 'default'
259
+ markmapTheme.value = savedTheme
260
+ originalMarkmapTheme.value = savedTheme
261
+
262
+ const savedLevel = localStorage.getItem(MARKMAP_EXPAND_LEVEL_KEY) || '5'
263
+ markmapExpandLevel.value = parseInt(savedLevel, 10)
264
+ originalMarkmapExpandLevel.value = markmapExpandLevel.value
265
+
266
+ const savedWidth = localStorage.getItem(CONTENT_WIDTH_KEY) || '688px'
267
+ contentWidth.value = savedWidth
268
+ originalContentWidth.value = savedWidth
269
+ applyContentWidth()
270
+
271
+ const savedShowNoteId = localStorage.getItem(SIDEBAR_SHOW_NOTE_ID_KEY)
272
+ showNoteId.value =
273
+ savedShowNoteId !== null
274
+ ? savedShowNoteId === 'true'
275
+ : tnotesConfig?.sidebarShowNoteId ?? false
276
+ originalShowNoteId.value = showNoteId.value
277
+
278
+ const savedSidebarDensity = normalizeSidebarDensity(
279
+ localStorage.getItem(SIDEBAR_DENSITY_KEY),
280
+ )
281
+ sidebarDensity.value = savedSidebarDensity
282
+ originalSidebarDensity.value = savedSidebarDensity
283
+
284
+ const savedDonePrefix = localStorage.getItem(SIDEBAR_DONE_PREFIX_KEY)
285
+ donePrefix.value =
286
+ savedDonePrefix !== null ? savedDonePrefix : DEFAULT_DONE_PREFIX
287
+ originalDonePrefix.value = donePrefix.value
288
+
289
+ const savedUndonePrefix = localStorage.getItem(SIDEBAR_UNDONE_PREFIX_KEY)
290
+ undonePrefix.value =
291
+ savedUndonePrefix !== null ? savedUndonePrefix : DEFAULT_UNDONE_PREFIX
292
+ originalUndonePrefix.value = undonePrefix.value
293
+ })
294
+
295
+ function normalizeSidebarDensity(value: string | null): SidebarDensity {
296
+ if (value === 'compact' || value === 'default' || value === 'loose') {
297
+ return value
298
+ }
299
+
300
+ return DEFAULT_SIDEBAR_DENSITY
301
+ }
302
+
303
+ function clearPath() {
304
+ path.value = ''
305
+ }
306
+
307
+ function save() {
308
+ if (!hasChanges.value) return
309
+
310
+ try {
311
+ const needReload =
312
+ showNoteId.value !== originalShowNoteId.value ||
313
+ sidebarDensity.value !== originalSidebarDensity.value ||
314
+ donePrefix.value !== originalDonePrefix.value ||
315
+ undonePrefix.value !== originalUndonePrefix.value
316
+
317
+ localStorage.setItem(NOTES_DIR_KEY, path.value)
318
+ localStorage.setItem(MARKMAP_THEME_KEY, markmapTheme.value)
319
+ localStorage.setItem(
320
+ MARKMAP_EXPAND_LEVEL_KEY,
321
+ markmapExpandLevel.value.toString(),
322
+ )
323
+ localStorage.setItem(CONTENT_WIDTH_KEY, contentWidth.value)
324
+ localStorage.setItem(SIDEBAR_SHOW_NOTE_ID_KEY, showNoteId.value.toString())
325
+ localStorage.setItem(SIDEBAR_DENSITY_KEY, sidebarDensity.value)
326
+ localStorage.setItem(SIDEBAR_DONE_PREFIX_KEY, donePrefix.value)
327
+ localStorage.setItem(SIDEBAR_UNDONE_PREFIX_KEY, undonePrefix.value)
328
+ applyContentWidth()
329
+
330
+ originalPath.value = path.value
331
+ originalMarkmapTheme.value = markmapTheme.value
332
+ originalMarkmapExpandLevel.value = markmapExpandLevel.value
333
+ originalContentWidth.value = contentWidth.value
334
+ originalShowNoteId.value = showNoteId.value
335
+ originalSidebarDensity.value = sidebarDensity.value
336
+ originalDonePrefix.value = donePrefix.value
337
+ originalUndonePrefix.value = undonePrefix.value
338
+
339
+ showSuccessToast.value = true
340
+ setTimeout(() => {
341
+ showSuccessToast.value = false
342
+ }, 2400)
343
+
344
+ if (needReload) {
345
+ setTimeout(() => {
346
+ window.location.reload()
347
+ }, 450)
348
+ }
349
+ } catch (error) {
350
+ console.error('保存配置失败:', error)
351
+ alert('保存失败,请检查浏览器设置')
352
+ }
353
+ }
354
+
355
+ function reset() {
356
+ path.value = originalPath.value
357
+ markmapTheme.value = originalMarkmapTheme.value
358
+ markmapExpandLevel.value = originalMarkmapExpandLevel.value
359
+ contentWidth.value = originalContentWidth.value
360
+ showNoteId.value = originalShowNoteId.value
361
+ sidebarDensity.value = originalSidebarDensity.value
362
+ donePrefix.value = originalDonePrefix.value
363
+ undonePrefix.value = originalUndonePrefix.value
364
+ applyContentWidth()
365
+ }
366
+
367
+ function applyContentWidth() {
368
+ if (typeof document === 'undefined') return
369
+
370
+ document.documentElement.style.setProperty(
371
+ '--tn-content-width',
372
+ contentWidth.value,
373
+ )
374
+ }
375
+ </script>
376
+
377
+ <style module src="./Settings.module.scss"></style>