@sugarat/theme 0.4.13 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/node.d.ts +13 -1
  2. package/node.js +144 -51
  3. package/node.mjs +753 -0
  4. package/package.json +9 -6
  5. package/src/components/BlogAlert.vue +10 -9
  6. package/src/components/BlogArticleAnalyze.vue +8 -9
  7. package/src/components/BlogAuthor.vue +6 -5
  8. package/src/components/BlogBackToTop.vue +8 -10
  9. package/src/components/BlogButtonAfterArticle.vue +5 -14
  10. package/src/components/BlogCommentWrapper.vue +11 -28
  11. package/src/components/BlogDocCover.vue +3 -3
  12. package/src/components/BlogFooter.vue +3 -1
  13. package/src/components/BlogFriendLink.vue +4 -3
  14. package/src/components/BlogHomeBanner.vue +7 -6
  15. package/src/components/BlogHomeHeaderAvatar.vue +3 -3
  16. package/src/components/BlogHomeOverview.vue +3 -3
  17. package/src/components/BlogHomeTags.vue +5 -5
  18. package/src/components/BlogHotArticle.vue +4 -7
  19. package/src/components/BlogItem.vue +1 -1
  20. package/src/components/BlogList.vue +7 -6
  21. package/src/components/BlogRecommendArticle.vue +11 -14
  22. package/src/components/BlogSidebar.vue +9 -15
  23. package/src/components/CommentArtalk.vue +7 -5
  24. package/src/components/CommentGiscus.vue +7 -8
  25. package/src/components/Icon.vue +33 -0
  26. package/src/composables/config/blog.ts +203 -87
  27. package/src/composables/config/index.ts +9 -0
  28. package/src/hooks/useOml2d.ts +15 -8
  29. package/src/index.ts +3 -0
  30. package/src/node.ts +5 -1
  31. package/src/styles/index.scss +6 -6
  32. package/src/utils/node/hot-reload-plugin.ts +31 -1
  33. package/src/utils/node/index.ts +0 -2
  34. package/src/utils/node/mdPlugins.ts +4 -0
  35. package/src/utils/node/theme.ts +15 -18
  36. package/src/utils/node/vitePlugins.ts +117 -33
@@ -1,35 +1,29 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
- import { useBlogConfig } from '../composables/config/blog'
3
+ import { useRecommendConfig, useShowRecommend } from '../composables/config/blog'
4
4
  import BlogRecommendArticle from './BlogRecommendArticle.vue'
5
5
 
6
- const { recommend: _recommend } = useBlogConfig()
7
-
6
+ const recommend = useRecommendConfig()
7
+ const show = useShowRecommend()
8
8
  const sidebarStyle = computed(() =>
9
- _recommend && _recommend?.style ? _recommend.style : 'card'
10
- )
11
- const marginTop = computed(() =>
12
- sidebarStyle.value === 'card' ? '40px' : '0px'
13
- )
14
- const marginTopMini = computed(() =>
15
- sidebarStyle.value === 'card' ? '60px' : '0px'
9
+ recommend.value?.style || 'sidebar'
16
10
  )
17
11
  </script>
18
12
 
19
13
  <template>
20
- <div v-if="_recommend !== false" class="sidebar" data-pagefind-ignore="all">
14
+ <div v-if="show" class="sidebar" :class="{ card: sidebarStyle === 'card' }" data-pagefind-ignore="all">
21
15
  <BlogRecommendArticle />
22
16
  </div>
23
17
  </template>
24
18
 
25
19
  <style lang="scss" scoped>
