@sugarat/theme 0.2.28 → 0.2.30

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/node.d.ts CHANGED
@@ -308,7 +308,7 @@ declare namespace Theme {
308
308
  pagesData: PageData[];
309
309
  srcDir?: string;
310
310
  author?: string;
311
- hotArticle?: HotArticle;
311
+ hotArticle?: HotArticle | false;
312
312
  home?: HomeBlog;
313
313
  /**
314
314
  * 本地全文搜索定制
@@ -390,6 +390,7 @@ declare namespace Theme {
390
390
  * 详见 https://oml2d.com/options/Options.html
391
391
  */
392
392
  oml2d?: Options;
393
+ homeTags?: boolean;
393
394
  }
394
395
  interface BackToTop {
395
396
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -42,11 +42,11 @@
42
42
  "gray-matter": "^4.0.3",
43
43
  "markdown-it-task-checkbox": "^1.0.6",
44
44
  "mermaid": "^10.2.4",
45
- "oh-my-live2d": "^0.13.0",
45
+ "oh-my-live2d": "^0.15.2",
46
46
  "swiper": "^11.0.5",
47
47
  "vitepress-markdown-timeline": "^1.2.1",
48
48
  "vitepress-plugin-mermaid": "2.0.13",
49
- "vitepress-plugin-pagefind": "0.2.13",
49
+ "vitepress-plugin-pagefind": "0.2.14",
50
50
  "vitepress-plugin-rss": "0.2.2",
51
51
  "vitepress-plugin-tabs": "0.2.0"
52
52
  },
@@ -9,7 +9,6 @@ import BlogHomeBanner from './BlogHomeBanner.vue'
9
9
  import BlogList from './BlogList.vue'
10
10
  import BlogComment from './BlogComment.vue'
11
11
 
12
- // import BlogSearch from './BlogSearch.vue'
13
12
  import BlogSidebar from './BlogSidebar.vue'
14
13
  import BlogImagePreview from './BlogImagePreview.vue'
15
14
  import BlogArticleAnalyze from './BlogArticleAnalyze.vue'
@@ -2,16 +2,17 @@
2
2
  import { computed, watch } from 'vue'
3
3
  import { ElTag } from 'element-plus'
4
4
  import { useBrowserLocation, useDark, useUrlSearchParams } from '@vueuse/core'
5
- import { useRouter, useRoute } from 'vitepress'
5
+ import { useRoute, useRouter } from 'vitepress'
6
6
  import {
7
7
  useActiveTag,
8
8
  useArticles,
9
- useCurrentPageNum
9
+ useConfig,
10
+ useCurrentPageNum,
10
11
  } from '../composables/config/blog'
11
12
 
12
13
  const route = useRoute()
13
14
  const docs = useArticles()
14
-
15
+ const showTags = useConfig()?.config?.blog?.homeTags ?? true
15
16
  const tags = computed(() => {
16
17
  return [...new Set(docs.value.map(v => v.meta.tag || []).flat(3))]
17
18
  })
