valaxy-theme-yun 0.12.9 → 0.13.0-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.
- package/App.vue +6 -4
- package/components/ValaxyMain.vue +10 -8
- package/components/YunCloud.vue +9 -6
- package/components/YunFooter.vue +4 -3
- package/components/YunFuseSearch.vue +176 -0
- package/components/YunOverview.vue +11 -11
- package/components/YunSearchTrigger.vue +91 -0
- package/components/YunSocialLinks.vue +3 -3
- package/components/YunSponsor.vue +3 -3
- package/components/YunTwikoo.vue +2 -3
- package/components/YunWalineMeta.vue +3 -3
- package/composables/config.ts +2 -2
- package/docs/zh-CN/README.md +1 -38
- package/docs/zh-CN/config.md +81 -0
- package/layouts/home.vue +1 -0
- package/layouts/post.vue +4 -4
- package/node/config.ts +3 -0
- package/node/unocss.ts +2 -2
- package/package.json +2 -2
- package/styles/vars.scss +2 -1
- package/types/index.d.ts +18 -8
- package/valaxy.config.ts +0 -1
- package/components/YunSearch.vue +0 -131
package/App.vue
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { useHead } from '@vueuse/head'
|
3
|
-
import {
|
3
|
+
import { useSiteConfig } from 'valaxy'
|
4
|
+
import { useThemeConfig } from './composables'
|
4
5
|
|
5
6
|
useHead({
|
6
7
|
link: [
|
@@ -11,12 +12,13 @@ useHead({
|
|
11
12
|
],
|
12
13
|
})
|
13
14
|
|
14
|
-
const
|
15
|
+
const siteConfig = useSiteConfig()
|
16
|
+
const themeConfig = useThemeConfig()
|
15
17
|
</script>
|
16
18
|
|
17
19
|
<template>
|
18
20
|
<slot name="bg">
|
19
|
-
<YunBg v-if="
|
21
|
+
<YunBg v-if="themeConfig.bg_image.enable" />
|
20
22
|
</slot>
|
21
|
-
<
|
23
|
+
<YunSearchTrigger v-if="siteConfig.search.enable" />
|
22
24
|
</template>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import type { PageData, Post } from 'valaxy'
|
3
|
-
import {
|
3
|
+
import { usePostTitle, useRuntimeConfig, useSiteConfig } from 'valaxy'
|
4
4
|
import { StyleValue, computed, defineAsyncComponent } from 'vue'
|
5
5
|
import { usePostProperty } from '../composables'
|
6
6
|
|
@@ -8,7 +8,9 @@ const props = defineProps<{
|
|
8
8
|
frontmatter: Post
|
9
9
|
data?: PageData
|
10
10
|
}>()
|
11
|
-
|
11
|
+
|
12
|
+
const runtimeConfig = useRuntimeConfig()
|
13
|
+
const siteConfig = useSiteConfig()
|
12
14
|
|
13
15
|
const { styles, icon, color } = usePostProperty(props.frontmatter.type)
|
14
16
|
const title = usePostTitle(computed(() => props.frontmatter))
|
@@ -16,14 +18,14 @@ const title = usePostTitle(computed(() => props.frontmatter))
|
|
16
18
|
const aside = computed(() => props.frontmatter.aside !== false)
|
17
19
|
|
18
20
|
// not import from 'valaxy-addon-waline' to judge
|
19
|
-
const YunWaline =
|
21
|
+
const YunWaline = runtimeConfig.value.addons['valaxy-addon-waline']
|
20
22
|
? defineAsyncComponent(() => import('./YunWaline.vue'))
|
21
23
|
: () => null
|
22
24
|
|
23
25
|
// todo: refactor
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
const YunTwikoo = runtimeConfig.value.addons['valaxy-addon-twikoo']
|
27
|
+
? defineAsyncComponent(() => import('./YunTwikoo.vue'))
|
28
|
+
: () => null
|
27
29
|
</script>
|
28
30
|
|
29
31
|
<template>
|
@@ -57,10 +59,10 @@ const YunWaline = config.value.runtime.addons['valaxy-addon-waline']
|
|
57
59
|
|
58
60
|
<slot name="main-nav-after" />
|
59
61
|
|
60
|
-
<slot v-if="
|
62
|
+
<slot v-if="siteConfig.comment.enable && frontmatter.comment !== false" name="comment">
|
61
63
|
<YunCard w="full" p="4" class="comment sm:p-6 lg:px-12 xl:px-16" :class="frontmatter.nav === false ? 'mt-4' : 0">
|
62
64
|
<YunWaline />
|
63
|
-
|
65
|
+
<YunTwikoo />
|
64
66
|
</YunCard>
|
65
67
|
</slot>
|
66
68
|
|
package/components/YunCloud.vue
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
<template>
|
2
|
-
<div class="cloud">
|
2
|
+
<div class="yun-cloud">
|
3
3
|
<svg class="waves" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
4
4
|
<defs>
|
5
|
-
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" fill="
|
5
|
+
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" fill="var(--yun-c-cloud)" />
|
6
6
|
</defs>
|
7
7
|
<g class="parallax">
|
8
|
-
<use xlink:href="#gentle-wave" x="48" y="0"
|
9
|
-
<use xlink:href="#gentle-wave" x="48" y="3"
|
10
|
-
<use xlink:href="#gentle-wave" x="48" y="5"
|
8
|
+
<use xlink:href="#gentle-wave" x="48" y="0" />
|
9
|
+
<use xlink:href="#gentle-wave" x="48" y="3" />
|
10
|
+
<use xlink:href="#gentle-wave" x="48" y="5" />
|
11
11
|
<use xlink:href="#gentle-wave" x="48" y="7" />
|
12
12
|
</g>
|
13
13
|
</svg>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<style lang="scss">
|
18
18
|
@use 'valaxy/client/styles/mixins' as *;
|
19
19
|
|
20
|
-
.cloud {
|
20
|
+
.yun-cloud {
|
21
21
|
display: flex;
|
22
22
|
width: 100%;
|
23
23
|
position: absolute;
|
@@ -45,16 +45,19 @@
|
|
45
45
|
}
|
46
46
|
|
47
47
|
> use:nth-child(1) {
|
48
|
+
opacity: 0.7;
|
48
49
|
animation-delay: -2s;
|
49
50
|
animation-duration: 7s;
|
50
51
|
}
|
51
52
|
|
52
53
|
> use:nth-child(2) {
|
54
|
+
opacity: 0.5;
|
53
55
|
animation-delay: -3s;
|
54
56
|
animation-duration: 10s;
|
55
57
|
}
|
56
58
|
|
57
59
|
> use:nth-child(3) {
|
60
|
+
opacity: 0.3;
|
58
61
|
animation-delay: -4s;
|
59
62
|
animation-duration: 13s;
|
60
63
|
}
|
package/components/YunFooter.vue
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { capitalize, computed } from 'vue'
|
3
|
-
import {
|
3
|
+
import { useSiteConfig, useValaxyConfig } from 'valaxy'
|
4
4
|
import { useI18n } from 'vue-i18n'
|
5
5
|
import pkg from 'valaxy/package.json'
|
6
6
|
import { useThemeConfig } from '../composables'
|
7
7
|
|
8
8
|
const { t } = useI18n()
|
9
|
-
const config =
|
9
|
+
const config = useValaxyConfig()
|
10
|
+
const siteConfig = useSiteConfig()
|
10
11
|
const themeConfig = useThemeConfig()
|
11
12
|
const year = new Date().getFullYear()
|
12
13
|
|
@@ -42,7 +43,7 @@ const footerIcon = computed(() => themeConfig.value.footer.icon || {
|
|
42
43
|
<a v-if="themeConfig.footer.icon?.enable" class="inline-flex animate-pulse" :href="footerIcon.url" target="_blank" :title="footerIcon.title">
|
43
44
|
<div :class="footerIcon.name" />
|
44
45
|
</a>
|
45
|
-
<span>{{
|
46
|
+
<span>{{ siteConfig.author.name }}</span>
|
46
47
|
</div>
|
47
48
|
|
48
49
|
<div v-if="themeConfig.footer.powered" class="powered" m="2">
|
@@ -0,0 +1,176 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
3
|
+
import { useFuse } from '@vueuse/integrations/useFuse'
|
4
|
+
import { computed, ref, watch } from 'vue'
|
5
|
+
import { useI18n } from 'vue-i18n'
|
6
|
+
import { useBodyScrollLock } from 'valaxy'
|
7
|
+
import { useRouter } from 'vue-router'
|
8
|
+
|
9
|
+
export interface FuseDataItem {
|
10
|
+
title: string
|
11
|
+
excerpt: string
|
12
|
+
link: string
|
13
|
+
}
|
14
|
+
|
15
|
+
const props = defineProps<{
|
16
|
+
open: boolean
|
17
|
+
}>()
|
18
|
+
const emit = defineEmits(['close'])
|
19
|
+
|
20
|
+
const searchContainer = ref<HTMLElement>()
|
21
|
+
|
22
|
+
const { lockBodyScroll, unlockBodyScroll } = useBodyScrollLock(searchContainer)
|
23
|
+
|
24
|
+
const { t } = useI18n()
|
25
|
+
|
26
|
+
const fuseListData = ref<any[]>([])
|
27
|
+
// const { data } = await useFetch('/fuse-list.json').get().json()
|
28
|
+
// console.log(data)
|
29
|
+
|
30
|
+
const keys = computed(() => {
|
31
|
+
const keys = ['title', 'excerpt']
|
32
|
+
return keys
|
33
|
+
})
|
34
|
+
|
35
|
+
const input = ref('')
|
36
|
+
// todo export options
|
37
|
+
const fuseOptions = computed<UseFuseOptions<FuseDataItem>>(() => ({
|
38
|
+
fuseOptions: {
|
39
|
+
keys: keys.value,
|
40
|
+
// isCaseSensitive: isCaseSensitive.value,
|
41
|
+
// threshold: exactMatch.value ? 0 : undefined,
|
42
|
+
},
|
43
|
+
// resultLimit: resultLimit.value,
|
44
|
+
// matchAllWhenSearchEmpty: matchAllWhenSearchEmpty.value,
|
45
|
+
}))
|
46
|
+
const { results } = useFuse(input, fuseListData, fuseOptions)
|
47
|
+
|
48
|
+
const searchInputRef = ref<HTMLInputElement>()
|
49
|
+
|
50
|
+
watch(() => props.open, async () => {
|
51
|
+
if (!props.open)
|
52
|
+
return
|
53
|
+
|
54
|
+
fetch('/fuse-list.json')
|
55
|
+
.then(res => res.json())
|
56
|
+
.then((data) => {
|
57
|
+
if (Array.isArray(data))
|
58
|
+
fuseListData.value = data as unknown as any[]
|
59
|
+
|
60
|
+
searchInputRef.value?.focus()
|
61
|
+
})
|
62
|
+
})
|
63
|
+
|
64
|
+
const router = useRouter()
|
65
|
+
const jumpToLink = (link: string) => {
|
66
|
+
router.push(link)
|
67
|
+
emit('close')
|
68
|
+
}
|
69
|
+
</script>
|
70
|
+
|
71
|
+
<template>
|
72
|
+
<transition
|
73
|
+
name="fade"
|
74
|
+
@enter="lockBodyScroll"
|
75
|
+
@after-leave="unlockBodyScroll"
|
76
|
+
>
|
77
|
+
<div
|
78
|
+
v-if="open" ref="searchContainer"
|
79
|
+
class="yun-popup yun-search-popup yun-fuse-search flex-center" flex="col"
|
80
|
+
>
|
81
|
+
<div class="yun-search-input-container flex-center" w="full">
|
82
|
+
<input ref="searchInputRef" v-model="input" class="yun-search-input" :placeholder="t('search.placeholder')">
|
83
|
+
</div>
|
84
|
+
<div v-if="input" class="flex-center" w="full" py="4">
|
85
|
+
{{ t('search.hits', results.length || 0) }}
|
86
|
+
</div>
|
87
|
+
<div overflow="auto" flex="~ 1" w="full">
|
88
|
+
<div class="yun-fuse-result-container" flex="~ col" w="full">
|
89
|
+
<template v-if="results.length > 0">
|
90
|
+
<div
|
91
|
+
v-for="result in results" :key="result.item.title"
|
92
|
+
:to="result.item.link"
|
93
|
+
class="yun-fuse-result-item text-$va-c-text hover:(text-$va-c-bg bg-$va-c-text-dark bg-opacity-100)"
|
94
|
+
flex="~ col" pb-2
|
95
|
+
@click="jumpToLink(result.item.link)"
|
96
|
+
>
|
97
|
+
<h3 font="serif black">
|
98
|
+
{{ result.item.title }}
|
99
|
+
</h3>
|
100
|
+
<span text="sm" opacity="80">
|
101
|
+
{{ result.item.excerpt }}
|
102
|
+
</span>
|
103
|
+
<span text-xs opacity-50 mt="1">
|
104
|
+
Score Index: {{ result.refIndex }}
|
105
|
+
</span>
|
106
|
+
</div>
|
107
|
+
</template>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
</div>
|
111
|
+
</transition>
|
112
|
+
</template>
|
113
|
+
|
114
|
+
<style lang="scss">
|
115
|
+
.yun-search-popup {
|
116
|
+
position: fixed;
|
117
|
+
top: 0;
|
118
|
+
left: 0;
|
119
|
+
width: 100%;
|
120
|
+
height: 100%;
|
121
|
+
|
122
|
+
backdrop-filter: blur(30px);
|
123
|
+
-webkit-backdrop-filter: blur(30px);
|
124
|
+
|
125
|
+
text-align: center;
|
126
|
+
padding-top: 3.5rem;
|
127
|
+
margin: 0;
|
128
|
+
z-index: var(--yun-z-search-popup);
|
129
|
+
transition: 0.6s;
|
130
|
+
|
131
|
+
background-color: var(--va-c-bg-opacity);
|
132
|
+
}
|
133
|
+
|
134
|
+
.yun-search-input {
|
135
|
+
background: transparent;
|
136
|
+
color: var(--va-c-text);
|
137
|
+
font-size: 1.5rem;
|
138
|
+
border-radius: 3rem;
|
139
|
+
padding: 1rem 1.5rem;
|
140
|
+
border: 1px solid var(--va-c-gray);
|
141
|
+
box-sizing: border-box;
|
142
|
+
width: 90%;
|
143
|
+
max-width: 800px;
|
144
|
+
font-family: var(--va-font-serif);
|
145
|
+
font-weight: 900;
|
146
|
+
text-align: center;
|
147
|
+
transition: all 0.2s;
|
148
|
+
|
149
|
+
&:focus {
|
150
|
+
border-color: var(--va-c-text);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
.yun-popup {
|
155
|
+
.search-icon, .close-icon {
|
156
|
+
display: inline-block;
|
157
|
+
width: 2rem;
|
158
|
+
height: 2rem;
|
159
|
+
padding: 0.5rem;
|
160
|
+
|
161
|
+
.icon {
|
162
|
+
width: 2rem;
|
163
|
+
height: 2rem;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
.yun-fuse-search {
|
169
|
+
.yun-fuse-result-item {
|
170
|
+
// padding: 0.5rem;
|
171
|
+
cursor: pointer;
|
172
|
+
|
173
|
+
border-top: 1px dashed #ccc;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
</style>
|
@@ -1,8 +1,8 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { useSiteConfig } from 'valaxy'
|
3
3
|
import { useRouter } from 'vue-router'
|
4
4
|
|
5
|
-
const
|
5
|
+
const siteConfig = useSiteConfig()
|
6
6
|
const router = useRouter()
|
7
7
|
</script>
|
8
8
|
|
@@ -10,23 +10,23 @@ const router = useRouter()
|
|
10
10
|
<div class="sidebar-panel">
|
11
11
|
<div class="site-info" m="t-6">
|
12
12
|
<router-link class="site-author-avatar" to="/about">
|
13
|
-
<img class="rounded-full" :src="
|
14
|
-
<span class="site-author-status">{{
|
13
|
+
<img class="rounded-full" :src="siteConfig.author.avatar" alt="avatar">
|
14
|
+
<span class="site-author-status">{{ siteConfig.author.status.emoji }}</span>
|
15
15
|
</router-link>
|
16
16
|
<div class="site-author-name">
|
17
17
|
<router-link to="/about">
|
18
|
-
{{
|
18
|
+
{{ siteConfig.author.name }}
|
19
19
|
</router-link>
|
20
20
|
</div>
|
21
21
|
<router-link v-if="router.hasRoute('about-site')" to="/about/site" class="site-name">
|
22
|
-
{{
|
22
|
+
{{ siteConfig.title }}
|
23
23
|
</router-link>
|
24
|
-
<span v-else class="site-name">{{
|
25
|
-
<h4 v-if="
|
26
|
-
{{
|
24
|
+
<span v-else class="site-name">{{ siteConfig.title }}</span>
|
25
|
+
<h4 v-if="siteConfig.subtitle" class="site-subtitle block" text="xs">
|
26
|
+
{{ siteConfig.subtitle }}
|
27
27
|
</h4>
|
28
|
-
<div v-if="
|
29
|
-
{{
|
28
|
+
<div v-if="siteConfig.description" class="site-description my-1">
|
29
|
+
{{ siteConfig.description }}
|
30
30
|
</div>
|
31
31
|
</div>
|
32
32
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { computed } from '@vue/reactivity'
|
3
|
+
import { useSiteConfig } from 'valaxy'
|
4
|
+
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
5
|
+
import { useI18n } from 'vue-i18n'
|
6
|
+
import { useMagicKeys } from '@vueuse/core'
|
7
|
+
|
8
|
+
const siteConfig = useSiteConfig()
|
9
|
+
const { t } = useI18n()
|
10
|
+
|
11
|
+
// to avoid loading the docsearch js upfront (which is more than 1/3 of the
|
12
|
+
// payload), we delay initializing it until the user has actually clicked or
|
13
|
+
// hit the hotkey to invoke it.
|
14
|
+
const loaded = ref(false)
|
15
|
+
|
16
|
+
function load() {
|
17
|
+
if (!loaded.value)
|
18
|
+
loaded.value = true
|
19
|
+
}
|
20
|
+
|
21
|
+
const isAlgolia = computed(() => siteConfig.value.search.type === 'algolia')
|
22
|
+
|
23
|
+
onMounted(() => {
|
24
|
+
if (!isAlgolia.value)
|
25
|
+
return
|
26
|
+
|
27
|
+
const handleSearchHotKey = (e: KeyboardEvent) => {
|
28
|
+
if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
|
29
|
+
e.preventDefault()
|
30
|
+
load()
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
32
|
+
remove()
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
const remove = () => {
|
37
|
+
window.removeEventListener('keydown', handleSearchHotKey)
|
38
|
+
}
|
39
|
+
|
40
|
+
window.addEventListener('keydown', handleSearchHotKey)
|
41
|
+
|
42
|
+
onUnmounted(remove)
|
43
|
+
})
|
44
|
+
|
45
|
+
const open = ref(false)
|
46
|
+
|
47
|
+
const trigger = () => {
|
48
|
+
// todo, refactor shortcut
|
49
|
+
const e = new Event('keydown') as any
|
50
|
+
|
51
|
+
e.key = 'k'
|
52
|
+
e.metaKey = true
|
53
|
+
}
|
54
|
+
|
55
|
+
const togglePopup = () => {
|
56
|
+
open.value = !open.value
|
57
|
+
|
58
|
+
trigger()
|
59
|
+
}
|
60
|
+
|
61
|
+
const { Meta_K } = useMagicKeys()
|
62
|
+
|
63
|
+
watch(Meta_K, (val) => {
|
64
|
+
if (val)
|
65
|
+
togglePopup()
|
66
|
+
})
|
67
|
+
</script>
|
68
|
+
|
69
|
+
<template>
|
70
|
+
<button class="search-btn popup-trigger yun-icon-btn" :title="t('menu.search')" @click="togglePopup">
|
71
|
+
<div v-if="!open" i-ri-search-line />
|
72
|
+
<div v-else text="!2xl" i-ri-close-line />
|
73
|
+
</button>
|
74
|
+
|
75
|
+
<AlgoliaSearchBox v-if="isAlgolia && loaded" />
|
76
|
+
<YunFuseSearch v-else-if="siteConfig.search.type === 'fuse'" :open="open" @close="open = false" />
|
77
|
+
</template>
|
78
|
+
|
79
|
+
<style lang="scss">
|
80
|
+
@use 'sass:map';
|
81
|
+
@use 'valaxy/client/styles/vars' as *;
|
82
|
+
|
83
|
+
.search-btn {
|
84
|
+
position: fixed;
|
85
|
+
top: 0.6rem;
|
86
|
+
right: 0.8rem;
|
87
|
+
|
88
|
+
color: var(--va-c-primary);
|
89
|
+
z-index: var(--yun-z-search-btn);
|
90
|
+
}
|
91
|
+
</style>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { useSiteConfig } from 'valaxy'
|
3
3
|
|
4
|
-
const
|
4
|
+
const siteConfig = useSiteConfig()
|
5
5
|
</script>
|
6
6
|
|
7
7
|
<template>
|
8
8
|
<div class="links-of-author">
|
9
|
-
<a v-for="item, i in
|
9
|
+
<a v-for="item, i in siteConfig.social" :key="i" class="links-of-author-item yun-icon-btn" rel="noopener" :href="item.link" :title="item.name" target="_blank" :style="`color:${item.color}`">
|
10
10
|
<div class="icon" :class="item.icon" />
|
11
11
|
</a>
|
12
12
|
</div>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { useSiteConfig } from 'valaxy'
|
3
3
|
import { ref } from 'vue'
|
4
4
|
import { useI18n } from 'vue-i18n'
|
5
5
|
const { t } = useI18n()
|
6
6
|
|
7
|
-
const
|
7
|
+
const siteConfig = useSiteConfig()
|
8
8
|
|
9
9
|
const showQr = ref(false)
|
10
10
|
</script>
|
@@ -17,7 +17,7 @@ const showQr = ref(false)
|
|
17
17
|
|
18
18
|
<div class="qrcode-container qrcode flex justify-around" m="y-4" :class="showQr && 'show'">
|
19
19
|
<a
|
20
|
-
v-for="method, i in
|
20
|
+
v-for="method, i in siteConfig.sponsor.methods" :key="i"
|
21
21
|
class="flex flex-col justify-center items-center animate-iteration-1"
|
22
22
|
:class="showQr && 'animate-fade-in'"
|
23
23
|
:href="method.url" target="_blank"
|
package/components/YunTwikoo.vue
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
|
2
|
+
import { useTwikooWithOptions } from 'valaxy-addon-twikoo'
|
3
3
|
|
4
|
-
|
5
|
-
// useTwikoo(config.value.comment.twikoo)
|
4
|
+
useTwikooWithOptions()
|
6
5
|
</script>
|
7
6
|
|
8
7
|
<template>
|
@@ -1,14 +1,14 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { useRuntimeConfig } from 'valaxy'
|
3
3
|
import { computed } from 'vue'
|
4
4
|
import { useRoute } from 'vue-router'
|
5
5
|
|
6
6
|
import { useI18n } from 'vue-i18n'
|
7
7
|
|
8
8
|
const route = useRoute()
|
9
|
-
const
|
9
|
+
const runtimeConfig = useRuntimeConfig()
|
10
10
|
|
11
|
-
const addonWaline = computed(() =>
|
11
|
+
const addonWaline = computed(() => runtimeConfig.value.addons['valaxy-addon-waline'])
|
12
12
|
|
13
13
|
const { t } = useI18n()
|
14
14
|
</script>
|
package/composables/config.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { computed } from 'vue'
|
2
|
-
import {
|
2
|
+
import { useValaxyConfig } from 'valaxy'
|
3
3
|
import type { YunTheme } from '../types'
|
4
4
|
|
5
5
|
/**
|
@@ -7,6 +7,6 @@ import type { YunTheme } from '../types'
|
|
7
7
|
* @returns
|
8
8
|
*/
|
9
9
|
export function useThemeConfig<ThemeConfig = YunTheme.Config>() {
|
10
|
-
const config =
|
10
|
+
const config = useValaxyConfig<ThemeConfig>()
|
11
11
|
return computed(() => config!.value.themeConfig)
|
12
12
|
}
|
package/docs/zh-CN/README.md
CHANGED
@@ -1,40 +1,3 @@
|
|
1
1
|
# valaxy-theme-yun 文档
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
新建 `pages/links/index.md` 文件。
|
6
|
-
|
7
|
-
您可以在 `frontmatter` 编写链接信息。
|
8
|
-
|
9
|
-
- `links`: 友情链接信息(可以是 YAML 数组形式,也可以是一个 JSON 文件链接)
|
10
|
-
- `random`: 是否随机展示
|
11
|
-
|
12
|
-
譬如:
|
13
|
-
|
14
|
-
```md
|
15
|
-
---
|
16
|
-
title: 我的小伙伴们
|
17
|
-
keywords: 链接
|
18
|
-
description: 云游的小伙伴们
|
19
|
-
links:
|
20
|
-
- url: https://www.yunyoujun.cn
|
21
|
-
avatar: https://www.yunyoujun.cn/images/avatar.jpg
|
22
|
-
name: 云游君
|
23
|
-
blog: 云游君的小站
|
24
|
-
desc: 希望能成为一个有趣的人。
|
25
|
-
email: me@yunyoujun.cn
|
26
|
-
color: "#0078e7"
|
27
|
-
- url: https://valaxy.site
|
28
|
-
avatar: https://valaxy.site/favicon.svg
|
29
|
-
name: Valaxy Org
|
30
|
-
blog: Valaxy Site
|
31
|
-
desc: 下一代静态博客框架
|
32
|
-
email: i@valaxy.site
|
33
|
-
color: "#6058d9"
|
34
|
-
# 也可以是一个 JSON 链接
|
35
|
-
# links: https://friends.yunyoujun.cn/links.json
|
36
|
-
random: true
|
37
|
-
---
|
38
|
-
|
39
|
-
<YunLinks :links="frontmatter.links" :random="frontmatter.random" />
|
40
|
-
```
|
3
|
+
- [主题配置](./config.md)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
---
|
2
|
+
title: 主题配置
|
3
|
+
---
|
4
|
+
|
5
|
+
## 首页
|
6
|
+
|
7
|
+
### 标语动画
|
8
|
+
|
9
|
+
首页的垂直交错排列文字效果。默认开启。
|
10
|
+
|
11
|
+
- `enable`: 是否开启
|
12
|
+
- `title`: 设置文字内容
|
13
|
+
- `cloud`: 在首页下方显示流动的云
|
14
|
+
- `enable`: 是否开启
|
15
|
+
|
16
|
+
```ts
|
17
|
+
import { defineConfig } from 'valaxy'
|
18
|
+
import type { ThemeConfig } from 'valaxy-theme-yun'
|
19
|
+
|
20
|
+
export default defineConfig<ThemeConfig>({
|
21
|
+
theme: 'yun',
|
22
|
+
|
23
|
+
themeConfig: {
|
24
|
+
banner: {
|
25
|
+
enable: true,
|
26
|
+
title: '云游君的小站',
|
27
|
+
cloud: {
|
28
|
+
enable: true
|
29
|
+
}
|
30
|
+
},
|
31
|
+
}
|
32
|
+
})
|
33
|
+
```
|
34
|
+
|
35
|
+
> 如果您想要更改云的色彩,请更改 `var(--yun-c-cloud)` 的值
|
36
|
+
|
37
|
+
```scss
|
38
|
+
// 新建 styles/css-vars.scss
|
39
|
+
:root {
|
40
|
+
--yun-c-cloud: red;
|
41
|
+
}
|
42
|
+
```
|
43
|
+
|
44
|
+
## 自定义友情链接
|
45
|
+
|
46
|
+
新建 `pages/links/index.md` 文件。
|
47
|
+
|
48
|
+
您可以在 `frontmatter` 编写链接信息。
|
49
|
+
|
50
|
+
- `links`: 友情链接信息(可以是 YAML 数组形式,也可以是一个 JSON 文件链接)
|
51
|
+
- `random`: 是否随机展示
|
52
|
+
|
53
|
+
譬如:
|
54
|
+
|
55
|
+
```md
|
56
|
+
---
|
57
|
+
title: 我的小伙伴们
|
58
|
+
keywords: 链接
|
59
|
+
description: 云游的小伙伴们
|
60
|
+
links:
|
61
|
+
- url: https://www.yunyoujun.cn
|
62
|
+
avatar: https://www.yunyoujun.cn/images/avatar.jpg
|
63
|
+
name: 云游君
|
64
|
+
blog: 云游君的小站
|
65
|
+
desc: 希望能成为一个有趣的人。
|
66
|
+
email: me@yunyoujun.cn
|
67
|
+
color: "#0078e7"
|
68
|
+
- url: https://valaxy.site
|
69
|
+
avatar: https://valaxy.site/favicon.svg
|
70
|
+
name: Valaxy Org
|
71
|
+
blog: Valaxy Site
|
72
|
+
desc: 下一代静态博客框架
|
73
|
+
email: i@valaxy.site
|
74
|
+
color: "#6058d9"
|
75
|
+
# 也可以是一个 JSON 链接
|
76
|
+
# links: https://friends.yunyoujun.cn/links.json
|
77
|
+
random: true
|
78
|
+
---
|
79
|
+
|
80
|
+
<YunLinks :links="frontmatter.links" :random="frontmatter.random" />
|
81
|
+
```
|
package/layouts/home.vue
CHANGED
@@ -17,6 +17,7 @@ const themeConfig = useThemeConfig()
|
|
17
17
|
|
18
18
|
<YunBanner />
|
19
19
|
<YunSay v-if="themeConfig.say.enable" w="full" />
|
20
|
+
<YunCloud v-if="themeConfig.banner.cloud.enable" />
|
20
21
|
<YunNotice
|
21
22
|
v-if="themeConfig.notice.enable"
|
22
23
|
:content="themeConfig.notice.content" mt="4"
|
package/layouts/post.vue
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { computed } from 'vue'
|
3
|
-
import {
|
3
|
+
import { useFrontmatter, useFullUrl, useSiteConfig } from 'valaxy'
|
4
4
|
|
5
|
-
const
|
5
|
+
const siteConfig = useSiteConfig()
|
6
6
|
const frontmatter = useFrontmatter()
|
7
7
|
const url = useFullUrl()
|
8
8
|
|
@@ -10,7 +10,7 @@ const showSponsor = computed(() => {
|
|
10
10
|
if (typeof frontmatter.value.sponsor === 'boolean')
|
11
11
|
return frontmatter.value.sponsor
|
12
12
|
|
13
|
-
return
|
13
|
+
return siteConfig.value.sponsor.enable
|
14
14
|
})
|
15
15
|
</script>
|
16
16
|
|
@@ -24,7 +24,7 @@ const showSponsor = computed(() => {
|
|
24
24
|
|
25
25
|
<template #main-content-after>
|
26
26
|
<YunSponsor v-if="showSponsor" m="t-6" />
|
27
|
-
<ValaxyCopyright v-if="frontmatter.copyright ||
|
27
|
+
<ValaxyCopyright v-if="frontmatter.copyright || siteConfig.license.enabled" :url="url" m="y-4" />
|
28
28
|
</template>
|
29
29
|
|
30
30
|
<template #aside-custom>
|
package/node/config.ts
CHANGED
package/node/unocss.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
import type { ResolvedValaxyOptions } from 'valaxy'
|
2
|
-
import type {
|
2
|
+
import type { UserThemeConfig } from '../types'
|
3
3
|
|
4
4
|
/**
|
5
5
|
* generateSafelist by config
|
6
6
|
* @param themeConfig
|
7
7
|
* @returns
|
8
8
|
*/
|
9
|
-
export function generateSafelist(options: ResolvedValaxyOptions<
|
9
|
+
export function generateSafelist(options: ResolvedValaxyOptions<UserThemeConfig>) {
|
10
10
|
const themeConfig = options.config.themeConfig || {}
|
11
11
|
const safelist = []
|
12
12
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "valaxy-theme-yun",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.13.0-0",
|
4
4
|
"author": {
|
5
5
|
"email": "me@yunyoujun.cn",
|
6
6
|
"name": "YunYouJun",
|
@@ -22,6 +22,6 @@
|
|
22
22
|
"valaxy-addon-waline": "0.0.8"
|
23
23
|
},
|
24
24
|
"devDependencies": {
|
25
|
-
"valaxy": "0.
|
25
|
+
"valaxy": "0.13.0-0"
|
26
26
|
}
|
27
27
|
}
|
package/styles/vars.scss
CHANGED
@@ -27,6 +27,7 @@ $common: () !default;
|
|
27
27
|
$common: map.merge(
|
28
28
|
(
|
29
29
|
"post-card-max-width": 900px,
|
30
|
+
"c-cloud": "white"
|
30
31
|
),
|
31
32
|
$common
|
32
33
|
);
|
@@ -35,7 +36,7 @@ $z-index: () !default;
|
|
35
36
|
$z-index: map.merge(
|
36
37
|
(
|
37
38
|
"toc-btn": 7,
|
38
|
-
"cloud":
|
39
|
+
"cloud": 7,
|
39
40
|
"go-down": 9,
|
40
41
|
"backdrop": 9,
|
41
42
|
"sidebar": 10,
|
package/types/index.d.ts
CHANGED
@@ -3,6 +3,22 @@ export * from '../composables'
|
|
3
3
|
export namespace YunTheme {
|
4
4
|
export type Config = ThemeConfig
|
5
5
|
export type Sidebar = any
|
6
|
+
|
7
|
+
export type Banner = {
|
8
|
+
enable: boolean
|
9
|
+
/**
|
10
|
+
* 标题
|
11
|
+
*/
|
12
|
+
title: string
|
13
|
+
|
14
|
+
/**
|
15
|
+
* 首页下方的动态云
|
16
|
+
* If you want change color of cloud, please change css var `--yun-c-cloud`
|
17
|
+
*/
|
18
|
+
cloud: {
|
19
|
+
enable: boolean
|
20
|
+
}
|
21
|
+
}
|
6
22
|
}
|
7
23
|
|
8
24
|
/**
|
@@ -26,13 +42,7 @@ export interface ThemeConfig {
|
|
26
42
|
/**
|
27
43
|
* 首页标语
|
28
44
|
*/
|
29
|
-
banner:
|
30
|
-
enable: boolean
|
31
|
-
/**
|
32
|
-
* 标题
|
33
|
-
*/
|
34
|
-
title: string
|
35
|
-
}
|
45
|
+
banner: YunTheme.Banner
|
36
46
|
|
37
47
|
bg_image: {
|
38
48
|
enable: boolean
|
@@ -135,4 +145,4 @@ export interface ThemeConfig {
|
|
135
145
|
}
|
136
146
|
}
|
137
147
|
|
138
|
-
export type
|
148
|
+
export type UserThemeConfig = Partial<ThemeConfig>
|
package/valaxy.config.ts
CHANGED
package/components/YunSearch.vue
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
<script lang="ts" setup>
|
2
|
-
import { computed } from '@vue/reactivity'
|
3
|
-
import { useConfig } from 'valaxy'
|
4
|
-
import { onMounted, onUnmounted, ref } from 'vue'
|
5
|
-
import { useI18n } from 'vue-i18n'
|
6
|
-
|
7
|
-
const config = useConfig()
|
8
|
-
const { t } = useI18n()
|
9
|
-
|
10
|
-
// to avoid loading the docsearch js upfront (which is more than 1/3 of the
|
11
|
-
// payload), we delay initializing it until the user has actually clicked or
|
12
|
-
// hit the hotkey to invoke it.
|
13
|
-
const loaded = ref(false)
|
14
|
-
|
15
|
-
function load() {
|
16
|
-
if (!loaded.value)
|
17
|
-
loaded.value = true
|
18
|
-
}
|
19
|
-
|
20
|
-
const isAlgolia = computed(() => config.value.search.type === 'algolia')
|
21
|
-
|
22
|
-
onMounted(() => {
|
23
|
-
if (!isAlgolia.value)
|
24
|
-
return
|
25
|
-
|
26
|
-
const handleSearchHotKey = (e: KeyboardEvent) => {
|
27
|
-
if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
|
28
|
-
e.preventDefault()
|
29
|
-
load()
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
31
|
-
remove()
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
|
-
const remove = () => {
|
36
|
-
window.removeEventListener('keydown', handleSearchHotKey)
|
37
|
-
}
|
38
|
-
|
39
|
-
window.addEventListener('keydown', handleSearchHotKey)
|
40
|
-
|
41
|
-
onUnmounted(remove)
|
42
|
-
})
|
43
|
-
|
44
|
-
const trigger = () => {
|
45
|
-
const e = new Event('keydown') as any
|
46
|
-
|
47
|
-
e.key = 'k'
|
48
|
-
e.metaKey = true
|
49
|
-
|
50
|
-
window.dispatchEvent(e)
|
51
|
-
}
|
52
|
-
</script>
|
53
|
-
|
54
|
-
<template>
|
55
|
-
<div>
|
56
|
-
<button class="search-btn popup-trigger yun-icon-btn" :title="t('menu.search')" @click="trigger">
|
57
|
-
<div i-ri-search-line />
|
58
|
-
<!-- <div v-else text="!2xl" i-ri-close-line /> -->
|
59
|
-
</button>
|
60
|
-
|
61
|
-
<AlgoliaSearchBox v-if="isAlgolia && loaded" />
|
62
|
-
</div>
|
63
|
-
</template>
|
64
|
-
|
65
|
-
<style lang="scss">
|
66
|
-
@use 'sass:map';
|
67
|
-
@use 'valaxy/client/styles/vars' as *;
|
68
|
-
|
69
|
-
.search-btn {
|
70
|
-
position: fixed;
|
71
|
-
top: 0.6rem;
|
72
|
-
right: 0.8rem;
|
73
|
-
|
74
|
-
color: var(--va-c-primary);
|
75
|
-
z-index: var(--yun-z-search-btn);
|
76
|
-
}
|
77
|
-
|
78
|
-
.search-popup {
|
79
|
-
position: fixed;
|
80
|
-
top: 0;
|
81
|
-
left: 0;
|
82
|
-
width: 100%;
|
83
|
-
height: 100%;
|
84
|
-
|
85
|
-
backdrop-filter: blur(30px);
|
86
|
-
-webkit-backdrop-filter: blur(30px);
|
87
|
-
|
88
|
-
text-align: center;
|
89
|
-
padding-top: 3.5rem;
|
90
|
-
margin: 0;
|
91
|
-
z-index: var(--yun-z-search-popup);
|
92
|
-
transition: 0.6s;
|
93
|
-
}
|
94
|
-
|
95
|
-
.search-header {
|
96
|
-
.close-icon {
|
97
|
-
position: fixed;
|
98
|
-
top: 0.6rem;
|
99
|
-
right: 0.8rem;
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
.search-input {
|
104
|
-
background: transparent;
|
105
|
-
color: var(--va-c-text);
|
106
|
-
font-size: 1.5rem;
|
107
|
-
border-radius: 3rem;
|
108
|
-
padding: 1rem 1.5rem;
|
109
|
-
border: 1px solid var(--va-c-gray);
|
110
|
-
box-sizing: border-box;
|
111
|
-
width: 90%;
|
112
|
-
max-width: 800px;
|
113
|
-
font-family: var(--va-font-serif);
|
114
|
-
font-weight: 900;
|
115
|
-
text-align: center;
|
116
|
-
}
|
117
|
-
|
118
|
-
.popup {
|
119
|
-
.search-icon, .close-icon {
|
120
|
-
display: inline-block;
|
121
|
-
width: 2rem;
|
122
|
-
height: 2rem;
|
123
|
-
padding: 0.5rem;
|
124
|
-
|
125
|
-
.icon {
|
126
|
-
width: 2rem;
|
127
|
-
height: 2rem;
|
128
|
-
}
|
129
|
-
}
|
130
|
-
}
|
131
|
-
</style>
|