valaxy-theme-yun 0.1.2 → 0.2.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/YunCategories.vue +8 -3
- package/components/YunCategory.vue +24 -12
- package/components/YunConfig.vue +2 -10
- package/components/YunOverview.vue +113 -0
- package/components/YunPostMeta.vue +23 -1
- package/components/YunSidebar.vue +24 -96
- package/components/YunSidebarNav.vue +4 -3
- package/components/YunSponsor.vue +1 -4
- package/components/YunToggleLocale.vue +25 -0
- package/components/YunTwikoo.vue +14 -0
- package/components/YunWaline.vue +1 -3
- package/composables/index.ts +2 -1
- package/config/index.ts +48 -0
- package/dist/index.d.ts +20 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/index.ts +1 -1
- package/layouts/archives.vue +4 -2
- package/layouts/base.vue +13 -6
- package/layouts/categories.vue +4 -2
- package/layouts/home.vue +6 -1
- package/layouts/post.vue +5 -3
- package/layouts/tags.vue +8 -3
- package/package.json +2 -2
- package/styles/layout/post.scss +6 -13
- package/styles/markdown.scss +18 -0
@@ -1,21 +1,26 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import type { Categories } from 'valaxy'
|
3
|
+
import { ref } from 'vue'
|
3
4
|
|
4
|
-
withDefaults(defineProps<{
|
5
|
+
const props = withDefaults(defineProps<{
|
5
6
|
categories: Categories
|
6
7
|
/**
|
7
8
|
* 当前层级
|
8
9
|
*/
|
9
10
|
level?: number
|
10
|
-
displayCategory
|
11
|
+
displayCategory?: (category: string) => void
|
12
|
+
collapsable?: boolean
|
11
13
|
}>(), {
|
12
14
|
level: 0,
|
15
|
+
collapsable: true,
|
13
16
|
})
|
17
|
+
|
18
|
+
const collapsable = ref(props.collapsable)
|
14
19
|
</script>
|
15
20
|
|
16
21
|
<template>
|
17
22
|
<ul v-for="category, key in Object.fromEntries(categories)" :key="key" class="category-list" m="l-4">
|
18
|
-
<YunCategory :name="key.toString()" :category="category" :level="level + 1" :display-category="displayCategory" />
|
23
|
+
<YunCategory :name="key.toString()" :category="category" :level="level + 1" :display-category="displayCategory" :collapsable="collapsable" />
|
19
24
|
</ul>
|
20
25
|
</template>
|
21
26
|
|
@@ -1,39 +1,51 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { ref } from 'vue'
|
3
|
-
import type { Category, ParentCategory, PostCategory } from 'valaxy'
|
3
|
+
import type { Category, ParentCategory, Post, PostCategory } from 'valaxy'
|
4
4
|
import { useI18n } from 'vue-i18n'
|
5
5
|
|
6
|
-
defineProps<{
|
6
|
+
const props = withDefaults(defineProps<{
|
7
7
|
name: string
|
8
8
|
// to eliminate the warning
|
9
9
|
category: Category
|
10
10
|
level?: number
|
11
|
-
displayCategory
|
12
|
-
}>()
|
11
|
+
displayCategory?: (category: string) => void
|
13
12
|
|
14
|
-
|
15
|
-
|
13
|
+
/**
|
14
|
+
* collapse children
|
15
|
+
*/
|
16
|
+
collapsable?: boolean
|
17
|
+
}>(), {
|
18
|
+
collapsable: true,
|
19
|
+
})
|
20
|
+
|
21
|
+
const collapsable = ref(props.collapsable)
|
22
|
+
const { t, locale } = useI18n()
|
23
|
+
const lang = locale.value === 'zh-CN' ? 'zh' : locale.value
|
24
|
+
|
25
|
+
const getTitle = (post: Post | any) => {
|
26
|
+
return post[`title_${lang}`] ? post[`title_${lang}`] : post.title
|
27
|
+
}
|
16
28
|
</script>
|
17
29
|
|
18
30
|
<template>
|
19
31
|
<li v-if="category.total" class="category-list-item inline-flex items-center cursor-pointer">
|
20
|
-
<span class="folder-action inline-flex" @click="
|
21
|
-
<div v-if="
|
32
|
+
<span class="folder-action inline-flex" @click="collapsable = !collapsable">
|
33
|
+
<div v-if="collapsable" i-ri-folder-add-line />
|
22
34
|
<div v-else style="color:var(--va-c-primary)" i-ri-folder-reduce-line /></span>
|
23
|
-
<span class="category-name" m="l-1" @click="displayCategory(name)">
|
35
|
+
<span class="category-name" m="l-1" @click="displayCategory ? displayCategory(name) : null">
|
24
36
|
{{ name === 'Uncategorized' ? t('category.uncategorized') : name }} [{{ category.total }}]
|
25
37
|
</span>
|
26
38
|
</li>
|
27
39
|
|
28
|
-
<template v-if="
|
40
|
+
<template v-if="!collapsable">
|
29
41
|
<ul v-if="(category as PostCategory).posts">
|
30
42
|
<li v-for="post, i in (category as PostCategory).posts" :key="i" class="post-list-item" m="l-4">
|
31
43
|
<router-link v-if="post.title" :to="post.path" class="inline-flex items-center">
|
32
44
|
<div i-ri-file-text-line />
|
33
|
-
<span m="l-1" font="serif black">{{ post
|
45
|
+
<span m="l-1" font="serif black">{{ getTitle(post) }}</span>
|
34
46
|
</router-link>
|
35
47
|
</li>
|
36
48
|
</ul>
|
37
|
-
<YunCategories v-else :categories="(category as ParentCategory).children" :display-category="displayCategory" />
|
49
|
+
<YunCategories v-else :categories="(category as ParentCategory).children" :display-category="displayCategory" :collapsable="collapsable" />
|
38
50
|
</template>
|
39
51
|
</template>
|
package/components/YunConfig.vue
CHANGED
@@ -2,13 +2,7 @@
|
|
2
2
|
import { useI18n } from 'vue-i18n'
|
3
3
|
import { isDark, toggleDark } from '~/composables'
|
4
4
|
|
5
|
-
const { t
|
6
|
-
|
7
|
-
const toggleLocales = () => {
|
8
|
-
// change to some real logic
|
9
|
-
const locales = availableLocales
|
10
|
-
locale.value = locales[(locales.indexOf(locale.value) + 1) % locales.length]
|
11
|
-
}
|
5
|
+
const { t } = useI18n()
|
12
6
|
</script>
|
13
7
|
|
14
8
|
<template>
|
@@ -17,8 +11,6 @@ const toggleLocales = () => {
|
|
17
11
|
<div i="ri-sun-line dark:ri-moon-line" />
|
18
12
|
</button>
|
19
13
|
|
20
|
-
<
|
21
|
-
<div i-ri-translate class="transition transform" :class="locale === 'en' ? 'rotate-y-180' : ''" />
|
22
|
-
</button>
|
14
|
+
<YunToggleLocale />
|
23
15
|
</div>
|
24
16
|
</template>
|
@@ -0,0 +1,113 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { useConfig } from 'valaxy'
|
3
|
+
import { useRouter } from 'vue-router'
|
4
|
+
|
5
|
+
const config = useConfig()
|
6
|
+
const router = useRouter()
|
7
|
+
</script>
|
8
|
+
|
9
|
+
<template>
|
10
|
+
<div class="sidebar-panel">
|
11
|
+
<div class="site-info" m="t-6">
|
12
|
+
<a class="site-author-avatar" href="/about">
|
13
|
+
<img class="rounded-full" :src="config.author.avatar" alt="avatar">
|
14
|
+
<span class="site-author-status">{{ config.author.status.emoji }}</span>
|
15
|
+
</a>
|
16
|
+
<div class="site-author-name">
|
17
|
+
<a href="/about">
|
18
|
+
{{ config.author.name }}
|
19
|
+
</a>
|
20
|
+
</div>
|
21
|
+
<router-link v-if="router.hasRoute('about-site')" to="/about/site" class="site-name">
|
22
|
+
{{ config.title }}
|
23
|
+
</router-link>
|
24
|
+
<span v-else class="site-name">{{ config.title }}</span>
|
25
|
+
<h4 v-if="config.subtitle" class="site-subtitle block" text="xs">
|
26
|
+
{{ config.subtitle }}
|
27
|
+
</h4>
|
28
|
+
<div v-if="config.description" class="site-description my-1">
|
29
|
+
{{ config.description }}
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<YunSidebarNav />
|
34
|
+
<hr m="t-4 b-2">
|
35
|
+
<YunSocialLinks />
|
36
|
+
<hr m="y-2">
|
37
|
+
<YunSidebarLinks />
|
38
|
+
<br>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<YunConfig />
|
42
|
+
</template>
|
43
|
+
|
44
|
+
<style lang="scss">
|
45
|
+
@use "~/styles/mixins" as *;
|
46
|
+
|
47
|
+
.sidebar-panel {
|
48
|
+
padding: 0.5rem;
|
49
|
+
}
|
50
|
+
|
51
|
+
.site-info {
|
52
|
+
&.fix-top {
|
53
|
+
margin-top: -1.5rem;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
.site-author-avatar {
|
58
|
+
display: inline-block;
|
59
|
+
line-height: 0;
|
60
|
+
position: relative;
|
61
|
+
|
62
|
+
img {
|
63
|
+
height: 96px;
|
64
|
+
width: 96px;
|
65
|
+
max-width: 100%;
|
66
|
+
margin: 0px;
|
67
|
+
padding: 4px;
|
68
|
+
background-color: white;
|
69
|
+
box-shadow: 0 0 10px rgba(black, 0.2);
|
70
|
+
transition: 0.4s;
|
71
|
+
|
72
|
+
&:hover {
|
73
|
+
box-shadow: 0 0 30px rgba(var(--va-c-primary-rgb), 0.2);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
.site-author-name {
|
79
|
+
margin-top: 0;
|
80
|
+
margin-bottom: 1rem;
|
81
|
+
line-height: 1.5;
|
82
|
+
}
|
83
|
+
|
84
|
+
.site-author-status {
|
85
|
+
position: absolute;
|
86
|
+
height: 1.8rem;
|
87
|
+
width: 1.8rem;
|
88
|
+
bottom: 0;
|
89
|
+
right: 0;
|
90
|
+
line-height: 1.8rem;
|
91
|
+
border-radius: 50%;
|
92
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
93
|
+
background-color: var(--va-c-bg-light);
|
94
|
+
|
95
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
96
|
+
}
|
97
|
+
|
98
|
+
.site-name {
|
99
|
+
color: var(--va-c-text);
|
100
|
+
font-family: get-css-var('font-serif');
|
101
|
+
font-weight: get-css-var('font-serif-weight');
|
102
|
+
}
|
103
|
+
|
104
|
+
.site-subtitle {
|
105
|
+
color: get-css-var('c-gray');
|
106
|
+
display: block;
|
107
|
+
}
|
108
|
+
|
109
|
+
.site-description {
|
110
|
+
color: var(--va-c-text);
|
111
|
+
font-size: 0.8rem;
|
112
|
+
}
|
113
|
+
</style>
|
@@ -11,6 +11,13 @@ defineProps<{
|
|
11
11
|
</script>
|
12
12
|
|
13
13
|
<template>
|
14
|
+
<div v-if="frontmatter.draft" class="post-draft-icon" title="draft">
|
15
|
+
<div i-ri-draft-line />
|
16
|
+
</div>
|
17
|
+
<div v-if="frontmatter.top" class="post-top-icon">
|
18
|
+
<div i-ri-pushpin-line />
|
19
|
+
</div>
|
20
|
+
|
14
21
|
<div v-if="frontmatter" class="post-meta justify-center" flex="~" text="sm">
|
15
22
|
<div v-if="frontmatter.date" class="post-time flex items-center">
|
16
23
|
<div class="inline-block" i-ri-calendar-line />
|
@@ -25,5 +32,20 @@ defineProps<{
|
|
25
32
|
</div>
|
26
33
|
</template>
|
27
34
|
|
28
|
-
<style
|
35
|
+
<style>
|
36
|
+
.post-draft-icon {
|
37
|
+
position: absolute;
|
38
|
+
top: 1rem;
|
39
|
+
left: 1rem;
|
40
|
+
color: var(--va-c-gray);
|
41
|
+
font-size: 1.2rem;
|
42
|
+
}
|
43
|
+
|
44
|
+
.post-top-icon {
|
45
|
+
position: absolute;
|
46
|
+
top: 1rem;
|
47
|
+
right: 1rem;
|
48
|
+
color: var(--va-c-warning);
|
49
|
+
font-size: 1.2rem;
|
50
|
+
}
|
29
51
|
</style>
|
@@ -1,111 +1,39 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
3
|
-
|
4
|
-
|
5
|
-
const config = useConfig()
|
6
|
-
const router = useRouter()
|
2
|
+
import { ref } from 'vue'
|
3
|
+
const showOverview = ref(false)
|
7
4
|
</script>
|
8
5
|
|
9
6
|
<template>
|
10
|
-
<div class="sidebar-
|
11
|
-
<
|
12
|
-
<
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
{{ config.title }}
|
23
|
-
</router-link>
|
24
|
-
<span v-else class="site-name">{{ config.title }}</span>
|
25
|
-
<h4 v-if="config.subtitle" class="site-subtitle block" text="xs">
|
26
|
-
{{ config.subtitle }}
|
27
|
-
</h4>
|
28
|
-
<div v-if="config.description" class="site-description my-1">
|
29
|
-
{{ config.description }}
|
30
|
-
</div>
|
31
|
-
</div>
|
7
|
+
<div v-if="$slots.default" class="sidebar-nav" m="t-6">
|
8
|
+
<button m="x-4" class="sidebar-nav-item yun-icon-btn" :class="showOverview && 'active'" @click="showOverview = true">
|
9
|
+
<div i-ri-passport-line />
|
10
|
+
</button>
|
11
|
+
<button m="x-4" class="sidebar-nav-item yun-icon-btn" :class="!showOverview && 'active'" @click="showOverview = false">
|
12
|
+
<div i-ri-list-ordered />
|
13
|
+
</button>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div v-if="showOverview || !$slots.default" :class="$slots.default && '-mt-4'">
|
17
|
+
<YunOverview />
|
18
|
+
</div>
|
32
19
|
|
33
|
-
|
34
|
-
<
|
35
|
-
<YunSocialLinks />
|
36
|
-
<hr m="y-2">
|
37
|
-
<YunSidebarLinks />
|
38
|
-
<br>
|
20
|
+
<div v-else>
|
21
|
+
<slot />
|
39
22
|
</div>
|
40
23
|
</template>
|
41
24
|
|
42
25
|
<style lang="scss">
|
43
|
-
|
26
|
+
.sidebar-nav {
|
27
|
+
.sidebar-nav-item {
|
28
|
+
color: var(--va-c-primary);
|
29
|
+
border: 1px solid var(--va-c-primary);
|
44
30
|
|
45
|
-
|
46
|
-
|
47
|
-
}
|
31
|
+
&.active {
|
32
|
+
border: 1px solid var(--va-c-primary);
|
48
33
|
|
49
|
-
|
50
|
-
|
51
|
-
margin-top: -1.5rem;
|
52
|
-
}
|
53
|
-
}
|
54
|
-
|
55
|
-
.site-author-avatar {
|
56
|
-
display: inline-block;
|
57
|
-
line-height: 0;
|
58
|
-
position: relative;
|
59
|
-
|
60
|
-
img {
|
61
|
-
height: 96px;
|
62
|
-
width: 96px;
|
63
|
-
max-width: 100%;
|
64
|
-
margin: 0px;
|
65
|
-
padding: 4px;
|
66
|
-
background-color: white;
|
67
|
-
box-shadow: 0 0 10px rgba(black, 0.2);
|
68
|
-
transition: 0.4s;
|
69
|
-
|
70
|
-
&:hover {
|
71
|
-
box-shadow: 0 0 30px rgba(var(--va-c-primary-rgb), 0.2);
|
34
|
+
color: white;
|
35
|
+
background-color: var(--va-c-primary);
|
72
36
|
}
|
73
37
|
}
|
74
38
|
}
|
75
|
-
|
76
|
-
.site-author-name {
|
77
|
-
margin-top: 0;
|
78
|
-
margin-bottom: 1rem;
|
79
|
-
line-height: 1.5;
|
80
|
-
}
|
81
|
-
|
82
|
-
.site-author-status {
|
83
|
-
position: absolute;
|
84
|
-
height: 1.8rem;
|
85
|
-
width: 1.8rem;
|
86
|
-
bottom: 0;
|
87
|
-
right: 0;
|
88
|
-
line-height: 1.8rem;
|
89
|
-
border-radius: 50%;
|
90
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
91
|
-
background-color: var(--va-c-bg-light);
|
92
|
-
|
93
|
-
border: 1px solid rgba(255, 255, 255, 0.1);
|
94
|
-
}
|
95
|
-
|
96
|
-
.site-name {
|
97
|
-
color: var(--va-c-text);
|
98
|
-
font-family: get-css-var('font-serif');
|
99
|
-
font-weight: get-css-var('font-serif-weight');
|
100
|
-
}
|
101
|
-
|
102
|
-
.site-subtitle {
|
103
|
-
color: get-css-var('c-gray');
|
104
|
-
display: block;
|
105
|
-
}
|
106
|
-
|
107
|
-
.site-description {
|
108
|
-
color: var(--va-c-text);
|
109
|
-
font-size: 0.8rem;
|
110
|
-
}
|
111
39
|
</style>
|
@@ -1,9 +1,10 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import { useCategory, usePostList, useTag } from 'valaxy'
|
2
|
+
import { useCategory, usePostList, useTag, useThemeConfig } from 'valaxy'
|
3
3
|
import { useI18n } from 'vue-i18n'
|
4
4
|
|
5
5
|
const { t } = useI18n()
|
6
6
|
|
7
|
+
const themeConfig = useThemeConfig()
|
7
8
|
const posts = usePostList()
|
8
9
|
const categories = useCategory()
|
9
10
|
const tags = useTag()
|
@@ -28,8 +29,8 @@ const tags = useTag()
|
|
28
29
|
<span class="count">{{ Array.from(tags).length }}</span>
|
29
30
|
</router-link>
|
30
31
|
|
31
|
-
<router-link class="site-link-item yun-icon-btn" to="
|
32
|
-
<div
|
32
|
+
<router-link class="site-link-item yun-icon-btn" :to="themeConfig.menu.custom.url" :title="t(themeConfig.menu.custom.title)">
|
33
|
+
<div :class="themeConfig.menu.custom.icon" />
|
33
34
|
</router-link>
|
34
35
|
</nav>
|
35
36
|
</template>
|
@@ -11,9 +11,6 @@ const showQr = ref(false)
|
|
11
11
|
|
12
12
|
<template>
|
13
13
|
<div class="yun-sponsor-container flex justify-center items-center flex-col">
|
14
|
-
<!-- <a href="" :title="t('reward.donate')">
|
15
|
-
|
16
|
-
</a> -->
|
17
14
|
<button class="sponsor-button yun-icon-btn shadow hover:shadow-md" :title="t('reward.donate')" text="red-400" @click="showQr = !showQr">
|
18
15
|
<div i-ri-heart-line />
|
19
16
|
</button>
|
@@ -26,7 +23,7 @@ const showQr = ref(false)
|
|
26
23
|
:href="method.url" target="_blank"
|
27
24
|
:style="`color:${method.color}`"
|
28
25
|
>
|
29
|
-
<img
|
26
|
+
<img class="sponsor-method-img" border="~ rounded" p="1" loading="lazy" :src="method.url" :title="method.name">
|
30
27
|
<div text="xl" m="2" :class="method.icon" />
|
31
28
|
</a>
|
32
29
|
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { isClient, useStorage } from '@vueuse/core'
|
3
|
+
import { useI18n } from 'vue-i18n'
|
4
|
+
const { t, availableLocales, locale } = useI18n()
|
5
|
+
|
6
|
+
const lang = useStorage('valaxy-locale', locale.value)
|
7
|
+
|
8
|
+
const toggleLocales = () => {
|
9
|
+
// change to some real logic
|
10
|
+
const locales = availableLocales
|
11
|
+
|
12
|
+
locale.value = locales[(locales.indexOf(locale.value) + 1) % locales.length]
|
13
|
+
// for localStorage
|
14
|
+
lang.value = locale.value
|
15
|
+
|
16
|
+
if (isClient)
|
17
|
+
document.documentElement.setAttribute('lang', locale.value)
|
18
|
+
}
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<template>
|
22
|
+
<button class="yun-icon-btn" :title="t('button.toggle_langs')" style="color:var(--va-c-text)" @click="toggleLocales">
|
23
|
+
<div i-ri-translate class="transition transform" :class="locale === 'en' ? 'rotate-y-180' : ''" />
|
24
|
+
</button>
|
25
|
+
</template>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { useConfig, useTwikoo } from 'valaxy'
|
3
|
+
|
4
|
+
const config = useConfig()
|
5
|
+
useTwikoo(config.value.comment.twikoo)
|
6
|
+
</script>
|
7
|
+
|
8
|
+
<template>
|
9
|
+
<div id="tcomment" w="full" />
|
10
|
+
</template>
|
11
|
+
|
12
|
+
<style lang="scss">
|
13
|
+
// custom twikoo style
|
14
|
+
</style>
|
package/components/YunWaline.vue
CHANGED
package/composables/index.ts
CHANGED
@@ -13,7 +13,8 @@ export function useRandomData<T>(source: string | T[], random = false) {
|
|
13
13
|
watch(() => source, async() => {
|
14
14
|
let rawData: T[]
|
15
15
|
if (typeof source === 'string') {
|
16
|
-
if (!isClient)
|
16
|
+
if (!isClient)
|
17
|
+
return
|
17
18
|
rawData = await fetch(source).then(res => res.json()) as T[]
|
18
19
|
}
|
19
20
|
else { rawData = source }
|
package/config/index.ts
CHANGED
@@ -4,9 +4,12 @@ export const anonymousImage = 'https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/ava
|
|
4
4
|
* Theme Config
|
5
5
|
*/
|
6
6
|
export interface ThemeConfig {
|
7
|
+
// for unocss
|
8
|
+
safelist: string[]
|
7
9
|
colors: {
|
8
10
|
/**
|
9
11
|
* primary color
|
12
|
+
* @default '#0078E7'
|
10
13
|
*/
|
11
14
|
primary: string
|
12
15
|
}
|
@@ -99,6 +102,17 @@ export interface ThemeConfig {
|
|
99
102
|
color: string
|
100
103
|
icon: string
|
101
104
|
}>
|
105
|
+
|
106
|
+
/**
|
107
|
+
* 菜单栏
|
108
|
+
*/
|
109
|
+
menu: {
|
110
|
+
custom: {
|
111
|
+
title: string
|
112
|
+
url: string
|
113
|
+
icon: string
|
114
|
+
}
|
115
|
+
}
|
102
116
|
}
|
103
117
|
|
104
118
|
export type ThemeUserConfig = Partial<ThemeConfig>
|
@@ -107,6 +121,7 @@ export type ThemeUserConfig = Partial<ThemeConfig>
|
|
107
121
|
* Default Config
|
108
122
|
*/
|
109
123
|
export const defaultThemeConfig: ThemeConfig = {
|
124
|
+
safelist: ['i-ri-clipboard-line'],
|
110
125
|
colors: {
|
111
126
|
primary: '#0078E7',
|
112
127
|
},
|
@@ -197,6 +212,39 @@ export const defaultThemeConfig: ThemeConfig = {
|
|
197
212
|
icon: 'i-ri-zhihu-line',
|
198
213
|
},
|
199
214
|
},
|
215
|
+
|
216
|
+
menu: {
|
217
|
+
custom: {
|
218
|
+
title: 'button.about',
|
219
|
+
icon: 'i-ri-clipboard-line',
|
220
|
+
url: '/about',
|
221
|
+
},
|
222
|
+
},
|
200
223
|
}
|
201
224
|
|
225
|
+
defaultThemeConfig.safelist = defaultThemeConfig.safelist.concat(generateSafelist(defaultThemeConfig))
|
226
|
+
|
202
227
|
export default defaultThemeConfig
|
228
|
+
|
229
|
+
/**
|
230
|
+
* generateSafelist by config
|
231
|
+
* @param themeConfig
|
232
|
+
* @returns
|
233
|
+
*/
|
234
|
+
export function generateSafelist(themeConfig: ThemeUserConfig) {
|
235
|
+
const safelist = []
|
236
|
+
|
237
|
+
const types = themeConfig.types
|
238
|
+
if (types) {
|
239
|
+
for (const type in types)
|
240
|
+
safelist.push(types[type].icon)
|
241
|
+
}
|
242
|
+
|
243
|
+
if (themeConfig.footer?.icon?.name)
|
244
|
+
safelist.push(themeConfig.footer?.icon?.name)
|
245
|
+
|
246
|
+
if (themeConfig.menu?.custom?.icon)
|
247
|
+
safelist.push(themeConfig.menu?.custom?.icon)
|
248
|
+
|
249
|
+
return safelist
|
250
|
+
}
|
package/dist/index.d.ts
CHANGED
@@ -5,9 +5,11 @@ declare const anonymousImage = "https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/av
|
|
5
5
|
* Theme Config
|
6
6
|
*/
|
7
7
|
interface ThemeConfig {
|
8
|
+
safelist: string[];
|
8
9
|
colors: {
|
9
10
|
/**
|
10
11
|
* primary color
|
12
|
+
* @default '#0078E7'
|
11
13
|
*/
|
12
14
|
primary: string;
|
13
15
|
};
|
@@ -91,6 +93,16 @@ interface ThemeConfig {
|
|
91
93
|
color: string;
|
92
94
|
icon: string;
|
93
95
|
}>;
|
96
|
+
/**
|
97
|
+
* 菜单栏
|
98
|
+
*/
|
99
|
+
menu: {
|
100
|
+
custom: {
|
101
|
+
title: string;
|
102
|
+
url: string;
|
103
|
+
icon: string;
|
104
|
+
};
|
105
|
+
};
|
94
106
|
}
|
95
107
|
declare type ThemeUserConfig = Partial<ThemeConfig>;
|
96
108
|
/**
|
@@ -98,6 +110,13 @@ declare type ThemeUserConfig = Partial<ThemeConfig>;
|
|
98
110
|
*/
|
99
111
|
declare const defaultThemeConfig: ThemeConfig;
|
100
112
|
|
113
|
+
/**
|
114
|
+
* generateSafelist by config
|
115
|
+
* @param themeConfig
|
116
|
+
* @returns
|
117
|
+
*/
|
118
|
+
declare function generateSafelist(themeConfig: ThemeUserConfig): string[];
|
119
|
+
|
101
120
|
interface UserOptions {
|
102
121
|
colors: {
|
103
122
|
primary: string;
|
@@ -105,4 +124,4 @@ interface UserOptions {
|
|
105
124
|
}
|
106
125
|
declare function yunPlugin(userOptions?: Partial<ThemeConfig>): Plugin;
|
107
126
|
|
108
|
-
export { ThemeConfig, ThemeUserConfig, UserOptions, anonymousImage, yunPlugin as default, defaultThemeConfig, yunPlugin };
|
127
|
+
export { ThemeConfig, ThemeUserConfig, UserOptions, anonymousImage, yunPlugin as default, defaultThemeConfig, generateSafelist, yunPlugin };
|
package/dist/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var d="https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg",n= exports.defaultThemeConfig ={safelist:["i-ri-clipboard-line"],colors:{primary:"#0078E7"},banner:{enable:!0,title:"\u4E91\u6E38\u541B\u7684\u5C0F\u7AD9"},bg_image:{enable:!0,url:"https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/bg/stars-timing-0-blur-30px.jpg",dark:"https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/bg/galaxy.jpg",opacity:1},say:{enable:!0,api:"https://el-bot-api.vercel.app/api/words/young",hitokoto:{enable:!1,api:"https://v1.hitokoto.cn"}},pages:[],footer:{since:2022,icon:{name:"i-ri-cloud-line",animated:!0,color:"var(--va-c-primary)",url:"https://sponsors.yunyoujun.cn",title:"Sponsor YunYouJun"},powered:!0,beian:{enable:!1,icp:""}},types:{link:{color:"var(--va-c-primary)",icon:"i-ri-external-link-line"},bilibili:{color:"#FF8EB3",icon:"i-ri-bilibili-line"},douban:{color:"#007722",icon:"i-ri-douban-line"},github:{color:"var(--va-c-text)",icon:"i-ri-github-line"},"netease-cloud-music":{color:"#C10D0C",icon:"i-ri-netease-cloud-music-line"},notion:{color:"var(--va-c-text)",icon:"i-simple-icons-notion"},twitter:{color:"#1da1f2",icon:"i-ri-twitter-line"},wechat:{color:"#1AAD19",icon:"i-ri-wechat-2-line"},weibo:{color:"#E6162D",icon:"i-ri-weibo-line"},yuque:{color:"#25b864",icon:"i-ant-design-yuque-outlined"},zhihu:{color:"#0084FF",icon:"i-ri-zhihu-line"}},menu:{custom:{title:"button.about",icon:"i-ri-clipboard-line",url:"/about"}}};n.safelist=n.safelist.concat(b(n));function b(i){var t,r,a,l,s,c,u,p;let e=[],o=i.types;if(o)for(let g in o)e.push(o[g].icon);return(r=(t=i.footer)==null?void 0:t.icon)!=null&&r.name&&e.push((l=(a=i.footer)==null?void 0:a.icon)==null?void 0:l.name),(c=(s=i.menu)==null?void 0:s.custom)!=null&&c.icon&&e.push((p=(u=i.menu)==null?void 0:u.custom)==null?void 0:p.icon),e}function f(i=n){return{name:"valaxy-theme-yun",enforce:"pre",config(){var e;return{css:{preprocessorOptions:{scss:{additionalData:`$c-primary: ${((e=i.colors)==null?void 0:e.primary)||"#0078E7"} !default;`}}}}}}}var v=f;exports.anonymousImage = d; exports.default = v; exports.defaultThemeConfig = n; exports.generateSafelist = b; exports.yunPlugin = f;
|
package/dist/index.mjs
CHANGED
@@ -1 +1 @@
|
|
1
|
-
var
|
1
|
+
var h="https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg",n={safelist:["i-ri-clipboard-line"],colors:{primary:"#0078E7"},banner:{enable:!0,title:"\u4E91\u6E38\u541B\u7684\u5C0F\u7AD9"},bg_image:{enable:!0,url:"https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/bg/stars-timing-0-blur-30px.jpg",dark:"https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/bg/galaxy.jpg",opacity:1},say:{enable:!0,api:"https://el-bot-api.vercel.app/api/words/young",hitokoto:{enable:!1,api:"https://v1.hitokoto.cn"}},pages:[],footer:{since:2022,icon:{name:"i-ri-cloud-line",animated:!0,color:"var(--va-c-primary)",url:"https://sponsors.yunyoujun.cn",title:"Sponsor YunYouJun"},powered:!0,beian:{enable:!1,icp:""}},types:{link:{color:"var(--va-c-primary)",icon:"i-ri-external-link-line"},bilibili:{color:"#FF8EB3",icon:"i-ri-bilibili-line"},douban:{color:"#007722",icon:"i-ri-douban-line"},github:{color:"var(--va-c-text)",icon:"i-ri-github-line"},"netease-cloud-music":{color:"#C10D0C",icon:"i-ri-netease-cloud-music-line"},notion:{color:"var(--va-c-text)",icon:"i-simple-icons-notion"},twitter:{color:"#1da1f2",icon:"i-ri-twitter-line"},wechat:{color:"#1AAD19",icon:"i-ri-wechat-2-line"},weibo:{color:"#E6162D",icon:"i-ri-weibo-line"},yuque:{color:"#25b864",icon:"i-ant-design-yuque-outlined"},zhihu:{color:"#0084FF",icon:"i-ri-zhihu-line"}},menu:{custom:{title:"button.about",icon:"i-ri-clipboard-line",url:"/about"}}};n.safelist=n.safelist.concat(f(n));function f(i){var t,r,a,l,s,c,u,p;let e=[],o=i.types;if(o)for(let g in o)e.push(o[g].icon);return(r=(t=i.footer)==null?void 0:t.icon)!=null&&r.name&&e.push((l=(a=i.footer)==null?void 0:a.icon)==null?void 0:l.name),(c=(s=i.menu)==null?void 0:s.custom)!=null&&c.icon&&e.push((p=(u=i.menu)==null?void 0:u.custom)==null?void 0:p.icon),e}function d(i=n){return{name:"valaxy-theme-yun",enforce:"pre",config(){var e;return{css:{preprocessorOptions:{scss:{additionalData:`$c-primary: ${((e=i.colors)==null?void 0:e.primary)||"#0078E7"} !default;`}}}}}}}var x=d;export{h as anonymousImage,x as default,n as defaultThemeConfig,f as generateSafelist,d as yunPlugin};
|
package/index.ts
CHANGED
@@ -20,7 +20,7 @@ export function yunPlugin(userOptions: Partial<ThemeConfig> = defaultThemeConfig
|
|
20
20
|
css: {
|
21
21
|
preprocessorOptions: {
|
22
22
|
scss: {
|
23
|
-
additionalData: `$
|
23
|
+
additionalData: `$c-primary: ${userOptions.colors?.primary || '#0078E7'} !default;`,
|
24
24
|
},
|
25
25
|
},
|
26
26
|
},
|
package/layouts/archives.vue
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import { useFrontmatter, usePostList } from 'valaxy'
|
2
|
+
import { useFrontmatter, usePostList, usePostTitle } from 'valaxy'
|
3
3
|
import { useI18n } from 'vue-i18n'
|
4
4
|
|
5
5
|
const { t } = useI18n()
|
6
6
|
|
7
7
|
const frontmatter = useFrontmatter()
|
8
8
|
const postList = usePostList()
|
9
|
+
|
10
|
+
const title = usePostTitle(frontmatter)
|
9
11
|
</script>
|
10
12
|
|
11
13
|
<template>
|
12
14
|
<YunBase>
|
13
15
|
<template #header>
|
14
|
-
<YunPageHeader :title="
|
16
|
+
<YunPageHeader :title="title || t('menu.archives')" :icon="frontmatter.icon || 'i-ri-archive-line'" :color="frontmatter.color" />
|
15
17
|
</template>
|
16
18
|
<template #content>
|
17
19
|
<router-view />
|
package/layouts/base.vue
CHANGED
@@ -1,22 +1,28 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import { useConfig, useFrontmatter, usePostProperty } from 'valaxy'
|
2
|
+
import { useConfig, useFrontmatter, usePostProperty, usePostTitle } from 'valaxy'
|
3
3
|
const frontmatter = useFrontmatter()
|
4
4
|
|
5
5
|
const config = useConfig()
|
6
6
|
|
7
7
|
const { styles, icon, color } = usePostProperty(frontmatter.value.type)
|
8
|
+
const title = usePostTitle(frontmatter)
|
8
9
|
</script>
|
9
10
|
|
10
11
|
<template>
|
11
12
|
<ValaxySidebar>
|
12
|
-
<slot name="sidebar"
|
13
|
+
<slot name="sidebar">
|
14
|
+
<YunSidebar v-if="$slots['sidebar-child']">
|
15
|
+
<slot name="sidebar-child" />
|
16
|
+
</YunSidebar>
|
17
|
+
<YunSidebar v-else />
|
18
|
+
</slot>
|
13
19
|
</ValaxySidebar>
|
14
20
|
|
15
21
|
<main class="yun-main flex lt-md:ml-0">
|
16
22
|
<div flex="~ 1 col" w="full" p="l-4 lt-md:0">
|
17
|
-
<YunCard m="0" p="4" class="page-card sm:p-6 lg:px-12 xl:px-16" :style="styles">
|
23
|
+
<YunCard m="0" p="4" class="relative page-card sm:p-6 lg:px-12 xl:px-16" :style="styles">
|
18
24
|
<slot name="header">
|
19
|
-
<YunPageHeader :title="
|
25
|
+
<YunPageHeader :title="title" :icon="frontmatter.icon || icon" :color="frontmatter.color || color" />
|
20
26
|
</slot>
|
21
27
|
<template #content>
|
22
28
|
<slot name="content">
|
@@ -32,8 +38,9 @@ const { styles, icon, color } = usePostProperty(frontmatter.value.type)
|
|
32
38
|
</slot>
|
33
39
|
|
34
40
|
<slot v-if="frontmatter.comment !== false" name="comment">
|
35
|
-
<YunCard
|
36
|
-
<YunWaline />
|
41
|
+
<YunCard w="full" p="4" class="comment sm:p-8 lg:px-12 xl:px-16" :class="frontmatter.nav === false ? 'mt-4' : 0">
|
42
|
+
<YunWaline v-if="config.comment.waline.enable" />
|
43
|
+
<YunTwikoo v-if="config.comment.twikoo.enable" />
|
37
44
|
</YunCard>
|
38
45
|
</slot>
|
39
46
|
|
package/layouts/categories.vue
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { computed, ref } from 'vue'
|
3
|
-
import { useCategory, useFrontmatter, useInvisibleElement, usePostList } from 'valaxy'
|
3
|
+
import { useCategory, useFrontmatter, useInvisibleElement, usePostList, usePostTitle } from 'valaxy'
|
4
4
|
import { useI18n } from 'vue-i18n'
|
5
5
|
import { useRoute, useRouter } from 'vue-router'
|
6
6
|
|
@@ -39,13 +39,15 @@ const displayCategory = (category: string) => {
|
|
39
39
|
|
40
40
|
show()
|
41
41
|
}
|
42
|
+
|
43
|
+
const title = usePostTitle(frontmatter)
|
42
44
|
</script>
|
43
45
|
|
44
46
|
<template>
|
45
47
|
<YunBase>
|
46
48
|
<template #header>
|
47
49
|
<YunPageHeader
|
48
|
-
:title="
|
50
|
+
:title="title || t('menu.categories')"
|
49
51
|
:icon="frontmatter.icon || 'i-ri-folder-2-line'"
|
50
52
|
:color="frontmatter.color"
|
51
53
|
/>
|
package/layouts/home.vue
CHANGED
@@ -12,7 +12,12 @@ const isHome = useLayout('home')
|
|
12
12
|
|
13
13
|
<template>
|
14
14
|
<main class="yun-main justify-center items-center" :class="(isHome && !app.isSidebarOpen) && 'pl-0'" flex="~ col" w="full">
|
15
|
-
<ValaxySidebar
|
15
|
+
<ValaxySidebar>
|
16
|
+
<slot name="sidebar">
|
17
|
+
<YunSidebar />
|
18
|
+
</slot>
|
19
|
+
</ValaxySidebar>
|
20
|
+
|
16
21
|
<YunBanner />
|
17
22
|
<YunSay w="full" />
|
18
23
|
|
package/layouts/post.vue
CHANGED
@@ -13,9 +13,11 @@ const url = useFullUrl()
|
|
13
13
|
<slot name="header">
|
14
14
|
<YunPostMeta :frontmatter="frontmatter" />
|
15
15
|
</slot>
|
16
|
-
<
|
17
|
-
<
|
18
|
-
|
16
|
+
<router-view v-slot="{Component}">
|
17
|
+
<Transition appear>
|
18
|
+
<component :is="Component" />
|
19
|
+
</Transition>
|
20
|
+
</router-view>
|
19
21
|
<YunSponsor v-if="frontmatter.sponsor || config.sponsor.enable" />
|
20
22
|
<ValaxyCopyright v-if="frontmatter.copyright || config.license.enabled" :url="url" m="y-4" />
|
21
23
|
</main>
|
package/layouts/tags.vue
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import { useFrontmatter, useInvisibleElement, usePostList, useTags } from 'valaxy'
|
2
|
+
import { useFrontmatter, useInvisibleElement, usePostList, usePostTitle, useTags, useThemeConfig } from 'valaxy'
|
3
3
|
import { useI18n } from 'vue-i18n'
|
4
4
|
import { computed, ref } from 'vue'
|
5
5
|
import { useRoute, useRouter } from 'vue-router'
|
@@ -7,9 +7,13 @@ import { useRoute, useRouter } from 'vue-router'
|
|
7
7
|
const route = useRoute()
|
8
8
|
const router = useRouter()
|
9
9
|
|
10
|
+
const themeConfig = useThemeConfig()
|
11
|
+
|
10
12
|
const { t } = useI18n()
|
11
13
|
const frontmatter = useFrontmatter()
|
12
|
-
const { tags, getTagStyle } = useTags(
|
14
|
+
const { tags, getTagStyle } = useTags({
|
15
|
+
primary: themeConfig.value.colors.primary,
|
16
|
+
})
|
13
17
|
|
14
18
|
const postList = usePostList()
|
15
19
|
const curTag = computed(() => route.query.tag as string || '')
|
@@ -40,13 +44,14 @@ const displayTag = (tag: string) => {
|
|
40
44
|
show()
|
41
45
|
}
|
42
46
|
|
47
|
+
const title = usePostTitle(frontmatter)
|
43
48
|
</script>
|
44
49
|
|
45
50
|
<template>
|
46
51
|
<YunBase>
|
47
52
|
<template #header>
|
48
53
|
<YunPageHeader
|
49
|
-
:title="
|
54
|
+
:title="title || t('menu.tags')"
|
50
55
|
:icon="frontmatter.icon || 'i-ri-tag-line'"
|
51
56
|
:color="frontmatter.color"
|
52
57
|
/>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "valaxy-theme-yun",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.2",
|
4
4
|
"main": "dist/index.js",
|
5
5
|
"types": "dist/index.d.ts",
|
6
6
|
"repository": {
|
@@ -18,7 +18,7 @@
|
|
18
18
|
"url": "https://www.yunyoujun.cn"
|
19
19
|
},
|
20
20
|
"devDependencies": {
|
21
|
-
"valaxy": "0.
|
21
|
+
"valaxy": "0.2.2"
|
22
22
|
},
|
23
23
|
"scripts": {
|
24
24
|
"build": "tsup",
|
package/styles/layout/post.scss
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
@use
|
1
|
+
@use "~/styles/mixins" as *;
|
2
2
|
|
3
3
|
.post-card {
|
4
4
|
position: relative;
|
5
5
|
max-width: var(--yun-post-card-max-width);
|
6
6
|
}
|
7
7
|
|
8
|
-
|
9
8
|
.post-title {
|
10
9
|
display: flex;
|
11
10
|
justify-content: center;
|
@@ -28,8 +27,9 @@
|
|
28
27
|
margin-right: 0.4rem;
|
29
28
|
}
|
30
29
|
|
31
|
-
&::before,
|
32
|
-
|
30
|
+
&::before,
|
31
|
+
&::after {
|
32
|
+
content: "";
|
33
33
|
position: absolute;
|
34
34
|
width: 10px;
|
35
35
|
height: 10px;
|
@@ -53,20 +53,13 @@
|
|
53
53
|
transform: translate3d(-10px, -10px, 0);
|
54
54
|
}
|
55
55
|
|
56
|
-
&:hover::before,
|
56
|
+
&:hover::before,
|
57
|
+
&:hover::after {
|
57
58
|
opacity: 1;
|
58
59
|
transform: translate3d(0, 0, 0);
|
59
60
|
}
|
60
61
|
}
|
61
62
|
|
62
|
-
.post-top-icon {
|
63
|
-
position: absolute;
|
64
|
-
top: 1rem;
|
65
|
-
right: 1rem;
|
66
|
-
color: var(--va-c-warning);
|
67
|
-
font-size: 1.2rem;
|
68
|
-
}
|
69
|
-
|
70
63
|
.post-link-btn,
|
71
64
|
.markdown-body .post-link-btn {
|
72
65
|
background-color: var(--card-c-primary);
|
package/styles/markdown.scss
CHANGED
@@ -9,3 +9,21 @@ html.dark {
|
|
9
9
|
--c-toc-link: var(--va-c-text-dark);
|
10
10
|
}
|
11
11
|
}
|
12
|
+
|
13
|
+
.markdown-body {
|
14
|
+
h1,
|
15
|
+
h2,
|
16
|
+
h3,
|
17
|
+
h4,
|
18
|
+
h5,
|
19
|
+
h6 {
|
20
|
+
font-family: var(--va-font-serif);
|
21
|
+
font-weight: 900;
|
22
|
+
}
|
23
|
+
|
24
|
+
ul {
|
25
|
+
li > p {
|
26
|
+
margin-bottom: 0;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|