@tnotesjs/core 0.1.22 → 0.1.23

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 (101) hide show
  1. package/README.md +87 -87
  2. package/package.json +1 -1
  3. package/types/config.ts +60 -60
  4. package/types/index.ts +11 -11
  5. package/types/note.ts +33 -33
  6. package/vitepress/assets/icons/icon__check.svg +3 -3
  7. package/vitepress/assets/icons/icon__clipboard.svg +7 -7
  8. package/vitepress/assets/icons/icon__close.svg +1 -1
  9. package/vitepress/assets/icons/icon__collapse.svg +1 -1
  10. package/vitepress/assets/icons/icon__confirm.svg +1 -1
  11. package/vitepress/assets/icons/icon__copy.svg +4 -4
  12. package/vitepress/assets/icons/icon__focus.svg +1 -1
  13. package/vitepress/assets/icons/icon__fold.svg +3 -3
  14. package/vitepress/assets/icons/icon__folder.svg +1 -1
  15. package/vitepress/assets/icons/icon__fullscreen.svg +1 -1
  16. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -1
  17. package/vitepress/assets/icons/icon__github.svg +3 -3
  18. package/vitepress/assets/icons/icon__mindmap.svg +1 -1
  19. package/vitepress/assets/icons/icon__next.svg +1 -1
  20. package/vitepress/assets/icons/icon__number_gray.svg +1 -1
  21. package/vitepress/assets/icons/icon__number_purple.svg +1 -1
  22. package/vitepress/assets/icons/icon__prev.svg +1 -1
  23. package/vitepress/assets/icons/icon__restore.svg +1 -1
  24. package/vitepress/assets/icons/icon__rotate.svg +4 -4
  25. package/vitepress/assets/icons/icon__search.svg +1 -1
  26. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -1
  27. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -1
  28. package/vitepress/assets/icons/icon__totop.svg +5 -5
  29. package/vitepress/assets/icons/icon__vscode.svg +5 -5
  30. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -1
  31. package/vitepress/assets/icons/icon__zoom_in.svg +1 -1
  32. package/vitepress/assets/icons/icon__zoom_out.svg +1 -1
  33. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -1
  34. package/vitepress/assets/icons/index.ts +39 -39
  35. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -20
  36. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +369 -369
  37. package/vitepress/components/CodeBlockFullscreen/index.ts +209 -209
  38. package/vitepress/components/CodeBlockFullscreen/styles.css +126 -126
  39. package/vitepress/components/Discussions/Discussions.vue +242 -242
  40. package/vitepress/components/EnWordList/EnWordList.vue +664 -664
  41. package/vitepress/components/EnWordList/RightClickMenu.vue +87 -87
  42. package/vitepress/components/Footprints/Footprints.vue +472 -472
  43. package/vitepress/components/Layout/AboutModal.vue +329 -329
  44. package/vitepress/components/Layout/AboutPanel.vue +485 -485
  45. package/vitepress/components/Layout/ContentCollapse.vue +591 -591
  46. package/vitepress/components/Layout/CustomSidebar.vue +817 -817
  47. package/vitepress/components/Layout/DocBeforeControls.vue +145 -145
  48. package/vitepress/components/Layout/DocFooter.vue +233 -233
  49. package/vitepress/components/Layout/ImagePreview.vue +485 -485
  50. package/vitepress/components/Layout/Layout.vue +744 -744
  51. package/vitepress/components/Layout/NoteStatus.vue +140 -140
  52. package/vitepress/components/Layout/SidebarItems.vue +1083 -1083
  53. package/vitepress/components/Layout/SidebarNavBefore.vue +170 -170
  54. package/vitepress/components/Layout/SidebarResizeHandle.vue +311 -311
  55. package/vitepress/components/Layout/Swiper.vue +167 -167
  56. package/vitepress/components/Layout/ToggleFullContent.vue +45 -45
  57. package/vitepress/components/Layout/ToggleSidebar.vue +35 -35
  58. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -88
  59. package/vitepress/components/Layout/composables/useNoteConfig.ts +119 -119
  60. package/vitepress/components/Layout/composables/useNoteSave.ts +178 -178
  61. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -85
  62. package/vitepress/components/Layout/composables/useRedirect.ts +113 -113
  63. package/vitepress/components/Layout/composables/useSidebarLayout.ts +122 -122
  64. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -85
  65. package/vitepress/components/Layout/homeReadme.data.ts +124 -124
  66. package/vitepress/components/MarkMap/MarkMap.vue +562 -562
  67. package/vitepress/components/Mermaid/Mermaid.vue +644 -644
  68. package/vitepress/components/NotesTable/NotesTable.vue +175 -175
  69. package/vitepress/components/NotesTable/README.md +67 -67
  70. package/vitepress/components/Settings/Settings.vue +711 -711
  71. package/vitepress/components/SidebarCard/MindMapView.vue +484 -484
  72. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -108
  73. package/vitepress/components/SidebarCard/SidebarCard.vue +945 -945
  74. package/vitepress/components/Tooltip/Tooltip.vue +70 -70
  75. package/vitepress/components/constants.ts +109 -109
  76. package/vitepress/components/notesConfig.data.ts +73 -73
  77. package/vitepress/components/sidebar.data.ts +59 -59
  78. package/vitepress/components/tnotes-config.data.ts +21 -21
  79. package/vitepress/components/utils.ts +26 -26
  80. package/vitepress/config/index.ts +169 -169
  81. package/vitepress/configs/constants.ts +26 -26
  82. package/vitepress/configs/head.config.ts +25 -25
  83. package/vitepress/configs/index.ts +9 -9
  84. package/vitepress/configs/markdown-it.d.ts +23 -23
  85. package/vitepress/configs/markdown.config.ts +368 -368
  86. package/vitepress/configs/theme.config.ts +108 -108
  87. package/vitepress/plugins/buildProgressPlugin.ts +434 -434
  88. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -107
  89. package/vitepress/plugins/renameNotePlugin.ts +68 -68
  90. package/vitepress/plugins/sidebarStructurePlugin.ts +260 -260
  91. package/vitepress/plugins/updateConfigPlugin.ts +63 -63
  92. package/vitepress/theme/index.ts +137 -137
  93. package/vitepress/theme/styles/base.scss +66 -64
  94. package/vitepress/theme/styles/components/404.scss +31 -31
  95. package/vitepress/theme/styles/components/collapse.scss +172 -172
  96. package/vitepress/theme/styles/components/markmap.scss +101 -101
  97. package/vitepress/theme/styles/components/swiper.scss +255 -255
  98. package/vitepress/theme/styles/index.scss +25 -25
  99. package/vitepress/theme/styles/layout.scss +78 -78
  100. package/vitepress/theme/styles/utilities.scss +39 -39
  101. package/vitepress/theme/styles/vitepress-override.scss +25 -25