26
- .sidebar {
27
- margin-top: v-bind(marginTop);
20
+ .sidebar.card {
21
+ margin-top: 40px;
28
22
  }
29
23
 
30
24
  @media screen and (min-width: 960px) and (max-width: 1120px) {
31
- .sidebar {
32
- margin-top: v-bind(marginTopMini);
25
+ .sidebar.card {
26
+ margin-top: 60px;
33
27
  }
34
28
  }
35
29
  </style>
@@ -3,7 +3,7 @@ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
3
3
  import { useData, useRoute } from 'vitepress'
4
4
  import type Artalk from 'artalk'
5
5
 
6
- import { useBlogConfig } from '../composables/config/blog'
6
+ import { useCommentConfig, useOpenCommentConfig } from '../composables/config/blog'
7
7
 
8
8
  const { isDark, page } = useData()
9
9
  const el = ref<HTMLDivElement>()
@@ -12,14 +12,16 @@ const route = useRoute()
12
12
 
13
13
  const artalk = ref<Artalk>()
14
14
 
15
- const { comment } = useBlogConfig()
15
+ const comment = useCommentConfig()
16
+
16
17
  const commentConfig = computed(() => {
17
- if (comment && 'type' in comment && comment.type === 'artalk') {
18
- return comment.options
18
+ if (comment.value && 'type' in comment.value && comment.value?.type === 'artalk') {
19
+ return comment.value.options
19
20
  }
20
21
 
21
22
  return false
22
23
  })
24
+ const open = useOpenCommentConfig()
23
25
 
24
26
  onMounted(() => {
25
27
  // CDN 异步加载,有优化空间
@@ -64,7 +66,7 @@ watch(isDark, () => {
64
66
  </script>
65
67
 
66
68
  <template>
67
- <div v-if="commentConfig" ref="el" class="artalk-container" />
69
+ <div v-if="open" ref="el" class="artalk-container" />
68
70
  </template>
69
71
 
70
72
  <style lang="scss" scoped>
@@ -2,19 +2,18 @@
2
2
  import { useData, useRoute } from 'vitepress'
3
3
  import { computed, nextTick, ref, watch } from 'vue'
4
4
  import Giscus from '@giscus/vue'
5
- import { useBlogConfig } from '../composables/config/blog'
5
+ import { useCommentConfig } from '../composables/config/blog'
6
6
 
7
- // 读取配制
8
- const { comment } = useBlogConfig()
7
+ const comment = useCommentConfig()
9
8
  const commentConfig = computed(() => {
10
- if (!comment) {
9
+ if (!comment.value) {
11
10
  return false
12
11
  }
13
- if ('type' in comment && comment.type === 'giscus') {
14
- return comment.options
12
+ if ('type' in comment.value && comment.value?.type === 'giscus') {
13
+ return comment.value.options
15
14
  }
16
- else if (!('type' in comment)) {
17
- return comment
15
+ else if (!('type' in comment.value)) {
16
+ return comment.value
18
17
  }
19
18
 
20
19
  return false
@@ -0,0 +1,33 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ size?: string | number
6
+ icon?: string
7
+ }>()
8
+
9
+ const size = computed(() => props.size && (typeof props.size === 'number' ? `${props.size}px` : props.size))
10
+ </script>
11
+
12
+ <template>
13
+ <i v-if="props.icon" class="sugar-theme-icon" :style="{ fontSize: size }" v-html="props.icon" />
14
+ <i v-else class="sugar-theme-icon" :style="{ fontSize: size }">
15
+ <slot />
16
+ </i>
17
+ </template>
18
+
19
+ <style lang="css" scoped>
20
+ .sugar-theme-icon {
21
+ --color: inherit;
22
+ align-items: center;
23
+ display: inline-flex;
24
+ height: 1em;
25
+ justify-content: center;
26
+ line-height: 1em;
27
+ position: relative;
28
+ width: 1em;
29
+ fill: currentColor;
30
+ color: var(--color);
31
+ font-size: inherit;
32
+ }
33
+ </style>
@@ -14,7 +14,8 @@ import {
14
14
  onUnmounted,
15
15
  provide,
16
16
  reactive,
17
- ref
17
+ ref,
18
+ watch,
18
19
  } from 'vue'
19
20
  import { useColorMode } from '@vueuse/core'
20
21
 
@@ -27,28 +28,14 @@ const activeTagSymbol: InjectionKey<Ref<Theme.activeTag>> = Symbol('active-tag')
27
28
 
28
29
  const currentPageNum: InjectionKey<Ref<number>> = Symbol('home-page-num')
29
30
 
30
- const userWorks: InjectionKey<Ref<Theme.UserWorks>> = Symbol('user-works')
31
-
32
- const homeFooter: InjectionKey<Theme.Footer | Theme.Footer[] | undefined> = Symbol('home-footer')
33
-
34
31
  export function withConfigProvider(App: Component) {
35
32
  return defineComponent({
36
33
  name: 'ConfigProvider',
37
34
  setup(_, { slots }) {
38
- const { theme } = useData()
39
- const config = computed(() => resolveConfig(theme.value))
40
- provide(homeFooter, config.value.blog?.footer)
35
+ const { theme, localeIndex } = useData()
36
+ const config = computed(() => resolveConfig(theme.value, localeIndex.value))
37
+
41
38
  provide(configSymbol, config)
42
- provide(
43
- userWorks,
44
- ref(
45
- config.value.blog?.works || {
46
- title: '',
47
- description: '',
48
- list: []
49
- }
50
- )
51
- )
52
39
 
53
40
  const activeTag = ref<Theme.activeTag>({
54
41
  label: '',
@@ -73,37 +60,122 @@ export function withConfigProvider(App: Component) {
73
60
  'el-red': 'el-red'
74
61
  }
75
62
  })
76
- mode.value = config.value.blog?.themeColor ?? 'vp-default'
63
+ watch(config, () => {
64
+ mode.value = config.value.blog?.themeColor ?? 'vp-default'
65
+ }, {
66
+ immediate: true
67
+ })
68
+
77
69
  return () => h(App, null, slots)
78
70
  }
79
71
  })
80
72
  }
73
+
74
+ export function useGlobalAuthor() {
75
+ const blogConfig = useBlogConfig()
76
+ return computed(() => blogConfig.value?.author || '')
77
+ }
78
+
79
+ export function useArticleConfig() {
80
+ const blogConfig = useBlogConfig()
81
+ return computed(() => blogConfig.value?.article)
82
+ }
83
+
84
+ export function useAuthorList() {
85
+ const blogConfig = useBlogConfig()
86
+ return computed(() => blogConfig.value?.authorList)
87
+ }
88
+
89
+ export function useHotArticleConfig() {
90
+ const blogConfig = useBlogConfig()
91
+ return computed(() => {
92
+ const cfg = blogConfig.value?.hotArticle
93
+ return cfg === false ? undefined : cfg
94
+ })
95
+ }
96
+
97
+ export function useShowHotArticle() {
98
+ const blogConfig = useBlogConfig()
99
+ return computed(() => {
100
+ const cfg = blogConfig.value?.hotArticle
101
+ return cfg !== false
102
+ })
103
+ }
104
+
105
+ export function useRecommendConfig() {
106
+ const blogConfig = useBlogConfig()
107
+ return computed(() => {
108
+ const cfg = blogConfig.value?.recommend
109
+ return cfg === false ? undefined : cfg
110
+ })
111
+ }
112
+
113
+ export function useShowRecommend() {
114
+ const blogConfig = useBlogConfig()
115
+ return computed(() => {
116
+ const cfg = blogConfig.value?.recommend
117
+ return cfg !== false
118
+ })
119
+ }
120
+
121
+ export function useAlertConfig() {
122
+ const blogConfig = useBlogConfig()
123
+ return computed(() => blogConfig.value?.alert)
124
+ }
125
+
126
+ export function useHomeConfig() {
127
+ const blogConfig = useBlogConfig()
128
+ return computed(() => blogConfig.value?.home)
129
+ }
130
+
131
+ export function useHomeTagsConfig() {
132
+ const blogConfig = useBlogConfig()
133
+ return computed(() => blogConfig.value?.homeTags)
134
+ }
135
+
81
136
  export function useDocMetaInsertSelector() {
82
137
  const blogConfig = useConfig()
83
138
  const { frontmatter } = useData()
84
- return computed(() => frontmatter.value?.docMetaInsertSelector || blogConfig.config?.blog?.docMetaInsertSelector || 'h1')
139
+ return computed(() => frontmatter.value?.docMetaInsertSelector || blogConfig?.value?.blog?.docMetaInsertSelector || 'h1')
85
140
  }
86
141
 
87
142
  export function useDocMetaInsertPosition() {
88
143
  const blogConfig = useConfig()
89
144
  const { frontmatter } = useData()
90
- return computed(() => frontmatter.value?.docMetaInsertPosition || blogConfig.config?.blog?.docMetaInsertPosition || 'after')
145
+ return computed(() => frontmatter.value?.docMetaInsertPosition || blogConfig?.value?.blog?.docMetaInsertPosition || 'after')
91
146
  }
92
147
 
93
148
  export function useConfig() {
94
- return {
95
- config: inject(configSymbol)!.value
96
- }
149
+ return inject(configSymbol)
97
150
  }
98
151
 
99
152
  export function useBlogConfig() {
100
- return inject(configSymbol)!.value.blog!
153
+ const resolveConfig = useConfig()
154
+ return computed(() => resolveConfig?.value?.blog)
101
155
  }
156
+
157
+ export function useButtonAfterConfig() {
158
+ const blogConfig = useBlogConfig()
159
+ const { frontmatter } = useData()
160
+ const frontmatterConfig = computed(() => frontmatter.value.buttonAfterArticle)
161
+
162
+ const buttonAfterArticleConfig = computed<Theme.ButtonAfterArticleConfig | false>(() => {
163
+ if (frontmatterConfig.value === false || (!frontmatterConfig.value && !blogConfig.value?.buttonAfterArticle)) {
164
+ return false
165
+ }
166
+
167
+ return { ...blogConfig.value?.buttonAfterArticle, ...frontmatterConfig.value }
168
+ })
169
+
170
+ return buttonAfterArticleConfig
171
+ }
172
+
102
173
  /**
103
174
  * 获取 oh-my-live2d的配置选项
104
175
  */
105
176
  export function useOml2dOptions() {
106
- return inject(configSymbol)!.value.blog?.oml2d
177
+ const blogConfig = useBlogConfig()
178
+ return computed(() => blogConfig.value?.oml2d)
107
179
  }
108
180
 
109
181
  export function useDarkTransitionConfig() {
@@ -116,7 +188,16 @@ export function useBlogThemeMode() {
116
188
 
117
189
  export function useArticles() {
118
190
  const blogConfig = useConfig()
119
- const articles = computed(() => blogConfig.config?.blog?.pagesData || [])
191
+ const { localeIndex, site } = useData()
192
+
193
+ const localeKeys = computed(() => Object.keys(site.value.locales))
194
+
195
+ const articles = computed(() => {
196
+ if (localeKeys.value.length === 0) {
197
+ return (blogConfig?.value?.blog?.pagesData || [])
198
+ }
199
+ return blogConfig?.value?.blog?.locales?.[localeIndex.value]?.pagesData || []
200
+ })
120
201
  return articles
121
202
  }
122
203
 
@@ -128,10 +209,9 @@ export function useCurrentPageNum() {
128
209
  }
129
210
 
130
211
  export function useCurrentArticle() {
131
- const blogConfig = useConfig()
132
212
  const route = useRoute()
133
213
 
134
- const docs = computed(() => blogConfig.config?.blog?.pagesData)
214
+ const docs = useArticles()
135
215
  const currentArticle = computed(() => {
136
216
  const currentPath = route.path.replace(/.html$/, '')
137
217
  // 兼容中文路径
@@ -149,16 +229,25 @@ export function useCurrentArticle() {
149
229
  }
150
230
 
151
231
  export function useUserWorks() {
152
- return inject(userWorks)!
232
+ const blogConfig = useBlogConfig()
233
+
234
+ return computed(() => blogConfig.value?.works || {
235
+ title: '',
236
+ description: '',
237
+ list: []
238
+ })
153
239
  }
154
- function resolveConfig(config: Theme.Config): Theme.Config {
155
- return {
240
+ function resolveConfig(config: Theme.Config, locale: string = 'root'): Theme.Config {
241
+ const mergeConfig = {
156
242
  ...config,
157
243
  blog: {
158
244
  ...config?.blog,
159
- pagesData: config?.blog?.pagesData || []
245
+ pagesData: config?.blog?.pagesData || [],
246
+ // i18n 支持
247
+ ...config?.blog?.locales?.[locale]
160
248
  }
161
249
  }
250
+ return mergeConfig
162
251
  }
163
252
 
164
253
  /**
@@ -220,11 +309,36 @@ export function useAutoUpdateAnchor() {
220
309
  }
221
310
 
222
311
  export function useHomeFooterConfig() {
223
- return inject(homeFooter)
312
+ const blogConfig = useBlogConfig()
313
+ return computed(() => blogConfig.value?.footer)
224
314
  }
225
315
 
226
316
  export function useBackToTopConfig() {
227
- return useBlogConfig().backToTop
317
+ const blogConfig = useBlogConfig()
318
+ return computed(() => typeof blogConfig.value?.backToTop === 'boolean' ? undefined : blogConfig.value?.backToTop)
319
+ }
320
+
321
+ export function useOpenBackToTop() {
322
+ const blogConfig = useBlogConfig()
323
+ return computed(() => blogConfig.value?.backToTop !== false)
324
+ }
325
+
326
+ export function useCommentConfig() {
327
+ const blogConfig = useBlogConfig()
328
+ return computed(() => {
329
+ return blogConfig.value?.comment === false ? undefined : blogConfig.value?.comment
330
+ })
331
+ }
332
+
333
+ export function useOpenCommentConfig() {
334
+ const blogConfig = useBlogConfig()
335
+ const { frontmatter } = useData()
336
+ return computed(() => !!blogConfig.value?.comment && frontmatter.value.comment !== false)
337
+ }
338
+
339
+ export function useFriendData() {
340
+ const blogConfig = useBlogConfig()
341
+ return computed(() => blogConfig.value?.friend)
228
342
  }
229
343
 
230
344
  export function useCleanUrls() {
@@ -241,42 +355,42 @@ export function useHomeAnalysis() {
241
355
  }
242
356
 
243
357
  export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<number>) {
244
- const { article } = useBlogConfig()
358
+ const article = computed(() => useConfig()?.value.blog?.article)
245
359
 
246
360
  const topWordCount = computed(() =>
247
- replaceValue(article?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
361
+ replaceValue(article.value?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
248
362
  )
249
363
  const topReadTime = computed(() =>
250
- replaceValue(article?.analyzeTitles?.topReadTime || '预计:{{value}} 分钟', readTime.value)
364
+ replaceValue(article.value?.analyzeTitles?.topReadTime || '预计:{{value}} 分钟', readTime.value)
251
365
  )
252
366
  const inlineWordCount = computed(() =>
253
- replaceValue(article?.analyzeTitles?.inlineWordCount || '{{value}} 个字', wordCount.value)
367
+ replaceValue(article.value?.analyzeTitles?.inlineWordCount || '{{value}} 个字', wordCount.value)
254
368
  )
255
369
  const inlineReadTime = computed(() =>
256
- replaceValue(article?.analyzeTitles?.inlineReadTime || '{{value}} 分钟', readTime.value)
370
+ replaceValue(article.value?.analyzeTitles?.inlineReadTime || '{{value}} 分钟', readTime.value)
257
371
  )
258
372
 
259
373
  const wordCountTitle = computed(() =>
260
- article?.analyzeTitles?.wordCount || '文章字数'
374
+ article.value?.analyzeTitles?.wordCount || '文章字数'
261
375
  )
262
376
  const readTimeTitle = computed(() =>
263
- article?.analyzeTitles?.readTime || '预计阅读时间'
377
+ article.value?.analyzeTitles?.readTime || '预计阅读时间'
264
378
  )
265
379
 
266
380
  const authorTitle = computed(() =>
267
- article?.analyzeTitles?.author || '本文作者'
381
+ article.value?.analyzeTitles?.author || '本文作者'
268
382
  )
269
383
 
270
384
  const publishDateTitle = computed(() =>
271
- article?.analyzeTitles?.publishDate || '发布时间'
385
+ article.value?.analyzeTitles?.publishDate || '发布时间'
272
386
  )
273
387
 
274
388
  const lastUpdatedTitle = computed(() =>
275
- article?.analyzeTitles?.lastUpdated || '最近修改时间'
389
+ article.value?.analyzeTitles?.lastUpdated || '最近修改时间'
276
390
  )
277
391
 
278
392
  const tagTitle = computed(() =>
279
- article?.analyzeTitles?.tag || '标签'
393
+ article.value?.analyzeTitles?.tag || '标签'
280
394
  )
281
395
 
282
396
  return {
@@ -295,48 +409,50 @@ export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<n
295
409
 
296
410
  export function useFormatShowDate() {
297
411
  const blog = useBlogConfig()
298
- if (typeof blog.formatShowDate === 'function') {
299
- return blog.formatShowDate
300
- }
301
-
302
- function formatShowDate(date: any) {
303
- const source = +new Date(date)
304
- const now = +new Date()
305
- const diff = now - source
306
- const oneSeconds = 1000
307
- const oneMinute = oneSeconds * 60
308
- const oneHour = oneMinute * 60
309
- const oneDay = oneHour * 24
310
- const oneWeek = oneDay * 7
311
-
312
- const langMap = {
313
- justNow: '刚刚',
314
- secondsAgo: '秒前',
315
- minutesAgo: '分钟前',
316
- hoursAgo: '小时前',
317
- daysAgo: '天前',
318
- weeksAgo: '周前',
319
- ...blog.formatShowDate
412
+ return computed(() => {
413
+ if (typeof blog.value?.formatShowDate === 'function') {
414
+ return blog.value.formatShowDate
320
415
  }
321
- const mapValue = langMap
322
416
 
323
- if (diff < 10) {
324
- return mapValue.justNow
325
- }
326
- if (diff < oneMinute) {
327
- return `${Math.floor(diff / oneSeconds)}${mapValue.secondsAgo}`
328
- }
329
- if (diff < oneHour) {
330
- return `${Math.floor(diff / oneMinute)}${mapValue.minutesAgo}`
331
- }
332
- if (diff < oneDay) {
333
- return `${Math.floor(diff / oneHour)}${mapValue.hoursAgo}`
334
- }
335
- if (diff < oneWeek) {
336
- return `${Math.floor(diff / oneDay)}${mapValue.daysAgo}`
337
- }
417
+ function formatShowDate(date: any) {
418
+ const source = +new Date(date)
419
+ const now = +new Date()
420
+ const diff = now - source
421
+ const oneSeconds = 1000
422
+ const oneMinute = oneSeconds * 60
423
+ const oneHour = oneMinute * 60
424
+ const oneDay = oneHour * 24
425
+ const oneWeek = oneDay * 7
426
+
427
+ const langMap = {
428
+ justNow: '刚刚',
429
+ secondsAgo: '秒前',
430
+ minutesAgo: '分钟前',
431
+ hoursAgo: '小时前',
432
+ daysAgo: '天前',
433
+ weeksAgo: '周前',
434
+ ...blog.value?.formatShowDate
435
+ }
436
+ const mapValue = langMap
338
437
 
339
- return formatDate(new Date(date), 'yyyy-MM-dd')
340
- }
341
- return formatShowDate
438
+ if (diff < 10) {
439
+ return mapValue.justNow
440
+ }
441
+ if (diff < oneMinute) {
442
+ return `${Math.floor(diff / oneSeconds)}${mapValue.secondsAgo}`
443
+ }
444
+ if (diff < oneHour) {
445
+ return `${Math.floor(diff / oneMinute)}${mapValue.minutesAgo}`
446
+ }
447
+ if (diff < oneDay) {
448
+ return `${Math.floor(diff / oneHour)}${mapValue.hoursAgo}`
449
+ }
450
+ if (diff < oneWeek) {
451
+ return `${Math.floor(diff / oneDay)}${mapValue.daysAgo}`
452
+ }
453
+
454
+ return formatDate(new Date(date), 'yyyy-MM-dd')
455
+ }
456
+ return formatShowDate
457
+ })
342
458
  }
@@ -349,6 +349,7 @@ export namespace Theme {
349
349
  | 'el-red'
350
350
  export interface BlogConfig {
351
351
  blog?: false
352
+ locales?: Record<string, Omit<BlogConfig, 'locales' | 'pagesData' | 'search' | 'popover' | 'RSS'> & { pagesData?: PageData[] }>
352
353
  /**
353
354
  * 展示日期格式化
354
355
  */
@@ -360,6 +361,11 @@ export namespace Theme {
360
361
  */
361
362
  themeColor?: ThemeColor
362
363
  pagesData: PageData[]
364
+ /**
365
+ * @deprecated 请使用 VitePress 官方 srcDir 替代
366
+ *
367
+ * @doc https://vitepress.dev/zh/reference/site-config#srcdir
368
+ */
363
369
  srcDir?: string
364
370
  author?: string
365
371
  hotArticle?: HotArticle | false
@@ -459,6 +465,9 @@ export namespace Theme {
459
465
  * 渲染时替换图片地址
460
466
  */
461
467
  imageStyle?: ImageStyleConfig
468
+ groupIcon?: {
469
+ customIcon: Record<string, string>
470
+ }
462
471
  }
463
472
 
464
473
  export type FormatShowDate = {
@@ -1,4 +1,4 @@
1
- import { onMounted } from 'vue'
1
+ import { onMounted, watch } from 'vue'
2
2
  import type { Options } from 'oh-my-live2d'
3
3
  import { useOml2dOptions } from '../composables/config/blog'
4
4
 
@@ -37,13 +37,13 @@ const defaultOptions: Options = {
37
37
  }
38
38
  export function useOml2d() {
39
39
  const oml2dOptions = useOml2dOptions()
40
- onMounted(async () => {
41
- if (oml2dOptions) {
40
+ const init = async () => {
41
+ if (oml2dOptions.value) {
42
42
  const { loadOml2d } = await import('oh-my-live2d')
43
43
  loadOml2d({
44
44
  ...defaultOptions,
45
45
  ...oml2dOptions,
46
- models: oml2dOptions?.models?.map(model => ({
46
+ models: oml2dOptions?.value?.models?.map(model => ({
47
47
  ...defaultModelOptions,
48
48
  ...model,
49
49
  stageStyle: {
@@ -57,27 +57,34 @@ export function useOml2d() {
57
57
  })),
58
58
  tips: {
59
59
  ...defaultOptions.tips,
60
- ...oml2dOptions.tips,
60
+ ...oml2dOptions?.value.tips,
61
61
  style: {
62
62
  // @ts-expect-error
63
63
  ...defaultOptions?.tips?.style,
64
64
  // @ts-expect-error
65
- ...oml2dOptions?.tips?.style
65
+ ...oml2dOptions?.value.tips?.style
66
66
  },
67
67
  mobileStyle: {
68
68
  // @ts-expect-error
69
69
  ...defaultOptions?.tips?.mobileStyle,
70
70
  // @ts-expect-error
71
- ...oml2dOptions?.tips?.mobileStyle
71
+ ...oml2dOptions?.value.tips?.mobileStyle
72
72
  },
73
73
  copyTips: {
74
74
  // @ts-expect-error
75
75
  ...defaultOptions?.tips?.copyTips,
76
76
  // @ts-expect-error
77
- ...oml2dOptions?.tips?.copyTips
77
+ ...oml2dOptions?.value.tips?.copyTips
78
78
  }
79
79
  }
80
80
  })
81
81
  }
82
+ }
83
+ // TODO: destroy
84
+ // watch(oml2dOptions, () => {
85
+ // init()
86
+ // })
87
+ onMounted(() => {
88
+ init()
82
89
  })
83
90
  }
package/src/index.ts CHANGED
@@ -35,6 +35,9 @@ import UserWorksPage from './components/UserWorks.vue'
35
35
  // 内置一些特殊的主题色
36
36
  import './styles/theme/inline-theme.var.css'
37
37
 
38
+ // 导入group icons
39
+ import 'virtual:group-icons.css'
40
+
38
41
  export const BlogTheme: Theme = {
39
42
  ...DefaultTheme,
40
43
  Layout: withConfigProvider(BlogApp),
package/src/node.ts CHANGED
@@ -46,7 +46,7 @@ export function getThemeConfig(cfg: Partial<Theme.BlogConfig> = {}) {
46
46
  return {
47
47
  themeConfig: {
48
48
  blog: {
49
- pagesData,
49
+ pagesData, // 插件里补全
50
50
  ...cfg
51
51
  },
52
52
  // 补充一些额外的配置用于继承
@@ -63,6 +63,10 @@ export function defineConfig(config: UserConfig<Theme.Config>): any {
63
63
  return config
64
64
  }
65
65
 
66
+ export function defineLocaleConfig(cfg: Omit<Theme.BlogConfig, 'locales' | 'pagesData'>) {
67
+ return cfg
68
+ }
69
+
66
70
  // 重新导包 tabsMarkdownPlugin 导出CJS格式支持
67
71
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
68
72