@tnotesjs/core 0.1.27 → 0.2.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 (71) hide show
  1. package/README.md +2 -1
  2. package/package.json +3 -1
  3. package/templates/sub-repo/.gitattributes +30 -0
  4. package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
  5. package/templates/sub-repo/.vitepress/config.mts +12 -0
  6. package/templates/sub-repo/.vitepress/env.d.ts +10 -0
  7. package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
  8. package/templates/sub-repo/.vscode/settings.json +330 -0
  9. package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
  10. package/templates/sub-repo/public/favicon.ico +0 -0
  11. package/templates/sub-repo/public/logo.png +0 -0
  12. package/templates/sub-repo/tsconfig.json +45 -0
  13. package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
  14. package/vitepress/assets/icons/icon__setting_light.svg +4 -0
  15. package/vitepress/assets/icons/index.ts +2 -2
  16. package/vitepress/client/localSearchIndexBridge.ts +24 -0
  17. package/vitepress/components/Discussions/Discussions.vue +1 -1
  18. package/vitepress/components/Footprints/Footprints.vue +90 -25
  19. package/vitepress/components/Layout/CustomSidebar.vue +489 -117
  20. package/vitepress/components/Layout/Layout.vue +5 -0
  21. package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
  22. package/vitepress/components/Layout/SidebarItems.vue +517 -342
  23. package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
  24. package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
  25. package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
  26. package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
  27. package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
  28. package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
  29. package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
  30. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
  31. package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
  32. package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
  33. package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
  34. package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
  35. package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
  36. package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
  37. package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
  38. package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
  39. package/vitepress/components/MarkMap/MarkMap.vue +67 -10
  40. package/vitepress/components/Settings/Settings.vue +78 -74
  41. package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
  42. package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
  43. package/vitepress/components/constants.ts +18 -6
  44. package/vitepress/components/sidebar.data.ts +13 -11
  45. package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
  46. package/vitepress/components/sidebarTreeHelpers.ts +168 -0
  47. package/vitepress/components/utils/vscodePaths.test.ts +41 -0
  48. package/vitepress/components/utils/vscodePaths.ts +52 -0
  49. package/vitepress/config/index.ts +2 -0
  50. package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
  51. package/vitepress/plugins/localSearchReindex.test.ts +46 -0
  52. package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
  53. package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
  54. package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
  55. package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
  56. package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
  57. package/vitepress/theme/index.ts +8 -0
  58. package/vitepress/theme/styles/base.scss +0 -8
  59. package/vitepress/theme/styles/components/markmap.scss +23 -2
  60. package/vitepress/theme/styles/components/swiper.scss +1 -1
  61. package/vitepress/theme/styles/doc-layout.scss +212 -0
  62. package/vitepress/theme/styles/index.scss +3 -0
  63. package/vitepress/theme/styles/layout.scss +60 -10
  64. package/dist/chunk-4KG6RXFS.js +0 -4637
  65. package/dist/chunk-AGSL3VZE.js +0 -193
  66. package/dist/cli/index.js +0 -683
  67. package/dist/index.js +0 -8
  68. package/dist/vitepress/config/index.js +0 -1178
  69. package/vitepress/assets/icons/icon__mindmap.svg +0 -1
  70. package/vitepress/assets/icons/icon__setting.svg +0 -1
  71. package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
@@ -4,26 +4,34 @@ vitepress/components/Layout/SidebarItems.vue
4
4
 
5
5
  <template>
6
6
  <template v-for="item in items" :key="getItemKey(item)">
7
- <!-- 只有在不超过最大层级时才渲染组 -->
8
7
  <div
9
- v-if="hasChildren(item) && depth < maxDepth - 1"
8
+ v-if="item.isDragPlaceholder"
9
+ class="sidebar-row is-drag-placeholder"
10
+ :style="getNodeStyle(depth)"
11
+ aria-hidden="true"
12
+ />
13
+
14
+ <!-- 纯目录行(无 link) -->
15
+ <div
16
+ v-else-if="isPureFolder(item)"
10
17
  class="group"
11
- :class="getDensityClass()"
18
+ :class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
12
19
  >
13
20
  <div
14
- class="sidebar-row group-row"
15
- :class="[getGroupDropClass(item), getDensityClass()]"
21
+ class="sidebar-row group-row folder-row"
22
+ :class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
16
23
  :style="getNodeStyle(depth)"
17
- :draggable="isDev"
18
- @dragstart="onGroupDragStart($event, item)"
19
- @dragend="clearDropTarget"
20
- @dragover.prevent.stop="onGroupDragOver($event, item)"
21
- @drop.stop="onGroupDrop($event, item)"
24
+ :data-folder-path="serializeFolderPath(item)"
25
+ :data-toc-line-index="item.tocLineIndex"
26
+ :data-node-id="getNodeId(item)"
27
+ @pointerdown="onFolderRowPointerDown($event, item)"
22
28
  >
23
29
  <button
24
- class="group-title row-main"
25
- :class="`group-title-level-${depth}`"
26
- @click="toggleItem(item)"
30
+ v-if="hasChildren(item)"
31
+ class="group-arrow-btn"
32
+ type="button"
33
+ :title="item.collapsed ? '展开' : '折叠'"
34
+ @click.stop="toggleItem(item)"
27
35
  >
28
36
  <span class="arrow" :class="{ collapsed: item.collapsed }">
29
37
  <img
@@ -33,44 +41,55 @@ vitepress/components/Layout/SidebarItems.vue
33
41
  alt=""
34
42
  />
35
43
  </span>
44
+ </button>
45
+ <span v-else class="group-arrow-spacer" aria-hidden="true" />
36
46
 
47
+ <button
48
+ type="button"
49
+ class="group-title row-main"
50
+ @click.stop="onGroupTitleClick(item)"
51
+ >
37
52
  <span class="group-title-text">{{ item.text }}</span>
38
53
  </button>
39
54
 
40
55
  <div
41
56
  v-if="isDev"
42
57
  class="row-actions"
43
- :class="{ 'menu-open': isGroupMenuOpen(item) }"
58
+ :class="{ 'menu-open': isGroupMenuOpen(item) || isFolderMenuOpen(item) }"
44
59
  @click.stop
