@tnotesjs/core 0.1.28 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/{chunk-4KG6RXFS.js → chunk-5QN44ES5.js} +2946 -1533
- package/dist/{chunk-AGSL3VZE.js → chunk-5Y5IYS6C.js} +7 -0
- package/dist/cli/index.js +343 -40
- package/dist/index.js +1 -1
- package/dist/vitepress/config/index.js +573 -65
- package/package.json +4 -1
- package/templates/sub-repo/.gitattributes +30 -0
- package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
- package/templates/sub-repo/.vitepress/config.mts +12 -0
- package/templates/sub-repo/.vitepress/env.d.ts +10 -0
- package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
- package/templates/sub-repo/.vscode/settings.json +330 -0
- package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
- package/templates/sub-repo/public/favicon.ico +0 -0
- package/templates/sub-repo/public/logo.png +0 -0
- package/templates/sub-repo/tsconfig.json +45 -0
- package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
- package/vitepress/assets/icons/icon__setting_light.svg +4 -0
- package/vitepress/assets/icons/index.ts +2 -2
- package/vitepress/client/localSearchIndexBridge.ts +24 -0
- package/vitepress/components/Discussions/Discussions.vue +1 -1
- package/vitepress/components/Footprints/Footprints.vue +90 -25
- package/vitepress/components/Layout/CustomSidebar.vue +489 -117
- package/vitepress/components/Layout/Layout.vue +5 -0
- package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
- package/vitepress/components/Layout/SidebarItems.vue +517 -342
- package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
- package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
- package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
- package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
- package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
- package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
- package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
- package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
- package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
- package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
- package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
- package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
- package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
- package/vitepress/components/MarkMap/MarkMap.vue +67 -10
- package/vitepress/components/Settings/Settings.vue +78 -74
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
- package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
- package/vitepress/components/constants.ts +18 -6
- package/vitepress/components/sidebar.data.ts +13 -11
- package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
- package/vitepress/components/sidebarTreeHelpers.ts +168 -0
- package/vitepress/components/utils/vscodePaths.test.ts +41 -0
- package/vitepress/components/utils/vscodePaths.ts +52 -0
- package/vitepress/config/index.ts +2 -0
- package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
- package/vitepress/plugins/localSearchReindex.test.ts +46 -0
- package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
- package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
- package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
- package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
- package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
- package/vitepress/theme/index.ts +8 -0
- package/vitepress/theme/styles/base.scss +0 -8
- package/vitepress/theme/styles/components/markmap.scss +23 -2
- package/vitepress/theme/styles/components/swiper.scss +1 -1
- package/vitepress/theme/styles/doc-layout.scss +212 -0
- package/vitepress/theme/styles/index.scss +3 -0
- package/vitepress/theme/styles/layout.scss +60 -10
- package/vitepress/assets/icons/icon__mindmap.svg +0 -1
- package/vitepress/assets/icons/icon__setting.svg +0 -1
- package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
|
@@ -4,29 +4,80 @@ vitepress/components/Layout/CustomSidebar.vue
|
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
6
|
<div class="custom-sidebar-wrapper">
|
|
7
|
-
<nav class="nav" ref="navRef">
|
|
7
|
+
<nav class="nav sidebar-nav" ref="navRef">
|
|
8
|
+
<a
|
|
9
|
+
:href="readmeLink"
|
|
10
|
+
class="nav-item readme-nav-item"
|
|
11
|
+
:class="{ active: isReadmeActive }"
|
|
12
|
+
>
|
|
13
|
+
README
|
|
14
|
+
</a>
|
|
15
|
+
|
|
8
16
|
<!-- 使用递归组件渲染侧边栏,支持任意层级嵌套 -->
|
|
9
17
|
<SidebarItems
|
|
10
|
-
:items="
|
|
18
|
+
:items="displaySidebarGroups"
|
|
11
19
|
:depth="0"
|
|
12
20
|
:max-depth="maxDepth"
|
|
13
21
|
:show-note-id="showNoteId"
|
|
14
22
|
:base="base"
|
|
15
23
|
:current-path="route.path"
|
|
16
24
|
:is-dev="isDev"
|
|
17
|
-
:sidebar-density="sidebarDensity"
|
|
18
25
|
:done-prefix="donePrefix"
|
|
19
26
|
:undone-prefix="undonePrefix"
|
|
20
|
-
|
|
21
|
-
@delete-group="deleteGroup"
|
|
27
|
+
:drag-context="sidebarDrag"
|
|
22
28
|
@create-note-in-group="createNoteInGroup"
|
|
23
29
|
@create-notes-in-group="createNotesInGroup"
|
|
30
|
+
@create-folder-in-group="createFolderInGroup"
|
|
31
|
+
@rename-folder="renameFolder"
|
|
32
|
+
@delete-entry="deleteEntry"
|
|
24
33
|
@rename-note="renameNote"
|
|
25
34
|
@delete-note="deleteNote"
|
|
26
35
|
@create-note-around="createNoteAround"
|
|
27
36
|
@open-note-about="openNoteAbout"
|
|
28
|
-
@reorder-sidebar="reorderSidebar"
|
|
29
37
|
/>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
v-if="dropSlot.visible"
|
|
41
|
+
class="sidebar-drop-slot"
|
|
42
|
+
:style="{
|
|
43
|
+
top: `${dropSlot.top}px`,
|
|
44
|
+
left: `${dropSlot.left}px`,
|
|
45
|
+
width: `${dropSlot.width}px`,
|
|
46
|
+
height: `${dropSlot.height}px`,
|
|
47
|
+
}"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<div
|
|
51
|
+
v-if="dropIndicator.visible"
|
|
52
|
+
class="sidebar-drop-layer"
|
|
53
|
+
:style="{ top: `${dropIndicator.top}px` }"
|
|
54
|
+
>
|
|
55
|
+
<template v-if="dropIndicator.mode === 'tail-rail'">
|
|
56
|
+
<div
|
|
57
|
+
v-for="depth in dropIndicator.depthLevels"
|
|
58
|
+
:key="depth"
|
|
59
|
+
class="sidebar-drop-rail-segment"
|
|
60
|
+
:class="{ 'is-active': depth === dropIndicator.activeDepth }"
|
|
61
|
+
:style="{
|
|
62
|
+
left: `${getDropRailLeft(depth)}px`,
|
|
63
|
+
width: `${getDropRailWidth(depth)}px`,
|
|
64
|
+
}"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
<div
|
|
68
|
+
v-else
|
|
69
|
+
class="sidebar-drop-indicator"
|
|
70
|
+
:class="{ 'is-inside': dropIndicator.mode === 'inside' }"
|
|
71
|
+
:style="{
|
|
72
|
+
left: `${dropIndicator.left}px`,
|
|
73
|
+
width: `${dropIndicator.width}px`,
|
|
74
|
+
height:
|
|
75
|
+
dropIndicator.mode === 'inside'
|
|
76
|
+
? `${getDropInsideHeight()}px`
|
|
77
|
+
: undefined,
|
|
78
|
+
}"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
30
81
|
</nav>
|
|
31
82
|
</div>
|
|
32
83
|
</template>
|
|
@@ -35,13 +86,33 @@ vitepress/components/Layout/CustomSidebar.vue
|
|
|
35
86
|
import { useRoute, useData } from 'vitepress'
|
|
36
87
|
import { ref, onMounted, watch, computed } from 'vue'
|
|
37
88
|
|
|
89
|
+
import {
|
|
90
|
+
useSidebarDrag,
|
|
91
|
+
getDropRailLeft as calcDropRailLeft,
|
|
92
|
+
type DragReorderMeta,
|
|
93
|
+
type YuqueReorderPayload,
|
|
94
|
+
} from './composables/useSidebarDrag'
|
|
95
|
+
import {
|
|
96
|
+
applySidebarCollapsedState,
|
|
97
|
+
saveSidebarCollapsedState,
|
|
98
|
+
loadSidebarCollapsedState,
|
|
99
|
+
mergeCollapseStore,
|
|
100
|
+
saveCollapseEntries,
|
|
101
|
+
buildPostDropCollapsePatch,
|
|
102
|
+
shouldSuppressActiveItemScroll,
|
|
103
|
+
suppressActiveItemScroll,
|
|
104
|
+
getCollapseKeyForTreeItem,
|
|
105
|
+
} from './sidebarCollapsedState'
|
|
106
|
+
import { SIDEBAR_ROW_HEIGHT } from './sidebarDragLogic'
|
|
38
107
|
import SidebarItems from './SidebarItems.vue'
|
|
108
|
+
import { enrichSidebarNodeIds, computeSidebarNodeId } from '../../../utils/tocNodeId'
|
|
39
109
|
import {
|
|
40
110
|
SIDEBAR_SHOW_NOTE_ID_KEY,
|
|
41
111
|
SIDEBAR_MAX_DEPTH_KEY,
|
|
42
|
-
SIDEBAR_DENSITY_KEY,
|
|
43
112
|
SIDEBAR_DONE_PREFIX_KEY,
|
|
44
113
|
SIDEBAR_UNDONE_PREFIX_KEY,
|
|
114
|
+
SIDEBAR_COLLAPSED_STATE_KEY,
|
|
115
|
+
SIDEBAR_SUPPRESS_ACTIVE_SCROLL_KEY,
|
|
45
116
|
} from '../constants'
|
|
46
117
|
// @ts-expect-error - VitePress Data Loader
|
|
47
118
|
import { data as sidebarConfig } from '../sidebar.data'
|
|
@@ -54,6 +125,9 @@ interface SidebarItem {
|
|
|
54
125
|
link?: string
|
|
55
126
|
items?: SidebarItem[]
|
|
56
127
|
collapsed?: boolean
|
|
128
|
+
tocLineIndex?: number
|
|
129
|
+
folderPath?: string[]
|
|
130
|
+
nodeId?: string
|
|
57
131
|
}
|
|
58
132
|
|
|
59
133
|
const route = useRoute()
|
|
@@ -62,12 +136,32 @@ const sidebarGroups = ref<SidebarItem[]>([])
|
|
|
62
136
|
const navRef = ref<HTMLElement | null>(null)
|
|
63
137
|
const currentFocusIndex = ref(0)
|
|
64
138
|
|
|
139
|
+
const base = computed(() => site.value.base || '/')
|
|
140
|
+
const readmeLink = computed(() => `${base.value}README`)
|
|
141
|
+
|
|
142
|
+
function normalizeRoutePath(path: string): string {
|
|
143
|
+
const normalizedPath = path.split(/[?#]/)[0].replace(/\.html$/, '')
|
|
144
|
+
|
|
145
|
+
return normalizedPath.length > 1
|
|
146
|
+
? normalizedPath.replace(/\/$/, '')
|
|
147
|
+
: normalizedPath
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const isReadmeActive = computed(() => {
|
|
151
|
+
const currentPath = normalizeRoutePath(route.path)
|
|
152
|
+
const readmePath = normalizeRoutePath(readmeLink.value)
|
|
153
|
+
|
|
154
|
+
return currentPath === readmePath || currentPath === '/README'
|
|
155
|
+
})
|
|
156
|
+
|
|
65
157
|
const emit = defineEmits<{
|
|
66
158
|
'open-note-about': [noteIndex: string]
|
|
67
159
|
}>()
|
|
68
160
|
|
|
69
|
-
interface
|
|
70
|
-
|
|
161
|
+
interface ParentNoteActionPayload {
|
|
162
|
+
parentNoteIndex?: string
|
|
163
|
+
parentFolderPath?: string[]
|
|
164
|
+
parentTocLineIndex?: number
|
|
71
165
|
text: string
|
|
72
166
|
}
|
|
73
167
|
|
|
@@ -83,36 +177,47 @@ interface SidebarActionResult {
|
|
|
83
177
|
message?: string
|
|
84
178
|
redirectUrl?: string
|
|
85
179
|
newUrl?: string
|
|
180
|
+
deletedNoteIndexes?: string[]
|
|
86
181
|
}
|
|
87
182
|
|
|
88
183
|
interface ReorderPayload {
|
|
89
|
-
dragType: '
|
|
90
|
-
|
|
184
|
+
dragType: 'note' | 'folder'
|
|
185
|
+
dragTocLineIndex?: number
|
|
91
186
|
noteIndex?: string
|
|
92
187
|
targetType: 'group' | 'note'
|
|
93
|
-
targetGroupPath?: string[]
|
|
94
188
|
targetNoteIndex?: string
|
|
95
|
-
|
|
189
|
+
targetFolderPath?: string[]
|
|
190
|
+
targetTocLineIndex?: number
|
|
191
|
+
placement?: 'before' | 'after' | 'inside'
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
interface FolderActionPayload {
|
|
195
|
+
tocLineIndex: number
|
|
196
|
+
text: string
|
|
96
197
|
}
|
|
97
198
|
|
|
98
|
-
|
|
199
|
+
interface EntryActionPayload {
|
|
200
|
+
tocLineIndex: number
|
|
201
|
+
text: string
|
|
202
|
+
noteIndex?: string
|
|
203
|
+
}
|
|
99
204
|
|
|
100
|
-
const DEFAULT_SIDEBAR_DENSITY: SidebarDensity = 'default'
|
|
101
205
|
const DEFAULT_DONE_PREFIX = '✅'
|
|
102
206
|
const DEFAULT_UNDONE_PREFIX = '⏰'
|
|
103
207
|
|
|
104
|
-
//
|
|
208
|
+
// 最大解析层级(0 = 不限制)
|
|
105
209
|
const maxDepth = computed(() => {
|
|
106
210
|
if (typeof window === 'undefined') {
|
|
107
|
-
return tnotesConfig.sidebarMaxDepth ??
|
|
211
|
+
return tnotesConfig.sidebarMaxDepth ?? 0
|
|
108
212
|
}
|
|
109
213
|
|
|
110
214
|
const savedMaxDepth = localStorage.getItem(SIDEBAR_MAX_DEPTH_KEY)
|
|
111
215
|
if (savedMaxDepth !== null) {
|
|
112
|
-
|
|
216
|
+
const parsed = parseInt(savedMaxDepth, 10)
|
|
217
|
+
return parsed > 0 ? parsed : 0
|
|
113
218
|
}
|
|
114
219
|
|
|
115
|
-
return tnotesConfig.sidebarMaxDepth ??
|
|
220
|
+
return tnotesConfig.sidebarMaxDepth ?? 0
|
|
116
221
|
})
|
|
117
222
|
|
|
118
223
|
// 获取配置:是否显示笔记 ID
|
|
@@ -129,12 +234,6 @@ const showNoteId = computed(() => {
|
|
|
129
234
|
return tnotesConfig.sidebarShowNoteId ?? false
|
|
130
235
|
})
|
|
131
236
|
|
|
132
|
-
const sidebarDensity = computed<SidebarDensity>(() => {
|
|
133
|
-
if (typeof window === 'undefined') return DEFAULT_SIDEBAR_DENSITY
|
|
134
|
-
|
|
135
|
-
return normalizeSidebarDensity(localStorage.getItem(SIDEBAR_DENSITY_KEY))
|
|
136
|
-
})
|
|
137
|
-
|
|
138
237
|
const donePrefix = computed(() => {
|
|
139
238
|
if (typeof window === 'undefined') return DEFAULT_DONE_PREFIX
|
|
140
239
|
|
|
@@ -149,10 +248,8 @@ const undonePrefix = computed(() => {
|
|
|
149
248
|
return savedPrefix !== null ? savedPrefix : DEFAULT_UNDONE_PREFIX
|
|
150
249
|
})
|
|
151
250
|
|
|
152
|
-
// 获取 base 路径
|
|
153
|
-
const base = computed(() => site.value.base || '/')
|
|
154
|
-
|
|
155
251
|
const isDev = computed(() => {
|
|
252
|
+
if (import.meta.env.DEV) return true
|
|
156
253
|
if (typeof window === 'undefined') return false
|
|
157
254
|
return (
|
|
158
255
|
window.location.hostname === 'localhost' ||
|
|
@@ -160,6 +257,110 @@ const isDev = computed(() => {
|
|
|
160
257
|
)
|
|
161
258
|
})
|
|
162
259
|
|
|
260
|
+
function getDropInsideHeight(): number {
|
|
261
|
+
const nodeId = sidebarDrag.dropIndicator.value.targetNodeId
|
|
262
|
+
if (!nodeId || !navRef.value) return SIDEBAR_ROW_HEIGHT
|
|
263
|
+
|
|
264
|
+
const row = navRef.value.querySelector<HTMLElement>(
|
|
265
|
+
`.sidebar-row[data-node-id="${nodeId}"]`,
|
|
266
|
+
)
|
|
267
|
+
return row?.getBoundingClientRect().height ?? SIDEBAR_ROW_HEIGHT
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function findSidebarItemByNodeId(
|
|
271
|
+
items: SidebarItem[],
|
|
272
|
+
nodeId: string,
|
|
273
|
+
): SidebarItem | null {
|
|
274
|
+
for (const item of items) {
|
|
275
|
+
const id = item.nodeId ?? computeSidebarNodeId(item)
|
|
276
|
+
if (id === nodeId) return item
|
|
277
|
+
if (item.items?.length) {
|
|
278
|
+
const found = findSidebarItemByNodeId(item.items, nodeId)
|
|
279
|
+
if (found) return found
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function resolveTargetCollapseKey(
|
|
286
|
+
targetNodeId: string | null | undefined,
|
|
287
|
+
): string | null {
|
|
288
|
+
if (!targetNodeId) return null
|
|
289
|
+
const item = findSidebarItemByNodeId(sidebarGroups.value, targetNodeId)
|
|
290
|
+
return item ? resolveCollapseKey(item) : null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function applyPostDropCollapsePatch(patch: Record<string, boolean>) {
|
|
294
|
+
if (!Object.keys(patch).length) return
|
|
295
|
+
|
|
296
|
+
const merged = mergeCollapseStore(
|
|
297
|
+
loadSidebarCollapsedState(SIDEBAR_COLLAPSED_STATE_KEY),
|
|
298
|
+
patch,
|
|
299
|
+
)
|
|
300
|
+
saveCollapseEntries(SIDEBAR_COLLAPSED_STATE_KEY, merged)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function reorderSidebarOptimistic(
|
|
304
|
+
payload: YuqueReorderPayload,
|
|
305
|
+
meta: DragReorderMeta,
|
|
306
|
+
) {
|
|
307
|
+
if (!payload.node_uuid || !payload.action) return
|
|
308
|
+
const isRootPrepend =
|
|
309
|
+
payload.action === 'prependChild' && !payload.target_uuid
|
|
310
|
+
if (!isRootPrepend && !payload.target_uuid) return
|
|
311
|
+
|
|
312
|
+
suppressActiveItemScroll(SIDEBAR_SUPPRESS_ACTIVE_SCROLL_KEY)
|
|
313
|
+
persistSidebarCollapsedState()
|
|
314
|
+
applyPostDropCollapsePatch(
|
|
315
|
+
buildPostDropCollapsePatch(payload, {
|
|
316
|
+
...meta,
|
|
317
|
+
targetCollapseKey: resolveTargetCollapseKey(
|
|
318
|
+
meta.targetNodeId ?? payload.target_uuid,
|
|
319
|
+
),
|
|
320
|
+
}),
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
void runSidebarAction(async () => {
|
|
324
|
+
await requestSidebarAction('/__tnotes_sidebar_reorder', payload)
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function resolveCollapseKey(item: SidebarItem): string | null {
|
|
329
|
+
return getCollapseKeyForTreeItem(item)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const sidebarDrag = useSidebarDrag({
|
|
333
|
+
items: sidebarGroups,
|
|
334
|
+
navRef,
|
|
335
|
+
maxDepth,
|
|
336
|
+
isDev,
|
|
337
|
+
resolveCollapseKey,
|
|
338
|
+
onReorder: reorderSidebarOptimistic,
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
const displaySidebarGroups = computed(
|
|
342
|
+
() => sidebarDrag.previewItems.value ?? sidebarGroups.value,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
const dropIndicator = computed(() => sidebarDrag.dropIndicator.value)
|
|
346
|
+
const dropSlot = computed(() => sidebarDrag.dropSlot.value)
|
|
347
|
+
|
|
348
|
+
function getDropRailLeft(depth: number): number {
|
|
349
|
+
const indentSize = dropIndicator.value.indentSize
|
|
350
|
+
return calcDropRailLeft(depth, indentSize)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function getDropRailWidth(depth: number): number {
|
|
354
|
+
const nav = navRef.value
|
|
355
|
+
if (!nav) return 24
|
|
356
|
+
|
|
357
|
+
if (depth === dropIndicator.value.activeDepth) {
|
|
358
|
+
return Math.max(nav.clientWidth - getDropRailLeft(depth) - 8, 24)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return 12
|
|
362
|
+
}
|
|
363
|
+
|
|
163
364
|
// 加载 sidebar 数据
|
|
164
365
|
function loadSidebar() {
|
|
165
366
|
if (sidebarConfig && sidebarConfig['/notes/']) {
|
|
@@ -167,13 +368,24 @@ function loadSidebar() {
|
|
|
167
368
|
}
|
|
168
369
|
}
|
|
169
370
|
|
|
170
|
-
|
|
371
|
+
function persistSidebarCollapsedState() {
|
|
372
|
+
saveSidebarCollapsedState(SIDEBAR_COLLAPSED_STATE_KEY, sidebarGroups.value)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// 递归处理侧边栏项,合并 localStorage 中的展开/折叠状态
|
|
171
376
|
function processItems(items: any[]): SidebarItem[] {
|
|
172
|
-
|
|
377
|
+
const normalized = items.map((item) => ({
|
|
173
378
|
...item,
|
|
174
379
|
collapsed: item.collapsed ?? true,
|
|
175
|
-
items: item.items
|
|
380
|
+
items: item.items as SidebarItem[] | undefined,
|
|
176
381
|
}))
|
|
382
|
+
|
|
383
|
+
return enrichSidebarNodeIds(
|
|
384
|
+
applySidebarCollapsedState(
|
|
385
|
+
normalized,
|
|
386
|
+
loadSidebarCollapsedState(SIDEBAR_COLLAPSED_STATE_KEY),
|
|
387
|
+
) as SidebarItem[],
|
|
388
|
+
)
|
|
177
389
|
}
|
|
178
390
|
|
|
179
391
|
function stripHeadingNumber(text: string): string {
|
|
@@ -187,19 +399,17 @@ function stripNoteTitlePrefix(text: string): string {
|
|
|
187
399
|
.trim()
|
|
188
400
|
}
|
|
189
401
|
|
|
190
|
-
function normalizeSidebarDensity(value: string | null): SidebarDensity {
|
|
191
|
-
if (value === 'compact' || value === 'default' || value === 'loose') {
|
|
192
|
-
return value
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return DEFAULT_SIDEBAR_DENSITY
|
|
196
|
-
}
|
|
197
|
-
|
|
198
402
|
function isCurrentNote(noteIndex: string): boolean {
|
|
199
403
|
const decodedRoutePath = decodeURIComponent(route.path)
|
|
200
404
|
return decodedRoutePath.includes(`/notes/${noteIndex}.`)
|
|
201
405
|
}
|
|
202
406
|
|
|
407
|
+
function getCurrentNoteIndex(): string | null {
|
|
408
|
+
const decodedRoutePath = decodeURIComponent(route.path)
|
|
409
|
+
const match = decodedRoutePath.match(/\/notes\/(\d{4})\./)
|
|
410
|
+
return match ? match[1] : null
|
|
411
|
+
}
|
|
412
|
+
|
|
203
413
|
function resolveUrl(path: string): string {
|
|
204
414
|
const normalizedBase = base.value.endsWith('/') ? base.value : `${base.value}/`
|
|
205
415
|
return normalizedBase + path.replace(/^\/+/, '')
|
|
@@ -208,6 +418,7 @@ function resolveUrl(path: string): string {
|
|
|
208
418
|
async function requestSidebarAction(
|
|
209
419
|
path: string,
|
|
210
420
|
body: unknown,
|
|
421
|
+
options?: { skipReload?: boolean },
|
|
211
422
|
): Promise<SidebarActionResult> {
|
|
212
423
|
const response = await fetch(path, {
|
|
213
424
|
method: 'POST',
|
|
@@ -225,9 +436,11 @@ async function requestSidebarAction(
|
|
|
225
436
|
throw new Error(result?.message || response.statusText || '操作失败')
|
|
226
437
|
}
|
|
227
438
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
439
|
+
if (!options?.skipReload) {
|
|
440
|
+
setTimeout(() => {
|
|
441
|
+
loadSidebar()
|
|
442
|
+
}, 150)
|
|
443
|
+
}
|
|
231
444
|
|
|
232
445
|
return result
|
|
233
446
|
}
|
|
@@ -242,45 +455,18 @@ async function runSidebarAction(action: () => Promise<void>) {
|
|
|
242
455
|
}
|
|
243
456
|
}
|
|
244
457
|
|
|
245
|
-
function
|
|
246
|
-
void runSidebarAction(async () => {
|
|
247
|
-
const newTitle = window.prompt('请输入新的目录名称', stripHeadingNumber(payload.text))
|
|
248
|
-
if (newTitle === null) return
|
|
249
|
-
|
|
250
|
-
await requestSidebarAction('/__tnotes_sidebar_rename_group', {
|
|
251
|
-
groupPath: payload.groupPath,
|
|
252
|
-
newTitle,
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function deleteGroup(payload: GroupActionPayload) {
|
|
258
|
-
void runSidebarAction(async () => {
|
|
259
|
-
const confirmed = window.confirm(
|
|
260
|
-
`确定删除目录「${payload.groupPath.join(' / ')}」及其下所有笔记吗?`,
|
|
261
|
-
)
|
|
262
|
-
if (!confirmed) return
|
|
263
|
-
|
|
264
|
-
const result = await requestSidebarAction('/__tnotes_sidebar_delete_group', {
|
|
265
|
-
groupPath: payload.groupPath,
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
if (result.redirectUrl) {
|
|
269
|
-
window.location.replace(resolveUrl(result.redirectUrl))
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function createNoteInGroup(payload: GroupActionPayload) {
|
|
458
|
+
function createNoteInGroup(payload: ParentNoteActionPayload) {
|
|
275
459
|
void runSidebarAction(async () => {
|
|
276
460
|
await requestSidebarAction('/__tnotes_sidebar_create_note', {
|
|
277
|
-
|
|
461
|
+
parentNoteIndex: payload.parentNoteIndex,
|
|
462
|
+
parentFolderPath: payload.parentFolderPath,
|
|
463
|
+
parentTocLineIndex: payload.parentTocLineIndex,
|
|
278
464
|
count: 1,
|
|
279
465
|
})
|
|
280
466
|
})
|
|
281
467
|
}
|
|
282
468
|
|
|
283
|
-
function createNotesInGroup(payload:
|
|
469
|
+
function createNotesInGroup(payload: ParentNoteActionPayload) {
|
|
284
470
|
void runSidebarAction(async () => {
|
|
285
471
|
const input = window.prompt('请输入新增笔记数量(1-100)', '1')
|
|
286
472
|
if (input === null) return
|
|
@@ -291,12 +477,35 @@ function createNotesInGroup(payload: GroupActionPayload) {
|
|
|
291
477
|
}
|
|
292
478
|
|
|
293
479
|
await requestSidebarAction('/__tnotes_sidebar_create_notes', {
|
|
294
|
-
|
|
480
|
+
parentNoteIndex: payload.parentNoteIndex,
|
|
481
|
+
parentFolderPath: payload.parentFolderPath,
|
|
482
|
+
parentTocLineIndex: payload.parentTocLineIndex,
|
|
295
483
|
count,
|
|
296
484
|
})
|
|
297
485
|
})
|
|
298
486
|
}
|
|
299
487
|
|
|
488
|
+
function createFolderInGroup(payload: ParentNoteActionPayload) {
|
|
489
|
+
void runSidebarAction(async () => {
|
|
490
|
+
if (payload.parentTocLineIndex === undefined) {
|
|
491
|
+
throw new Error('无法定位父节点(缺少 tocLineIndex,请刷新侧边栏后重试)')
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const title = window.prompt('请输入子目录名称')
|
|
495
|
+
if (title === null) return
|
|
496
|
+
|
|
497
|
+
const trimmed = title.trim()
|
|
498
|
+
if (!trimmed) {
|
|
499
|
+
throw new Error('目录标题不能为空')
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
await requestSidebarAction('/__tnotes_sidebar_create_folder', {
|
|
503
|
+
parentTocLineIndex: payload.parentTocLineIndex,
|
|
504
|
+
title: trimmed,
|
|
505
|
+
})
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
|
|
300
509
|
function renameNote(payload: NoteActionPayload) {
|
|
301
510
|
void runSidebarAction(async () => {
|
|
302
511
|
if (!payload.noteIndex) return
|
|
@@ -318,6 +527,48 @@ function renameNote(payload: NoteActionPayload) {
|
|
|
318
527
|
})
|
|
319
528
|
}
|
|
320
529
|
|
|
530
|
+
function renameFolder(payload: FolderActionPayload) {
|
|
531
|
+
void runSidebarAction(async () => {
|
|
532
|
+
if (payload.tocLineIndex < 0) return
|
|
533
|
+
|
|
534
|
+
const newTitle = window.prompt(
|
|
535
|
+
'请输入新的目录名称',
|
|
536
|
+
stripHeadingNumber(payload.text),
|
|
537
|
+
)
|
|
538
|
+
if (newTitle === null) return
|
|
539
|
+
|
|
540
|
+
const trimmed = newTitle.trim()
|
|
541
|
+
if (!trimmed) {
|
|
542
|
+
throw new Error('目录标题不能为空')
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
await requestSidebarAction('/__tnotes_sidebar_rename_folder', {
|
|
546
|
+
tocLineIndex: payload.tocLineIndex,
|
|
547
|
+
newTitle: trimmed,
|
|
548
|
+
})
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function deleteEntry(payload: EntryActionPayload) {
|
|
553
|
+
void runSidebarAction(async () => {
|
|
554
|
+
if (payload.tocLineIndex < 0) return
|
|
555
|
+
|
|
556
|
+
const confirmed = window.confirm(
|
|
557
|
+
`确定删除「${stripHeadingNumber(payload.text)}」及其所有子项吗?子树内的笔记将从磁盘删除。`,
|
|
558
|
+
)
|
|
559
|
+
if (!confirmed) return
|
|
560
|
+
|
|
561
|
+
const result = await requestSidebarAction('/__tnotes_sidebar_delete_entry', {
|
|
562
|
+
tocLineIndex: payload.tocLineIndex,
|
|
563
|
+
currentNoteIndex: getCurrentNoteIndex() ?? '',
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
if (result.redirectUrl) {
|
|
567
|
+
window.location.replace(resolveUrl(result.redirectUrl))
|
|
568
|
+
}
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
|
|
321
572
|
function deleteNote(payload: NoteActionPayload) {
|
|
322
573
|
void runSidebarAction(async () => {
|
|
323
574
|
if (!payload.noteIndex) return
|
|
@@ -354,12 +605,6 @@ function openNoteAbout(payload: NoteActionPayload) {
|
|
|
354
605
|
emit('open-note-about', payload.noteIndex)
|
|
355
606
|
}
|
|
356
607
|
|
|
357
|
-
function reorderSidebar(payload: ReorderPayload) {
|
|
358
|
-
void runSidebarAction(async () => {
|
|
359
|
-
await requestSidebarAction('/__tnotes_sidebar_reorder', payload)
|
|
360
|
-
})
|
|
361
|
-
}
|
|
362
|
-
|
|
363
608
|
// 判断项是否有子项
|
|
364
609
|
function hasChildren(item: SidebarItem): boolean {
|
|
365
610
|
return !!(item.items && item.items.length > 0)
|
|
@@ -457,7 +702,7 @@ function expandParentGroup(element: HTMLElement) {
|
|
|
457
702
|
currentElement.closest<HTMLElement>('.group')
|
|
458
703
|
if (!groupElement) break
|
|
459
704
|
|
|
460
|
-
const groupTitleText = groupElement.querySelector('.group-title-text')
|
|
705
|
+
const groupTitleText = groupElement.querySelector('.parent-note-link, .group-title-text')
|
|
461
706
|
if (groupTitleText) {
|
|
462
707
|
const groupText = groupTitleText.textContent?.trim()
|
|
463
708
|
if (groupText) {
|
|
@@ -611,6 +856,9 @@ function focusCurrentNote() {
|
|
|
611
856
|
|
|
612
857
|
// 展开当前激活笔记的所有父级分组
|
|
613
858
|
function expandActiveItemParents() {
|
|
859
|
+
if (shouldSuppressActiveItemScroll(SIDEBAR_SUPPRESS_ACTIVE_SCROLL_KEY)) {
|
|
860
|
+
return
|
|
861
|
+
}
|
|
614
862
|
expandActiveItemParentsRecursive(sidebarGroups.value)
|
|
615
863
|
}
|
|
616
864
|
|
|
@@ -620,7 +868,6 @@ function expandActiveItemParentsRecursive(items: SidebarItem[]): boolean {
|
|
|
620
868
|
|
|
621
869
|
for (const item of items) {
|
|
622
870
|
if (item.link) {
|
|
623
|
-
// 检查当前项是否激活
|
|
624
871
|
const fullLink = getFullLink(item.link)
|
|
625
872
|
const decodedRoutePath = decodeURIComponent(route.path)
|
|
626
873
|
const decodedFullLink = decodeURIComponent(fullLink)
|
|
@@ -631,7 +878,9 @@ function expandActiveItemParentsRecursive(items: SidebarItem[]): boolean {
|
|
|
631
878
|
if (itemActive) {
|
|
632
879
|
hasActive = true
|
|
633
880
|
}
|
|
634
|
-
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (item.items?.length) {
|
|
635
884
|
const childHasActive = expandActiveItemParentsRecursive(item.items)
|
|
636
885
|
if (childHasActive) {
|
|
637
886
|
item.collapsed = false
|
|
@@ -651,18 +900,24 @@ function getFullLink(link: string) {
|
|
|
651
900
|
|
|
652
901
|
// 滚动到当前激活的笔记
|
|
653
902
|
function scrollToActiveItem() {
|
|
903
|
+
if (shouldSuppressActiveItemScroll(SIDEBAR_SUPPRESS_ACTIVE_SCROLL_KEY)) {
|
|
904
|
+
return
|
|
905
|
+
}
|
|
906
|
+
|
|
654
907
|
// 等待 DOM 更新
|
|
655
908
|
setTimeout(() => {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
909
|
+
expandActiveItemParents()
|
|
910
|
+
|
|
911
|
+
setTimeout(() => {
|
|
912
|
+
const positions = getCurrentNotePositions()
|
|
913
|
+
|
|
914
|
+
if (positions.length > 0) {
|
|
915
|
+
expandParentGroup(positions[0])
|
|
916
|
+
setTimeout(() => {
|
|
917
|
+
scrollToElement(positions[0])
|
|
918
|
+
}, 100)
|
|
919
|
+
}
|
|
920
|
+
}, 100)
|
|
666
921
|
}, 300)
|
|
667
922
|
}
|
|
668
923
|
|
|
@@ -677,29 +932,19 @@ defineExpose({
|
|
|
677
932
|
|
|
678
933
|
onMounted(() => {
|
|
679
934
|
loadSidebar()
|
|
680
|
-
|
|
681
|
-
// 调试:打印配置信息
|
|
682
|
-
// console.log('🔧 [CustomSidebar] showNoteId:', showNoteId.value)
|
|
683
|
-
// if (typeof window !== 'undefined') {
|
|
684
|
-
// console.log(
|
|
685
|
-
// '🔧 [CustomSidebar] localStorage value:',
|
|
686
|
-
// localStorage.getItem(SIDEBAR_SHOW_NOTE_ID_KEY)
|
|
687
|
-
// )
|
|
688
|
-
// }
|
|
689
|
-
// console.log(
|
|
690
|
-
// '🔧 [CustomSidebar] tnotesConfig value:',
|
|
691
|
-
// tnotesConfig.sidebarShowNoteId
|
|
692
|
-
// )
|
|
693
|
-
|
|
694
|
-
// 自动滚动到当前激活的笔记
|
|
695
935
|
scrollToActiveItem()
|
|
696
936
|
})
|
|
697
937
|
|
|
938
|
+
watch(
|
|
939
|
+
sidebarGroups,
|
|
940
|
+
() => persistSidebarCollapsedState(),
|
|
941
|
+
{ deep: true, flush: 'post' },
|
|
942
|
+
)
|
|
943
|
+
|
|
698
944
|
// 监听 sidebarConfig 的变化(HMR 会更新这个导入的数据)
|
|
699
945
|
watch(
|
|
700
946
|
() => sidebarConfig,
|
|
701
947
|
() => {
|
|
702
|
-
// console.log('🔄 [CustomSidebar] Sidebar config changed, reloading...')
|
|
703
948
|
loadSidebar()
|
|
704
949
|
},
|
|
705
950
|
{ deep: true }
|
|
@@ -734,16 +979,101 @@ watch(
|
|
|
734
979
|
<style scoped>
|
|
735
980
|
/* 自定义 sidebar 容器,适配 VitePress 的 sidebar-nav-before 插槽 */
|
|
736
981
|
.custom-sidebar-wrapper {
|
|
737
|
-
|
|
982
|
+
width: 100%;
|
|
983
|
+
min-width: 0;
|
|
738
984
|
}
|
|
739
985
|
|
|
740
986
|
.nav {
|
|
987
|
+
position: relative;
|
|
741
988
|
font-size: 14px;
|
|
742
989
|
line-height: 2;
|
|
743
990
|
}
|
|
744
991
|
|
|
992
|
+
.sidebar-nav {
|
|
993
|
+
position: relative;
|
|
994
|
+
display: flex;
|
|
995
|
+
flex-direction: column;
|
|
996
|
+
flex-wrap: nowrap;
|
|
997
|
+
width: 100%;
|
|
998
|
+
min-width: 0;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.sidebar-drop-slot {
|
|
1002
|
+
position: absolute;
|
|
1003
|
+
z-index: 45;
|
|
1004
|
+
box-sizing: border-box;
|
|
1005
|
+
background: transparent;
|
|
1006
|
+
border: 1px dashed var(--vp-c-brand-1);
|
|
1007
|
+
border-radius: 6px;
|
|
1008
|
+
opacity: 0.65;
|
|
1009
|
+
pointer-events: none;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.sidebar-drop-layer {
|
|
1013
|
+
position: absolute;
|
|
1014
|
+
right: 0;
|
|
1015
|
+
left: 0;
|
|
1016
|
+
z-index: 50;
|
|
1017
|
+
height: 0;
|
|
1018
|
+
pointer-events: none;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
.sidebar-drop-rail-segment {
|
|
1022
|
+
position: absolute;
|
|
1023
|
+
top: -2px;
|
|
1024
|
+
height: 3px;
|
|
1025
|
+
background: var(--vp-c-text-2);
|
|
1026
|
+
border-radius: 999px;
|
|
1027
|
+
opacity: 0.65;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.sidebar-drop-rail-segment.is-active {
|
|
1031
|
+
top: -2px;
|
|
1032
|
+
height: 4px;
|
|
1033
|
+
background: var(--vp-c-brand-1);
|
|
1034
|
+
opacity: 1;
|
|
1035
|
+
box-shadow:
|
|
1036
|
+
0 0 0 1px var(--vp-c-brand-soft),
|
|
1037
|
+
0 0 10px color-mix(in srgb, var(--vp-c-brand-1) 70%, transparent);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.sidebar-drop-rail-segment.is-active::before {
|
|
1041
|
+
position: absolute;
|
|
1042
|
+
top: -4px;
|
|
1043
|
+
left: -1px;
|
|
1044
|
+
width: 0;
|
|
1045
|
+
height: 0;
|
|
1046
|
+
border-top: 4px solid transparent;
|
|
1047
|
+
border-bottom: 4px solid transparent;
|
|
1048
|
+
border-left: 6px solid var(--vp-c-brand-1);
|
|
1049
|
+
content: '';
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
.sidebar-drop-indicator {
|
|
1053
|
+
position: absolute;
|
|
1054
|
+
top: -1px;
|
|
1055
|
+
z-index: 50;
|
|
1056
|
+
height: 3px;
|
|
1057
|
+
background: var(--vp-c-brand-1);
|
|
1058
|
+
border-radius: 999px;
|
|
1059
|
+
box-shadow:
|
|
1060
|
+
0 0 0 1px var(--vp-c-brand-soft),
|
|
1061
|
+
0 0 8px color-mix(in srgb, var(--vp-c-brand-1) 65%, transparent);
|
|
1062
|
+
pointer-events: none;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.sidebar-drop-indicator.is-inside {
|
|
1066
|
+
height: auto;
|
|
1067
|
+
min-height: 28px;
|
|
1068
|
+
background: var(--vp-c-brand-soft);
|
|
1069
|
+
border: 1px solid var(--vp-c-brand-1);
|
|
1070
|
+
border-radius: 6px;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
745
1073
|
.group {
|
|
746
1074
|
margin-bottom: 16px;
|
|
1075
|
+
width: 100%;
|
|
1076
|
+
min-width: 0;
|
|
747
1077
|
}
|
|
748
1078
|
|
|
749
1079
|
.group-title {
|
|
@@ -777,12 +1107,15 @@ watch(
|
|
|
777
1107
|
|
|
778
1108
|
.nav-item {
|
|
779
1109
|
display: block;
|
|
780
|
-
|
|
1110
|
+
box-sizing: border-box;
|
|
1111
|
+
width: 100%;
|
|
1112
|
+
min-height: 36px;
|
|
1113
|
+
padding: 6px 8px;
|
|
781
1114
|
color: var(--vp-c-text-2);
|
|
782
1115
|
text-decoration: none;
|
|
783
|
-
border-radius:
|
|
1116
|
+
border-radius: 8px;
|
|
784
1117
|
font-size: 14px;
|
|
785
|
-
line-height:
|
|
1118
|
+
line-height: 22px;
|
|
786
1119
|
transition: all 0.25s;
|
|
787
1120
|
}
|
|
788
1121
|
|
|
@@ -796,6 +1129,12 @@ watch(
|
|
|
796
1129
|
font-weight: 600;
|
|
797
1130
|
}
|
|
798
1131
|
|
|
1132
|
+
.readme-nav-item {
|
|
1133
|
+
display: block;
|
|
1134
|
+
margin-bottom: 8px;
|
|
1135
|
+
font-weight: 600;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
799
1138
|
/* 聚焦高亮动画 */
|
|
800
1139
|
.nav-item.focus-highlight {
|
|
801
1140
|
animation: focusPulse 1s ease-in-out;
|
|
@@ -812,10 +1151,43 @@ watch(
|
|
|
812
1151
|
}
|
|
813
1152
|
</style>
|
|
814
1153
|
|
|
815
|
-
<!--
|
|
1154
|
+
<!-- 全局样式:侧边栏拖拽与默认导航隐藏 -->
|
|
816
1155
|
<style>
|
|
817
|
-
/* 隐藏 VitePress
|
|
818
|
-
|
|
1156
|
+
/* 隐藏 VitePress 默认 sidebar 分组(自定义目录在 sidebar-nav-after 插槽) */
|
|
1157
|
+
#VPSidebarNav > .group {
|
|
819
1158
|
display: none !important;
|
|
820
1159
|
}
|
|
1160
|
+
|
|
1161
|
+
body.sidebar-drag-active {
|
|
1162
|
+
cursor: grabbing;
|
|
1163
|
+
user-select: none;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
body.sidebar-drag-active .sidebar-row {
|
|
1167
|
+
cursor: grabbing;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.sidebar-drag-ghost {
|
|
1171
|
+
position: fixed;
|
|
1172
|
+
z-index: 9999;
|
|
1173
|
+
display: flex;
|
|
1174
|
+
align-items: center;
|
|
1175
|
+
min-height: 28px;
|
|
1176
|
+
padding: 4px 8px;
|
|
1177
|
+
opacity: 0.92;
|
|
1178
|
+
background: var(--vp-c-bg-elv);
|
|
1179
|
+
border: 1px solid var(--vp-c-brand-1);
|
|
1180
|
+
border-radius: 6px;
|
|
1181
|
+
box-shadow: var(--vp-shadow-3);
|
|
1182
|
+
pointer-events: none;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.sidebar-drag-ghost .ghost-title-text {
|
|
1186
|
+
overflow: hidden;
|
|
1187
|
+
font-size: 14px;
|
|
1188
|
+
font-weight: 500;
|
|
1189
|
+
color: var(--vp-c-text-1);
|
|
1190
|
+
text-overflow: ellipsis;
|
|
1191
|
+
white-space: nowrap;
|
|
1192
|
+
}
|
|
821
1193
|
</style>
|