@@ -1,711 +1,711 @@
1
- <template>
2
- <div class="settingsWrapper">
3
- <section class="settingsSection">
4
- <div class="settingItem">
5
- <div class="settingMeta">
6
- <div class="labelLine">
7
- <span class="itemName">本地知识库路径</span>
8
- <span class="infoWrap">
9
- <span class="infoIcon">?</span>
10
- <span class="tooltip">
11
- 适用于 PC 桌面环境,需要本地安装 VS Code。配置后可从页面快速打开本地笔记目录。
12
- </span>
13
- </span>
14
- </div>
15
- <span v-if="path" class="statusText">已配置</span>
16
- </div>
17
-
18
- <div class="inputGroup">
19
- <input
20
- v-model="path"
21
- type="text"
22
- placeholder="/Users/username/notes"
23
- class="input"
24
- />
25
- <button
26
- v-if="path"
27
- @click="clearPath"
28
- class="clearBtn"
29
- title="清空"
30
- type="button"
31
- >
32
- ×
33
- </button>
34
- </div>
35
- </div>
36
-
37
- <div class="settingItem">
38
- <div class="settingMeta">
39
- <div class="labelLine">
40
- <span class="itemName">侧边栏目录</span>
41
- <span class="infoWrap">
42
- <span class="infoIcon">?</span>
43
- <span class="tooltip">
44
- 控制侧边栏笔记编号显示,以及目录行距密度。保存后会刷新页面以应用侧边栏配置。
45
- </span>
46
- </span>
47
- </div>
48
- <span v-if="showNoteId" class="statusText">显示编号</span>
49
- </div>
50
-
51
- <div class="fieldStack">
52
- <label class="checkRow">
53
- <input
54
- v-model="showNoteId"
55
- type="checkbox"
56
- class="checkbox"
57
- />
58
- <span>显示笔记编号</span>
59
- </label>
60
-
61
- <div class="controlRow">
62
- <span class="controlLabel">目录风格</span>
63
- <div class="segmented">
64
- <label
65
- v-for="option in sidebarDensityOptions"
66
- :key="option.value"
67
- :class="[
68
- 'segment',
69
- { activeSegment: sidebarDensity === option.value },
70
- ]"
71
- >
72
- <input
73
- v-model="sidebarDensity"
74
- type="radio"
75
- name="sidebar-density"
76
- :value="option.value"
77
- />
78
- <span>{{ option.label }}</span>
79
- </label>
80
- </div>
81
- </div>
82
-
83
- <div class="prefixGrid">
84
- <label class="field">
85
- <span class="controlLabel">已完成笔记的前缀</span>
86
- <input
87
- v-model="donePrefix"
88
- type="text"
89
- placeholder="可留空"
90
- class="input prefixInput"
91
- />
92
- </label>
93
-
94
- <label class="field">
95
- <span class="controlLabel">未完成笔记前缀</span>
96
- <input
97
- v-model="undonePrefix"
98
- type="text"
99
- placeholder="可留空"
100
- class="input prefixInput"
101
- />
102
- </label>
103
- </div>
104
- </div>
105
- </div>
106
-
107
- <div class="settingItem">
108
- <div class="settingMeta">
109
- <div class="labelLine">
110
- <span class="itemName">MarkMap 思维导图</span>
111
- <span class="infoWrap">
112
- <span class="infoIcon">?</span>
113
- <span class="tooltip">
114
- 配置 MarkMap 的默认分支配色和初始展开层级。
115
- </span>
116
- </span>
117
- </div>
118
- </div>
119
-
120
- <div class="inlineGrid">
121
- <label class="field">
122
- <span class="controlLabel">分支主题</span>
123
- <select v-model="markmapTheme" class="select">
124
- <option value="default">默认</option>
125
- <option value="colorful">多彩</option>
126
- <option value="dark">深色</option>
127
- </select>
128
- </label>
129
-
130
- <label class="field">
131
- <span class="controlLabel">展开层级</span>
132
- <div class="inputWithUnit">
133
- <input
134
- v-model.number="markmapExpandLevel"
135
- type="number"
136
- min="1"
137
- max="100"
138
- class="input"
139
- />
140
- <span class="unit">层</span>
141
- </div>
142
- </label>
143
- </div>
144
- </div>
145
- </section>
146
-
147
- <div class="actionBar">
148
- <button v-if="hasChanges" @click="reset" class="resetBtn">
149
- 重置
150
- </button>
151
- <button
152
- @click="save"
153
- class="saveBtn"
154
- :class="{ disabled: !hasChanges }"
155
- :disabled="!hasChanges"
156
- >
157
- {{ saveText }}
158
- </button>
159
- </div>
160
-
161
- <Transition name="toast">
162
- <div v-if="showSuccessToast" class="toast">保存成功</div>
163
- </Transition>
164
- </div>
165
- </template>
166
-
167
- <script setup lang="ts">
168
- import { ref, computed, onMounted } from 'vue'
169
-
170
- import {
171
- NOTES_DIR_KEY,
172
- MARKMAP_THEME_KEY,
173
- MARKMAP_EXPAND_LEVEL_KEY,
174
- SIDEBAR_SHOW_NOTE_ID_KEY,
175
- SIDEBAR_DENSITY_KEY,
176
- SIDEBAR_DONE_PREFIX_KEY,
177
- SIDEBAR_UNDONE_PREFIX_KEY,
178
- } from '../constants'
179
- // @ts-expect-error - VitePress Data Loader
180
- import { data as tnotesConfig } from '../tnotes-config.data'
181
-
182
- type SidebarDensity = 'compact' | 'default' | 'loose'
183
-
184
- const DEFAULT_SIDEBAR_DENSITY: SidebarDensity = 'default'
185
- const DEFAULT_DONE_PREFIX = '✅'
186
- const DEFAULT_UNDONE_PREFIX = '⏰'
187
- const sidebarDensityOptions: Array<{
188
- label: string
189
- value: SidebarDensity
190
- }> = [
191
- { label: '紧凑', value: 'compact' },
192
- { label: '默认', value: 'default' },
193
- { label: '宽松', value: 'loose' },
194
- ]
195
-
196
- const path = ref('')
197
- const originalPath = ref('')
198
- const markmapTheme = ref('default')
199
- const originalMarkmapTheme = ref('default')
200
- const markmapExpandLevel = ref(5)
201
- const originalMarkmapExpandLevel = ref(5)
202
- const showNoteId = ref(false)
203
- const originalShowNoteId = ref(false)
204
- const sidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
205
- const originalSidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
206
- const donePrefix = ref(DEFAULT_DONE_PREFIX)
207
- const originalDonePrefix = ref(DEFAULT_DONE_PREFIX)
208
- const undonePrefix = ref(DEFAULT_UNDONE_PREFIX)
209
- const originalUndonePrefix = ref(DEFAULT_UNDONE_PREFIX)
210
- const showSuccessToast = ref(false)
211
-
212
- const hasChanges = computed(
213
- () =>
214
- path.value !== originalPath.value ||
215
- markmapTheme.value !== originalMarkmapTheme.value ||
216
- markmapExpandLevel.value !== originalMarkmapExpandLevel.value ||
217
- showNoteId.value !== originalShowNoteId.value ||
218
- sidebarDensity.value !== originalSidebarDensity.value ||
219
- donePrefix.value !== originalDonePrefix.value ||
220
- undonePrefix.value !== originalUndonePrefix.value,
221
- )
222
-
223
- const saveText = computed(() => {
224
- if (!hasChanges.value) return '无更改'
225
- return '保存'
226
- })
227
-
228
- onMounted(() => {
229
- if (typeof window === 'undefined') return
230
-
231
- const savedPath = localStorage.getItem(NOTES_DIR_KEY) || ''
232
- path.value = savedPath
233
- originalPath.value = savedPath
234
-
235
- const savedTheme = localStorage.getItem(MARKMAP_THEME_KEY) || 'default'
236
- markmapTheme.value = savedTheme
237
- originalMarkmapTheme.value = savedTheme
238
-
239
- const savedLevel = localStorage.getItem(MARKMAP_EXPAND_LEVEL_KEY) || '5'
240
- markmapExpandLevel.value = parseInt(savedLevel, 10)
241
- originalMarkmapExpandLevel.value = markmapExpandLevel.value
242
-
243
- const savedShowNoteId = localStorage.getItem(SIDEBAR_SHOW_NOTE_ID_KEY)
244
- showNoteId.value =
245
- savedShowNoteId !== null
246
- ? savedShowNoteId === 'true'
247
- : tnotesConfig?.sidebarShowNoteId ?? false
248
- originalShowNoteId.value = showNoteId.value
249
-
250
- const savedSidebarDensity = normalizeSidebarDensity(
251
- localStorage.getItem(SIDEBAR_DENSITY_KEY),
252
- )
253
- sidebarDensity.value = savedSidebarDensity
254
- originalSidebarDensity.value = savedSidebarDensity
255
-
256
- const savedDonePrefix = localStorage.getItem(SIDEBAR_DONE_PREFIX_KEY)
257
- donePrefix.value =
258
- savedDonePrefix !== null ? savedDonePrefix : DEFAULT_DONE_PREFIX
259
- originalDonePrefix.value = donePrefix.value
260
-
261
- const savedUndonePrefix = localStorage.getItem(SIDEBAR_UNDONE_PREFIX_KEY)
262
- undonePrefix.value =
263
- savedUndonePrefix !== null ? savedUndonePrefix : DEFAULT_UNDONE_PREFIX
264
- originalUndonePrefix.value = undonePrefix.value
265
- })
266
-
267
- function normalizeSidebarDensity(value: string | null): SidebarDensity {
268
- if (value === 'compact' || value === 'default' || value === 'loose') {
269
- return value
270
- }
271
-
272
- return DEFAULT_SIDEBAR_DENSITY
273
- }
274
-
275
- function clearPath() {
276
- path.value = ''
277
- }
278
-
279
- function save() {
280
- if (!hasChanges.value) return
281
-
282
- try {
283
- const needReload =
284
- showNoteId.value !== originalShowNoteId.value ||
285
- sidebarDensity.value !== originalSidebarDensity.value ||
286
- donePrefix.value !== originalDonePrefix.value ||
287
- undonePrefix.value !== originalUndonePrefix.value
288
-
289
- localStorage.setItem(NOTES_DIR_KEY, path.value)
290
- localStorage.setItem(MARKMAP_THEME_KEY, markmapTheme.value)
291
- localStorage.setItem(
292
- MARKMAP_EXPAND_LEVEL_KEY,
293
- markmapExpandLevel.value.toString(),
294
- )
295
- localStorage.setItem(SIDEBAR_SHOW_NOTE_ID_KEY, showNoteId.value.toString())
296
- localStorage.setItem(SIDEBAR_DENSITY_KEY, sidebarDensity.value)
297
- localStorage.setItem(SIDEBAR_DONE_PREFIX_KEY, donePrefix.value)
298
- localStorage.setItem(SIDEBAR_UNDONE_PREFIX_KEY, undonePrefix.value)
299
-
300
- originalPath.value = path.value
301
- originalMarkmapTheme.value = markmapTheme.value
302
- originalMarkmapExpandLevel.value = markmapExpandLevel.value
303
- originalShowNoteId.value = showNoteId.value
304
- originalSidebarDensity.value = sidebarDensity.value
305
- originalDonePrefix.value = donePrefix.value
306
- originalUndonePrefix.value = undonePrefix.value
307
-
308
- showSuccessToast.value = true
309
- setTimeout(() => {
310
- showSuccessToast.value = false
311
- }, 2400)
312
-
313
- if (needReload) {
314
- setTimeout(() => {
315
- window.location.reload()
316
- }, 450)
317
- }
318
- } catch (error) {
319
- console.error('保存配置失败:', error)
320
- alert('保存失败,请检查浏览器设置')
321
- }
322
- }
323
-
324
- function reset() {
325
- path.value = originalPath.value
326
- markmapTheme.value = originalMarkmapTheme.value
327
- markmapExpandLevel.value = originalMarkmapExpandLevel.value
328
- showNoteId.value = originalShowNoteId.value
329
- sidebarDensity.value = originalSidebarDensity.value
330
- donePrefix.value = originalDonePrefix.value
331
- undonePrefix.value = originalUndonePrefix.value
332
- }
333
- </script>
334
-
335
- <style scoped lang="scss">
336
- .settingsWrapper {
337
- max-width: 680px;
338
- margin: 0 auto;
339
- padding: 12px 4px 0;
340
- }
341
-
342
- .settingsSection {
343
- border-top: 1px solid var(--vp-c-divider);
344
- }
345
-
346
- .settingItem {
347
- display: grid;
348
- grid-template-columns: minmax(150px, 0.42fr) minmax(220px, 1fr);
349
- gap: 18px;
350
- padding: 18px 0;
351
- border-bottom: 1px solid var(--vp-c-divider);
352
- }
353
-
354
- .settingMeta {
355
- min-width: 0;
356
- }
357
-
358
- .labelLine {
359
- display: flex;
360
- align-items: center;
361
- gap: 6px;
362
- }
363
-
364
- .itemName {
365
- color: var(--vp-c-text-1);
366
- font-size: 14px;
367
- font-weight: 600;
368
- line-height: 22px;
369
- }
370
-
371
- .statusText {
372
- display: inline-block;
373
- margin-top: 4px;
374
- color: var(--vp-c-text-3);
375
- font-size: 12px;
376
- line-height: 18px;
377
- }
378
-
379
- .infoWrap {
380
- position: relative;
381
- display: inline-flex;
382
- }
383
-
384
- .infoIcon {
385
- display: inline-flex;
386
- align-items: center;
387
- justify-content: center;
388
- width: 16px;
389
- height: 16px;
390
- color: var(--vp-c-text-3);
391
- border: 1px solid var(--vp-c-divider);
392
- border-radius: 50%;
393
- cursor: help;
394
- font-size: 11px;
395
- line-height: 1;
396
- }
397
-
398
- .tooltip {
399
- position: absolute;
400
- left: 50%;
401
- bottom: calc(100% + 8px);
402
- z-index: 10;
403
- width: 240px;
404
- padding: 8px 10px;
405
- color: var(--vp-c-text-1);
406
- background: var(--vp-c-bg-elv);
407
- border: 1px solid var(--vp-c-divider);
408
- border-radius: 6px;
409
- box-shadow: var(--vp-shadow-2);
410
- font-size: 12px;
411
- line-height: 18px;
412
- opacity: 0;
413
- pointer-events: none;
414
- transform: translate(-50%, 4px);
415
- transition:
416
- opacity 0.15s ease,
417
- transform 0.15s ease;
418
- }
419
-
420
- .infoWrap:hover .tooltip,
421
- .infoWrap:focus-within .tooltip {
422
- opacity: 1;
423
- transform: translate(-50%, 0);
424
- }
425
-
426
- .inputGroup {
427
- position: relative;
428
- }
429
-
430
- .input,
431
- .select {
432
- width: 100%;
433
- height: 34px;
434
- box-sizing: border-box;
435
- color: var(--vp-c-text-1);
436
- background: var(--vp-c-bg);
437
- border: 1px solid var(--vp-c-divider);
438
- border-radius: 6px;
439
- font-size: 13px;
440
- outline: none;
441
- }
442
-
443
- .input {
444
- padding: 0 34px 0 10px;
445
- font-family: var(--vp-font-family-mono);
446
- }
447
-
448
- .select {
449
- padding: 0 10px;
450
- cursor: pointer;
451
- }
452
-
453
- .input::placeholder {
454
- color: var(--vp-c-text-3);
455
- }
456
-
457
- .input:focus,
458
- .select:focus {
459
- border-color: var(--vp-c-brand-1);
460
- }
461
-
462
- .clearBtn {
463
- position: absolute;
464
- top: 50%;
465
- right: 7px;
466
- display: inline-flex;
467
- align-items: center;
468
- justify-content: center;
469
- width: 20px;
470
- height: 20px;
471
- padding: 0;
472
- color: var(--vp-c-text-3);
473
- background: transparent;
474
- border: none;
475
- border-radius: 4px;
476
- cursor: pointer;
477
- font-size: 16px;
478
- transform: translateY(-50%);
479
- }
480
-
481
- .clearBtn:hover {
482
- color: var(--vp-c-danger-1);
483
- background: var(--vp-c-danger-soft);
484
- }
485
-
486
- .fieldStack {
487
- display: flex;
488
- flex-direction: column;
489
- gap: 12px;
490
- }
491
-
492
- .checkRow {
493
- display: inline-flex;
494
- align-items: center;
495
- gap: 8px;
496
- width: max-content;
497
- color: var(--vp-c-text-1);
498
- cursor: pointer;
499
- font-size: 13px;
500
- line-height: 20px;
501
- user-select: none;
502
- }
503
-
504
- .checkbox {
505
- width: 16px;
506
- height: 16px;
507
- margin: 0;
508
- accent-color: var(--vp-c-brand-1);
509
- cursor: pointer;
510
- }
511
-
512
- .controlRow,
513
- .field {
514
- display: flex;
515
- flex-direction: column;
516
- gap: 6px;
517
- }
518
-
519
- .controlLabel {
520
- color: var(--vp-c-text-2);
521
- font-size: 12px;
522
- line-height: 18px;
523
- }
524
-
525
- .segmented {
526
- display: grid;
527
- grid-template-columns: repeat(3, 1fr);
528
- overflow: hidden;
529
- border: 1px solid var(--vp-c-divider);
530
- border-radius: 6px;
531
- }
532
-
533
- .segment {
534
- display: inline-flex;
535
- align-items: center;
536
- justify-content: center;
537
- min-width: 0;
538
- height: 32px;
539
- color: var(--vp-c-text-2);
540
- border-right: 1px solid var(--vp-c-divider);
541
- cursor: pointer;
542
- font-size: 13px;
543
- user-select: none;
544
- }
545
-
546
- .segment:last-child {
547
- border-right: none;
548
- }
549
-
550
- .segment input {
551
- position: absolute;
552
- opacity: 0;
553
- pointer-events: none;
554
- }
555
-
556
- .segment:hover,
557
- .activeSegment {
558
- color: var(--vp-c-brand-1);
559
- background: var(--vp-c-default-soft);
560
- }
561
-
562
- .activeSegment {
563
- font-weight: 600;
564
- }
565
-
566
- .inlineGrid {
567
- display: grid;
568
- grid-template-columns: 1fr 120px;
569
- gap: 12px;
570
- }
571
-
572
- .prefixGrid {
573
- display: grid;
574
- grid-template-columns: repeat(2, minmax(0, 1fr));
575
- gap: 12px;
576
- }
577
-
578
- .prefixInput {
579
- padding-right: 10px;
580
- font-family: var(--vp-font-family-base);
581
- }
582
-
583
- .inputWithUnit {
584
- position: relative;
585
- }
586
-
587
- .inputWithUnit .input {
588
- padding-right: 34px;
589
- }
590
-
591
- .unit {
592
- position: absolute;
593
- top: 50%;
594
- right: 10px;
595
- color: var(--vp-c-text-3);
596
- font-size: 12px;
597
- pointer-events: none;
598
- transform: translateY(-50%);
599
- }
600
-
601
- .actionBar {
602
- display: flex;
603
- justify-content: flex-end;
604
- gap: 8px;
605
- padding-top: 16px;
606
- }
607
-
608
- .saveBtn,
609
- .resetBtn {
610
- display: inline-flex;
611
- align-items: center;
612
- justify-content: center;
613
- min-width: 72px;
614
- height: 34px;
615
- padding: 0 14px;
616
- border-radius: 6px;
617
- cursor: pointer;
618
- font-size: 13px;
619
- font-weight: 500;
620
- }
621
-
622
- .saveBtn {
623
- color: #fff;
624
- background: var(--vp-c-brand-1);
625
- border: 1px solid var(--vp-c-brand-1);
626
- }
627
-
628
- .saveBtn:hover:not(.disabled) {
629
- background: var(--vp-c-brand-2);
630
- }
631
-
632
- .saveBtn.disabled {
633
- opacity: 0.45;
634
- cursor: not-allowed;
635
- }
636
-
637
- .resetBtn {
638
- color: var(--vp-c-text-1);
639
- background: var(--vp-c-bg);
640
- border: 1px solid var(--vp-c-divider);
641
- }
642
-
643
- .resetBtn:hover {
644
- border-color: var(--vp-c-brand-1);
645
- }
646
-
647
- .toast {
648
- position: fixed;
649
- top: 78px;
650
- left: 50%;
651
- z-index: 1000;
652
- padding: 8px 12px;
653
- color: #fff;
654
- background: var(--vp-c-brand-1);
655
- border-radius: 6px;
656
- box-shadow: var(--vp-shadow-2);
657
- font-size: 13px;
658
- transform: translateX(-50%);
659
- }
660
-
661
- :global(.toast-enter-active),
662
- :global(.toast-leave-active) {
663
- transition: all 0.2s ease;
664
- }
665
-
666
- :global(.toast-enter-from),
667
- :global(.toast-leave-to) {
668
- opacity: 0;
669
- transform: translate(-50%, -8px);
670
- }
671
-
672
- @media (max-width: 768px) {
673
- .settingsWrapper {
674
- padding: 0;
675
- }
676
-
677
- .settingItem {
678
- grid-template-columns: 1fr;
679
- gap: 10px;
680
- padding: 16px 0;
681
- }
682
-
683
- .inlineGrid {
684
- grid-template-columns: 1fr;
685
- }
686
-
687
- .prefixGrid {
688
- grid-template-columns: 1fr;
689
- }
690
-
691
- .tooltip {
692
- left: 0;
693
- width: min(260px, calc(100vw - 48px));
694
- transform: translate(0, 4px);
695
- }
696
-
697
- .infoWrap:hover .tooltip,
698
- .infoWrap:focus-within .tooltip {
699
- transform: translate(0, 0);
700
- }
701
-
702
- .actionBar {
703
- flex-direction: column-reverse;
704
- }
705
-
706
- .saveBtn,
707
- .resetBtn {
708
- width: 100%;
709
- }
710
- }
711
- </style>
1
+ <template>
2
+ <div class="settingsWrapper">
3
+ <section class="settingsSection">
4
+ <div class="settingItem">
5
+ <div class="settingMeta">
6
+ <div class="labelLine">
7
+ <span class="itemName">本地知识库路径</span>
8
+ <span class="infoWrap">
9
+ <span class="infoIcon">?</span>
10
+ <span class="tooltip">
11
+ 适用于 PC 桌面环境,需要本地安装 VS Code。配置后可从页面快速打开本地笔记目录。
12
+ </span>
13
+ </span>
14
+ </div>
15
+ <span v-if="path" class="statusText">已配置</span>
16
+ </div>
17
+
18
+ <div class="inputGroup">
19
+ <input
20
+ v-model="path"
21
+ type="text"
22
+ placeholder="/Users/username/notes"
23
+ class="input"
24
+ />
25
+ <button
26
+ v-if="path"
27
+ @click="clearPath"
28
+ class="clearBtn"
29
+ title="清空"
30
+ type="button"
31
+ >
32
+ ×
33
+ </button>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="settingItem">
38
+ <div class="settingMeta">
39
+ <div class="labelLine">
40
+ <span class="itemName">侧边栏目录</span>
41
+ <span class="infoWrap">
42
+ <span class="infoIcon">?</span>
43
+ <span class="tooltip">
44
+ 控制侧边栏笔记编号显示,以及目录行距密度。保存后会刷新页面以应用侧边栏配置。
45
+ </span>
46
+ </span>
47
+ </div>
48
+ <span v-if="showNoteId" class="statusText">显示编号</span>
49
+ </div>
50
+
51
+ <div class="fieldStack">
52
+ <label class="checkRow">
53
+ <input
54
+ v-model="showNoteId"
55
+ type="checkbox"
56
+ class="checkbox"
57
+ />
58
+ <span>显示笔记编号</span>
59
+ </label>
60
+
61
+ <div class="controlRow">
62
+ <span class="controlLabel">目录风格</span>
63
+ <div class="segmented">
64
+ <label
65
+ v-for="option in sidebarDensityOptions"
66
+ :key="option.value"
67
+ :class="[
68
+ 'segment',
69
+ { activeSegment: sidebarDensity === option.value },
70
+ ]"
71
+ >
72
+ <input
73
+ v-model="sidebarDensity"
74
+ type="radio"
75
+ name="sidebar-density"
76
+ :value="option.value"
77
+ />
78
+ <span>{{ option.label }}</span>
79
+ </label>
80
+ </div>
81
+ </div>
82
+
83
+ <div class="prefixGrid">
84
+ <label class="field">
85
+ <span class="controlLabel">已完成笔记的前缀</span>
86
+ <input
87
+ v-model="donePrefix"
88
+ type="text"
89
+ placeholder="可留空"
90
+ class="input prefixInput"
91
+ />
92
+ </label>
93
+
94
+ <label class="field">
95
+ <span class="controlLabel">未完成笔记前缀</span>
96
+ <input
97
+ v-model="undonePrefix"
98
+ type="text"
99
+ placeholder="可留空"
100
+ class="input prefixInput"
101
+ />
102
+ </label>
103
+ </div>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="settingItem">
108
+ <div class="settingMeta">
109
+ <div class="labelLine">
110
+ <span class="itemName">MarkMap 思维导图</span>
111
+ <span class="infoWrap">
112
+ <span class="infoIcon">?</span>
113
+ <span class="tooltip">
114
+ 配置 MarkMap 的默认分支配色和初始展开层级。
115
+ </span>
116
+ </span>
117
+ </div>
118
+ </div>
119
+
120
+ <div class="inlineGrid">
121
+ <label class="field">
122
+ <span class="controlLabel">分支主题</span>
123
+ <select v-model="markmapTheme" class="select">
124
+ <option value="default">默认</option>
125
+ <option value="colorful">多彩</option>
126
+ <option value="dark">深色</option>
127
+ </select>
128
+ </label>
129
+
130
+ <label class="field">
131
+ <span class="controlLabel">展开层级</span>
132
+ <div class="inputWithUnit">
133
+ <input
134
+ v-model.number="markmapExpandLevel"
135
+ type="number"
136
+ min="1"
137
+ max="100"
138
+ class="input"
139
+ />
140
+ <span class="unit">层</span>
141
+ </div>
142
+ </label>
143
+ </div>
144
+ </div>
145
+ </section>
146
+
147
+ <div class="actionBar">
148
+ <button v-if="hasChanges" @click="reset" class="resetBtn">
149
+ 重置
150
+ </button>
151
+ <button
152
+ @click="save"
153
+ class="saveBtn"
154
+ :class="{ disabled: !hasChanges }"
155
+ :disabled="!hasChanges"
156
+ >
157
+ {{ saveText }}
158
+ </button>
159
+ </div>
160
+
161
+ <Transition name="toast">
162
+ <div v-if="showSuccessToast" class="toast">保存成功</div>
163
+ </Transition>
164
+ </div>
165
+ </template>
166
+
167
+ <script setup lang="ts">
168
+ import { ref, computed, onMounted } from 'vue'
169
+
170
+ import {
171
+ NOTES_DIR_KEY,
172
+ MARKMAP_THEME_KEY,
173
+ MARKMAP_EXPAND_LEVEL_KEY,
174
+ SIDEBAR_SHOW_NOTE_ID_KEY,
175
+ SIDEBAR_DENSITY_KEY,
176
+ SIDEBAR_DONE_PREFIX_KEY,
177
+ SIDEBAR_UNDONE_PREFIX_KEY,
178
+ } from '../constants'
179
+ // @ts-expect-error - VitePress Data Loader
180
+ import { data as tnotesConfig } from '../tnotes-config.data'
181
+
182
+ type SidebarDensity = 'compact' | 'default' | 'loose'
183
+
184
+ const DEFAULT_SIDEBAR_DENSITY: SidebarDensity = 'default'
185
+ const DEFAULT_DONE_PREFIX = '✅'
186
+ const DEFAULT_UNDONE_PREFIX = '⏰'
187
+ const sidebarDensityOptions: Array<{
188
+ label: string
189
+ value: SidebarDensity
190
+ }> = [
191
+ { label: '紧凑', value: 'compact' },
192
+ { label: '默认', value: 'default' },
193
+ { label: '宽松', value: 'loose' },
194
+ ]
195
+
196
+ const path = ref('')
197
+ const originalPath = ref('')
198
+ const markmapTheme = ref('default')
199
+ const originalMarkmapTheme = ref('default')
200
+ const markmapExpandLevel = ref(5)
201
+ const originalMarkmapExpandLevel = ref(5)
202
+ const showNoteId = ref(false)
203
+ const originalShowNoteId = ref(false)
204
+ const sidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
205
+ const originalSidebarDensity = ref<SidebarDensity>(DEFAULT_SIDEBAR_DENSITY)
206
+ const donePrefix = ref(DEFAULT_DONE_PREFIX)
207
+ const originalDonePrefix = ref(DEFAULT_DONE_PREFIX)
208
+ const undonePrefix = ref(DEFAULT_UNDONE_PREFIX)
209
+ const originalUndonePrefix = ref(DEFAULT_UNDONE_PREFIX)
210
+ const showSuccessToast = ref(false)
211
+
212
+ const hasChanges = computed(
213
+ () =>
214
+ path.value !== originalPath.value ||
215
+ markmapTheme.value !== originalMarkmapTheme.value ||
216
+ markmapExpandLevel.value !== originalMarkmapExpandLevel.value ||
217
+ showNoteId.value !== originalShowNoteId.value ||
218
+ sidebarDensity.value !== originalSidebarDensity.value ||
219
+ donePrefix.value !== originalDonePrefix.value ||
220
+ undonePrefix.value !== originalUndonePrefix.value,
221
+ )
222
+
223
+ const saveText = computed(() => {
224
+ if (!hasChanges.value) return '无更改'
225
+ return '保存'
226
+ })
227
+
228
+ onMounted(() => {
229
+ if (typeof window === 'undefined') return
230
+
231
+ const savedPath = localStorage.getItem(NOTES_DIR_KEY) || ''
232
+ path.value = savedPath
233
+ originalPath.value = savedPath
234
+
235
+ const savedTheme = localStorage.getItem(MARKMAP_THEME_KEY) || 'default'
236
+ markmapTheme.value = savedTheme
237
+ originalMarkmapTheme.value = savedTheme
238
+
239
+ const savedLevel = localStorage.getItem(MARKMAP_EXPAND_LEVEL_KEY) || '5'
240
+ markmapExpandLevel.value = parseInt(savedLevel, 10)
241
+ originalMarkmapExpandLevel.value = markmapExpandLevel.value
242
+
243
+ const savedShowNoteId = localStorage.getItem(SIDEBAR_SHOW_NOTE_ID_KEY)
244
+ showNoteId.value =
245
+ savedShowNoteId !== null
246
+ ? savedShowNoteId === 'true'
247
+ : tnotesConfig?.sidebarShowNoteId ?? false
248
+ originalShowNoteId.value = showNoteId.value
249
+
250
+ const savedSidebarDensity = normalizeSidebarDensity(
251
+ localStorage.getItem(SIDEBAR_DENSITY_KEY),
252
+ )
253
+ sidebarDensity.value = savedSidebarDensity
254
+ originalSidebarDensity.value = savedSidebarDensity
255
+
256
+ const savedDonePrefix = localStorage.getItem(SIDEBAR_DONE_PREFIX_KEY)
257
+ donePrefix.value =
258
+ savedDonePrefix !== null ? savedDonePrefix : DEFAULT_DONE_PREFIX
259
+ originalDonePrefix.value = donePrefix.value
260
+
261
+ const savedUndonePrefix = localStorage.getItem(SIDEBAR_UNDONE_PREFIX_KEY)
262
+ undonePrefix.value =
263
+ savedUndonePrefix !== null ? savedUndonePrefix : DEFAULT_UNDONE_PREFIX
264
+ originalUndonePrefix.value = undonePrefix.value
265
+ })
266
+
267
+ function normalizeSidebarDensity(value: string | null): SidebarDensity {
268
+ if (value === 'compact' || value === 'default' || value === 'loose') {
269
+ return value
270
+ }
271
+
272
+ return DEFAULT_SIDEBAR_DENSITY
273
+ }
274
+
275
+ function clearPath() {
276
+ path.value = ''
277
+ }
278
+
279
+ function save() {
280
+ if (!hasChanges.value) return
281
+
282
+ try {
283
+ const needReload =
284
+ showNoteId.value !== originalShowNoteId.value ||
285
+ sidebarDensity.value !== originalSidebarDensity.value ||
286
+ donePrefix.value !== originalDonePrefix.value ||
287
+ undonePrefix.value !== originalUndonePrefix.value
288
+
289
+ localStorage.setItem(NOTES_DIR_KEY, path.value)
290
+ localStorage.setItem(MARKMAP_THEME_KEY, markmapTheme.value)
291
+ localStorage.setItem(
292
+ MARKMAP_EXPAND_LEVEL_KEY,
293
+ markmapExpandLevel.value.toString(),
294
+ )
295
+ localStorage.setItem(SIDEBAR_SHOW_NOTE_ID_KEY, showNoteId.value.toString())
296
+ localStorage.setItem(SIDEBAR_DENSITY_KEY, sidebarDensity.value)
297
+ localStorage.setItem(SIDEBAR_DONE_PREFIX_KEY, donePrefix.value)
298
+ localStorage.setItem(SIDEBAR_UNDONE_PREFIX_KEY, undonePrefix.value)
299
+
300
+ originalPath.value = path.value
301
+ originalMarkmapTheme.value = markmapTheme.value
302
+ originalMarkmapExpandLevel.value = markmapExpandLevel.value
303
+ originalShowNoteId.value = showNoteId.value
304
+ originalSidebarDensity.value = sidebarDensity.value
305
+ originalDonePrefix.value = donePrefix.value
306
+ originalUndonePrefix.value = undonePrefix.value
307
+
308
+ showSuccessToast.value = true
309
+ setTimeout(() => {
310
+ showSuccessToast.value = false
311
+ }, 2400)
312
+
313
+ if (needReload) {
314
+ setTimeout(() => {
315
+ window.location.reload()
316
+ }, 450)
317
+ }
318
+ } catch (error) {
319
+ console.error('保存配置失败:', error)
320
+ alert('保存失败,请检查浏览器设置')
321
+ }
322
+ }
323
+
324
+ function reset() {
325
+ path.value = originalPath.value
326
+ markmapTheme.value = originalMarkmapTheme.value
327
+ markmapExpandLevel.value = originalMarkmapExpandLevel.value
328
+ showNoteId.value = originalShowNoteId.value
329
+ sidebarDensity.value = originalSidebarDensity.value
330
+ donePrefix.value = originalDonePrefix.value
331
+ undonePrefix.value = originalUndonePrefix.value
332
+ }
333
+ </script>
334
+
335
+ <style scoped lang="scss">
336
+ .settingsWrapper {
337
+ max-width: 680px;
338
+ margin: 0 auto;
339
+ padding: 12px 4px 0;
340
+ }
341
+
342
+ .settingsSection {
343
+ border-top: 1px solid var(--vp-c-divider);
344
+ }
345
+
346
+ .settingItem {
347
+ display: grid;
348
+ grid-template-columns: minmax(150px, 0.42fr) minmax(220px, 1fr);
349
+ gap: 18px;
350
+ padding: 18px 0;
351
+ border-bottom: 1px solid var(--vp-c-divider);
352
+ }
353
+
354
+ .settingMeta {
355
+ min-width: 0;
356
+ }
357
+
358
+ .labelLine {
359
+ display: flex;
360
+ align-items: center;
361
+ gap: 6px;
362
+ }
363
+
364
+ .itemName {
365
+ color: var(--vp-c-text-1);
366
+ font-size: 14px;
367
+ font-weight: 600;
368
+ line-height: 22px;
369
+ }
370
+
371
+ .statusText {
372
+ display: inline-block;
373
+ margin-top: 4px;
374
+ color: var(--vp-c-text-3);
375
+ font-size: 12px;
376
+ line-height: 18px;
377
+ }
378
+
379
+ .infoWrap {
380
+ position: relative;
381
+ display: inline-flex;
382
+ }
383
+
384
+ .infoIcon {
385
+ display: inline-flex;
386
+ align-items: center;
387
+ justify-content: center;
388
+ width: 16px;
389
+ height: 16px;
390
+ color: var(--vp-c-text-3);
391
+ border: 1px solid var(--vp-c-divider);
392
+ border-radius: 50%;
393
+ cursor: help;
394
+ font-size: 11px;
395
+ line-height: 1;
396
+ }
397
+
398
+ .tooltip {
399
+ position: absolute;
400
+ left: 50%;
401
+ bottom: calc(100% + 8px);
402
+ z-index: 10;
403
+ width: 240px;
404
+ padding: 8px 10px;
405
+ color: var(--vp-c-text-1);
406
+ background: var(--vp-c-bg-elv);
407
+ border: 1px solid var(--vp-c-divider);
408
+ border-radius: 6px;
409
+ box-shadow: var(--vp-shadow-2);
410
+ font-size: 12px;
411
+ line-height: 18px;
412
+ opacity: 0;
413
+ pointer-events: none;
414
+ transform: translate(-50%, 4px);
415
+ transition:
416
+ opacity 0.15s ease,
417
+ transform 0.15s ease;
418
+ }
419
+
420
+ .infoWrap:hover .tooltip,
421
+ .infoWrap:focus-within .tooltip {
422
+ opacity: 1;
423
+ transform: translate(-50%, 0);
424
+ }
425
+
426
+ .inputGroup {
427
+ position: relative;
428
+ }
429
+
430
+ .input,
431
+ .select {
432
+ width: 100%;
433
+ height: 34px;
434
+ box-sizing: border-box;
435
+ color: var(--vp-c-text-1);
436
+ background: var(--vp-c-bg);
437
+ border: 1px solid var(--vp-c-divider);
438
+ border-radius: 6px;
439
+ font-size: 13px;
440
+ outline: none;
441
+ }
442
+
443
+ .input {
444
+ padding: 0 34px 0 10px;
445
+ font-family: var(--vp-font-family-mono);
446
+ }
447
+
448
+ .select {
449
+ padding: 0 10px;
450
+ cursor: pointer;
451
+ }
452
+
453
+ .input::placeholder {
454
+ color: var(--vp-c-text-3);
455
+ }
456
+
457
+ .input:focus,
458
+ .select:focus {
459
+ border-color: var(--vp-c-brand-1);
460
+ }
461
+
462
+ .clearBtn {
463
+ position: absolute;
464
+ top: 50%;
465
+ right: 7px;
466
+ display: inline-flex;
467
+ align-items: center;
468
+ justify-content: center;
469
+ width: 20px;
470
+ height: 20px;
471
+ padding: 0;
472
+ color: var(--vp-c-text-3);
473
+ background: transparent;
474
+ border: none;
475
+ border-radius: 4px;
476
+ cursor: pointer;
477
+ font-size: 16px;
478
+ transform: translateY(-50%);
479
+ }
480
+
481
+ .clearBtn:hover {
482
+ color: var(--vp-c-danger-1);
483
+ background: var(--vp-c-danger-soft);
484
+ }
485
+
486
+ .fieldStack {
487
+ display: flex;
488
+ flex-direction: column;
489
+ gap: 12px;
490
+ }
491
+
492
+ .checkRow {
493
+ display: inline-flex;
494
+ align-items: center;
495
+ gap: 8px;
496
+ width: max-content;
497
+ color: var(--vp-c-text-1);
498
+ cursor: pointer;
499
+ font-size: 13px;
500
+ line-height: 20px;
501
+ user-select: none;
502
+ }
503
+
504
+ .checkbox {
505
+ width: 16px;
506
+ height: 16px;
507
+ margin: 0;
508
+ accent-color: var(--vp-c-brand-1);
509
+ cursor: pointer;
510
+ }
511
+
512
+ .controlRow,
513
+ .field {
514
+ display: flex;
515
+ flex-direction: column;
516
+ gap: 6px;
517
+ }
518
+
519
+ .controlLabel {
520
+ color: var(--vp-c-text-2);
521
+ font-size: 12px;
522
+ line-height: 18px;
523
+ }
524
+
525
+ .segmented {
526
+ display: grid;
527
+ grid-template-columns: repeat(3, 1fr);
528
+ overflow: hidden;
529
+ border: 1px solid var(--vp-c-divider);
530
+ border-radius: 6px;
531
+ }
532
+
533
+ .segment {
534
+ display: inline-flex;
535
+ align-items: center;
536
+ justify-content: center;
537
+ min-width: 0;
538
+ height: 32px;
539
+ color: var(--vp-c-text-2);
540
+ border-right: 1px solid var(--vp-c-divider);
541
+ cursor: pointer;
542
+ font-size: 13px;
543
+ user-select: none;
544
+ }
545
+
546
+ .segment:last-child {
547
+ border-right: none;
548
+ }
549
+
550
+ .segment input {
551
+ position: absolute;
552
+ opacity: 0;
553
+ pointer-events: none;
554
+ }
555
+
556
+ .segment:hover,
557
+ .activeSegment {
558
+ color: var(--vp-c-brand-1);
559
+ background: var(--vp-c-default-soft);
560
+ }
561
+
562
+ .activeSegment {
563
+ font-weight: 600;
564
+ }
565
+
566
+ .inlineGrid {
567
+ display: grid;
568
+ grid-template-columns: 1fr 120px;
569
+ gap: 12px;
570
+ }
571
+
572
+ .prefixGrid {
573
+ display: grid;
574
+ grid-template-columns: repeat(2, minmax(0, 1fr));
575
+ gap: 12px;
576
+ }
577
+
578
+ .prefixInput {
579
+ padding-right: 10px;
580
+ font-family: var(--vp-font-family-base);
581
+ }
582
+
583
+ .inputWithUnit {
584
+ position: relative;
585
+ }
586
+
587
+ .inputWithUnit .input {
588
+ padding-right: 34px;
589
+ }
590
+
591
+ .unit {
592
+ position: absolute;
593
+ top: 50%;
594
+ right: 10px;
595
+ color: var(--vp-c-text-3);
596
+ font-size: 12px;
597
+ pointer-events: none;
598
+ transform: translateY(-50%);
599
+ }
600
+
601
+ .actionBar {
602
+ display: flex;
603
+ justify-content: flex-end;
604
+ gap: 8px;
605
+ padding-top: 16px;
606
+ }
607
+
608
+ .saveBtn,
609
+ .resetBtn {
610
+ display: inline-flex;
611
+ align-items: center;
612
+ justify-content: center;
613
+ min-width: 72px;
614
+ height: 34px;
615
+ padding: 0 14px;
616
+ border-radius: 6px;
617
+ cursor: pointer;
618
+ font-size: 13px;
619
+ font-weight: 500;
620
+ }
621
+
622
+ .saveBtn {
623
+ color: #fff;
624
+ background: var(--vp-c-brand-1);
625
+ border: 1px solid var(--vp-c-brand-1);
626
+ }
627
+
628
+ .saveBtn:hover:not(.disabled) {
629
+ background: var(--vp-c-brand-2);
630
+ }
631
+
632
+ .saveBtn.disabled {
633
+ opacity: 0.45;
634
+ cursor: not-allowed;
635
+ }
636
+
637
+ .resetBtn {
638
+ color: var(--vp-c-text-1);
639
+ background: var(--vp-c-bg);
640
+ border: 1px solid var(--vp-c-divider);
641
+ }
642
+
643
+ .resetBtn:hover {
644
+ border-color: var(--vp-c-brand-1);
645
+ }
646
+
647
+ .toast {
648
+ position: fixed;
649
+ top: 78px;
650
+ left: 50%;
651
+ z-index: 1000;
652
+ padding: 8px 12px;
653
+ color: #fff;
654
+ background: var(--vp-c-brand-1);
655
+ border-radius: 6px;
656
+ box-shadow: var(--vp-shadow-2);
657
+ font-size: 13px;
658
+ transform: translateX(-50%);
659
+ }
660
+
661
+ :global(.toast-enter-active),
662
+ :global(.toast-leave-active) {
663
+ transition: all 0.2s ease;
664
+ }
665
+
666
+ :global(.toast-enter-from),
667
+ :global(.toast-leave-to) {
668
+ opacity: 0;
669
+ transform: translate(-50%, -8px);
670
+ }
671
+
672
+ @media (max-width: 768px) {
673
+ .settingsWrapper {
674
+ padding: 0;
675
+ }
676
+
677
+ .settingItem {
678
+ grid-template-columns: 1fr;
679
+ gap: 10px;
680
+ padding: 16px 0;
681
+ }
682
+
683
+ .inlineGrid {
684
+ grid-template-columns: 1fr;
685
+ }
686
+
687
+ .prefixGrid {
688
+ grid-template-columns: 1fr;
689
+ }
690
+
691
+ .tooltip {
692
+ left: 0;
693
+ width: min(260px, calc(100vw - 48px));
694
+ transform: translate(0, 4px);
695
+ }
696
+
697
+ .infoWrap:hover .tooltip,
698
+ .infoWrap:focus-within .tooltip {
699
+ transform: translate(0, 0);
700
+ }
701
+
702
+ .actionBar {
703
+ flex-direction: column-reverse;
704
+ }
705
+
706
+ .saveBtn,
707
+ .resetBtn {
708
+ width: 100%;
709
+ }
710
+ }
711
+ </style>