valaxy-theme-press 0.28.0 → 0.28.2
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/components/PressAlgoliaSearch.vue +11 -4
- package/components/PressBackdrop.vue +1 -1
- package/components/PressDocFooter.vue +1 -1
- package/components/PressHomeHero.vue +32 -32
- package/components/PressLocalSearch.vue +19 -0
- package/components/PressLocalSearchModal.vue +502 -0
- package/components/PressNavBar.vue +4 -4
- package/components/PressNavBarSearch.vue +21 -1
- package/components/PressNavScreen.vue +1 -1
- package/components/PressPostActions.vue +1 -0
- package/components/PressSidebar.vue +1 -2
- package/components/PressSwitchAppearance.vue +3 -3
- package/layouts/404.vue +1 -1
- package/package.json +7 -6
- package/styles/main.scss +2 -2
- package/styles/markdown.scss +10 -0
- package/types/algolia.ts +56 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { DocSearchInstance, DocSearchProps } from '@docsearch/js'
|
|
3
3
|
import type { SidepanelInstance, SidepanelProps } from '@docsearch/sidepanel-js'
|
|
4
|
-
import type { AlgoliaSearchOptions } from '
|
|
5
|
-
import {
|
|
6
|
-
import { nextTick, onUnmounted, watch } from 'vue'
|
|
4
|
+
import type { AlgoliaSearchOptions } from '../types/algolia'
|
|
5
|
+
import { nextTick, onUnmounted, ref, watch } from 'vue'
|
|
7
6
|
import { useRouter } from 'vue-router'
|
|
8
7
|
|
|
9
8
|
import '../styles/docsearch.css'
|
|
@@ -16,7 +15,15 @@ const props = defineProps<{
|
|
|
16
15
|
}>()
|
|
17
16
|
|
|
18
17
|
const router = useRouter()
|
|
19
|
-
|
|
18
|
+
|
|
19
|
+
// Dynamically import addon to avoid hard dependency
|
|
20
|
+
const algoliaConfig = ref<{ options?: AlgoliaSearchOptions }>()
|
|
21
|
+
import('valaxy-addon-algolia').then(({ useAddonAlgoliaConfig }) => {
|
|
22
|
+
// Direct assignment — the synchronous watcher below will react to this change
|
|
23
|
+
algoliaConfig.value = useAddonAlgoliaConfig().value
|
|
24
|
+
}).catch(() => {
|
|
25
|
+
console.warn('[valaxy-theme-press] valaxy-addon-algolia is not installed. Algolia search will not work.')
|
|
26
|
+
})
|
|
20
27
|
|
|
21
28
|
let cleanup = () => {}
|
|
22
29
|
let docsearchInstance: DocSearchInstance | undefined
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { useFrontmatter } from 'valaxy'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
|
-
import { useI18n } from 'vue-i18n'
|
|
5
4
|
import { useLocaleConfig } from '../composables'
|
|
6
5
|
import PressButton from './PressButton.vue'
|
|
7
6
|
|
|
8
7
|
const fm = useFrontmatter()
|
|
9
8
|
|
|
10
|
-
const { t } = useI18n()
|
|
11
|
-
|
|
12
9
|
const { currentLocale, currentLocaleKey, hasLocales } = useLocaleConfig()
|
|
13
10
|
|
|
14
11
|
/**
|
|
@@ -31,36 +28,39 @@ const actions = computed(() => {
|
|
|
31
28
|
</script>
|
|
32
29
|
|
|
33
30
|
<template>
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
<template v-if="fm.hero">
|
|
32
|
+
<div text="center" m="md:t-24 t-10 md:t-20" flex="~ col" justify="center" items="center">
|
|
33
|
+
<ValaxyLogo mb="2" />
|
|
34
|
+
<h1 my="10" text="4xl md:8xl" font="black" class="gradient-text from-purple-800 to-blue-500 bg-gradient-to-r">
|
|
35
|
+
{{ fm.hero.name }}
|
|
36
|
+
</h1>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<h2 v-if="fm.hero?.text" flex="~ wrap justify-center" px="2" m="b-10" text="center 6xl" font="black" leading="tight">
|
|
40
|
+
{{ fm.hero.text }}
|
|
41
|
+
</h2>
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<span mx-1 class="gradient-text from-blue-500 to-purple-700 bg-gradient-to-r">{{ t('banner.blog') }}</span>
|
|
45
|
-
<span mx-1 class="break-keep">{{ t('banner.framework') }}</span>
|
|
46
|
-
</h2>
|
|
43
|
+
<p v-if="fm.hero?.tagline" m="b-10" text="center xl" op="80">
|
|
44
|
+
{{ fm.hero.tagline }}
|
|
45
|
+
</p>
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
<div v-if="actions.length" p="2" text="center" class="flex justify-center items-center">
|
|
48
|
+
<template v-for="action in actions" :key="action.link">
|
|
49
|
+
<PressGetStarted
|
|
50
|
+
v-if="action.type === 'fly'"
|
|
51
|
+
:theme="action.theme"
|
|
52
|
+
:link="action.link"
|
|
53
|
+
:text="action.text"
|
|
54
|
+
/>
|
|
55
|
+
<PressButton
|
|
56
|
+
v-else
|
|
57
|
+
:theme="action.theme"
|
|
58
|
+
:link="action.link"
|
|
59
|
+
:text="action.text"
|
|
60
|
+
/>
|
|
61
|
+
</template>
|
|
62
|
+
</div>
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
<br>
|
|
65
|
+
</template>
|
|
66
66
|
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const open = ref(false)
|
|
5
|
+
|
|
6
|
+
function toggleSearch() {
|
|
7
|
+
open.value = !open.value
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
defineExpose({
|
|
11
|
+
open,
|
|
12
|
+
toggleSearch,
|
|
13
|
+
})
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<PressFuseSearchButton :open="open" @toggle="toggleSearch" />
|
|
18
|
+
<PressLocalSearchModal :open="open" @close="open = false" />
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { isClient, useScrollLock } from '@vueuse/core'
|
|
3
|
+
import { nextTick, ref, watch } from 'vue'
|
|
4
|
+
import { useI18n } from 'vue-i18n'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
open: boolean
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits(['close'])
|
|
11
|
+
|
|
12
|
+
const isLocked = useScrollLock(isClient ? document.documentElement : null)
|
|
13
|
+
const { t } = useI18n()
|
|
14
|
+
|
|
15
|
+
const searchInputRef = ref<HTMLInputElement>()
|
|
16
|
+
|
|
17
|
+
function handleModalClick(event: Event) {
|
|
18
|
+
if (event.target === event.currentTarget) {
|
|
19
|
+
emit('close')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
watch(() => props.open, (val) => {
|
|
24
|
+
if (val) {
|
|
25
|
+
nextTick(() => {
|
|
26
|
+
searchInputRef.value?.focus()
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<Teleport to="body">
|
|
34
|
+
<Transition
|
|
35
|
+
name="modal"
|
|
36
|
+
@enter="isLocked = true"
|
|
37
|
+
@after-leave="isLocked = false"
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
v-if="open"
|
|
41
|
+
class="press-search-modal"
|
|
42
|
+
@click="handleModalClick"
|
|
43
|
+
>
|
|
44
|
+
<div class="press-search-content">
|
|
45
|
+
<ValaxyLocalSearch :open="open" @close="emit('close')">
|
|
46
|
+
<template #default="{ query, results, loading, selectedIndex, updateQuery, navigate, onKeydown, getPageTitle, getSectionTitle }">
|
|
47
|
+
<div class="press-search-header">
|
|
48
|
+
<div class="press-search-input-wrapper">
|
|
49
|
+
<div class="press-search-icon">
|
|
50
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
51
|
+
<path d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" fill="currentColor" />
|
|
52
|
+
</svg>
|
|
53
|
+
</div>
|
|
54
|
+
<input
|
|
55
|
+
ref="searchInputRef"
|
|
56
|
+
:value="query"
|
|
57
|
+
class="press-search-input"
|
|
58
|
+
:placeholder="t('search.placeholder')"
|
|
59
|
+
@input="updateQuery(($event.target as HTMLInputElement).value)"
|
|
60
|
+
@keydown="onKeydown"
|
|
61
|
+
>
|
|
62
|
+
<button
|
|
63
|
+
class="press-search-close"
|
|
64
|
+
@click="emit('close')"
|
|
65
|
+
>
|
|
66
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
67
|
+
<path d="M6.28 5.72a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.72z" fill="currentColor" />
|
|
68
|
+
</svg>
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div v-if="loading" class="press-search-hits">
|
|
74
|
+
<span class="press-search-hits-text">{{ t('search.loading') }}</span>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div v-if="query" class="press-search-hits">
|
|
78
|
+
<span class="press-search-hits-text">
|
|
79
|
+
{{ t('search.hits', results.length || 0) }}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div v-if="results.length > 0" class="press-search-results">
|
|
84
|
+
<div class="press-search-container">
|
|
85
|
+
<div class="press-result-list">
|
|
86
|
+
<a
|
|
87
|
+
v-for="(result, index) in results"
|
|
88
|
+
:key="result.id"
|
|
89
|
+
class="press-result-item"
|
|
90
|
+
:class="{ 'press-result-item-active': index === selectedIndex }"
|
|
91
|
+
:style="{ animationDelay: `${index * 50}ms` }"
|
|
92
|
+
href="javascript:void(0)"
|
|
93
|
+
@click="navigate(result)"
|
|
94
|
+
>
|
|
95
|
+
<div class="press-result-content">
|
|
96
|
+
<div class="press-result-breadcrumb">
|
|
97
|
+
<span class="press-result-page">{{ getPageTitle(result.id) }}</span>
|
|
98
|
+
<span v-for="(title, i) in result.titles" :key="i" class="press-result-crumb">
|
|
99
|
+
› {{ title }}
|
|
100
|
+
</span>
|
|
101
|
+
</div>
|
|
102
|
+
<h3 class="press-result-title">
|
|
103
|
+
{{ result.title }}
|
|
104
|
+
</h3>
|
|
105
|
+
<div class="press-result-meta">
|
|
106
|
+
<span class="press-result-link">
|
|
107
|
+
{{ getSectionTitle(result) }}
|
|
108
|
+
</span>
|
|
109
|
+
<span class="press-result-score">
|
|
110
|
+
<span class="press-result-score-label">Score:</span>
|
|
111
|
+
<span class="press-result-score-value">{{ result.score.toFixed(1) }}</span>
|
|
112
|
+
</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="press-result-arrow">
|
|
116
|
+
<svg width="16" height="16" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
117
|
+
<path d="M7.5 4.5L12.5 9.5L7.5 14.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
|
118
|
+
</svg>
|
|
119
|
+
</div>
|
|
120
|
+
</a>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="press-search-footer">
|
|
125
|
+
<div class="press-search-footer-content">
|
|
126
|
+
<span class="press-search-powered-by">Search by MiniSearch</span>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div v-else-if="query && results.length === 0 && !loading" class="press-search-empty">
|
|
132
|
+
<div class="press-search-empty-icon">
|
|
133
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
134
|
+
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
135
|
+
</svg>
|
|
136
|
+
</div>
|
|
137
|
+
<p class="press-search-empty-text">
|
|
138
|
+
{{ t('search.no_results') }}
|
|
139
|
+
</p>
|
|
140
|
+
<p class="press-search-empty-subtext">
|
|
141
|
+
{{ t('search.no_results_hint') }}
|
|
142
|
+
</p>
|
|
143
|
+
</div>
|
|
144
|
+
</template>
|
|
145
|
+
</ValaxyLocalSearch>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</Transition>
|
|
149
|
+
</Teleport>
|
|
150
|
+
</template>
|
|
151
|
+
|
|
152
|
+
<style lang="scss" scoped>
|
|
153
|
+
.press-search-modal {
|
|
154
|
+
position: fixed;
|
|
155
|
+
top: 0;
|
|
156
|
+
left: 0;
|
|
157
|
+
width: 100%;
|
|
158
|
+
height: 100%;
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
align-items: center;
|
|
162
|
+
padding-top: 2rem;
|
|
163
|
+
backdrop-filter: blur(20px);
|
|
164
|
+
z-index: var(--pr-z-search);
|
|
165
|
+
background-color: rgb(0 0 0 / 0.4);
|
|
166
|
+
pointer-events: auto;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.press-search-content {
|
|
170
|
+
width: 100%;
|
|
171
|
+
max-width: 680px;
|
|
172
|
+
padding: 0 1.5rem;
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-direction: column;
|
|
175
|
+
align-items: center;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.press-search-header {
|
|
179
|
+
width: 100%;
|
|
180
|
+
margin-bottom: 1.5rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.press-search-input-wrapper {
|
|
184
|
+
position: relative;
|
|
185
|
+
width: 100%;
|
|
186
|
+
max-width: 600px;
|
|
187
|
+
margin: 0 auto;
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
background: var(--vp-c-bg);
|
|
191
|
+
border-radius: 12px;
|
|
192
|
+
border: 2px solid var(--vp-c-divider);
|
|
193
|
+
box-shadow: 0 4px 20px rgb(0 0 0 / 0.1);
|
|
194
|
+
transition: all var(--va-transition-duration-fast) ease;
|
|
195
|
+
|
|
196
|
+
&:focus-within {
|
|
197
|
+
border-color: var(--vp-c-brand);
|
|
198
|
+
box-shadow: 0 4px 20px rgb(var(--vp-c-brand-rgb), 0.15);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.press-search-icon {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
width: 48px;
|
|
207
|
+
height: 48px;
|
|
208
|
+
color: var(--vp-c-text-3);
|
|
209
|
+
flex-shrink: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.press-search-input {
|
|
213
|
+
flex: 1;
|
|
214
|
+
padding: 0.875rem 0;
|
|
215
|
+
font-size: 1.125rem;
|
|
216
|
+
line-height: 1.5;
|
|
217
|
+
color: var(--vp-c-text-1);
|
|
218
|
+
background: transparent;
|
|
219
|
+
border: none;
|
|
220
|
+
outline: none;
|
|
221
|
+
font-weight: 500;
|
|
222
|
+
|
|
223
|
+
&::placeholder {
|
|
224
|
+
color: var(--vp-c-text-3);
|
|
225
|
+
font-weight: 400;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.press-search-close {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
justify-content: center;
|
|
233
|
+
width: 48px;
|
|
234
|
+
height: 48px;
|
|
235
|
+
color: var(--vp-c-text-3);
|
|
236
|
+
background: transparent;
|
|
237
|
+
border: none;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
border-radius: 8px;
|
|
240
|
+
transition: all var(--va-transition-duration-fast) ease;
|
|
241
|
+
flex-shrink: 0;
|
|
242
|
+
|
|
243
|
+
&:hover {
|
|
244
|
+
color: var(--vp-c-text-1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.press-search-hits {
|
|
249
|
+
width: 100%;
|
|
250
|
+
max-width: 600px;
|
|
251
|
+
padding: 0.75rem 0;
|
|
252
|
+
margin-bottom: 1rem;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.press-search-hits-text {
|
|
256
|
+
color: var(--vp-c-text-2);
|
|
257
|
+
font-size: 0.875rem;
|
|
258
|
+
font-weight: 500;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.press-search-results {
|
|
262
|
+
width: 100%;
|
|
263
|
+
max-width: 600px;
|
|
264
|
+
max-height: calc(100vh - 280px);
|
|
265
|
+
display: flex;
|
|
266
|
+
flex-direction: column;
|
|
267
|
+
border-radius: 12px;
|
|
268
|
+
background: var(--vp-c-bg);
|
|
269
|
+
box-shadow: 0 4px 20px rgb(0 0 0 / 0.1);
|
|
270
|
+
overflow: hidden;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.press-search-container {
|
|
274
|
+
flex: 1;
|
|
275
|
+
overflow-y: auto;
|
|
276
|
+
min-height: 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.press-result-list {
|
|
280
|
+
width: 100%;
|
|
281
|
+
flex: 1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.press-result-item {
|
|
285
|
+
display: flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
padding: 1rem 1.25rem;
|
|
288
|
+
color: var(--vp-c-text-1);
|
|
289
|
+
text-decoration: none;
|
|
290
|
+
transition: all var(--va-transition-duration-fast) ease;
|
|
291
|
+
animation: slide-in-up 0.3s ease forwards;
|
|
292
|
+
opacity: 0;
|
|
293
|
+
transform: translateY(10px);
|
|
294
|
+
|
|
295
|
+
&:hover,
|
|
296
|
+
&.press-result-item-active {
|
|
297
|
+
background-color: var(--vp-c-bg-soft);
|
|
298
|
+
transform: translateY(-1px);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&:not(:last-child) {
|
|
302
|
+
border-bottom: 1px solid var(--vp-c-divider);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.press-result-content {
|
|
307
|
+
flex: 1;
|
|
308
|
+
min-width: 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.press-result-breadcrumb {
|
|
312
|
+
margin-bottom: 0.25rem;
|
|
313
|
+
font-size: 0.75rem;
|
|
314
|
+
color: var(--vp-c-text-3);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.press-result-page {
|
|
318
|
+
font-weight: 500;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.press-result-crumb {
|
|
322
|
+
margin-left: 0.125rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.press-result-title {
|
|
326
|
+
margin: 0 0 0.5rem;
|
|
327
|
+
font-size: 1rem;
|
|
328
|
+
font-weight: 600;
|
|
329
|
+
line-height: 1.4;
|
|
330
|
+
color: var(--vp-c-text-1);
|
|
331
|
+
display: -webkit-box;
|
|
332
|
+
line-clamp: 2;
|
|
333
|
+
-webkit-box-orient: vertical;
|
|
334
|
+
overflow: hidden;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.press-result-meta {
|
|
338
|
+
display: flex;
|
|
339
|
+
justify-content: space-between;
|
|
340
|
+
align-items: center;
|
|
341
|
+
font-size: 0.75rem;
|
|
342
|
+
line-height: 1.4;
|
|
343
|
+
color: var(--vp-c-text-3);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.press-result-link {
|
|
347
|
+
flex: 1;
|
|
348
|
+
min-width: 0;
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
text-overflow: ellipsis;
|
|
351
|
+
white-space: nowrap;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.press-result-score {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
gap: 0.25rem;
|
|
358
|
+
flex-shrink: 0;
|
|
359
|
+
margin-left: 1rem;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.press-result-score-label {
|
|
363
|
+
color: var(--vp-c-text-3);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.press-result-score-value {
|
|
367
|
+
color: var(--vp-c-brand);
|
|
368
|
+
font-weight: 600;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.press-result-arrow {
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: center;
|
|
374
|
+
justify-content: center;
|
|
375
|
+
width: 24px;
|
|
376
|
+
height: 24px;
|
|
377
|
+
color: var(--vp-c-text-3);
|
|
378
|
+
margin-left: 0.75rem;
|
|
379
|
+
flex-shrink: 0;
|
|
380
|
+
transition: all var(--va-transition-duration-fast) ease;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.press-result-item:hover .press-result-arrow {
|
|
384
|
+
color: var(--vp-c-brand);
|
|
385
|
+
transform: translateX(2px);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.press-search-footer {
|
|
389
|
+
width: 100%;
|
|
390
|
+
padding: 0.75rem 1.25rem;
|
|
391
|
+
border-top: 1px solid var(--vp-c-divider);
|
|
392
|
+
background: var(--vp-c-bg-soft);
|
|
393
|
+
flex-shrink: 0;
|
|
394
|
+
border-bottom-left-radius: 12px;
|
|
395
|
+
border-bottom-right-radius: 12px;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.press-search-footer-content {
|
|
399
|
+
display: flex;
|
|
400
|
+
justify-content: flex-end;
|
|
401
|
+
align-items: center;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.press-search-powered-by {
|
|
405
|
+
font-size: 0.75rem;
|
|
406
|
+
color: var(--vp-c-text-3);
|
|
407
|
+
font-weight: 500;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.press-search-empty {
|
|
411
|
+
width: 100%;
|
|
412
|
+
max-width: 600px;
|
|
413
|
+
padding: 3rem 1rem;
|
|
414
|
+
text-align: center;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.press-search-empty-icon {
|
|
418
|
+
display: flex;
|
|
419
|
+
justify-content: center;
|
|
420
|
+
margin-bottom: 1rem;
|
|
421
|
+
color: var(--vp-c-text-3);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.press-search-empty-text {
|
|
425
|
+
margin: 0 0 0.5rem;
|
|
426
|
+
font-size: 1.125rem;
|
|
427
|
+
font-weight: 600;
|
|
428
|
+
color: var(--vp-c-text-1);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.press-search-empty-subtext {
|
|
432
|
+
margin: 0;
|
|
433
|
+
font-size: 0.875rem;
|
|
434
|
+
color: var(--vp-c-text-2);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
@keyframes slide-in-up {
|
|
438
|
+
to {
|
|
439
|
+
opacity: 1;
|
|
440
|
+
transform: translateY(0);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.modal-enter-active,
|
|
445
|
+
.modal-leave-active {
|
|
446
|
+
transition: all var(--va-transition-duration) cubic-bezier(0.4, 0, 0.2, 1);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.modal-enter-from {
|
|
450
|
+
opacity: 0;
|
|
451
|
+
transform: scale(0.95);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.modal-leave-to {
|
|
455
|
+
opacity: 0;
|
|
456
|
+
transform: scale(0.95);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Scrollbar styling
|
|
460
|
+
.press-search-container::-webkit-scrollbar {
|
|
461
|
+
width: 6px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.press-search-container::-webkit-scrollbar-track {
|
|
465
|
+
background: transparent;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.press-search-container::-webkit-scrollbar-thumb {
|
|
469
|
+
background: var(--vp-c-divider);
|
|
470
|
+
border-radius: 3px;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.press-search-container::-webkit-scrollbar-thumb:hover {
|
|
474
|
+
background: var(--vp-c-text-3);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@media (width <= 768px) {
|
|
478
|
+
.press-search-modal {
|
|
479
|
+
padding-top: 1rem;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.press-search-content {
|
|
483
|
+
padding: 0 1rem;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.press-search-input-wrapper {
|
|
487
|
+
max-width: 100%;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.press-search-results {
|
|
491
|
+
max-height: calc(100vh - 240px);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.press-result-item {
|
|
495
|
+
padding: 0.875rem 1rem;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.press-result-title {
|
|
499
|
+
font-size: 0.9375rem;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
</style>
|
|
@@ -43,11 +43,11 @@ const homeLink = computed(() => hasLocales.value ? currentLocale.value.link : '/
|
|
|
43
43
|
@use 'valaxy/client/styles/mixins/index.scss' as *;
|
|
44
44
|
|
|
45
45
|
:root {
|
|
46
|
-
--pr-navbar-c-bg:
|
|
46
|
+
--pr-navbar-c-bg: rgb(255 255 255 / 0.8);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
.dark {
|
|
50
|
-
--pr-navbar-c-bg:
|
|
50
|
+
--pr-navbar-c-bg: rgb(24 24 24 / 0.3);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
.logo {
|
|
@@ -81,11 +81,11 @@ const homeLink = computed(() => hasLocales.value ? currentLocale.value.link : '/
|
|
|
81
81
|
|
|
82
82
|
@supports not (backdrop-filter: saturate(50%) blur(8px)) {
|
|
83
83
|
.pr-navbar.has-sidebar .content {
|
|
84
|
-
background:
|
|
84
|
+
background: rgb(255 255 255 / 0.95);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
.dark .pr-navbar.has-sidebar .content {
|
|
88
|
-
background:
|
|
88
|
+
background: rgb(36 36 36 / 0.95);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import type { AlgoliaSearchOptions } from '../types/algolia'
|
|
2
3
|
import { onKeyStroke } from '@vueuse/core'
|
|
3
4
|
import { useSiteConfig } from 'valaxy'
|
|
4
5
|
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
|
@@ -9,6 +10,7 @@ const siteConfig = useSiteConfig()
|
|
|
9
10
|
|
|
10
11
|
const isAlgolia = computed(() => siteConfig.value.search.provider === 'algolia')
|
|
11
12
|
const isFuse = computed(() => siteConfig.value.search.provider === 'fuse')
|
|
13
|
+
const isLocal = computed(() => siteConfig.value.search.provider === 'local')
|
|
12
14
|
|
|
13
15
|
// Whether to show the Ask AI button (requires askAi config in addon-algolia)
|
|
14
16
|
const showAskAi = ref(false)
|
|
@@ -16,7 +18,7 @@ const showAskAi = ref(false)
|
|
|
16
18
|
if (isAlgolia.value) {
|
|
17
19
|
import('valaxy-addon-algolia').then(({ useAddonAlgoliaConfig }) => {
|
|
18
20
|
const algoliaConfig = useAddonAlgoliaConfig()
|
|
19
|
-
const askAi = algoliaConfig.value?.options?.askAi
|
|
21
|
+
const askAi = (algoliaConfig.value?.options as AlgoliaSearchOptions | undefined)?.askAi
|
|
20
22
|
showAskAi.value = !!askAi
|
|
21
23
|
}).catch(() => {})
|
|
22
24
|
}
|
|
@@ -29,6 +31,10 @@ const PressFuseSearch = isFuse.value
|
|
|
29
31
|
? defineAsyncComponent(() => import('./PressFuseSearch.vue'))
|
|
30
32
|
: () => null
|
|
31
33
|
|
|
34
|
+
const PressLocalSearch = isLocal.value
|
|
35
|
+
? defineAsyncComponent(() => import('./PressLocalSearch.vue'))
|
|
36
|
+
: () => null
|
|
37
|
+
|
|
32
38
|
// #region Algolia lazy loading
|
|
33
39
|
|
|
34
40
|
type OpenTarget = 'search' | 'askAi' | 'toggleAskAi'
|
|
@@ -105,6 +111,17 @@ function loadAndOpen(target: OpenTarget) {
|
|
|
105
111
|
|
|
106
112
|
// #endregion
|
|
107
113
|
|
|
114
|
+
// Local search keyboard shortcut
|
|
115
|
+
const localSearchRef = ref()
|
|
116
|
+
if (isLocal.value) {
|
|
117
|
+
onKeyStroke('k', (event) => {
|
|
118
|
+
if (event.ctrlKey || event.metaKey) {
|
|
119
|
+
event.preventDefault()
|
|
120
|
+
localSearchRef.value?.toggleSearch()
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
108
125
|
function isEditingContent(event: KeyboardEvent): boolean {
|
|
109
126
|
const element = event.target as HTMLElement
|
|
110
127
|
const tagName = element.tagName
|
|
@@ -141,6 +158,9 @@ function isEditingContent(event: KeyboardEvent): boolean {
|
|
|
141
158
|
<template v-else-if="isFuse">
|
|
142
159
|
<PressFuseSearch />
|
|
143
160
|
</template>
|
|
161
|
+
<template v-else-if="isLocal">
|
|
162
|
+
<PressLocalSearch ref="localSearchRef" />
|
|
163
|
+
</template>
|
|
144
164
|
</div>
|
|
145
165
|
</template>
|
|
146
166
|
|
|
@@ -34,7 +34,7 @@ const isLocked = useScrollLock(isClient ? document.body : null)
|
|
|
34
34
|
/* stylelint-disable selector-class-pattern */
|
|
35
35
|
.pr-NavScreen {
|
|
36
36
|
position: fixed;
|
|
37
|
-
inset: calc(var(--pr-nav-height) + var(--pr-layout-top-height, 0px) + 1px) 0 0
|
|
37
|
+
inset: calc(var(--pr-nav-height) + var(--pr-layout-top-height, 0px) + 1px) 0 0;
|
|
38
38
|
|
|
39
39
|
/* rtl:ignore */
|
|
40
40
|
|
|
@@ -158,9 +158,8 @@ const { hasSidebar } = useSidebar()
|
|
|
158
158
|
max-width: 320px;
|
|
159
159
|
background-color: var(--va-c-bg);
|
|
160
160
|
opacity: 0;
|
|
161
|
-
overflow
|
|
161
|
+
overflow: hidden auto;
|
|
162
162
|
overflow-y: auto;
|
|
163
|
-
overflow-y: overlay;
|
|
164
163
|
transform: translateX(-100%);
|
|
165
164
|
transition: opacity var(--va-transition-duration-moderate), transform var(--va-transition-duration) ease;
|
|
166
165
|
|
|
@@ -40,7 +40,7 @@ const appStore = useAppStore()
|
|
|
40
40
|
height: 18px;
|
|
41
41
|
border-radius: 50%;
|
|
42
42
|
background-color: #fff;
|
|
43
|
-
box-shadow: 0 1px 2px
|
|
43
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 1px 2px rgb(0 0 0 / 0.06);
|
|
44
44
|
transition: background-color var(--va-transition-duration), transform var(--va-transition-duration);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -60,11 +60,11 @@ const appStore = useAppStore()
|
|
|
60
60
|
.icon {
|
|
61
61
|
width: 12px;
|
|
62
62
|
height: 12px;
|
|
63
|
-
background-color:
|
|
63
|
+
background-color: rgb(60 60 60 / 0.7);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
.dark .icon {
|
|
67
|
-
background-color:
|
|
67
|
+
background-color: rgb(255 255 255 / 0.87);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.dark .switch-appearance :deep(.check) {
|
package/layouts/404.vue
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "valaxy-theme-press",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.2",
|
|
4
4
|
"description": "Docs Theme for Valaxy",
|
|
5
5
|
"author": {
|
|
6
6
|
"email": "me@yunyoujun.cn",
|
|
@@ -22,12 +22,13 @@
|
|
|
22
22
|
"main": "node/index.ts",
|
|
23
23
|
"types": "types/index.d.ts",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@docsearch/css": "^4.6.
|
|
26
|
-
"@docsearch/js": "^4.6.
|
|
27
|
-
"@docsearch/sidepanel-js": "^4.6.
|
|
28
|
-
"reka-ui": "^2.9.2"
|
|
25
|
+
"@docsearch/css": "^4.6.2",
|
|
26
|
+
"@docsearch/js": "^4.6.2",
|
|
27
|
+
"@docsearch/sidepanel-js": "^4.6.2",
|
|
28
|
+
"reka-ui": "^2.9.2",
|
|
29
|
+
"vitepress": "^2.0.0-alpha.17"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"valaxy": "0.28.
|
|
32
|
+
"valaxy": "0.28.2"
|
|
32
33
|
}
|
|
33
34
|
}
|
package/styles/main.scss
CHANGED
package/styles/markdown.scss
CHANGED
|
@@ -61,14 +61,24 @@
|
|
|
61
61
|
.va-git-log-contributor {
|
|
62
62
|
padding-left: 0;
|
|
63
63
|
padding-top: 0;
|
|
64
|
+
margin-top: 0;
|
|
65
|
+
margin-bottom: 0;
|
|
64
66
|
|
|
65
67
|
li,
|
|
66
68
|
li+li {
|
|
67
69
|
margin-top: 0;
|
|
70
|
+
margin-bottom: 0;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
li {
|
|
71
74
|
margin-right: 0.5rem;
|
|
72
75
|
}
|
|
76
|
+
|
|
77
|
+
.va-contributor-avatar img {
|
|
78
|
+
width: 32px;
|
|
79
|
+
height: 32px;
|
|
80
|
+
margin: 0;
|
|
81
|
+
object-fit: cover;
|
|
82
|
+
}
|
|
73
83
|
}
|
|
74
84
|
}
|
package/types/algolia.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Algolia search types for valaxy-theme-press.
|
|
3
|
+
*
|
|
4
|
+
* These are defined locally to avoid depending on unpublished types
|
|
5
|
+
* from valaxy-addon-algolia. When the addon publishes updated types,
|
|
6
|
+
* these can be replaced with re-exports.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface AlgoliaSearchOptions extends DocSearchProps {
|
|
10
|
+
locales?: Record<string, Partial<DocSearchProps>>
|
|
11
|
+
/**
|
|
12
|
+
* Configuration or assistant id to enable Ask AI mode.
|
|
13
|
+
* Pass a string (assistant id) or a full config object.
|
|
14
|
+
* Omit to disable the Ask AI button entirely.
|
|
15
|
+
*/
|
|
16
|
+
askAi?: AlgoliaAskAiOptions | string
|
|
17
|
+
/**
|
|
18
|
+
* Ask AI side panel integration mode.
|
|
19
|
+
*
|
|
20
|
+
* @default 'auto'
|
|
21
|
+
*/
|
|
22
|
+
mode?: 'auto' | 'sidePanel' | 'hybrid' | 'modal'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AlgoliaAskAiOptions {
|
|
26
|
+
assistantId: string
|
|
27
|
+
appId?: string
|
|
28
|
+
apiKey?: string
|
|
29
|
+
indexName?: string
|
|
30
|
+
suggestedQuestions?: boolean
|
|
31
|
+
sidePanel?: boolean | AlgoliaSidepanelOptions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AlgoliaSidepanelOptions {
|
|
35
|
+
button?: Record<string, any>
|
|
36
|
+
keyboardShortcuts?: Record<string, boolean>
|
|
37
|
+
panel?: {
|
|
38
|
+
variant?: 'floating' | 'inline'
|
|
39
|
+
side?: 'left' | 'right'
|
|
40
|
+
width?: string
|
|
41
|
+
expandedWidth?: string
|
|
42
|
+
suggestedQuestions?: boolean
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DocSearchProps {
|
|
47
|
+
appId: string
|
|
48
|
+
apiKey: string
|
|
49
|
+
indexName: string
|
|
50
|
+
placeholder?: string
|
|
51
|
+
searchParameters?: Record<string, any>
|
|
52
|
+
disableUserPersonalization?: boolean
|
|
53
|
+
initialQuery?: string
|
|
54
|
+
insights?: boolean
|
|
55
|
+
translations?: Record<string, any>
|
|
56
|
+
}
|