vitepress 2.0.0-alpha.14 → 2.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/app/index.js +3 -1
- package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +214 -59
- package/dist/client/theme-default/components/VPBadge.vue +4 -5
- package/dist/client/theme-default/components/VPHero.vue +5 -2
- package/dist/client/theme-default/components/VPLocalSearchBox.vue +13 -18
- package/dist/client/theme-default/components/VPNavBar.vue +16 -19
- package/dist/client/theme-default/components/VPNavBarAskAiButton.vue +31 -0
- package/dist/client/theme-default/components/VPNavBarSearch.vue +120 -76
- package/dist/client/theme-default/components/VPNavBarSearchButton.vue +45 -125
- package/dist/client/theme-default/components/VPNavScreen.vue +1 -3
- package/dist/client/theme-default/components/VPNavScreenMenuGroupLink.vue +14 -1
- package/dist/client/theme-default/components/VPNavScreenMenuLink.vue +14 -1
- package/dist/client/theme-default/styles/base.css +213 -211
- package/dist/client/theme-default/styles/components/vp-doc.css +10 -0
- package/dist/client/theme-default/styles/docsearch.css +120 -0
- package/dist/client/theme-default/styles/icons.css +4 -1
- package/dist/client/theme-default/support/docsearch.js +169 -0
- package/dist/client/theme-default/support/reactivity.js +9 -0
- package/dist/node/{chunk-BvyEjT9v.js → chunk-CCYAX98b.js} +575 -1421
- package/dist/node/cli.js +3 -5
- package/dist/node/index.js +4 -6
- package/package.json +34 -33
- package/template/.vitepress/theme/style.css +0 -8
- package/types/default-theme.d.ts +5 -5
- package/types/docsearch.d.ts +51 -193
package/dist/client/app/index.js
CHANGED
|
@@ -103,7 +103,9 @@ function newRouter() {
|
|
|
103
103
|
pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js');
|
|
104
104
|
}
|
|
105
105
|
if (import.meta.env.DEV) {
|
|
106
|
-
pageModule = import(/*@vite-ignore*/ pageFilePath).catch(() => {
|
|
106
|
+
pageModule = import(/*@vite-ignore*/ pageFilePath).catch((e) => {
|
|
107
|
+
// page load could fail for other reasons, don't swallow
|
|
108
|
+
console.error(e);
|
|
107
109
|
// try with/without trailing slash
|
|
108
110
|
// in prod this is handled in src/client/app/utils.ts#pathToFile
|
|
109
111
|
const url = new URL(pageFilePath, 'http://a.com');
|
|
@@ -1,84 +1,238 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import type { DocSearchInstance, DocSearchProps } from '@docsearch/js'
|
|
3
|
+
import type { SidepanelInstance, SidepanelProps } from '@docsearch/sidepanel-js'
|
|
4
|
+
import { inBrowser, useRouter } from 'vitepress'
|
|
4
5
|
import type { DefaultTheme } from 'vitepress/theme'
|
|
5
|
-
import { nextTick,
|
|
6
|
+
import { nextTick, onUnmounted, watch } from 'vue'
|
|
7
|
+
import type { DocSearchAskAi } from '../../../../types/docsearch'
|
|
6
8
|
import { useData } from '../composables/data'
|
|
9
|
+
import { resolveMode, validateCredentials } from '../support/docsearch'
|
|
10
|
+
|
|
11
|
+
import '../styles/docsearch.css'
|
|
7
12
|
|
|
8
13
|
const props = defineProps<{
|
|
9
|
-
|
|
14
|
+
algoliaOptions: DefaultTheme.AlgoliaSearchOptions
|
|
15
|
+
openRequest?: {
|
|
16
|
+
target: 'search' | 'askAi' | 'toggleAskAi'
|
|
17
|
+
nonce: number
|
|
18
|
+
} | null
|
|
10
19
|
}>()
|
|
11
20
|
|
|
12
21
|
const router = useRouter()
|
|
13
|
-
const { site
|
|
22
|
+
const { site } = useData()
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
let cleanup = () => {}
|
|
25
|
+
let docsearchInstance: DocSearchInstance | undefined
|
|
26
|
+
let sidepanelInstance: SidepanelInstance | undefined
|
|
27
|
+
let openOnReady: 'search' | 'askAi' | null = null
|
|
28
|
+
let initializeCount = 0
|
|
29
|
+
let docsearchLoader: Promise<typeof import('@docsearch/js')> | undefined
|
|
30
|
+
let sidepanelLoader: Promise<typeof import('@docsearch/sidepanel-js')> | undefined
|
|
31
|
+
let lastFocusedElement: HTMLElement | null = null
|
|
32
|
+
let skipEventDocsearch = false
|
|
33
|
+
let skipEventSidepanel = false
|
|
17
34
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
searchParameters: facetFilters.length ? { facetFilters } : undefined
|
|
35
|
+
watch(() => props.algoliaOptions, update, { immediate: true })
|
|
36
|
+
onUnmounted(cleanup)
|
|
37
|
+
|
|
38
|
+
watch(
|
|
39
|
+
() => props.openRequest?.nonce,
|
|
40
|
+
() => {
|
|
41
|
+
const req = props.openRequest
|
|
42
|
+
if (!req) return
|
|
43
|
+
if (req.target === 'search') {
|
|
44
|
+
if (docsearchInstance?.isReady) {
|
|
45
|
+
onBeforeOpen('docsearch', () => docsearchInstance?.open())
|
|
46
|
+
} else {
|
|
47
|
+
openOnReady = 'search'
|
|
48
|
+
}
|
|
49
|
+
} else if (req.target === 'toggleAskAi') {
|
|
50
|
+
if (sidepanelInstance?.isOpen) {
|
|
51
|
+
sidepanelInstance.close()
|
|
52
|
+
} else {
|
|
53
|
+
onBeforeOpen('sidepanel', () => sidepanelInstance?.open())
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
// askAi - open sidepanel or fallback to docsearch modal
|
|
57
|
+
if (sidepanelInstance?.isReady) {
|
|
58
|
+
onBeforeOpen('sidepanel', () => sidepanelInstance?.open())
|
|
59
|
+
} else if (sidepanelInstance) {
|
|
60
|
+
openOnReady = 'askAi'
|
|
61
|
+
} else if (docsearchInstance?.isReady) {
|
|
62
|
+
onBeforeOpen('docsearch', () => docsearchInstance?.openAskAi())
|
|
63
|
+
} else {
|
|
64
|
+
openOnReady = 'askAi'
|
|
49
65
|
}
|
|
50
|
-
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{ immediate: true }
|
|
69
|
+
)
|
|
51
70
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
async function update(options: DefaultTheme.AlgoliaSearchOptions) {
|
|
72
|
+
if (!inBrowser) return
|
|
73
|
+
await nextTick()
|
|
74
|
+
|
|
75
|
+
const askAi = options.askAi as DocSearchAskAi | undefined
|
|
76
|
+
|
|
77
|
+
const { valid, ...credentials } = validateCredentials({
|
|
78
|
+
appId: options.appId ?? askAi?.appId,
|
|
79
|
+
apiKey: options.apiKey ?? askAi?.apiKey,
|
|
80
|
+
indexName: options.indexName ?? askAi?.indexName
|
|
59
81
|
})
|
|
82
|
+
|
|
83
|
+
if (!valid) {
|
|
84
|
+
console.warn('[vitepress] Algolia search cannot be initialized: missing appId/apiKey/indexName.')
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await initialize({ ...options, ...credentials })
|
|
60
89
|
}
|
|
61
90
|
|
|
62
|
-
function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
|
|
63
|
-
const
|
|
64
|
-
|
|
91
|
+
async function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
|
|
92
|
+
const currentInitialize = ++initializeCount
|
|
93
|
+
|
|
94
|
+
// Always tear down previous instances first (e.g. on locale changes)
|
|
95
|
+
cleanup()
|
|
96
|
+
|
|
97
|
+
const { useSidePanel } = resolveMode(userOptions)
|
|
98
|
+
const askAi = userOptions.askAi as DocSearchAskAi | undefined
|
|
99
|
+
|
|
100
|
+
const { default: docsearch } = await loadDocsearch()
|
|
101
|
+
if (currentInitialize !== initializeCount) return
|
|
102
|
+
|
|
103
|
+
if (useSidePanel && askAi?.sidePanel) {
|
|
104
|
+
const { default: sidepanel } = await loadSidepanel()
|
|
105
|
+
if (currentInitialize !== initializeCount) return
|
|
65
106
|
|
|
107
|
+
sidepanelInstance = sidepanel({
|
|
108
|
+
...(askAi.sidePanel === true ? {} : askAi.sidePanel),
|
|
109
|
+
container: '#vp-docsearch-sidepanel',
|
|
110
|
+
indexName: askAi.indexName ?? userOptions.indexName,
|
|
111
|
+
appId: askAi.appId ?? userOptions.appId,
|
|
112
|
+
apiKey: askAi.apiKey ?? userOptions.apiKey,
|
|
113
|
+
assistantId: askAi.assistantId,
|
|
114
|
+
onOpen: focusInput,
|
|
115
|
+
onClose: onClose.bind(null, 'sidepanel'),
|
|
116
|
+
onReady: () => {
|
|
117
|
+
if (openOnReady === 'askAi') {
|
|
118
|
+
openOnReady = null
|
|
119
|
+
onBeforeOpen('sidepanel', () => sidepanelInstance?.open())
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
keyboardShortcuts: {
|
|
123
|
+
'Ctrl/Cmd+I': false
|
|
124
|
+
}
|
|
125
|
+
} as SidepanelProps)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const options = {
|
|
129
|
+
...userOptions,
|
|
130
|
+
container: '#vp-docsearch',
|
|
66
131
|
navigator: {
|
|
67
|
-
navigate(item
|
|
132
|
+
navigate(item) {
|
|
68
133
|
router.go(item.itemUrl)
|
|
69
134
|
}
|
|
70
135
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
136
|
+
transformItems: (items) => items.map((item) => ({ ...item, url: getRelativePath(item.url) })),
|
|
137
|
+
// When sidepanel is enabled, intercept Ask AI events to open it instead (hybrid mode)
|
|
138
|
+
...(useSidePanel && sidepanelInstance && {
|
|
139
|
+
interceptAskAiEvent: (initialMessage) => {
|
|
140
|
+
onBeforeOpen('sidepanel', () => sidepanelInstance?.open(initialMessage))
|
|
141
|
+
return true
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
onOpen: focusInput,
|
|
145
|
+
onClose: onClose.bind(null, 'docsearch'),
|
|
146
|
+
onReady: () => {
|
|
147
|
+
if (openOnReady === 'search') {
|
|
148
|
+
openOnReady = null
|
|
149
|
+
onBeforeOpen('docsearch', () => docsearchInstance?.open())
|
|
150
|
+
} else if (openOnReady === 'askAi' && !sidepanelInstance) {
|
|
151
|
+
// No sidepanel configured, use docsearch modal for askAi
|
|
152
|
+
openOnReady = null
|
|
153
|
+
onBeforeOpen('docsearch', () => docsearchInstance?.openAskAi())
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
keyboardShortcuts: {
|
|
157
|
+
'/': false,
|
|
158
|
+
'Ctrl/Cmd+K': false
|
|
78
159
|
}
|
|
160
|
+
} as DocSearchProps
|
|
161
|
+
|
|
162
|
+
docsearchInstance = docsearch(options)
|
|
163
|
+
|
|
164
|
+
cleanup = () => {
|
|
165
|
+
docsearchInstance?.destroy()
|
|
166
|
+
sidepanelInstance?.destroy()
|
|
167
|
+
docsearchInstance = undefined
|
|
168
|
+
sidepanelInstance = undefined
|
|
169
|
+
openOnReady = null
|
|
170
|
+
lastFocusedElement = null
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function focusInput() {
|
|
175
|
+
requestAnimationFrame(() => {
|
|
176
|
+
const input =
|
|
177
|
+
document.querySelector<HTMLInputElement>('#docsearch-input') ||
|
|
178
|
+
document.querySelector<HTMLInputElement>('#docsearch-sidepanel textarea')
|
|
179
|
+
input?.focus()
|
|
79
180
|
})
|
|
181
|
+
}
|
|
80
182
|
|
|
81
|
-
|
|
183
|
+
function onBeforeOpen(target: 'docsearch' | 'sidepanel', cb: () => void) {
|
|
184
|
+
if (target === 'docsearch') {
|
|
185
|
+
if (sidepanelInstance?.isOpen) {
|
|
186
|
+
skipEventSidepanel = true
|
|
187
|
+
sidepanelInstance.close()
|
|
188
|
+
} else if (!docsearchInstance?.isOpen) {
|
|
189
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
190
|
+
lastFocusedElement = document.activeElement
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else if (target === 'sidepanel') {
|
|
194
|
+
if (docsearchInstance?.isOpen) {
|
|
195
|
+
skipEventDocsearch = true
|
|
196
|
+
docsearchInstance.close()
|
|
197
|
+
} else if (!sidepanelInstance?.isOpen) {
|
|
198
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
199
|
+
lastFocusedElement = document.activeElement
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
setTimeout(cb, 0)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function onClose(target: 'docsearch' | 'sidepanel') {
|
|
207
|
+
if (target === 'docsearch') {
|
|
208
|
+
if (skipEventDocsearch) {
|
|
209
|
+
skipEventDocsearch = false
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
} else if (target === 'sidepanel') {
|
|
213
|
+
if (skipEventSidepanel) {
|
|
214
|
+
skipEventSidepanel = false
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (lastFocusedElement) {
|
|
219
|
+
lastFocusedElement.focus()
|
|
220
|
+
lastFocusedElement = null
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function loadDocsearch() {
|
|
225
|
+
if (!docsearchLoader) {
|
|
226
|
+
docsearchLoader = import('@docsearch/js')
|
|
227
|
+
}
|
|
228
|
+
return docsearchLoader
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function loadSidepanel() {
|
|
232
|
+
if (!sidepanelLoader) {
|
|
233
|
+
sidepanelLoader = import('@docsearch/sidepanel-js')
|
|
234
|
+
}
|
|
235
|
+
return sidepanelLoader
|
|
82
236
|
}
|
|
83
237
|
|
|
84
238
|
function getRelativePath(url: string) {
|
|
@@ -88,5 +242,6 @@ function getRelativePath(url: string) {
|
|
|
88
242
|
</script>
|
|
89
243
|
|
|
90
244
|
<template>
|
|
91
|
-
<div id="docsearch" />
|
|
245
|
+
<div id="vp-docsearch" />
|
|
246
|
+
<div id="vp-docsearch-sidepanel" />
|
|
92
247
|
</template>
|
|
@@ -39,15 +39,14 @@ withDefaults(defineProps<Props>(), {
|
|
|
39
39
|
display: none;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
.vp-doc h1 > .VPBadge
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
.vp-doc h1 > .VPBadge,
|
|
43
|
+
.vp-doc h2 > .VPBadge {
|
|
44
|
+
margin: 0 0 0 2px;
|
|
45
|
+
vertical-align: middle;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
.vp-doc h2 > .VPBadge {
|
|
48
|
-
margin-top: 3px;
|
|
49
49
|
padding: 0 8px;
|
|
50
|
-
vertical-align: top;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
.vp-doc h3 > .VPBadge {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { DefaultTheme } from 'vitepress/theme'
|
|
3
|
-
import { inject } from 'vue'
|
|
3
|
+
import { computed, inject } from 'vue'
|
|
4
4
|
import { layoutInfoInjectionKey } from '../composables/layout'
|
|
5
5
|
import VPButton from './VPButton.vue'
|
|
6
6
|
import VPImage from './VPImage.vue'
|
|
@@ -21,7 +21,10 @@ defineProps<{
|
|
|
21
21
|
actions?: HeroAction[]
|
|
22
22
|
}>()
|
|
23
23
|
|
|
24
|
-
const { heroImageSlotExists } = inject(
|
|
24
|
+
const { heroImageSlotExists } = inject(
|
|
25
|
+
layoutInfoInjectionKey,
|
|
26
|
+
{ heroImageSlotExists: computed(() => false) }
|
|
27
|
+
)
|
|
25
28
|
</script>
|
|
26
29
|
|
|
27
30
|
<template>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import localSearchIndex from '@localSearchIndex'
|
|
3
3
|
import {
|
|
4
4
|
computedAsync,
|
|
5
|
-
|
|
5
|
+
watchDebounced,
|
|
6
6
|
onKeyStroke,
|
|
7
7
|
useEventListener,
|
|
8
8
|
useLocalStorage,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
watchEffect,
|
|
27
27
|
type Ref
|
|
28
28
|
} from 'vue'
|
|
29
|
-
import type {
|
|
29
|
+
import type { LocalSearchTranslations } from '../../../../types/local-search'
|
|
30
30
|
import { pathToFile } from '../../app/utils'
|
|
31
31
|
import { escapeRegExp } from '../../shared'
|
|
32
32
|
import { useData } from '../composables/data'
|
|
@@ -113,16 +113,6 @@ const disableDetailedView = computed(() => {
|
|
|
113
113
|
)
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
const buttonText = computed(() => {
|
|
117
|
-
const options = theme.value.search?.options ?? theme.value.algolia
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
options?.locales?.[localeIndex.value]?.translations?.button?.buttonText ||
|
|
121
|
-
options?.translations?.button?.buttonText ||
|
|
122
|
-
'Search'
|
|
123
|
-
)
|
|
124
|
-
})
|
|
125
|
-
|
|
126
116
|
watchEffect(() => {
|
|
127
117
|
if (disableDetailedView.value) {
|
|
128
118
|
showDetailedList.value = false
|
|
@@ -144,7 +134,7 @@ const mark = computedAsync(async () => {
|
|
|
144
134
|
|
|
145
135
|
const cache = new LRUCache<string, Map<string, string>>(16) // 16 files
|
|
146
136
|
|
|
147
|
-
|
|
137
|
+
watchDebounced(
|
|
148
138
|
() => [searchIndex.value, filterText.value, showDetailedList.value] as const,
|
|
149
139
|
async ([index, filterTextValue, showDetailedListValue], old, onCleanup) => {
|
|
150
140
|
if (old?.[0] !== index) {
|
|
@@ -339,8 +329,12 @@ onKeyStroke('Escape', () => {
|
|
|
339
329
|
emit('close')
|
|
340
330
|
})
|
|
341
331
|
|
|
342
|
-
|
|
343
|
-
|
|
332
|
+
/* Translations */
|
|
333
|
+
|
|
334
|
+
const defaultTranslations: LocalSearchTranslations = {
|
|
335
|
+
button: {
|
|
336
|
+
buttonText: 'Search'
|
|
337
|
+
},
|
|
344
338
|
modal: {
|
|
345
339
|
displayDetails: 'Display detailed list',
|
|
346
340
|
resetButtonTitle: 'Reset search',
|
|
@@ -360,7 +354,7 @@ const defaultTranslations: { modal: ModalTranslations } = {
|
|
|
360
354
|
|
|
361
355
|
const translate = createSearchTranslate(defaultTranslations)
|
|
362
356
|
|
|
363
|
-
|
|
357
|
+
/* Back */
|
|
364
358
|
|
|
365
359
|
onMounted(() => {
|
|
366
360
|
// Prevents going to previous site
|
|
@@ -373,6 +367,7 @@ useEventListener('popstate', (event) => {
|
|
|
373
367
|
})
|
|
374
368
|
|
|
375
369
|
/** Lock body */
|
|
370
|
+
|
|
376
371
|
const isLocked = useScrollLock(inBrowser ? document.body : null)
|
|
377
372
|
|
|
378
373
|
onMounted(() => {
|
|
@@ -432,7 +427,7 @@ function onMouseMove(e: MouseEvent) {
|
|
|
432
427
|
@submit.prevent=""
|
|
433
428
|
>
|
|
434
429
|
<label
|
|
435
|
-
:title="buttonText"
|
|
430
|
+
:title="translate('button.buttonText')"
|
|
436
431
|
id="localsearch-label"
|
|
437
432
|
for="localsearch-input"
|
|
438
433
|
>
|
|
@@ -461,7 +456,7 @@ function onMouseMove(e: MouseEvent) {
|
|
|
461
456
|
id="localsearch-input"
|
|
462
457
|
enterkeyhint="go"
|
|
463
458
|
maxlength="64"
|
|
464
|
-
:placeholder="buttonText"
|
|
459
|
+
:placeholder="translate('button.buttonText')"
|
|
465
460
|
spellcheck="false"
|
|
466
461
|
type="search"
|
|
467
462
|
/>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { useWindowScroll } from '@vueuse/core'
|
|
3
|
-
import { ref, watchPostEffect } from 'vue'
|
|
4
3
|
import { useLayout } from '../composables/layout'
|
|
5
4
|
import VPNavBarAppearance from './VPNavBarAppearance.vue'
|
|
6
5
|
import VPNavBarExtra from './VPNavBarExtra.vue'
|
|
@@ -21,21 +20,18 @@ defineEmits<{
|
|
|
21
20
|
|
|
22
21
|
const { y } = useWindowScroll()
|
|
23
22
|
const { isHome, hasSidebar } = useLayout()
|
|
24
|
-
|
|
25
|
-
const classes = ref<Record<string, boolean>>({})
|
|
26
|
-
|
|
27
|
-
watchPostEffect(() => {
|
|
28
|
-
classes.value = {
|
|
29
|
-
'has-sidebar': hasSidebar.value,
|
|
30
|
-
'home': isHome.value,
|
|
31
|
-
'top': y.value === 0,
|
|
32
|
-
'screen-open': props.isScreenOpen
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
23
|
</script>
|
|
36
24
|
|
|
37
25
|
<template>
|
|
38
|
-
<div
|
|
26
|
+
<div
|
|
27
|
+
class="VPNavBar"
|
|
28
|
+
:class="{
|
|
29
|
+
'has-sidebar': hasSidebar,
|
|
30
|
+
'home': isHome,
|
|
31
|
+
'top': y === 0,
|
|
32
|
+
'screen-open': isScreenOpen
|
|
33
|
+
}"
|
|
34
|
+
>
|
|
39
35
|
<div class="wrapper">
|
|
40
36
|
<div class="container">
|
|
41
37
|
<div class="title">
|
|
@@ -55,7 +51,11 @@ watchPostEffect(() => {
|
|
|
55
51
|
<VPNavBarSocialLinks class="social-links" />
|
|
56
52
|
<VPNavBarExtra class="extra" />
|
|
57
53
|
<slot name="nav-bar-content-after" />
|
|
58
|
-
<VPNavBarHamburger
|
|
54
|
+
<VPNavBarHamburger
|
|
55
|
+
class="hamburger"
|
|
56
|
+
:active="isScreenOpen"
|
|
57
|
+
@click="$emit('toggle-screen')"
|
|
58
|
+
/>
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
@@ -187,8 +187,6 @@ watchPostEffect(() => {
|
|
|
187
187
|
justify-content: flex-end;
|
|
188
188
|
align-items: center;
|
|
189
189
|
height: var(--vp-nav-height);
|
|
190
|
-
margin-right: -100vw;
|
|
191
|
-
padding-right: 100vw;
|
|
192
190
|
transition: background-color 0.5s;
|
|
193
191
|
}
|
|
194
192
|
|
|
@@ -201,11 +199,10 @@ watchPostEffect(() => {
|
|
|
201
199
|
.VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
|
|
202
200
|
background-color: transparent;
|
|
203
201
|
}
|
|
204
|
-
}
|
|
205
202
|
|
|
206
|
-
@media (max-width: 767px) {
|
|
207
203
|
.content-body {
|
|
208
|
-
|
|
204
|
+
margin-right: -100vw;
|
|
205
|
+
padding-right: 100vw;
|
|
209
206
|
}
|
|
210
207
|
}
|
|
211
208
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button type="button" class="VPNavBarAskAiButton">
|
|
3
|
+
<span class="vpi-sparkles" aria-hidden="true"></span>
|
|
4
|
+
</button>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<style scoped>
|
|
8
|
+
.VPNavBarAskAiButton {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
height: var(--vp-nav-height);
|
|
12
|
+
padding: 8px 14px;
|
|
13
|
+
font-size: 20px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@media (min-width: 768px) {
|
|
17
|
+
.VPNavBarAskAiButton {
|
|
18
|
+
height: auto;
|
|
19
|
+
padding: 11.5px;
|
|
20
|
+
transition: color 0.3s ease;
|
|
21
|
+
background-color: var(--vp-c-bg-alt);
|
|
22
|
+
border-radius: 8px;
|
|
23
|
+
font-size: 15px;
|
|
24
|
+
color: var(--vp-c-text-2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.VPNavBarAskAiButton:hover {
|
|
28
|
+
color: var(--vp-c-brand-1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
</style>
|