@sugarat/theme 0.2.27 → 0.2.29

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
@@ -412,11 +412,24 @@ declare namespace Theme {
412
412
  liClass?: string;
413
413
  }
414
414
  type RSSOptions = RSSPluginOptions;
415
+ interface FooterItem {
416
+ text: string;
417
+ link?: string;
418
+ icon?: boolean | string;
419
+ }
415
420
  interface Footer {
416
421
  /**
417
- * 自定义补充信息(支持配置为HTML
422
+ * 自定义补充信息(支持配置为HTML),在内置的 footer 上方
418
423
  */
419
424
  message?: string | string[];
425
+ /**
426
+ * 自定义补充信息(支持配置为HTML),在内置的 footer 下方
427
+ */
428
+ bottomMessage?: string | string[];
429
+ /**
430
+ * 自定义补充信息(支持配置为HTML),紧随内置的后方
431
+ */
432
+ list?: string | string[] | FooterItem | FooterItem[];
420
433
  /**
421
434
  * 是否展示主题版本信息
422
435
  */
@@ -468,4 +481,6 @@ declare function getThemeConfig(cfg?: Partial<Theme.BlogConfig>): any;
468
481
  */
469
482
  declare function defineConfig(config: UserConfig<Theme.Config>): any;
470
483
 
471
- export { defineConfig, getThemeConfig };
484
+ declare function footerHTML(footerData: Theme.FooterItem | Theme.FooterItem[]): string;
485
+
486
+ export { defineConfig, footerHTML, getThemeConfig };
package/node.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var node_exports = {};
32
32
  __export(node_exports, {
33
33
  defineConfig: () => defineConfig,
34
+ footerHTML: () => footerHTML,
34
35
  getThemeConfig: () => getThemeConfig,
35
36
  tabsMarkdownPlugin: () => tabsPlugin
36
37
  });
@@ -39,7 +40,7 @@ module.exports = __toCommonJS(node_exports);
39
40
  // src/utils/node/mdPlugins.ts
40
41
  var import_module = require("module");
41
42
 
42
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.0.0-rc.45_vue@3.4.21/node_modules/vitepress-plugin-tabs/dist/index.js
43
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.0.1_vue@3.4.21/node_modules/vitepress-plugin-tabs/dist/index.js
43
44
  var tabsMarker = "=tabs";
44
45
  var tabsMarkerLen = tabsMarker.length;
45
46
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
@@ -588,9 +589,20 @@ function getThemeConfig(cfg) {
588
589
  function defineConfig(config) {
589
590
  return config;
590
591
  }
592
+ function footerHTML(footerData) {
593
+ const data = [footerData || []].flat();
594
+ return data.map((d) => {
595
+ const { icon, text, link } = d;
596
+ return `<span class="footer-item">
597
+ ${icon ? `<i>${icon}</i>` : ""}
598
+ ${link ? `<a href="${link}" target="_blank" rel="noopener noreferrer">${text}</a>` : `<span>${text}</span>`}
599
+ </span>`;
600
+ }).join("");
601
+ }
591
602
  // Annotate the CommonJS export names for ESM import in node:
592
603
  0 && (module.exports = {
593
604
  defineConfig,
605
+ footerHTML,
594
606
  getThemeConfig,
595
607
  tabsMarkdownPlugin
596
608
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.2.27",
3
+ "version": "0.2.29",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
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.10.0",
45
+ "oh-my-live2d": "^0.13.0",
46
46
  "swiper": "^11.0.5",
47
47
  "vitepress-markdown-timeline": "^1.2.1",
48
48
  "vitepress-plugin-mermaid": "2.0.13",
@@ -57,7 +57,7 @@
57
57
  "pagefind": "1.0.3",
58
58
  "sass": "^1.56.1",
59
59
  "typescript": "^4.8.2",
60
- "vitepress": "1.0.0-rc.45",
60
+ "vitepress": "1.0.1",
61
61
  "vue": "^3.4.21"
62
62
  },
63
63
  "scripts": {
@@ -3,6 +3,7 @@ import { computed } from 'vue'
3
3
  import { useHomeFooterConfig } from '../composables/config/blog'
4
4
  import packageJSON from '../../package.json'
5
5
  import { copyrightSVG, icpSVG, themeSVG } from '../constants/svg'
6
+ import { vOuterHtml } from '../directives'
6
7
 
7
8
  const footerData = useHomeFooterConfig()
8
9
 
@@ -12,14 +13,15 @@ const renderData = computed(() => {
12
13
  }
13
14
  const flatData = [footerData].flat()
14
15
  return flatData.flat().map((footer, idx) => {
15
- const { icpRecord, securityRecord, copyright, version, message } = footer
16
- const data: {
16
+ const { icpRecord, securityRecord, copyright, version, message, bottomMessage, list } = footer
17
+ const data: ({
17
18
  name: string
18
19
  link?: string
19
20
  icon?: string | boolean
20
- }[] = []
21
+ } | string) [] = []
21
22
  // message
22
- const messageData: string[] = [message || []].flat()
23
+ const messageData = [message || []].flat()
24
+ const bottomMessageData = [bottomMessage || []].flat()
23
25
 
24
26
  // version
25
27
  const isLast = idx === flatData.length - 1
@@ -58,9 +60,23 @@ const renderData = computed(() => {
58
60
  ...securityRecord
59
61
  })
60
62
  }
63
+ if (list) {
64
+ const listData = [list || []].flat()
65
+ data.push(...listData.map((v) => {
66
+ if (typeof v === 'string') {
67
+ return v
68
+ }
69
+ return {
70
+ name: v.text,
71
+ icon: v.icon,
72
+ link: v.link
73
+ }
74
+ }))
75
+ }
61
76
  return {
62
77
  data,
63
- messageData
78
+ messageData,
79
+ bottomMessageData
64
80
  }
65
81
  })
66
82
  })
@@ -70,20 +86,27 @@ const renderData = computed(() => {
70
86
  <footer v-if="renderData.length" class="blog-footer">
71
87
  <!-- eslint-disable vue/require-v-for-key -->
72
88
  <!-- see https://cn.vuejs.org/guide/essentials/list.html#v-for-on-template -->
73
- <template v-for="({ data, messageData }) in renderData">
89
+ <template v-for="({ data, messageData, bottomMessageData }) in renderData">
90
+ <!-- 在内置footer上方渲染 -->
74
91
  <p v-for="message in messageData" v-html="message" />
92
+ <!-- 内置的列表 -->
75
93
  <p class="footer-item-list">
76
- <span v-for="item in data" class="footer-item">
77
- <i v-if="item.icon === 'security'">
78
- <img src="./../styles/gongan.png" alt="公网安备">
79
- </i>
80
- <i v-else-if="item.icon" v-html="item.icon" />
81
- <a v-if="item.link" :href="item.link" target="_blank" rel="noopener noreferrer">
82
- {{ item.name }}
83
- </a>
84
- <span v-else>{{ item.name }}</span>
85
- </span>
94
+ <template v-for="item in data">
95
+ <span v-if="typeof item !== 'string'" class="footer-item">
96
+ <i v-if="item.icon === 'security'">
97
+ <img src="./../styles/gongan.png" alt="公网安备">
98
+ </i>
99
+ <i v-else-if="item.icon" v-html="item.icon" />
100
+ <a v-if="item.link" :href="item.link" target="_blank" rel="noopener noreferrer">
101
+ {{ item.name }}
102
+ </a>
103
+ <span v-else>{{ item.name }}</span>
104
+ </span>
105
+ <span v-else v-outer-html="item" />
106
+ </template>
86
107
  </p>
108
+ <!-- 在内置的footer下方渲染 -->
109
+ <p v-for="message in bottomMessageData" v-html="message" />
87
110
  </template>
88
111
  </footer>
89
112
  </template>
@@ -124,6 +147,7 @@ footer.blog-footer {
124
147
 
125
148
  i {
126
149
  margin-right: 4px;
150
+ font-style: normal;
127
151
  }
128
152
 
129
153
  i :deep(svg) {
@@ -2,8 +2,8 @@
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
9
  const { hotArticle } = useBlogConfig()
@@ -27,10 +27,14 @@ function changePage() {
27
27
  currentPage.value = newIdx + 1
28
28
  }
29
29
 
30
+ const cleanUrls = useCleanUrls()
30
31
  const currentWikiData = computed(() => {
31
32
  const startIdx = (currentPage.value - 1) * pageSize.value
32
33
  const endIdx = startIdx + pageSize.value
33
- return recommendList.value.slice(startIdx, endIdx)
34
+ return recommendList.value.slice(startIdx, endIdx).map(v => ({
35
+ ...v,
36
+ route: wrapperCleanUrls(cleanUrls, v.route)
37
+ }))
34
38
  })
35
39
 
36
40
  const showChangeBtn = computed(() => {
@@ -2,7 +2,8 @@
2
2
  import { withBase } from 'vitepress'
3
3
  import { computed } from 'vue'
4
4
  import { useWindowSize } from '@vueuse/core'
5
- import { formatShowDate } from '../utils/client'
5
+ import { formatShowDate, wrapperCleanUrls } from '../utils/client'
6
+ import { useCleanUrls } from '../composables/config/blog'
6
7
 
7
8
  const props = defineProps<{
8
9
  route: string
@@ -22,22 +23,12 @@ const showTime = computed(() => {
22
23
  return formatShowDate(props.date)
23
24
  })
24
25
 
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
- // }
26
+ const cleanUrls = useCleanUrls()
27
+ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route)))
37
28
  </script>
38
29
 
39
30
  <template>
40
- <a class="blog-item" :href="withBase(route)">
31
+ <a class="blog-item" :href="link">
41
32
  <i v-if="!!pin" class="pin" />
42
33
  <!-- 标题 -->
43
34
  <p v-if="inMobile" class="title">{{ title }}</p>
@@ -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
+ }
@@ -450,11 +450,25 @@ export namespace Theme {
450
450
 
451
451
  export type RSSOptions = RSSPluginOptions
452
452
 
453
+ export interface FooterItem {
454
+ text: string
455
+ link?: string
456
+ icon?: boolean | string
457
+ }
458
+
453
459
  export interface Footer {
454
460
  /**
455
- * 自定义补充信息(支持配置为HTML
461
+ * 自定义补充信息(支持配置为HTML),在内置的 footer 上方
456
462
  */
457
463
  message?: string | string[]
464
+ /**
465
+ * 自定义补充信息(支持配置为HTML),在内置的 footer 下方
466
+ */
467
+ bottomMessage?: string | string[]
468
+ /**
469
+ * 自定义补充信息(支持配置为HTML),紧随内置的后方
470
+ */
471
+ list?: string | string[] | FooterItem | FooterItem[]
458
472
  /**
459
473
  * 是否展示主题版本信息
460
474
  */
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
 
package/src/node.ts CHANGED
@@ -56,3 +56,15 @@ export function defineConfig(config: UserConfig<Theme.Config>): any {
56
56
 
57
57
  // 重新导包 tabsMarkdownPlugin 导出CJS格式支持
58
58
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
59
+
60
+ export function footerHTML(footerData: Theme.FooterItem | Theme.FooterItem[]) {
61
+ const data = [footerData || []].flat()
62
+ return data.map((d) => {
63
+ const { icon, text, link } = d
64
+
65
+ return `<span class="footer-item">
66
+ ${icon ? `<i>${icon}</i>` : ''}
67
+ ${link ? `<a href="${link}" target="_blank" rel="noopener noreferrer">${text}</a>` : `<span>${text}</span>`}
68
+ </span>`
69
+ }).join('')
70
+ }
@@ -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
+ }