@@ -77,7 +78,7 @@ watch(
77
78
  </script>
78
79
 
79
80
  <template>
80
- <div v-if="tags.length" class="card tags" data-pagefind-ignore="all">
81
+ <div v-if="showTags && tags.length" class="card tags" data-pagefind-ignore="all">
81
82
  <!-- 头部 -->
82
83
  <div class="card-header">
83
84
  <span class="title svg-icon"><svg
@@ -2,15 +2,20 @@
2
2
  import { computed, ref } from 'vue'
3
3
  import { ElButton, ElLink } from 'element-plus'
4
4
  import { withBase } from 'vitepress'
5
- import { useArticles, useBlogConfig } from '../composables/config/blog'
6
- import { formatShowDate } from '../utils/client'
5
+ import { useArticles, useBlogConfig, useCleanUrls } from '../composables/config/blog'
6
+ import { formatShowDate, wrapperCleanUrls } from '../utils/client'
7
7
  import { fireSVG } from '../constants/svg'
8
8
 
9
- const { hotArticle } = useBlogConfig()
10
- const title = computed(() => hotArticle?.title || (`<span class="svg-icon">${fireSVG}</span>` + ' 精选文章'))
11
- const nextText = computed(() => hotArticle?.nextText || '换一组')
12
- const pageSize = computed(() => hotArticle?.pageSize || 9)
13
- const empty = computed(() => hotArticle?.empty ?? '暂无精选内容')
9
+ const { hotArticle: _hotArticle } = useBlogConfig()
10
+
11
+ const hotArticle = computed(() =>
12
+ _hotArticle === false ? undefined : _hotArticle
13
+ )
14
+
15
+ const title = computed(() => hotArticle.value?.title || (`<span class="svg-icon">${fireSVG}</span>` + ' 精选文章'))
16
+ const nextText = computed(() => hotArticle.value?.nextText || '换一组')
17
+ const pageSize = computed(() => hotArticle.value?.pageSize || 9)
18
+ const empty = computed(() => hotArticle.value?.empty ?? '暂无精选内容')
14
19
 
15
20
  const docs = useArticles()
16
21
 
@@ -27,10 +32,14 @@ function changePage() {
27
32
  currentPage.value = newIdx + 1
28
33
  }
29
34
 
35
+ const cleanUrls = useCleanUrls()
30
36
  const currentWikiData = computed(() => {
31
37
  const startIdx = (currentPage.value - 1) * pageSize.value
32
38
  const endIdx = startIdx + pageSize.value
33
- return recommendList.value.slice(startIdx, endIdx)
39
+ return recommendList.value.slice(startIdx, endIdx).map(v => ({
40
+ ...v,
41
+ route: wrapperCleanUrls(cleanUrls, v.route)
42
+ }))
34
43
  })
35
44
 
36
45
  const showChangeBtn = computed(() => {
@@ -39,7 +48,7 @@ const showChangeBtn = computed(() => {
39
48
  </script>
40
49
 
41
50
  <template>
42
- <div v-if="recommendList.length || empty" class="card recommend" data-pagefind-ignore="all">
51
+ <div v-if="_hotArticle !== false && (recommendList.length || empty) " class="card recommend" data-pagefind-ignore="all">
43
52
  <!-- 头部 -->
44
53
  <div class="card-header">
45
54
  <span class="title" v-html="title" />
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
2
  import { withBase } from 'vitepress'
3
3
  import { computed } from 'vue'
4
- import { useWindowSize } from '@vueuse/core'
5
- import { formatShowDate } from '../utils/client'
4
+ import { formatShowDate, wrapperCleanUrls } from '../utils/client'
5
+ import { useCleanUrls } from '../composables/config/blog'
6
6
 
7
7
  const props = defineProps<{
8
8
  route: string
@@ -16,36 +16,25 @@ const props = defineProps<{
16
16
  cover?: string | boolean
17
17
  pin?: number
18
18
  }>()
19
- const { width } = useWindowSize()
20
- const inMobile = computed(() => width.value <= 500)
21
19
  const showTime = computed(() => {
22
20
  return formatShowDate(props.date)
23
21
  })
24
22
 
25
- // function isWrappedWithPreventDefault(element: HTMLElement) {
26
- // let parent = element.parentElement
27
-
28
- // while (parent) {
29
- // if (parent.hasAttribute('preventDefault')) {
30
- // return true
31
- // }
32
- // parent = parent.parentElement
33
- // }
34
-
35
- // return false
36
- // }
23
+ const cleanUrls = useCleanUrls()
24
+ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route)))
37
25
  </script>
38
26
 
39
27
  <template>
40
- <a class="blog-item" :href="withBase(route)">
28
+ <!-- TODO: 响应式优化使用纯 CSS -->
29
+ <a class="blog-item" :href="link">
41
30
  <i v-if="!!pin" class="pin" />
42
31
  <!-- 标题 -->
43
- <p v-if="inMobile" class="title">{{ title }}</p>
32
+ <p class="title mobile-visible">{{ title }}</p>
44
33
  <div class="info-container">
45
34
  <!-- 左侧信息 -->
46
35
  <div class="info-part">
47
36
  <!-- 标题 -->
48
- <p v-if="!inMobile" class="title">{{ title }}</p>
37
+ <p class="title pc-visible">{{ title }}</p>
49
38
  <!-- 简短描述 -->
50
39
  <p v-if="!descriptionHTML && !!description" class="description">
51
40
  {{ description }}
@@ -54,21 +43,17 @@ const showTime = computed(() => {
54
43
  <div class="description-html" v-html="descriptionHTML" />
55
44
  </template>
56
45
  <!-- 底部补充描述 -->
57
- <div v-if="!inMobile" class="badge-list">
46
+ <div class="badge-list pc-visible">
58
47
  <span v-if="author" class="split">{{ author }}</span>
59
48
  <span class="split">{{ showTime }}</span>
60
49
  <span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
61
50
  </div>
62
51
  </div>
63
52
  <!-- 右侧封面图 -->
64
- <div
65
- v-if="cover"
66
- class="cover-img"
67
- :style="`background-image: url(${withBase(`${cover}`)});`"
68
- />
53
+ <div v-if="cover" class="cover-img" :style="`background-image: url(${withBase(`${cover}`)});`" />
69
54
  </div>
70
55
  <!-- 底部补充描述 -->
71
- <div v-if="inMobile" class="badge-list">
56
+ <div class="badge-list mobile-visible">
72
57
  <span v-if="author" class="split">{{ author }}</span>
73
58
  <span class="split">{{ showTime }}</span>
74
59
  <span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
@@ -86,19 +71,19 @@ const showTime = computed(() => {
86
71
  left: -4px;
87
72
  opacity: 0.5;
88
73
  }
74
+
89
75
  .blog-item:hover .pin {
90
76
  opacity: 1;
91
77
  }
78
+
92
79
  .blog-item .pin::before {
93
80
  content: '';
94
81
  position: absolute;
95
82
  width: 120%;
96
83
  height: 30px;
97
- background-image: linear-gradient(
98
- 45deg,
99
- var(--blog-theme-color),
100
- var(--blog-theme-color)
101
- );
84
+ background-image: linear-gradient(45deg,
85
+ var(--blog-theme-color),
86
+ var(--blog-theme-color));
102
87
  transform: rotate(-45deg) translateY(-20px);
103
88
  display: flex;
104
89
  align-items: center;
@@ -120,10 +105,12 @@ const showTime = computed(() => {
120
105
  cursor: pointer;
121
106
  display: flex;
122
107
  flex-direction: column;
108
+
123
109
  &:hover {
124
110
  box-shadow: var(--box-shadow-hover);
125
111
  }
126
112
  }
113
+
127
114
  .info-container {
128
115
  display: flex;
129
116
  align-items: center;
@@ -133,11 +120,13 @@ const showTime = computed(() => {
133
120
  .info-part {
134
121
  flex: 1;
135
122
  }
123
+
136
124
  .title {
137
125
  font-size: 18px;
138
126
  font-weight: 600;
139
127
  margin-bottom: 8px;
140
128
  }
129
+
141
130
  .description {
142
131
  color: var(--description-font-color);
143
132
  font-size: 14px;
@@ -149,13 +138,16 @@ const showTime = computed(() => {
149
138
  -webkit-line-clamp: 2;
150
139
  -webkit-box-orient: vertical;
151
140
  }
141
+
152
142
  .description-html {
153
143
  font-size: 14px;
154
144
  }
145
+
155
146
  .badge-list {
156
147
  font-size: 13px;
157
148
  color: var(--badge-font-color);
158
149
  margin-top: 8px;
150
+
159
151
  .split:not(:last-child) {
160
152
  &::after {
161
153
  content: '';
@@ -167,6 +159,7 @@ const showTime = computed(() => {
167
159
  }
168
160
  }
169
161
  }
162
+
170
163
  .cover-img {
171
164
  width: 120px;
172
165
  height: 80px;
@@ -176,11 +169,27 @@ const showTime = computed(() => {
176
169
  background-size: 120px 80px;
177
170
  }
178
171
 
172
+ .pc-visible {
173
+ display: block;
174
+ }
175
+
176
+ .mobile-visible {
177
+ display: none;
178
+ }
179
+
179
180
  @media screen and (max-width: 500px) {
180
181
  .cover-img {
181
182
  width: 100px;
182
183
  height: 60px;
183
184
  background-size: 100px 60px;
184
185
  }
186
+
187
+ .pc-visible {
188
+ display: none;
189
+ }
190
+
191
+ .mobile-visible {
192
+ display: block;
193
+ }
185
194
  }
186
195
  </style>
@@ -2,8 +2,8 @@
2
2
  import { computed, onMounted, ref } from 'vue'
3
3
  import { useRoute, withBase } from 'vitepress'
4
4
  import { ElButton, ElLink } from 'element-plus'
5
- import { formatShowDate } from '../utils/client'
6
- import { useArticles, useBlogConfig } from '../composables/config/blog'
5
+ import { formatShowDate, wrapperCleanUrls } from '../utils/client'
6
+ import { useArticles, useBlogConfig, useCleanUrls } from '../composables/config/blog'
7
7
  import { recommendSVG } from '../constants/svg'
8
8
  import type { Theme } from '../composables/config/index'
9
9
 
@@ -145,6 +145,8 @@ onMounted(() => {
145
145
  const currentPageNum = Math.floor(currentPageIndex / pageSize.value) + 1
146
146
  currentPage.value = currentPageNum
147
147
  })
148
+
149
+ const cleanUrls = useCleanUrls()
148
150
  </script>
149
151
 
150
152
  <template>
@@ -170,7 +172,7 @@ onMounted(() => {
170
172
  <ElLink
171
173
  type="info" class="title" :class="{
172
174
  current: isCurrentDoc(v.route),
173
- }" :href="v.route"
175
+ }" :href="wrapperCleanUrls(cleanUrls, v.route)"
174
176
  >
175
177
  {{ v.meta.title }}
176
178
  </ElLink>
@@ -225,3 +225,8 @@ export function useHomeFooterConfig() {
225
225
  export function useBackToTopConfig() {
226
226
  return useBlogConfig().backToTop
227
227
  }
228
+
229
+ export function useCleanUrls() {
230
+ const { site } = useData()
231
+ return !!site.value.cleanUrls
232
+ }
@@ -340,7 +340,7 @@ export namespace Theme {
340
340
  pagesData: PageData[]
341
341
  srcDir?: string
342
342
  author?: string
343
- hotArticle?: HotArticle
343
+ hotArticle?: HotArticle | false
344
344
  home?: HomeBlog
345
345
  /**
346
346
  * 本地全文搜索定制
@@ -423,6 +423,7 @@ export namespace Theme {
423
423
  * 详见 https://oml2d.com/options/Options.html
424
424
  */
425
425
  oml2d?: Oml2dOptions
426
+ homeTags?: boolean
426
427
  }
427
428
 
428
429
  export interface BackToTop {
package/src/index.ts CHANGED
@@ -31,7 +31,9 @@ export const BlogTheme: Theme = {
31
31
  DefaultTheme.enhanceApp(ctx)
32
32
  ctx.app.component('TimelinePage', TimelinePage)
33
33
  ctx.app.component('UserWorksPage', UserWorksPage)
34
- ctx.app.component('Mermaid', Mermaid as any)
34
+ if (!ctx.app.component('Mermaid')) {
35
+ ctx.app.component('Mermaid', Mermaid as any)
36
+ }
35
37
  }
36
38
  }
37
39
 
@@ -172,3 +172,8 @@ export function getImageUrl(
172
172
  } // 如果 ThemeableImage 类型不是上述情况,则返回空字符串
173
173
  return ''
174
174
  }
175
+
176
+ export function wrapperCleanUrls(cleanUrls: boolean, route: string) {
177
+ const tempUrl = route.replace(/\.html$/, '')
178
+ return cleanUrls ? tempUrl : `${tempUrl}.html`
179
+ }
@@ -1,419 +0,0 @@
1
- <script lang="ts" setup>
2
- // @ts-nocheck
3
- import { computed, nextTick, onBeforeMount, onMounted, ref, watch } from 'vue'
4
- import { Command } from 'vue-command-palette'
5
- import { useRoute, useRouter, withBase } from 'vitepress'
6
- import { useMagicKeys, useWindowSize } from '@vueuse/core'
7
- import { chineseSearchOptimize, formatDate } from '../utils/client'
8
- import { useArticles, useBlogConfig } from '../composables/config/blog'
9
- import type { Theme } from '../composables/config'
10
- import LogoPagefind from './LogoPagefind.vue'
11
-
12
- const searchResult = ref<Theme.PageData[]>([])
13
-
14
- const windowSize = useWindowSize()
15
-
16
- const isMinimized = computed(() => windowSize.width.value < 760)
17
- const flexValue = computed(() => (isMinimized.value ? 0 : 1))
18
- const { search: searchConfig } = useBlogConfig()
19
-
20
- const headingText = computed(() => {
21
- return searchConfig?.heading
22
- ? searchConfig.heading.replace(
23
- /\{\{searchResult\}\}/,
24
- searchResult.value.length
25
- )
26
- : `共: ${searchResult.value.length} 个搜索结果`
27
- })
28
- const openSearch = computed(() =>
29
- searchConfig instanceof Object
30
- ? searchConfig.mode ?? true
31
- : searchConfig ?? true
32
- )
33
-
34
- function addInlineScript() {
35
- const scriptText = `import('${withBase('/_pagefind/pagefind.js')}')
36
- .then((module) => {
37
- window.__pagefind__ = module
38
- })
39
- .catch(() => {
40
- console.log('not load /_pagefind/pagefind.js')
41
- })`
42
- const inlineScript = document.createElement('script')
43
- inlineScript.innerHTML = scriptText
44
- document.head.appendChild(inlineScript)
45
- }
46
-
47
- onBeforeMount(() => {
48
- if (openSearch.value === 'pagefind') {
49
- addInlineScript()
50
- }
51
- })
52
-
53
- const metaKey = ref('')
54
- onMounted(() => {
55
- metaKey.value = /(Mac|iPhone|iPod|iPad)/i.test(navigator?.platform)
56
- ? '⌘'
57
- : 'Ctrl'
58
- })
59
- const searchModal = ref(false)
60
- const searchWords = ref('')
61
- const docs = useArticles()
62
- const docsMap = computed(() => {
63
- const map = new Map<string, Theme.PageData.meta>()
64
- docs.value.forEach((doc) => {
65
- map.set(withBase(doc.route.replace(/index$/, '')), doc.meta)
66
- })
67
- return map
68
- })
69
-
70
- const keys = useMagicKeys({
71
- passive: false,
72
- onEventFired(e) {
73
- if (e.ctrlKey && e.key === 'k' && e.type === 'keydown')
74
- e.preventDefault()
75
- }
76
- })
77
- const CmdK = keys['Meta+K']
78
- const CtrlK = keys['Ctrl+K']
79
- // eslint-disable-next-line dot-notation, prefer-destructuring
80
- const Escape = keys['Escape']
81
-
82
- watch(CtrlK, (v) => {
83
- if (v) {
84
- searchModal.value = true
85
- }
86
- })
87
- watch(CmdK, (v) => {
88
- if (v) {
89
- searchModal.value = true
90
- }
91
- })
92
- watch(Escape, (v) => {
93
- if (v) {
94
- searchModal.value = false
95
- }
96
- })
97
-
98
- function inlineSearch() {
99
- if (!searchWords.value) {
100
- searchResult.value = []
101
- return
102
- }
103
- searchResult.value = docs.value
104
- .filter(v =>
105
- `${v.meta.description}${v.meta.title}`.includes(searchWords.value)
106
- )
107
- .map((v) => {
108
- return {
109
- ...v,
110
- meta: {
111
- ...v.meta,
112
- description:
113
- v.meta?.description?.replace(
114
- new RegExp(`(${searchWords.value})`, 'g'),
115
- '<mark>$1</mark>'
116
- ) || ''
117
- }
118
- }
119
- })
120
- searchResult.value.sort((a, b) => {
121
- return +new Date(b.meta.date) - +new Date(a.meta.date)
122
- })
123
- }
124
-
125
- watch(
126
- () => searchWords.value,
127
- async () => {
128
- if (openSearch.value === 'pagefind') {
129
- // dev-server兜底
130
- // @ts-expect-error
131
- if (!window?.__pagefind__?.search) {
132
- inlineSearch()
133
- }
134
- else {
135
- // @ts-expect-error
136
- await window?.__pagefind__
137
- ?.search?.(chineseSearchOptimize(searchWords.value))
138
- .then(async (search: any) => {
139
- const result = await Promise.all(
140
- search.results.map((v: any) => v.data())
141
- )
142
- searchResult.value = result.map((result) => {
143
- const { url, excerpt } = result
144
- const targetRoute = url.replace(/\.html$/, '')
145
- const meta = docsMap.value.get(targetRoute)
146
- if (meta) {
147
- return {
148
- route: targetRoute,
149
- meta: {
150
- ...meta,
151
- description: excerpt
152
- }
153
- }
154
- }
155
- return {
156
- route: url,
157
- meta: {
158
- ...result.meta,
159
- description: excerpt
160
- }
161
- }
162
- })
163
- })
164
- }
165
- }
166
- else {
167
- inlineSearch()
168
- }
169
- nextTick(() => {
170
- // hack 原组件实现
171
- document.querySelectorAll('div[aria-disabled="true"]').forEach((v) => {
172
- v.setAttribute('aria-disabled', 'false')
173
- })
174
- })
175
- }
176
- )
177
-
178
- function handleClickMask(e: any) {
179
- if (e.target === e.currentTarget) {
180
- searchModal.value = false
181
- }
182
- }
183
- watch(
184
- () => searchModal.value,
185
- (newValue) => {
186
- if (newValue) {
187
- nextTick(() => {
188
- document
189
- .querySelector('div[command-dialog-mask]')
190
- ?.addEventListener('click', handleClickMask)
191
- })
192
- }
193
- else {
194
- document
195
- .querySelector('div[command-dialog-mask]')
196
- ?.removeEventListener('click', handleClickMask)
197
- }
198
- }
199
- )
200
-
201
- // 搜索结果分页?
202
- // const pageSize = ref(6)
203
- // const currentPage = ref(0)
204
- // const showSearchResult = computed(() => {
205
- // // 合法性处理
206
- // const pageIdx =
207
- // currentPage.value % Math.ceil(searchResult.value.length / pageSize.value)
208
- // const startIdx = pageIdx * pageSize.value
209
- // return searchResult.value.slice(startIdx, startIdx + pageSize.value)
210
- // })
211
-
212
- const router = useRouter()
213
- const route = useRoute()
214
- function handleSelect(target: any) {
215
- searchModal.value = false
216
- if (!route.path.startsWith(target.value)) {
217
- // searchWords.value = ''
218
- router.go(target.value)
219
- }
220
- }
221
- </script>
222
-
223
- <template>
224
- <div v-if="openSearch" class="blog-search" data-pagefind-ignore="all">
225
- <div class="nav-search-btn-wait" @click="searchModal = true">
226
- <svg width="14" height="14" viewBox="0 0 20 20">
227
- <path
228
- d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z"
229
- stroke="currentColor"
230
- fill="none"
231
- fill-rule="evenodd"
232
- stroke-linecap="round"
233
- stroke-linejoin="round"
234
- />
235
- </svg>
236
- <span v-if="!isMinimized" class="search-tip">{{
237
- searchConfig?.btnPlaceholder || '搜索'
238
- }}</span>
239
- <span v-if="!isMinimized" class="metaKey"> {{ metaKey }} K </span>
240
- </div>
241
- <Command.Dialog :visible="searchModal" theme="algolia">
242
- <template #header>
243
- <Command.Input
244
- v-model:value="searchWords"
245
- :placeholder="searchConfig?.placeholder || '请输入要搜素的内容'"
246
- />
247
- </template>
248
- <template #body>
249
- <div class="search-dialog">
250
- <Command.List>
251
- <Command.Empty v-if="!searchResult.length">
252
- {{
253
- searchConfig?.emptyText || 'No results found.'
254
- }}
255
- </Command.Empty>
256
- <Command.Group v-else :heading="headingText">
257
- <Command.Item
258
- v-for="item in searchResult"
259
- :key="item.route"
260
- :data-value="item.route"
261
- @select="handleSelect"
262
- >
263
- <div class="link">
264
- <div class="title">
265
- <span>{{ item.meta.title }}</span>
266
- <span class="date">
267
- {{ formatDate(item.meta.date, 'yyyy-MM-dd') }}</span>
268
- </div>
269
- <div class="des" v-html="item.meta.description" />
270
- </div>
271
- </Command.Item>
272
- </Command.Group>
273
- </Command.List>
274
- </div>
275
- </template>
276
- <template v-if="searchResult.length" #footer>
277
- <div class="command-palette-logo">
278
- <a
279
- v-if="openSearch === 'pagefind'"
280
- href="https://github.com/cloudcannon/pagefind"
281
- target="_blank"
282
- rel="noopener noreferrer"
283
- >
284
- <span class="command-palette-Label">Search by</span>
285
- <LogoPagefind style="width: 77px" />
286
- </a>
287
- </div>
288
- <ul class="command-palette-commands">
289
- <li>
290
- <kbd class="command-palette-commands-key"><svg width="15" height="15" aria-label="Enter key" role="img">
291
- <g
292
- fill="none"
293
- stroke="currentColor"
294
- stroke-linecap="round"
295
- stroke-linejoin="round"
296
- stroke-width="1.2"
297
- >
298
- <path
299
- d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"
300
- />
301
- </g></svg></kbd><span class="command-palette-Label">to select</span>
302
- </li>
303
- <li>
304
- <kbd class="command-palette-commands-key"><svg width="15" height="15" aria-label="Arrow down" role="img">
305
- <g
306
- fill="none"
307
- stroke="currentColor"
308
- stroke-linecap="round"
309
- stroke-linejoin="round"
310
- stroke-width="1.2"
311
- >
312
- <path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3" />
313
- </g></svg></kbd><kbd class="command-palette-commands-key"><svg width="15" height="15" aria-label="Arrow up" role="img">
314
- <g
315
- fill="none"
316
- stroke="currentColor"
317
- stroke-linecap="round"
318
- stroke-linejoin="round"
319
- stroke-width="1.2"
320
- >
321
- <path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3" />
322
- </g></svg></kbd><span class="command-palette-Label">to navigate</span>
323
- </li>
324
- <li>
325
- <kbd class="command-palette-commands-key"><svg width="15" height="15" aria-label="Escape key" role="img">
326
- <g
327
- fill="none"
328
- stroke="currentColor"
329
- stroke-linecap="round"
330
- stroke-linejoin="round"
331
- stroke-width="1.2"
332
- >
333
- <path
334
- d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"
335
- />
336
- </g></svg></kbd><span class="command-palette-Label">to close</span>
337
- </li>
338
- </ul>
339
- </template>
340
- </Command.Dialog>
341
- </div>
342
- </template>
343
-
344
- <style lang="scss" scoped>
345
- .blog-search {
346
- flex: v-bind(flexValue);
347
- display: flex;
348
- padding-left: 32px;
349
- .nav-search-btn-wait {
350
- cursor: pointer;
351
- display: flex;
352
- align-items: center;
353
- justify-content: center;
354
- padding: 6px;
355
- box-sizing: border-box;
356
- border: 1px solid transparent;
357
- border-radius: 6px;
358
- transition: .2s border;
359
-
360
- .metaKey {
361
- margin-left: 10px;
362
- font-size: 12px;
363
- }
364
-
365
- &:hover {
366
- border: 1px solid var(--vp-c-brand-1);
367
- border-radius: 6px;
368
- }
369
-
370
- .search-tip {
371
- color: #909399;
372
- font-size: 12px;
373
- padding-left: 10px;
374
- }
375
- }
376
- }
377
- </style>
378
-
379
- <style lang="scss">
380
- @import '../styles/scss/global.scss';
381
- @import '../styles/scss/algolia.scss';
382
-
383
- div[command-group] {
384
- display: block !important;
385
- }
386
- div[command-item] {
387
- display: flex !important;
388
- }
389
- .search-dialog {
390
- div[command-item] > div.link {
391
- width: 100%;
392
- }
393
- div[command-item] .title {
394
- display: flex;
395
- justify-content: space-between;
396
- }
397
-
398
- div[command-item] .des {
399
- text-overflow: ellipsis;
400
- overflow: hidden;
401
- word-break: keep-all;
402
- white-space: nowrap;
403
- }
404
-
405
- div[command-item] .date {
406
- min-width: 80px;
407
- }
408
- div[command-item] mark {
409
- background: none;
410
- color: var(--vp-c-brand-1);
411
- }
412
-
413
- div[command-item][aria-selected='true'] mark,
414
- div[command-item]:hover mark {
415
- color: inherit;
416
- text-decoration: underline;
417
- }
418
- }
419
- </style>
@@ -1,55 +0,0 @@
1
- <template>
2
- <svg
3
- width="594"
4
- height="112"
5
- viewBox="0 0 594 112"
6
- fill="none"
7
- xmlns="http://www.w3.org/2000/svg"
8
- >
9
- <path
10
- d="M147.8 111.2H164V77.5998H164.6C164.6 77.5998 170.6 87.1998 183.2 87.1998C197 87.1998 209.6 74.5998 209.6 56.5998C209.6 38.5998 197 25.9998 183.2 25.9998C170.6 25.9998 164.6 35.5998 164.6 35.5998H164V27.1998H147.8V111.2ZM178.4 72.1998C170 72.1998 163.4 65.5998 163.4 56.5998C163.4 47.5998 170 40.9998 178.4 40.9998C186.8 40.9998 193.4 47.5998 193.4 56.5998C193.4 65.5998 186.8 72.1998 178.4 72.1998Z"
11
- fill="black"
12
- />
13
- <path
14
- d="M230.628 87.1998C242.028 87.1998 248.028 78.7998 248.028 78.7998H248.628V85.9998C252.228 85.9998 264.828 85.9998 264.828 85.9998V49.3998C264.828 36.1998 254.628 25.9998 239.628 25.9998C224.028 25.9998 215.628 37.3998 215.628 37.3998L225.228 46.9998C225.228 46.9998 230.028 40.3998 238.428 40.3998C244.428 40.3998 248.028 43.9998 248.628 48.1998L230.028 51.5598C219.228 53.4798 212.628 60.7998 212.628 70.3998C212.628 79.9998 219.828 87.1998 230.628 87.1998ZM236.028 73.9998C231.228 73.9998 228.828 71.5998 228.828 67.9998C228.828 64.9998 231.228 62.7198 235.428 61.9998L248.628 59.5998V60.7998C248.628 68.5998 243.228 73.9998 236.028 73.9998Z"
15
- fill="black"
16
- />
17
- <path
18
- d="M299.033 111.2C317.633 111.2 330.833 97.9998 330.833 79.9998V27.1998H314.633V35.5998H314.033C314.033 35.5998 308.633 25.9998 296.033 25.9998C282.833 25.9998 270.833 37.9998 270.833 55.3998C270.833 72.7998 282.833 84.7998 296.033 84.7998C308.633 84.7998 314.033 75.1998 314.033 75.1998H314.633V79.9998C314.633 89.5998 308.033 96.1998 299.033 96.1998C289.433 96.1998 283.433 88.9998 283.433 88.9998L273.233 99.1998C273.233 99.1998 281.633 111.2 299.033 111.2ZM300.833 69.7998C293.033 69.7998 287.033 63.7998 287.033 55.3998C287.033 46.9998 293.033 40.9998 300.833 40.9998C308.633 40.9998 314.633 46.9998 314.633 55.3998C314.633 63.7998 308.633 69.7998 300.833 69.7998Z"
19
- fill="black"
20
- />
21
- <path
22
- d="M367.986 87.1998C384.186 87.1998 393.186 77.5998 393.186 77.5998L384.786 66.1998C384.786 66.1998 379.386 72.7998 369.186 72.7998C360.186 72.7998 355.386 67.9998 353.586 62.5998H396.186C396.186 62.5998 396.786 59.5998 396.786 55.3998C396.786 39.1998 383.586 25.9998 367.386 25.9998C350.586 25.9998 336.786 39.7998 336.786 56.5998C336.786 73.3998 350.586 87.1998 367.986 87.1998ZM353.586 50.5998C355.386 45.1998 360.186 40.3998 366.786 40.3998C373.386 40.3998 378.186 45.1998 379.986 50.5998H353.586Z"
23
- fill="black"
24
- />
25
- <path
26
- d="M406.423 85.9998H422.624V43.3998H444.224V85.9998H460.423V28.3998H422.624V24.7998C422.624 19.3998 425.624 16.3998 430.423 16.3998C433.423 16.3998 435.823 17.5998 435.823 17.5998V2.5998C435.823 2.5998 431.624 0.799805 426.224 0.799805C414.224 0.799805 406.423 8.59981 406.423 22.3998V28.3998H397.423V43.3998H406.423V85.9998ZM452.263 19.3998C457.423 19.3998 461.624 15.1998 461.624 10.3998C461.624 5.59981 457.424 1.3998 452.384 1.3998C447.224 1.3998 443.023 5.59981 443.023 10.3998C443.023 15.1998 447.223 19.3998 452.263 19.3998Z"
27
- fill="black"
28
- />
29
- <path
30
- d="M470.652 85.9998H486.852V54.7998C486.852 46.9998 492.252 41.5998 499.452 41.5998C506.052 41.5998 510.252 45.7998 510.252 52.9998V85.9998H526.452V50.5998C526.452 35.5998 516.852 25.9998 504.852 25.9998C493.452 25.9998 487.452 35.5998 487.452 35.5998H486.852V27.1998H470.652V85.9998Z"
31
- fill="black"
32
- />
33
- <path
34
- d="M557.819 87.1998C570.419 87.1998 576.419 77.5998 576.419 77.5998H577.019V85.9998H593.219V1.9998H577.019V35.5998H576.419C576.419 35.5998 570.419 25.9998 557.819 25.9998C544.019 25.9998 531.419 38.5998 531.419 56.5998C531.419 74.5998 544.019 87.1998 557.819 87.1998ZM562.619 72.1998C554.219 72.1998 547.619 65.5998 547.619 56.5998C547.619 47.5998 554.219 40.9998 562.619 40.9998C571.019 40.9998 577.619 47.5998 577.619 56.5998C577.619 65.5998 571.019 72.1998 562.619 72.1998Z"
35
- fill="black"
36
- />
37
- <path
38
- fill-rule="evenodd"
39
- clip-rule="evenodd"
40
- d="M60 96.9999C93.1371 96.9999 120 81.8416 120 63.1428V50.8311H115.91C107.182 38.2198 85.4398 29.2856 60 29.2856C34.5602 29.2856 12.8183 38.2198 4.09026 50.8311H0V63.1428C0 81.8416 26.8629 96.9999 60 96.9999Z"
41
- fill="black"
42
- />
43
- <path
44
- d="M116 52C116 59.317 110.727 66.7404 100.454 72.5615C90.3014 78.3149 76.0069 82 60 82C43.9931 82 29.6986 78.3149 19.5456 72.5615C9.2731 66.7404 4 59.317 4 52C4 44.6831 9.2731 37.2596 19.5456 31.4385C29.6986 25.6851 43.9931 22 60 22C76.0069 22 90.3014 25.6851 100.454 31.4385C110.727 37.2596 116 44.6831 116 52Z"
45
- fill="white"
46
- stroke="black"
47
- stroke-width="8"
48
- />
49
- <path
50
- d="M57.8864 72.0605L87.2817 41.837C88.6253 40.4556 87.43 38.1599 85.5278 38.4684L26.0819 48.1083C23.9864 48.4481 23.794 51.3882 25.8273 51.9982L46.7151 58.2645C47.2181 58.4154 47.6415 58.7581 47.894 59.2185L54.6991 71.6277C55.3457 72.8069 56.9487 73.0246 57.8864 72.0605Z"
51
- fill="black"
52
- />
53
- <ellipse cx="58" cy="53.5" rx="7" ry="4.5" fill="white" />
54
- </svg>
55
- </template>