45
60
  >
46
61
  <button
47
62
  class="action-btn menu-trigger"
48
63
  title="更多操作"
49
64
  type="button"
50
- @click="toggleMenu(getGroupMenuKey(item, 'more'))"
65
+ @click="toggleMenu(getFolderMenuKey(item, 'more'))"
51
66
  >
52
67
  <span class="ellipsis">...</span>
53
68
  </button>
54
69
 
55
70
  <div
56
- v-if="isMenuOpen(getGroupMenuKey(item, 'more'))"
71
+ v-if="isMenuOpen(getFolderMenuKey(item, 'more'))"
57
72
  class="action-menu"
58
73
  >
59
- <button class="menu-item" type="button" @click="renameGroup(item)">
60
- 重命名目录
74
+ <button
75
+ class="menu-item"
76
+ type="button"
77
+ @click="renameFolder(item)"
78
+ >
79
+ 重命名
61
80
  </button>
62
81
  <button
63
82
  class="menu-item danger"
64
83
  type="button"
65
- @click="deleteGroup(item)"
84
+ @click="deleteFolder(item)"
66
85
  >
67
- 删除目录
86
+ 删除分组
68
87
  </button>
69
88
  </div>
70
89
 
71
90
  <button
72
91
  class="action-btn menu-trigger add-trigger"
73
- title="新增"
92
+ title="新增子笔记"
74
93
  type="button"
75
94
  @click="toggleMenu(getGroupMenuKey(item, 'add'))"
76
95
  >
@@ -95,6 +114,159 @@ vitepress/components/Layout/SidebarItems.vue
95
114
  >
96
115
  新增多篇笔记
97
116
  </button>
117
+ <button
118
+ class="menu-item"
119
+ type="button"
120
+ @click="createFolderInGroup(item)"
121
+ >
122
+ 新建子目录
123
+ </button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <div v-show="!item.collapsed" class="group-items">
129
+ <SidebarItems
130
+ :items="getChildItems(item)"
131
+ :depth="depth + 1"
132
+ :max-depth="maxDepth"
133
+ :show-note-id="showNoteId"
134
+ :base="base"
135
+ :current-path="currentPath"
136
+ :item-depth="depth + 1"
137
+ :group-path="getFolderPath(item)"
138
+ :is-dev="isDev"
139
+ :done-prefix="donePrefix"
140
+ :undone-prefix="undonePrefix"
141
+ :drag-context="getSidebarDrag()"
142
+ @create-note-in-group="$emit('create-note-in-group', $event)"
143
+ @create-notes-in-group="$emit('create-notes-in-group', $event)"
144
+ @create-folder-in-group="$emit('create-folder-in-group', $event)"
145
+ @rename-folder="$emit('rename-folder', $event)"
146
+ @delete-entry="$emit('delete-entry', $event)"
147
+ @rename-note="$emit('rename-note', $event)"
148
+ @delete-note="$emit('delete-note', $event)"
149
+ @create-note-around="$emit('create-note-around', $event)"
150
+ @open-note-about="$emit('open-note-about', $event)"
151
+ />
152
+ </div>
153
+ </div>
154
+
155
+ <!-- 父笔记行(legacy:有 link 且有子项) -->
156
+ <div
157
+ v-else-if="hasChildren(item)"
158
+ class="group"
159
+ :class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
160
+ >
161
+ <div
162
+ class="sidebar-row group-row parent-note-row"
163
+ :class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
164
+ :style="getNodeStyle(depth)"
165
+ :data-note-id="extractNoteIdFromLink(item.link) || undefined"
166
+ :data-toc-line-index="item.tocLineIndex"
167
+ :data-node-id="getNodeId(item)"
168
+ @pointerdown="onRowPointerDown($event, item)"
169
+ >
170
+ <button
171
+ class="group-arrow-btn"
172
+ type="button"
173
+ :title="item.collapsed ? '展开' : '折叠'"
174
+ @click.stop="toggleItem(item)"
175
+ >
176
+ <span class="arrow" :class="{ collapsed: item.collapsed }">
177
+ <img
178
+ :src="
179
+ item.collapsed ? icon__sidebar_collapsed : icon__sidebar_opened
180
+ "
181
+ alt=""
182
+ />
183
+ </span>
184
+ </button>
185
+
186
+ <a
187
+ :href="getFullLink(item.link)"
188
+ :class="[
189
+ 'nav-item',
190
+ 'row-main',
191
+ 'parent-note-link',
192
+ { active: isActive(item.link) },
193
+ `nav-item-${extractNoteIdFromLink(item.link)}`,
194
+ `nav-item-level-${depth + 1}`,
195
+ ]"
196
+ :data-note-id="extractNoteIdFromLink(item.link)"
197
+ @click="onParentNoteTitleClick($event, item)"
198
+ >
199
+ {{ getNoteDisplayText(item.text, item.link) }}
200
+ </a>
201
+
202
+ <div
203
+ v-if="isDev"
204
+ class="row-actions"
205
+ :class="{ 'menu-open': isGroupMenuOpen(item) || isNoteMenuOpen(item) }"
206
+ @click.stop
207
+ >
208
+ <button
209
+ class="action-btn menu-trigger"
210
+ title="更多操作"
211
+ type="button"
212
+ @click="toggleMenu(getNoteMenuKey(item, 'more'))"
213
+ >
214
+ <span class="ellipsis">...</span>
215
+ </button>
216
+
217
+ <div
218
+ v-if="isMenuOpen(getNoteMenuKey(item, 'more'))"
219
+ class="action-menu"
220
+ >
221
+ <button class="menu-item" type="button" @click="renameNote(item)">
222
+ 重命名
223
+ </button>
224
+ <button
225
+ class="menu-item danger"
226
+ type="button"
227
+ @click="deleteNote(item)"
228
+ >
229
+ 删除笔记
230
+ </button>
231
+ <button class="menu-item" type="button" @click="openNoteAbout(item)">
232
+ 关于
233
+ </button>
234
+ </div>
235
+
236
+ <button
237
+ class="action-btn menu-trigger add-trigger"
238
+ title="新增子笔记"
239
+ type="button"
240
+ @click="toggleMenu(getGroupMenuKey(item, 'add'))"
241
+ >
242
+ +
243
+ </button>
244
+
245
+ <div
246
+ v-if="isMenuOpen(getGroupMenuKey(item, 'add'))"
247
+ class="action-menu"
248
+ >
249
+ <button
250
+ class="menu-item"
251
+ type="button"
252
+ @click="createNoteInGroup(item)"
253
+ >
254
+ 新增笔记
255
+ </button>
256
+ <button
257
+ class="menu-item"
258
+ type="button"
259
+ @click="createNotesInGroup(item)"
260
+ >
261
+ 新增多篇笔记
262
+ </button>
263
+ <button
264
+ class="menu-item"
265
+ type="button"
266
+ @click="createFolderInGroup(item)"
267
+ >
268
+ 新建子目录
269
+ </button>
98
270
  </div>
