@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 +17 -2
- package/node.js +13 -1
- package/package.json +3 -3
- package/src/components/BlogFooter.vue +40 -16
- package/src/components/BlogHotArticle.vue +7 -3
- package/src/components/BlogItem.vue +5 -14
- package/src/components/BlogRecommendArticle.vue +5 -3
- package/src/composables/config/blog.ts +5 -0
- package/src/composables/config/index.ts +15 -1
- package/src/index.ts +3 -1
- package/src/node.ts +12 -0
- package/src/utils/client/index.ts +5 -0
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
<
|
|
77
|
-
<
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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="
|
|
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>
|
|
@@ -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'
|
|
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
|
+
}
|