99
271
  </div>
100
272
  </div>
@@ -110,18 +282,18 @@ vitepress/components/Layout/SidebarItems.vue
110
282
  :item-depth="depth + 1"
111
283
  :group-path="getGroupPath(item)"
112
284
  :is-dev="isDev"
113
- :sidebar-density="sidebarDensity"
114
285
  :done-prefix="donePrefix"
115
286
  :undone-prefix="undonePrefix"
116
- @rename-group="$emit('rename-group', $event)"
117
- @delete-group="$emit('delete-group', $event)"
287
+ :drag-context="getSidebarDrag()"
118
288
  @create-note-in-group="$emit('create-note-in-group', $event)"
119
289
  @create-notes-in-group="$emit('create-notes-in-group', $event)"
290
+ @create-folder-in-group="$emit('create-folder-in-group', $event)"
291
+ @rename-folder="$emit('rename-folder', $event)"
292
+ @delete-entry="$emit('delete-entry', $event)"
120
293
  @rename-note="$emit('rename-note', $event)"
121
294
  @delete-note="$emit('delete-note', $event)"
122
295
  @create-note-around="$emit('create-note-around', $event)"
123
296
  @open-note-about="$emit('open-note-about', $event)"
124
- @reorder-sidebar="$emit('reorder-sidebar', $event)"
125
297
  />
126
298
  </div>
127
299
  </div>
@@ -130,14 +302,15 @@ vitepress/components/Layout/SidebarItems.vue
130
302
  <div
131
303
  v-else-if="!hasChildren(item)"
132
304
  class="sidebar-row note-row"
133
- :class="[getNoteDropClass(item), getDensityClass()]"
305
+ :class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
134
306
  :style="getNodeStyle(actualItemDepth)"
135
- :draggable="isDev"
136
- @dragstart="onNoteDragStart($event, item)"
137
- @dragend="clearDropTarget"
138
- @dragover.prevent.stop="onNoteDragOver($event, item)"
139
- @drop.stop="onNoteDrop($event, item)"
307
+ :data-note-id="extractNoteIdFromLink(item.link) || undefined"
308
+ :data-toc-line-index="item.tocLineIndex"
309
+ :data-node-id="getNodeId(item)"
310
+ @pointerdown="onRowPointerDown($event, item)"
140
311
  >
312
+ <span class="group-arrow-spacer" aria-hidden="true" />
313
+
141
314
  <a
142
315
  :href="getFullLink(item.link)"
143
316
  :class="[
@@ -220,18 +393,29 @@ vitepress/components/Layout/SidebarItems.vue
220
393
  </template>
221
394
 
222
395
  <script setup lang="ts">
396
+ import { useRouter } from 'vitepress'
223
397
  import { computed, onBeforeUnmount, ref } from 'vue'
224
398
 
399
+ import { useSidebarDragContext } from './composables/useSidebarDrag'
400
+ import { SIDEBAR_INDENT_SIZE } from './sidebarDragLogic'
401
+ import { computeSidebarNodeId } from '../../../utils/tocNodeId'
225
402
  import {
226
403
  icon__sidebar_opened,
227
404
  icon__sidebar_collapsed,
228
405
  } from '../../assets/icons'
229
406
 
407
+ import type { SidebarDragContext } from './composables/useSidebarDrag'
408
+
230
409
  interface SidebarItem {
231
410
  text: string
232
411
  link?: string
233
412
  items?: SidebarItem[]
234
413
  collapsed?: boolean
414
+ folderPath?: string[]
415
+ tocLineIndex?: number
416
+ nodeId?: string
417
+ isDragSourceSlot?: boolean
418
+ isDragPlaceholder?: boolean
235
419
  }
236
420
 
237
421
  interface Props {
@@ -244,97 +428,85 @@ interface Props {
244
428
  itemDepth?: number // 用于计算链接项的缩进,默认等于 depth
245
429
  groupPath?: string[]
246
430
  isDev?: boolean
247
- sidebarDensity?: SidebarDensity
248
431
  donePrefix?: string
249
432
  undonePrefix?: string
433
+ dragContext?: SidebarDragContext | null
250
434
  }
251
435
 
436
+ const router = useRouter()
437
+
252
438
  const props = withDefaults(defineProps<Props>(), {
253
439
  depth: 0,
254
- maxDepth: 3,
440
+ maxDepth: 0,
255
441
  showNoteId: false,
256
442
  base: '/',
257
443
  currentPath: '',
258
444
  itemDepth: undefined,
259
445
  groupPath: () => [],
260
446
  isDev: false,
261
- sidebarDensity: 'default',
262
447
  donePrefix: '✅',
263
448
  undonePrefix: '⏰',
449
+ dragContext: null,
264
450
  })
265
451
 
266
452
  const emit = defineEmits<{
267
- 'rename-group': [payload: GroupPayload]
268
- 'delete-group': [payload: GroupPayload]
269
- 'create-note-in-group': [payload: GroupPayload]
270
- 'create-notes-in-group': [payload: GroupPayload]
453
+ 'create-note-in-group': [payload: ParentNotePayload]
454
+ 'create-notes-in-group': [payload: ParentNotePayload]
455
+ 'create-folder-in-group': [payload: ParentNotePayload]
456
+ 'rename-folder': [payload: FolderPayload]
457
+ 'delete-entry': [payload: EntryPayload]
271
458
  'rename-note': [payload: NotePayload]
272
459
  'delete-note': [payload: NotePayload]
273
460
  'create-note-around': [
274
461
  payload: NotePayload & { placement: 'before' | 'after' },
275
462
  ]
276
463
  'open-note-about': [payload: NotePayload]
277
- 'reorder-sidebar': [payload: ReorderPayload]
278
464
  }>()
279
465
 
280
- interface GroupPayload {
281
- groupPath: string[]
466
+ interface ParentNotePayload {
467
+ parentNoteIndex?: string
468
+ parentFolderPath?: string[]
469
+ parentTocLineIndex?: number
282
470
  text: string
283
471
  }
284
472
 
285
- interface NotePayload {
286
- noteIndex: string
473
+ interface FolderPayload {
474
+ tocLineIndex: number
287
475
  text: string
288
- link?: string
289
476
  }
290
477
 
291
- interface DragPayload {
292
- type: 'group' | 'note'
293
- groupPath?: string[]
478
+ interface EntryPayload {
479
+ tocLineIndex: number
480
+ text: string
294
481
  noteIndex?: string
295
482
  }
296
483
 
297
- interface DropTarget {
298
- type: 'group' | 'note'
299
- key: string
300
- placement: 'before' | 'after' | 'inside'
301
- }
302
-
303
- interface ReorderPayload {
304
- dragType: 'group' | 'note'
305
- groupPath?: string[]
306
- noteIndex?: string
307
- targetType: 'group' | 'note'
308
- targetGroupPath?: string[]
309
- targetNoteIndex?: string
310
- placement?: 'before' | 'after'
484
+ interface NotePayload {
485
+ noteIndex: string
486
+ text: string
487
+ link?: string
311
488
  }
312
489
 
313
490
  type MenuType = 'more' | 'add'
314
- type SidebarDensity = 'compact' | 'default' | 'loose'
315
491
  type NoteStatus = 'done' | 'undone' | null
316
492
 
317
493
  // 获取实际的 item depth(用于链接项的缩进)
318
494
  const actualItemDepth = computed(() => props.itemDepth ?? props.depth)
319
495
  const openMenuKey = ref<string | null>(null)
320
- const dropTarget = ref<DropTarget | null>(null)
496
+ const injectedDrag = useSidebarDragContext()
321
497
  let ignoredToggleKey: string | null = null
322
498
 
323
- const dragDataType = 'application/x-tnotes-sidebar-node'
324
- const dragNoteType = 'application/x-tnotes-sidebar-note'
325
- const dragGroupType = 'application/x-tnotes-sidebar-group'
499
+ function getSidebarDrag(): SidebarDragContext | null {
500
+ return props.dragContext ?? injectedDrag
501
+ }
326
502
 
327
503
  if (typeof document !== 'undefined') {
328
504
  document.addEventListener('click', closeMenuOnDocumentClick, true)
329
- document.addEventListener('dragend', clearDropTarget, true)
330
- document.addEventListener('drop', clearDropTarget, true)
331
505
  }
332
506
 
333
507
  onBeforeUnmount(() => {
334
508
  if (typeof document !== 'undefined') {
335
509
  document.removeEventListener('click', closeMenuOnDocumentClick, true)
336
- document.removeEventListener('dragend', clearDropTarget, true)
337
- document.removeEventListener('drop', clearDropTarget, true)
338
510
  }
339
511
  })
340
512
 
@@ -349,39 +521,98 @@ function getChildItems(item: SidebarItem): SidebarItem[] {
349
521
 
350
522
  // 获取项的唯一 key
351
523
  function getItemKey(item: SidebarItem): string {
524
+ if (item.isDragPlaceholder) {
525
+ return `drag-placeholder-${item.tocLineIndex ?? 'slot'}`
526
+ }
352
527
  return item.link || item.text
353
528
  }
354
529
 
355
530
  // 切换项的展开/折叠状态
356
531
  function toggleItem(item: SidebarItem) {
532
+ if (!hasChildren(item)) return
357
533
  item.collapsed = !item.collapsed
358
534
  }
359
535
 
536
+ function onGroupTitleClick(item: SidebarItem) {
537
+ toggleItem(item)
538
+ }
539
+
540
+ function navigateToSidebarLink(link?: string) {
541
+ if (!link || isActive(link)) return
542
+ router.go(getFullLink(link))
543
+ }
544
+
545
+ function activateParentNoteLink(item: SidebarItem) {
546
+ if (!hasChildren(item) || !item.link) return
547
+ toggleItem(item)
548
+ navigateToSidebarLink(item.link)
549
+ }
550
+
551
+ function suppressNextParentNoteClick(row: HTMLElement) {
552
+ row.addEventListener(
553
+ 'click',
554
+ (event) => {
555
+ if (!(event.target instanceof Element)) return
556
+ if (!event.target.closest('.parent-note-link')) return
557
+ event.preventDefault()
558
+ event.stopImmediatePropagation()
559
+ },
560
+ { capture: true, once: true },
561
+ )
562
+ }
563
+
564
+ function onParentNoteTitleClick(event: MouseEvent, item: SidebarItem) {
565
+ if (!hasChildren(item) || !item.link) return
566
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
567
+ return
568
+ }
569
+ event.preventDefault()
570
+ activateParentNoteLink(item)
571
+ }
572
+
360
573
  function getNodeStyle(depth: number) {
361
574
  return {
362
575
  paddingLeft: `${depth * getIndentSize()}px`,
363
576
  }
364
577
  }
365
578
 
366
- function getDensityClass(): string {
367
- return `density-${props.sidebarDensity}`
368
- }
369
-
370
579
  function getIndentSize(): number {
371
- if (props.sidebarDensity === 'compact') return 20
372
- if (props.sidebarDensity === 'loose') return 28
373
- return 24
580
+ return SIDEBAR_INDENT_SIZE
374
581
  }
375
582
 
376
583
  function getGroupPath(item: SidebarItem): string[] {
377
584
  return [...props.groupPath, item.text]
378
585
  }
379
586
 
380
- function getGroupPayload(item: SidebarItem): GroupPayload {
381
- return {
382
- groupPath: getGroupPath(item),
587
+ function isPureFolder(item: SidebarItem): boolean {
588
+ return !item.link && (!!(item.folderPath && item.folderPath.length > 0) || hasChildren(item))
589
+ }
590
+
591
+ function getFolderPath(item: SidebarItem): string[] {
592
+ return item.folderPath ?? getGroupPath(item)
593
+ }
594
+
595
+ function serializeFolderPath(item: SidebarItem): string {
596
+ return JSON.stringify(getFolderPath(item))
597
+ }
598
+
599
+ function getParentNotePayload(item: SidebarItem): ParentNotePayload {
600
+ const base = {
601
+ parentTocLineIndex: item.tocLineIndex,
383
602
  text: item.text,
384
603
  }
604
+
605
+ if (isPureFolder(item)) {
606
+ return {
607
+ ...base,
608
+ parentFolderPath: getFolderPath(item),
609
+ }
610
+ }
611
+
612
+ return {
613
+ ...base,
614
+ parentNoteIndex: extractNoteIdFromLink(item.link) || '',
615
+ }
385
616
  }
386
617
 
387
618
  function getNotePayload(item: SidebarItem): NotePayload {
@@ -393,21 +624,20 @@ function getNotePayload(item: SidebarItem): NotePayload {
393
624
  }
394
625
 
395
626
  function getGroupMenuKey(item: SidebarItem, menuType: MenuType): string {
627
+ if (item.tocLineIndex !== undefined) {
628
+ return `group:${menuType}:${item.tocLineIndex}`
629
+ }
396
630
  return `group:${menuType}:${getGroupPath(item).join('/')}`
397
631
  }
398
632
 
399
- function getGroupDropKey(item: SidebarItem): string {
400
- return `group:${getGroupPath(item).join('/')}`
633
+ function getFolderMenuKey(item: SidebarItem, menuType: MenuType): string {
634
+ return `folder:${menuType}:${item.tocLineIndex ?? item.text}`
401
635
  }
402
636
 
403
637
  function getNoteMenuKey(item: SidebarItem, menuType: MenuType): string {
404
638
  return `note:${menuType}:${extractNoteIdFromLink(item.link) || item.link || item.text}`
405
639
  }
406
640
 
407
- function getNoteDropKey(item: SidebarItem): string {
408
- return `note:${extractNoteIdFromLink(item.link) || item.link || item.text}`
409
- }
410
-
411
641
  function toggleMenu(menuKey: string) {
412
642
  if (ignoredToggleKey === menuKey) {
413
643
  ignoredToggleKey = null
@@ -440,10 +670,11 @@ function closeMenuOnDocumentClick() {
440
670
  }
441
671
 
442
672
  function isGroupMenuOpen(item: SidebarItem): boolean {
443
- return (
444
- isMenuOpen(getGroupMenuKey(item, 'more')) ||
445
- isMenuOpen(getGroupMenuKey(item, 'add'))
446
- )
673
+ return isMenuOpen(getGroupMenuKey(item, 'add'))
674
+ }
675
+
676
+ function isFolderMenuOpen(item: SidebarItem): boolean {
677
+ return isMenuOpen(getFolderMenuKey(item, 'more'))
447
678
  }
448
679
 
449
680
  function isNoteMenuOpen(item: SidebarItem): boolean {
@@ -453,23 +684,43 @@ function isNoteMenuOpen(item: SidebarItem): boolean {
453
684
  )
454
685
  }
455
686
 
456
- function renameGroup(item: SidebarItem) {
457
- emit('rename-group', getGroupPayload(item))
687
+ function createNoteInGroup(item: SidebarItem) {
688
+ emit('create-note-in-group', getParentNotePayload(item))
458
689
  closeMenu()
459
690
  }
460
691
 
461
- function deleteGroup(item: SidebarItem) {
462
- emit('delete-group', getGroupPayload(item))
692
+ function createNotesInGroup(item: SidebarItem) {
693
+ emit('create-notes-in-group', getParentNotePayload(item))
463
694
  closeMenu()
464
695
  }
465
696
 
466
- function createNoteInGroup(item: SidebarItem) {
467
- emit('create-note-in-group', getGroupPayload(item))
697
+ function createFolderInGroup(item: SidebarItem) {
698
+ emit('create-folder-in-group', getParentNotePayload(item))
468
699
  closeMenu()
469
700
  }
470
701
 
471
- function createNotesInGroup(item: SidebarItem) {
472
- emit('create-notes-in-group', getGroupPayload(item))
702
+ function getFolderPayload(item: SidebarItem): FolderPayload {
703
+ return {
704
+ tocLineIndex: item.tocLineIndex ?? -1,
705
+ text: item.text,
706
+ }
707
+ }
708
+
709
+ function getEntryPayload(item: SidebarItem): EntryPayload {
710
+ return {
711
+ tocLineIndex: item.tocLineIndex ?? -1,
712
+ text: item.text,
713
+ noteIndex: extractNoteIdFromLink(item.link) || undefined,
714
+ }
715
+ }
716
+
717
+ function renameFolder(item: SidebarItem) {
718
+ emit('rename-folder', getFolderPayload(item))
719
+ closeMenu()
720
+ }
721
+
722
+ function deleteFolder(item: SidebarItem) {
723
+ emit('delete-entry', getEntryPayload(item))
473
724
  closeMenu()
474
725
  }
475
726
 
@@ -479,7 +730,11 @@ function renameNote(item: SidebarItem) {
479
730
  }
480
731
 
481
732
  function deleteNote(item: SidebarItem) {
482
- emit('delete-note', getNotePayload(item))
733
+ if (hasChildren(item) && item.tocLineIndex !== undefined) {
734
+ emit('delete-entry', getEntryPayload(item))
735
+ } else {
736
+ emit('delete-note', getNotePayload(item))
737
+ }
483
738
  closeMenu()
484
739
  }
485
740
 
@@ -496,228 +751,147 @@ function createNoteAround(item: SidebarItem, placement: 'before' | 'after') {
496
751
  closeMenu()
497
752
  }
498
753
 
499
- function setDragData(event: DragEvent, payload: DragPayload) {
500
- if (!event.dataTransfer) return
501
-
502
- event.dataTransfer.effectAllowed = 'move'
503
- event.dataTransfer.setData(dragDataType, JSON.stringify(payload))
504
- event.dataTransfer.setData(
505
- payload.type === 'note' ? dragNoteType : dragGroupType,
506
- '1',
754
+ function isInteractiveDragTarget(target: EventTarget | null): boolean {
755
+ if (!(target instanceof HTMLElement)) return false
756
+ return !!target.closest(
757
+ '.group-arrow-btn, .row-actions, .action-menu, .action-btn',
507
758
  )
508
759
  }
509
760
 
510
- function getDragData(event: DragEvent): DragPayload | null {
511
- const raw = event.dataTransfer?.getData(dragDataType)
512
- if (!raw) return null
513
-
514
- try {
515
- return JSON.parse(raw) as DragPayload
516
- } catch {
517
- return null
518
- }
519
- }
520
-
521
- function getDragTypes(event: DragEvent): string[] {
522
- return Array.from(event.dataTransfer?.types ?? [])
523
- }
761
+ const DRAG_START_THRESHOLD_PX = 4
524
762
 
525
- function getDraggedType(event: DragEvent): DragPayload['type'] | null {
526
- const types = getDragTypes(event)
763
+ function onRowPointerDown(event: PointerEvent, item: SidebarItem) {
764
+ const sidebarDrag = getSidebarDrag()
765
+ if (!props.isDev || !sidebarDrag || event.button !== 0) return
766
+ if (isInteractiveDragTarget(event.target)) return
527
767
 
528
- if (types.includes(dragNoteType)) return 'note'
529
- if (types.includes(dragGroupType)) return 'group'
768
+ const noteIndex = extractNoteIdFromLink(item.link)
769
+ if (!noteIndex || item.tocLineIndex === undefined) return
530
770
 
531
- return getDragData(event)?.type ?? null
532
- }
771
+ const row = event.currentTarget as HTMLElement
772
+ const fromLink =
773
+ event.target instanceof Element && !!event.target.closest('.row-main')
533
774
 
534
- function setDropEffect(event: DragEvent) {
535
- if (event.dataTransfer) {
536
- event.dataTransfer.dropEffect = 'move'
775
+ if (fromLink) {
776
+ event.preventDefault()
537
777
  }
538
- }
539
778
 
540
- function clearDropTarget() {
541
- dropTarget.value = null
542
- }
779
+ row.setPointerCapture(event.pointerId)
543
780
 
544
- function isSamePath(left: string[], right: string[]): boolean {
545
- return (
546
- left.length === right.length &&
547
- left.every((item, index) => item === right[index])
548
- )
549
- }
781
+ const startX = event.clientX
782
+ const startY = event.clientY
783
+ const pointerId = event.pointerId
784
+ let dragged = false
550
785
 
551
- function getNoteDropPlacement(event: DragEvent): 'before' | 'after' {
552
- const row = event.currentTarget as HTMLElement | null
553
- if (!row) return 'before'
786
+ function onPendingMove(moveEvent: PointerEvent) {
787
+ if (moveEvent.pointerId !== pointerId) return
554
788
 
555
- const rect = row.getBoundingClientRect()
556
- return event.clientY < rect.top + rect.height / 2 ? 'before' : 'after'
557
- }
558
-
559
- function getGroupDropClass(item: SidebarItem): Record<string, boolean> {
560
- const key = getGroupDropKey(item)
561
- return {
562
- 'drop-before':
563
- dropTarget.value?.type === 'group' &&
564
- dropTarget.value.key === key &&
565
- dropTarget.value.placement === 'before',
566
- 'drop-inside':
567
- dropTarget.value?.type === 'group' &&
568
- dropTarget.value.key === key &&
569
- dropTarget.value.placement === 'inside',
570
- }
571
- }
789
+ const distance = Math.hypot(
790
+ moveEvent.clientX - startX,
791
+ moveEvent.clientY - startY,
792
+ )
793
+ if (distance < DRAG_START_THRESHOLD_PX) return
572
794
 
573
- function getNoteDropClass(item: SidebarItem): Record<string, boolean> {
574
- const key = getNoteDropKey(item)
575
- return {
576
- 'drop-before':
577
- dropTarget.value?.type === 'note' &&
578
- dropTarget.value.key === key &&
579
- dropTarget.value.placement === 'before',
580
- 'drop-after':
581
- dropTarget.value?.type === 'note' &&
582
- dropTarget.value.key === key &&
583
- dropTarget.value.placement === 'after',
795
+ dragged = true
796
+ moveEvent.preventDefault()
797
+ cleanupPending()
798
+ closeMenu()
799
+ sidebarDrag.startDrag(noteIndex, item.tocLineIndex!, row, moveEvent)
584
800
  }
585
- }
586
801
 
587
- function onGroupDragStart(event: DragEvent, item: SidebarItem) {
588
- if (!props.isDev) return
589
- closeMenu()
590
- setDragData(event, {
591
- type: 'group',
592
- groupPath: getGroupPath(item),
593
- })
594
- }
802
+ function onPendingUp(upEvent: PointerEvent) {
803
+ if (upEvent.pointerId !== pointerId) return
595
804
 
596
- function onNoteDragStart(event: DragEvent, item: SidebarItem) {
597
- if (!props.isDev) return
598
- const noteIndex = extractNoteIdFromLink(item.link)
599
- if (!noteIndex) return
805
+ cleanupPending()
600
806
 
601
- closeMenu()
602
- setDragData(event, {
603
- type: 'note',
604
- noteIndex,
605
- })
606
- }
607
-
608
- function onGroupDragOver(event: DragEvent, item: SidebarItem) {
609
- if (!props.isDev) return
610
-
611
- const draggedType = getDraggedType(event)
612
- if (!draggedType) return
613
-
614
- setDropEffect(event)
615
-
616
- const targetGroupPath = getGroupPath(item)
617
- const payload = getDragData(event)
618
- if (
619
- draggedType === 'group' &&
620
- payload?.groupPath &&
621
- isSamePath(payload.groupPath, targetGroupPath)
622
- ) {
623
- clearDropTarget()
624
- return
807
+ if (!dragged && fromLink) {
808
+ if (hasChildren(item) && item.link) {
809
+ suppressNextParentNoteClick(row)
810
+ activateParentNoteLink(item)
811
+ } else if (hasChildren(item)) {
812
+ toggleItem(item)
813
+ } else {
814
+ row.querySelector<HTMLAnchorElement>('.row-main')?.click()
815
+ }
816
+ }
625
817
  }
626
818
 
627
- dropTarget.value = {
628
- type: 'group',
629
- key: getGroupDropKey(item),
630
- placement: draggedType === 'note' ? 'inside' : 'before',
819
+ function cleanupPending() {
820
+ if (row.hasPointerCapture(pointerId)) {
821
+ row.releasePointerCapture(pointerId)
822
+ }
823
+ window.removeEventListener('pointermove', onPendingMove)
824
+ window.removeEventListener('pointerup', onPendingUp)
825
+ window.removeEventListener('pointercancel', onPendingUp)
631
826
  }
827
+
828
+ window.addEventListener('pointermove', onPendingMove)
829
+ window.addEventListener('pointerup', onPendingUp)
830
+ window.addEventListener('pointercancel', onPendingUp)
632
831
  }
633
832
 
634
- function onGroupDrop(event: DragEvent, item: SidebarItem) {
635
- if (!props.isDev) return
833
+ function onFolderRowPointerDown(event: PointerEvent, item: SidebarItem) {
834
+ const sidebarDrag = getSidebarDrag()
835
+ if (!props.isDev || !sidebarDrag || event.button !== 0) return
836
+ if (isInteractiveDragTarget(event.target)) return
837
+ if (item.tocLineIndex === undefined) return
636
838
 
637
- const payload = getDragData(event)
638
- if (!payload) {
639
- clearDropTarget()
640
- return
641
- }
642
-
643
- const targetGroupPath = getGroupPath(item)
839
+ const row = event.currentTarget as HTMLElement
840
+ const fromTitle =
841
+ event.target instanceof Element && !!event.target.closest('.group-title')
644
842
 
645
- if (payload.type === 'note' && payload.noteIndex) {
646
- emit('reorder-sidebar', {
647
- dragType: 'note',
648
- noteIndex: payload.noteIndex,
649
- targetType: 'group',
650
- targetGroupPath,
651
- })
652
- clearDropTarget()
653
- return
843
+ if (fromTitle) {
844
+ event.preventDefault()
654
845
  }
655
846
 
656
- if (payload.type === 'group' && payload.groupPath) {
657
- if (isSamePath(payload.groupPath, targetGroupPath)) {
658
- clearDropTarget()
659
- return
660
- }
661
-
662
- emit('reorder-sidebar', {
663
- dragType: 'group',
664
- groupPath: payload.groupPath,
665
- targetType: 'group',
666
- targetGroupPath,
667
- placement: 'before',
668
- })
669
- }
847
+ row.setPointerCapture(event.pointerId)
670
848
 
671
- clearDropTarget()
672
- }
849
+ const startX = event.clientX
850
+ const startY = event.clientY
851
+ const pointerId = event.pointerId
852
+ let dragged = false
673
853
 
674
- function onNoteDragOver(event: DragEvent, item: SidebarItem) {
675
- if (!props.isDev || getDraggedType(event) !== 'note') return
854
+ function onPendingMove(moveEvent: PointerEvent) {
855
+ if (moveEvent.pointerId !== pointerId) return
676
856
 
677
- const targetNoteIndex = extractNoteIdFromLink(item.link)
678
- const payload = getDragData(event)
679
- if (!targetNoteIndex || payload?.noteIndex === targetNoteIndex) {
680
- clearDropTarget()
681
- return
682
- }
857
+ const distance = Math.hypot(
858
+ moveEvent.clientX - startX,
859
+ moveEvent.clientY - startY,
860
+ )
861
+ if (distance < DRAG_START_THRESHOLD_PX) return
683
862
 
684
- setDropEffect(event)
685
- dropTarget.value = {
686
- type: 'note',
687
- key: getNoteDropKey(item),
688
- placement: getNoteDropPlacement(event),
863
+ dragged = true
864
+ moveEvent.preventDefault()
865
+ cleanupPending()
866
+ closeMenu()
867
+ sidebarDrag!.startFolderDrag(item.tocLineIndex!, row, moveEvent)
689
868
  }
690
- }
691
869
 
692
- function onNoteDrop(event: DragEvent, item: SidebarItem) {
693
- if (!props.isDev) return
870
+ function onPendingUp(upEvent: PointerEvent) {
871
+ if (upEvent.pointerId !== pointerId) return
872
+ cleanupPending()
694
873
 
695
- const payload = getDragData(event)
696
- const targetNoteIndex = extractNoteIdFromLink(item.link)
697
- if (
698
- !payload ||
699
- payload.type !== 'note' ||
700
- !payload.noteIndex ||
701
- !targetNoteIndex
702
- ) {
703
- clearDropTarget()
704
- return
874
+ if (!dragged && fromTitle) {
875
+ toggleItem(item)
876
+ }
705
877
  }
706
878
 
707
- if (payload.noteIndex === targetNoteIndex) {
708
- clearDropTarget()
709
- return
879
+ function cleanupPending() {
880
+ if (row.hasPointerCapture(pointerId)) {
881
+ row.releasePointerCapture(pointerId)
882
+ }
883
+ window.removeEventListener('pointermove', onPendingMove)
884
+ window.removeEventListener('pointerup', onPendingUp)
885
+ window.removeEventListener('pointercancel', onPendingUp)
710
886
  }
711
887
 
712
- const placement = getNoteDropPlacement(event)
713
- emit('reorder-sidebar', {
714
- dragType: 'note',
715
- noteIndex: payload.noteIndex,
716
- targetType: 'note',
717
- targetNoteIndex,
718
- placement,
719
- })
720
- clearDropTarget()
888
+ window.addEventListener('pointermove', onPendingMove)
889
+ window.addEventListener('pointerup', onPendingUp)
890
+ window.addEventListener('pointercancel', onPendingUp)
891
+ }
892
+
893
+ function getNodeId(item: SidebarItem): string {
894
+ return item.nodeId ?? computeSidebarNodeId(item)
721
895
  }
722
896
 
723
897
  // 获取完整链接(包含 base)
@@ -817,14 +991,8 @@ function getNoteDisplayText(text: string, link?: string): string {
817
991
  <style scoped>
818
992
  .group {
819
993
  margin-bottom: 2px;
820
- }
821
-
822
- .group.density-compact {
823
- margin-bottom: 0;
824
- }
825
-
826
- .group.density-loose {
827
- margin-bottom: 6px;
994
+ width: 100%;
995
+ min-width: 0;
828
996
  }
829
997
 
830
998
  .sidebar-row {
@@ -832,50 +1000,33 @@ function getNoteDisplayText(text: string, link?: string): string {
832
1000
  display: flex;
833
1001
  align-items: center;
834
1002
  width: 100%;
835
- min-height: 30px;
836
- border-radius: 6px;
837
- transition: background-color 0.18s ease;
838
- }
839
-
840
- .sidebar-row.density-compact {
841
- min-height: 26px;
842
- border-radius: 4px;
843
- }
844
-
845
- .sidebar-row.density-loose {
846
1003
  min-height: 36px;
847
1004
  border-radius: 8px;
1005
+ transition: background-color 0.18s ease;
848
1006
  }
849
1007
 
850
1008
  .sidebar-row:hover {
851
1009
  background: var(--vp-c-default-soft);
852
1010
  }
853
1011
 
854
- .sidebar-row.drop-before::before,
855
- .sidebar-row.drop-after::after {
856
- position: absolute;
857
- right: 8px;
858
- left: 8px;
859
- z-index: 4;
860
- height: 2px;
861
- background: var(--vp-c-brand-1);
862
- border-radius: 999px;
863
- content: '';
864
- pointer-events: none;
1012
+ body.sidebar-drag-active .sidebar-row:not(.is-dragging-source) {
1013
+ cursor: grabbing;
865
1014
  }
866
1015
 
867
- .sidebar-row.drop-before::before {
868
- top: -2px;
1016
+ .sidebar-row.is-dragging-source {
1017
+ opacity: 0.35;
869
1018
  }
870
1019
 
871
- .sidebar-row.drop-after::after {
872
- bottom: -2px;
1020
+ /* 语雀式:拖拽中源项原位保留布局高度,内容不可见 */
1021
+ .group.is-drag-source-slot,
1022
+ .sidebar-row.is-drag-source-slot {
1023
+ visibility: hidden;
1024
+ pointer-events: none;
873
1025
  }
874
1026
 
875
- .sidebar-row.drop-inside {
876
- background: var(--vp-c-brand-soft);
877
- outline: 1px solid var(--vp-c-brand-1);
878
- outline-offset: -1px;
1027
+ .sidebar-row.is-drag-placeholder {
1028
+ visibility: hidden;
1029
+ pointer-events: none;
879
1030
  }
880
1031
 
881
1032
  .row-main {
@@ -883,35 +1034,57 @@ function getNoteDisplayText(text: string, link?: string): string {
883
1034
  flex: 1;
884
1035
  }
885
1036
 
886
- .group-title {
1037
+ .parent-note-row {
887
1038
  display: flex;
888
1039
  align-items: center;
889
- gap: 6px;
890
- width: 100%;
891
- padding: 4px 6px;
892
- font-weight: 600;
893
- color: var(--vp-c-text-1);
894
- background: none;
1040
+ gap: 4px;
1041
+ }
1042
+
1043
+ .note-row {
1044
+ display: flex;
1045
+ align-items: center;
1046
+ gap: 4px;
1047
+ }
1048
+
1049
+ .group-arrow-btn {
1050
+ display: inline-flex;
1051
+ align-items: center;
1052
+ justify-content: center;
1053
+ flex: 0 0 20px;
1054
+ width: 20px;
1055
+ height: 20px;
1056
+ padding: 0;
895
1057
  border: none;
1058
+ background: none;
896
1059
  cursor: pointer;
897
- text-align: left;
898
- transition: color 0.25s;
899
1060
  }
900
1061
 
901
- .sidebar-row.density-compact .group-title,
902
- .sidebar-row.density-compact .nav-item {
903
- min-height: 26px;
904
- padding: 2px 5px;
905
- font-size: 13px;
906
- line-height: 18px;
1062
+ .group-arrow-spacer {
1063
+ flex: 0 0 20px;
1064
+ width: 20px;
1065
+ height: 20px;
907
1066
  }
908
1067
 
909
- .sidebar-row.density-loose .group-title,
910
- .sidebar-row.density-loose .nav-item {
1068
+ .parent-note-link {
1069
+ font-weight: 600;
1070
+ }
1071
+
1072
+ .group-title {
1073
+ display: flex;
1074
+ align-items: center;
1075
+ gap: 6px;
1076
+ width: 100%;
911
1077
  min-height: 36px;
912
1078
  padding: 6px 8px;
1079
+ font-weight: 600;
913
1080
  font-size: 14px;
914
1081
  line-height: 22px;
1082
+ color: var(--vp-c-text-1);
1083
+ background: none;
1084
+ border: none;
1085
+ cursor: pointer;
1086
+ text-align: left;
1087
+ transition: color 0.25s;
915
1088
  }
916
1089
 
917
1090
  .group-title:hover {
@@ -944,17 +1117,19 @@ function getNoteDisplayText(text: string, link?: string): string {
944
1117
  .nav-item {
945
1118
  display: flex;
946
1119
  align-items: center;
947
- min-height: 30px;
948
- padding: 4px 6px;
1120
+ min-height: 36px;
1121
+ padding: 6px 8px;
949
1122
  color: var(--vp-c-text-2);
950
1123
  text-decoration: none;
951
1124
  border-radius: 4px;
952
1125
  font-size: 14px;
953
- line-height: 20px;
1126
+ line-height: 22px;
954
1127
  transition: all 0.25s;
955
1128
  overflow: hidden;
956
1129
  text-overflow: ellipsis;
957
1130
  white-space: nowrap;
1131
+ -webkit-user-drag: none;
1132
+ user-select: none;
958
1133
  }
959
1134
 
960
1135
  .nav-item:hover {