valaxy-theme-yun 0.14.60 → 0.15.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 +10 -1
- package/assets/images/light/bg-img.jpg +0 -0
- package/assets/images/light/sidebar-bg-img.webp +0 -0
- package/assets/images/none.jpg +0 -0
- package/components/ValaxyMain.vue +1 -1
- package/components/YunBanner.vue +16 -4
- package/components/YunBg.vue +20 -18
- package/components/YunCard.vue +3 -2
- package/components/YunFuseSearch.vue +27 -22
- package/components/YunLinks.vue +13 -1
- package/components/YunLoading.vue +37 -0
- package/components/YunNotice.vue +1 -1
- package/components/YunOutlineItem.vue +1 -1
- package/components/YunOverview.vue +2 -2
- package/components/YunPostCard.vue +19 -8
- package/components/YunPostCategories.vue +4 -1
- package/components/YunPostList.vue +0 -1
- package/components/YunPostTags.vue +7 -3
- package/components/YunSearchBtn.vue +11 -2
- package/components/YunSearchTrigger.vue +11 -3
- package/components/YunSidebar.vue +7 -4
- package/composables/config.ts +0 -1
- package/composables/helper.ts +0 -1
- package/docs/zh-CN/config.md +17 -1
- package/layouts/404.vue +33 -0
- package/layouts/home.vue +2 -2
- package/node/config.ts +4 -3
- package/node/unocss.ts +0 -2
- package/package.json +5 -5
- package/styles/common/markdown.scss +35 -0
- package/styles/index.scss +2 -0
- package/styles/layout/index.scss +0 -2
- package/styles/vars.scss +2 -2
- package/styles/widgets/banner.scss +11 -1
- package/tsconfig.json +11 -11
- package/types/index.d.ts +5 -5
- package/unocss.config.ts +10 -1
- package/utils/index.ts +2 -2
package/App.vue
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { useHead } from '@vueuse/head'
|
3
|
-
import { useSiteConfig } from 'valaxy'
|
3
|
+
import { useAppStore, useSiteConfig } from 'valaxy'
|
4
|
+
import { onMounted } from 'vue'
|
4
5
|
import { useThemeConfig } from './composables'
|
5
6
|
|
6
7
|
useHead({
|
@@ -14,6 +15,11 @@ useHead({
|
|
14
15
|
|
15
16
|
const siteConfig = useSiteConfig()
|
16
17
|
const themeConfig = useThemeConfig()
|
18
|
+
|
19
|
+
const app = useAppStore()
|
20
|
+
onMounted(() => {
|
21
|
+
app.showLoading = false
|
22
|
+
})
|
17
23
|
</script>
|
18
24
|
|
19
25
|
<template>
|
@@ -22,4 +28,7 @@ const themeConfig = useThemeConfig()
|
|
22
28
|
<YunBg v-if="themeConfig.bg_image.enable" />
|
23
29
|
</slot>
|
24
30
|
<YunSearchTrigger v-if="siteConfig.search.enable" />
|
31
|
+
<Transition name="fade">
|
32
|
+
<YunLoading v-if="app.showLoading" />
|
33
|
+
</Transition>
|
25
34
|
</template>
|
Binary file
|
Binary file
|
Binary file
|
@@ -74,7 +74,7 @@ onContentUpdated(() => {
|
|
74
74
|
</script>
|
75
75
|
|
76
76
|
<template>
|
77
|
-
<main class="yun-main lt-md:ml-0" flex="~">
|
77
|
+
<main class="yun-main md:pl-$va-sidebar-width lt-md:ml-0" flex="~">
|
78
78
|
<div w="full" flex="~">
|
79
79
|
<slot name="main">
|
80
80
|
<div class="content" :class="!aside && 'no-aside'" flex="~ col grow" w="full" p="l-4 lt-md:0">
|
package/components/YunBanner.vue
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
*/
|
7
7
|
|
8
8
|
import type { CSSProperties } from 'vue'
|
9
|
-
import { computed } from 'vue'
|
9
|
+
import { computed, ref } from 'vue'
|
10
10
|
import { random } from 'valaxy'
|
11
11
|
import { useThemeConfig } from '../composables'
|
12
12
|
|
@@ -28,12 +28,19 @@ const bannerStyles = computed<CSSProperties>(() => {
|
|
28
28
|
'--banner-line-height': `calc(var(--banner-height, 100vh) / 2 - ${lineH.value}rem)`,
|
29
29
|
}
|
30
30
|
})
|
31
|
+
|
32
|
+
const playExtendLine = ref(true)
|
31
33
|
</script>
|
32
34
|
|
33
35
|
<template>
|
34
|
-
<div id="banner" :style="bannerStyles">
|
36
|
+
<div id="yun-banner" :style="bannerStyles">
|
35
37
|
<div class="banner-line-container">
|
36
|
-
<div
|
38
|
+
<div
|
39
|
+
class="banner-line vertical-line-top"
|
40
|
+
:class="{
|
41
|
+
active: playExtendLine,
|
42
|
+
}"
|
43
|
+
/>
|
37
44
|
</div>
|
38
45
|
<div class="banner-char-container">
|
39
46
|
<div v-for="c, i in themeConfig.banner.title" :key="i" class="char-box">
|
@@ -49,7 +56,12 @@ const bannerStyles = computed<CSSProperties>(() => {
|
|
49
56
|
</div>
|
50
57
|
</div>
|
51
58
|
<div class="banner-line-container bottom">
|
52
|
-
<div
|
59
|
+
<div
|
60
|
+
class="banner-line vertical-line-bottom"
|
61
|
+
:class="{
|
62
|
+
active: playExtendLine,
|
63
|
+
}"
|
64
|
+
/>
|
53
65
|
</div>
|
54
66
|
<YunCloud v-if="themeConfig.banner.cloud?.enable" />
|
55
67
|
<YunGoDown />
|
package/components/YunBg.vue
CHANGED
@@ -6,37 +6,39 @@ import { useThemeConfig } from '../composables'
|
|
6
6
|
|
7
7
|
const themeConfig = useThemeConfig()
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if (typeof themeConfig.value.bg_image.url !== 'undefined') {
|
10
|
+
const bgImgOpacity = useCssVar('--yun-bg-img-opacity')
|
11
|
+
if (themeConfig.value.bg_image.opacity)
|
12
|
+
bgImgOpacity.value = themeConfig.value.bg_image.opacity.toString() || '1'
|
12
13
|
|
13
|
-
const bgImgUrl = computed(() => {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
})
|
14
|
+
const bgImgUrl = computed(() => {
|
15
|
+
return isDark.value
|
16
|
+
? themeConfig.value.bg_image.dark
|
17
|
+
: themeConfig.value.bg_image.url
|
18
|
+
})
|
18
19
|
|
19
|
-
const bgImgCssVar = useCssVar('--
|
20
|
+
const bgImgCssVar = useCssVar('--yun-bg-img')
|
20
21
|
|
21
|
-
watch(() => bgImgUrl.value, () => {
|
22
|
-
|
23
|
-
}, { immediate: true })
|
22
|
+
watch(() => bgImgUrl.value, () => {
|
23
|
+
bgImgCssVar.value = `url('${bgImgUrl.value}')`
|
24
|
+
}, { immediate: true })
|
25
|
+
}
|
24
26
|
</script>
|
25
27
|
|
26
28
|
<template>
|
27
|
-
<div class="
|
29
|
+
<div class="yun-bg" />
|
28
30
|
</template>
|
29
31
|
|
30
32
|
<style lang="scss">
|
31
33
|
@use 'valaxy/client/styles/mixins/index.scss' as *;
|
32
34
|
|
33
|
-
.
|
35
|
+
.yun-bg {
|
34
36
|
position: fixed;
|
35
37
|
width: 100%;
|
36
38
|
height: 100%;
|
37
39
|
z-index: -1;
|
38
40
|
|
39
|
-
background-image: var(--
|
41
|
+
background-image: var(--yun-bg-img);
|
40
42
|
background-size: cover;
|
41
43
|
background-position: center;
|
42
44
|
background-repeat: no-repeat;
|
@@ -44,12 +46,12 @@ watch(() => bgImgUrl.value, () => {
|
|
44
46
|
background-attachment: fixed;
|
45
47
|
animation-name: bgFadeIn;
|
46
48
|
animation-duration: 2s;
|
47
|
-
opacity: var(--
|
49
|
+
opacity: var(--yun-bg-img-opacity, 1);
|
48
50
|
}
|
49
51
|
|
50
52
|
// for ios
|
51
53
|
@include ios {
|
52
|
-
.
|
54
|
+
.yun-bg {
|
53
55
|
background-attachment: scroll;
|
54
56
|
}
|
55
57
|
}
|
@@ -60,7 +62,7 @@ watch(() => bgImgUrl.value, () => {
|
|
60
62
|
}
|
61
63
|
|
62
64
|
to {
|
63
|
-
opacity: var(--
|
65
|
+
opacity: var(--yun-bg-img-opacity, 1);
|
64
66
|
}
|
65
67
|
}
|
66
68
|
</style>
|
package/components/YunCard.vue
CHANGED
@@ -3,11 +3,12 @@ defineProps<{ cover?: string }>()
|
|
3
3
|
</script>
|
4
4
|
|
5
5
|
<template>
|
6
|
-
<div class="yun-card flex-center" flex="col" min-h="100px" bg="$va-c-bg-light">
|
6
|
+
<div class="yun-card flex-center rounded" flex="col" min-h="100px" bg="$va-c-bg-light">
|
7
7
|
<img
|
8
8
|
v-if="cover"
|
9
9
|
width="640" height="360"
|
10
|
-
class="object-cover select-none" h="64 md:sm" w="full"
|
10
|
+
class="object-cover select-none" h="64 md:sm" w="full"
|
11
|
+
:src="cover"
|
11
12
|
>
|
12
13
|
|
13
14
|
<div v-if="$slots.header" class="yun-card-header">
|
@@ -7,6 +7,8 @@ import { useBodyScrollLock, useSiteConfig } from 'valaxy'
|
|
7
7
|
import { useRouter } from 'vue-router'
|
8
8
|
import type { FuseListItem } from 'valaxy/types'
|
9
9
|
|
10
|
+
import { onClickOutside } from '@vueuse/core'
|
11
|
+
|
10
12
|
const props = defineProps<{
|
11
13
|
open: boolean
|
12
14
|
}>()
|
@@ -69,6 +71,10 @@ function jumpToLink(link: string) {
|
|
69
71
|
router.push(link)
|
70
72
|
emit('close')
|
71
73
|
}
|
74
|
+
|
75
|
+
onClickOutside(searchInputRef, () => {
|
76
|
+
// emit('close')
|
77
|
+
})
|
72
78
|
</script>
|
73
79
|
|
74
80
|
<template>
|
@@ -79,7 +85,9 @@ function jumpToLink(link: string) {
|
|
79
85
|
>
|
80
86
|
<div
|
81
87
|
v-if="open" ref="searchContainer"
|
82
|
-
class="yun-popup yun-search-popup yun-fuse-search flex-center" flex="col"
|
88
|
+
class="yun-popup yun-search-popup yun-fuse-search flex-center pointer-events-auto" flex="col"
|
89
|
+
justify="start"
|
90
|
+
pt-12
|
83
91
|
>
|
84
92
|
<div class="yun-search-input-container flex-center" w="full">
|
85
93
|
<input ref="searchInputRef" v-model="input" class="yun-search-input" :placeholder="t('search.placeholder')">
|
@@ -87,27 +95,25 @@ function jumpToLink(link: string) {
|
|
87
95
|
<div v-if="input" class="flex-center" w="full" py="4">
|
88
96
|
{{ t('search.hits', results.length || 0) }}
|
89
97
|
</div>
|
90
|
-
<div overflow="auto" flex="~
|
98
|
+
<div v-if="results.length > 0" overflow="auto" flex="~" w="full">
|
91
99
|
<div class="yun-fuse-result-container" flex="~ col" w="full">
|
92
|
-
<
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
>
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
</div>
|
110
|
-
</template>
|
100
|
+
<div
|
101
|
+
v-for="result in results" :key="result.item.title"
|
102
|
+
:to="result.item.link"
|
103
|
+
class="yun-fuse-result-item text-$va-c-text hover:(text-$va-c-bg bg-$va-c-text-dark bg-opacity-100)"
|
104
|
+
flex="~ col" pb-2
|
105
|
+
@click="jumpToLink(result.item.link)"
|
106
|
+
>
|
107
|
+
<h3 font="serif black">
|
108
|
+
{{ result.item.title }}
|
109
|
+
</h3>
|
110
|
+
<span text="sm" opacity="80">
|
111
|
+
{{ result.item.excerpt }}
|
112
|
+
</span>
|
113
|
+
<span text-xs opacity-50 mt="1">
|
114
|
+
Score Index: {{ result.refIndex }}
|
115
|
+
</span>
|
116
|
+
</div>
|
111
117
|
</div>
|
112
118
|
</div>
|
113
119
|
</div>
|
@@ -126,7 +132,6 @@ function jumpToLink(link: string) {
|
|
126
132
|
-webkit-backdrop-filter: blur(30px);
|
127
133
|
|
128
134
|
text-align: center;
|
129
|
-
padding-top: 3.5rem;
|
130
135
|
margin: 0;
|
131
136
|
z-index: var(--yun-z-search-popup);
|
132
137
|
transition: 0.6s;
|
package/components/YunLinks.vue
CHANGED
@@ -14,9 +14,17 @@ interface LinkType {
|
|
14
14
|
const props = defineProps<{
|
15
15
|
links: string | LinkType[]
|
16
16
|
random: boolean
|
17
|
+
/**
|
18
|
+
* @description: 图片加载失败时显示的图片
|
19
|
+
*/
|
20
|
+
errorImg?: string
|
17
21
|
}>()
|
18
22
|
|
19
23
|
const { data } = useRandomData(props.links, props.random)
|
24
|
+
|
25
|
+
function onError(e: Event) {
|
26
|
+
onImgError(e, props.errorImg)
|
27
|
+
}
|
20
28
|
</script>
|
21
29
|
|
22
30
|
<template>
|
@@ -25,7 +33,11 @@ const { data } = useRandomData(props.links, props.random)
|
|
25
33
|
<li v-for="link, i in data" :key="i" class="link-item" :style="`--primary-color: ${link.color}`">
|
26
34
|
<a class="link-url" p="x-4 y-2" :href="link.url" :title="link.name" alt="portrait" rel="friend">
|
27
35
|
<div class="link-left">
|
28
|
-
<img
|
36
|
+
<img
|
37
|
+
class="link-avatar" width="64" height="64" w="16" h="16"
|
38
|
+
loading="lazy" :src="link.avatar" :alt="link.name"
|
39
|
+
@error="onError"
|
40
|
+
>
|
29
41
|
</div>
|
30
42
|
<div class="link-info" m="l-2">
|
31
43
|
<div class="link-blog" font="serif black">{{ link.blog }}</div>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
class="yun-page-loading"
|
4
|
+
absolute left-0 right-0 bottom-0 top-0 flex justify="center" items-center
|
5
|
+
z-10
|
6
|
+
bg="$va-c-bg"
|
7
|
+
>
|
8
|
+
<div class="spinner" />
|
9
|
+
</div>
|
10
|
+
</template>
|
11
|
+
|
12
|
+
<style scoped lang="scss">
|
13
|
+
.spinner {
|
14
|
+
width: 60px;
|
15
|
+
height: 60px;
|
16
|
+
border: 1px solid var(--va-c-primary);
|
17
|
+
margin: 100px auto;
|
18
|
+
animation: rotateplane 1.2s infinite ease-in-out;
|
19
|
+
}
|
20
|
+
|
21
|
+
@keyframes rotateplane {
|
22
|
+
0% {
|
23
|
+
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
|
24
|
+
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
|
25
|
+
}
|
26
|
+
|
27
|
+
50% {
|
28
|
+
transform: perspective(120px) rotateX(-180deg) rotateY(0deg);
|
29
|
+
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
|
30
|
+
}
|
31
|
+
|
32
|
+
100% {
|
33
|
+
transform: perspective(120px) rotateX(-180deg) rotateY(-180deg);
|
34
|
+
-webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-180deg);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
</style>
|
package/components/YunNotice.vue
CHANGED
@@ -14,7 +14,7 @@ const { icon, styles } = usePostProperty(props.post.type)
|
|
14
14
|
</script>
|
15
15
|
|
16
16
|
<template>
|
17
|
-
<YunCard m="y-4 auto" :class="post.cover ? 'post-card-image' : 'post-card'" :style="styles as StyleValue">
|
17
|
+
<YunCard m="y-4 auto" :class="post.cover ? 'post-card-image' : 'post-card'" overflow="hidden" :style="styles as StyleValue">
|
18
18
|
<div class="flex flex-1 of-hidden justify-start items-start post-card-info" w="full">
|
19
19
|
<img
|
20
20
|
v-if="post.cover"
|
@@ -25,7 +25,7 @@ const { icon, styles } = usePostProperty(props.post.type)
|
|
25
25
|
class="cover object-cover object-center md:shadow"
|
26
26
|
>
|
27
27
|
|
28
|
-
<div class="flex flex-col
|
28
|
+
<div class="flex flex-col items-center justify-center" :class="post.cover && 'h-54'" w="full">
|
29
29
|
<AppLink
|
30
30
|
class="post-title-link cursor-pointer"
|
31
31
|
:to="post.path || ''"
|
@@ -38,9 +38,13 @@ const { icon, styles } = usePostProperty(props.post.type)
|
|
38
38
|
|
39
39
|
<YunPostMeta :frontmatter="post" />
|
40
40
|
|
41
|
-
<div
|
42
|
-
|
43
|
-
|
41
|
+
<div class="flex flex-grow" justify="center" items="center">
|
42
|
+
<div v-if="post.excerpt_type === 'text'" py="1" />
|
43
|
+
<div v-if="post.excerpt" class="markdown-body" op="80" text="left" w="full" p="x-6 lt-sm:4 y-2" v-html="post.excerpt" />
|
44
|
+
<div v-else m="b-5" />
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<!-- <div m="b-5" /> -->
|
44
48
|
|
45
49
|
<a
|
46
50
|
v-if="post.url"
|
@@ -56,12 +60,19 @@ const { icon, styles } = usePostProperty(props.post.type)
|
|
56
60
|
</div>
|
57
61
|
|
58
62
|
<!-- always show -->
|
59
|
-
<div
|
60
|
-
|
63
|
+
<div
|
64
|
+
w="full" class="yun-card-actions flex justify-between"
|
65
|
+
min-h="10"
|
66
|
+
border="t" text="sm"
|
67
|
+
>
|
68
|
+
<div class="post-categories inline-flex" flex="wrap 1" items="center">
|
61
69
|
<YunPostCategories m="l-1" :categories="post.categories" />
|
62
70
|
</div>
|
63
71
|
|
64
|
-
<div
|
72
|
+
<div
|
73
|
+
class="post-tags inline-flex" items="center"
|
74
|
+
flex="wrap 1" justify="end" m="1"
|
75
|
+
>
|
65
76
|
<template v-if="post.tags">
|
66
77
|
<YunPostTags :tags="post.tags" />
|
67
78
|
</template>
|
@@ -12,7 +12,10 @@ defineProps<{
|
|
12
12
|
path: '/categories/',
|
13
13
|
query: { category: Array.isArray(categories) ? categories[categories.length - 1] : categories },
|
14
14
|
}"
|
15
|
-
class="post-category inline-flex-center"
|
15
|
+
class="transition post-category inline-flex-center text-xs border-$va-c-divider hover:(text-blue-500 border-blue-500)"
|
16
|
+
px-2 h="7"
|
17
|
+
border rounded-full
|
18
|
+
bg="hover:(blue-500 opacity-10)"
|
16
19
|
>
|
17
20
|
<div m="x-1" inline-flex i-ri-folder-2-line />
|
18
21
|
<span>
|
@@ -8,10 +8,14 @@ defineProps<{
|
|
8
8
|
|
9
9
|
<template>
|
10
10
|
<router-link
|
11
|
-
v-for="tag, i in tags" :key="i" :to="{ path: '/tags/', query: { tag } }"
|
12
|
-
class="post-tag inline-flex-center"
|
11
|
+
v-for="tag, i in tags" :key="i" :to="{ path: '/tags/', query: { tag } }" ml-1
|
12
|
+
class="transition post-tag inline-flex-center text-xs border-$va-c-divider hover:(text-blue-500 border-blue-500)"
|
13
|
+
px-2 h="7"
|
14
|
+
rounded-full
|
15
|
+
border
|
16
|
+
bg="hover:(blue-500 opacity-10)"
|
13
17
|
>
|
14
|
-
<div m="r-1" i-ri-price-tag-3-line />
|
18
|
+
<!-- <div m="r-1" i-ri-price-tag-3-line /> -->
|
15
19
|
<span>{{ tag }}</span>
|
16
20
|
</router-link>
|
17
21
|
</template>
|
@@ -1,17 +1,26 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { useI18n } from 'vue-i18n'
|
3
3
|
|
4
|
-
withDefaults(defineProps<{
|
4
|
+
const props = withDefaults(defineProps<{
|
5
5
|
open?: boolean
|
6
6
|
}>(), {
|
7
7
|
open: false,
|
8
8
|
})
|
9
9
|
|
10
|
+
const emit = defineEmits(['close', 'open'])
|
11
|
+
|
10
12
|
const { t } = useI18n()
|
13
|
+
|
14
|
+
function onClick() {
|
15
|
+
if (props.open)
|
16
|
+
emit('close')
|
17
|
+
else
|
18
|
+
emit('open')
|
19
|
+
}
|
11
20
|
</script>
|
12
21
|
|
13
22
|
<template>
|
14
|
-
<button class="yun-search-btn popup-trigger yun-icon-btn" :title="t('menu.search')">
|
23
|
+
<button class="yun-search-btn popup-trigger yun-icon-btn" :title="t('menu.search')" @click="onClick">
|
15
24
|
<div v-if="!open" i-ri-search-line />
|
16
25
|
<div v-else text="!2xl" i-ri-close-line />
|
17
26
|
</button>
|
@@ -21,14 +21,22 @@ watch(Meta_K, (val) => {
|
|
21
21
|
togglePopup()
|
22
22
|
})
|
23
23
|
|
24
|
+
function openSearch() {
|
25
|
+
open.value = true
|
26
|
+
}
|
27
|
+
|
28
|
+
function closeSearch() {
|
29
|
+
open.value = false
|
30
|
+
}
|
31
|
+
|
24
32
|
const YunAlgoliaSearch = isAlgolia.value
|
25
33
|
? defineAsyncComponent(() => import('./third/YunAlgoliaSearch.vue'))
|
26
34
|
: () => null
|
27
35
|
</script>
|
28
36
|
|
29
37
|
<template>
|
30
|
-
<YunSearchBtn :open="open && !isAlgolia" @
|
38
|
+
<YunSearchBtn :open="open && !isAlgolia" @open="openSearch" @close="closeSearch" />
|
31
39
|
|
32
|
-
<YunAlgoliaSearch v-if="isAlgolia" :open="open" @close="
|
33
|
-
<YunFuseSearch v-else-if="isFuse" :open="open" @close="
|
40
|
+
<YunAlgoliaSearch v-if="isAlgolia" :open="open" @close="closeSearch" />
|
41
|
+
<YunFuseSearch v-else-if="isFuse" :open="open" @close="closeSearch" />
|
34
42
|
</template>
|
@@ -1,10 +1,13 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { ref } from 'vue'
|
3
|
-
import { useAppStore
|
3
|
+
import { useAppStore } from 'valaxy'
|
4
|
+
|
5
|
+
defineProps<{
|
6
|
+
showHamburger?: boolean
|
7
|
+
}>()
|
4
8
|
|
5
9
|
const app = useAppStore()
|
6
10
|
const showOverview = ref(false)
|
7
|
-
const isHome = useLayout('home')
|
8
11
|
</script>
|
9
12
|
|
10
13
|
<template>
|
@@ -14,12 +17,12 @@ const isHome = useLayout('home')
|
|
14
17
|
:active="app.isSidebarOpen"
|
15
18
|
class="menu-btn sidebar-toggle yun-icon-btn leading-4 fixed left-0.8rem top-0.6rem"
|
16
19
|
inline-flex cursor="pointer" z="$yun-z-menu-btn"
|
17
|
-
:class="
|
20
|
+
:class="showHamburger ? '' : 'md:hidden'" @click="app.toggleSidebar()"
|
18
21
|
/>
|
19
22
|
|
20
23
|
<aside
|
21
24
|
class="va-card transition sidebar fixed inset-y-0 left-0 overflow-y-auto"
|
22
|
-
:class="[app.isSidebarOpen && 'open', !
|
25
|
+
:class="[app.isSidebarOpen && 'open', !showHamburger && 'md:translate-x-0']"
|
23
26
|
text="center" bg="$yun-sidebar-bg-color contain no-repeat" z="$yun-z-sidebar"
|
24
27
|
>
|
25
28
|
<div v-if="$slots.default" class="sidebar-nav" m="t-6">
|
package/composables/config.ts
CHANGED
package/composables/helper.ts
CHANGED
package/docs/zh-CN/config.md
CHANGED
@@ -53,6 +53,7 @@ export default defineConfig<ThemeConfig>({
|
|
53
53
|
|
54
54
|
- `links`: 友情链接信息(可以是 YAML 数组形式,也可以是一个 JSON 文件链接)
|
55
55
|
- `random`: 是否随机展示
|
56
|
+
- `errorImg`: 图片加载失败时的图片链接
|
56
57
|
|
57
58
|
譬如:
|
58
59
|
|
@@ -81,5 +82,20 @@ links:
|
|
81
82
|
random: true
|
82
83
|
---
|
83
84
|
|
84
|
-
<YunLinks :links="frontmatter.links" :random="frontmatter.random" />
|
85
|
+
<YunLinks :links="frontmatter.links" :random="frontmatter.random" errorImg="https://cdn.yunyoujun.cn/img/avatar/none.jpg" />
|
86
|
+
```
|
87
|
+
|
88
|
+
## 样式
|
89
|
+
|
90
|
+
### 覆盖背景、侧边栏图片
|
91
|
+
|
92
|
+
您可以新建 `styles/css-vars.scss`,覆盖默认 CSS 变量。
|
93
|
+
|
94
|
+
```css
|
95
|
+
:root {
|
96
|
+
/* 背景图片 */
|
97
|
+
--yun-bg-img: url("https://cdn.yunyoujun.cn/img/bg/stars-timing-0-blur-30px.jpg");
|
98
|
+
/* 侧边栏背景图片 */
|
99
|
+
--yun-sidebar-bg-img: url("https://cdn.yunyoujun.cn/img/bg/alpha-stars-timing-1.webp");
|
100
|
+
}
|
85
101
|
```
|
package/layouts/404.vue
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { useI18n } from 'vue-i18n'
|
3
|
+
import { useBack } from 'valaxy'
|
4
|
+
|
5
|
+
const { t } = useI18n()
|
6
|
+
const { back } = useBack()
|
7
|
+
</script>
|
8
|
+
|
9
|
+
<template>
|
10
|
+
<YunSidebar :show-hamburger="true" />
|
11
|
+
|
12
|
+
<main class="va-main w-full h-screen" text="center" flex="~ col" justify="center" items="center">
|
13
|
+
<div class="not-found" title="404" font="mono">
|
14
|
+
404
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<router-view />
|
18
|
+
|
19
|
+
<div>
|
20
|
+
<button class="btn rounded-full" p="x-6 y-2" text="sm" m="3 t8" :title="t('button.back')" @click="back">
|
21
|
+
{{ t('button.back') }}
|
22
|
+
</button>
|
23
|
+
</div>
|
24
|
+
</main>
|
25
|
+
</template>
|
26
|
+
|
27
|
+
<style lang="scss" scoped>
|
28
|
+
// inspired by https://codepen.io/pgalor/pen/OeRWJQ
|
29
|
+
.not-found {
|
30
|
+
font-size: 10rem;
|
31
|
+
text-shadow: 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
|
32
|
+
}
|
33
|
+
</style>
|
package/layouts/home.vue
CHANGED
@@ -15,9 +15,9 @@ const isPage = computed(() => route.path.startsWith('/page'))
|
|
15
15
|
<template>
|
16
16
|
<main
|
17
17
|
class="yun-main flex-center"
|
18
|
-
:class="(isHome && !app.isSidebarOpen)
|
18
|
+
:class="(isHome && !app.isSidebarOpen) ? 'pl-0' : 'md:pl-$va-sidebar-width'" flex="~ col" w="full"
|
19
19
|
>
|
20
|
-
<YunSidebar />
|
20
|
+
<YunSidebar :show-hamburger="true" />
|
21
21
|
|
22
22
|
<template v-if="!isPage">
|
23
23
|
<YunBanner v-if="themeConfig.banner.enable" />
|
package/node/config.ts
CHANGED
@@ -18,15 +18,16 @@ export const defaultThemeConfig: ThemeConfig = {
|
|
18
18
|
|
19
19
|
bg_image: {
|
20
20
|
enable: true,
|
21
|
-
url:
|
21
|
+
// url: bgImg,
|
22
22
|
// dark: 'https://cdn.yunyoujun.cn/img/bg/dark-stars-timing-0-blur-30px.png',
|
23
23
|
},
|
24
24
|
|
25
25
|
say: {
|
26
26
|
enable: true,
|
27
|
-
api: '
|
27
|
+
api: '',
|
28
|
+
// api: 'https://el-bot-api.elpsy.cn/api/words/young',
|
28
29
|
hitokoto: {
|
29
|
-
enable:
|
30
|
+
enable: true,
|
30
31
|
api: 'https://v1.hitokoto.cn',
|
31
32
|
},
|
32
33
|
},
|
package/node/unocss.ts
CHANGED
@@ -3,8 +3,6 @@ import type { UserThemeConfig } from '../types'
|
|
3
3
|
|
4
4
|
/**
|
5
5
|
* generateSafelist by config
|
6
|
-
* @param themeConfig
|
7
|
-
* @returns
|
8
6
|
*/
|
9
7
|
export function generateSafelist(options: ResolvedValaxyOptions<UserThemeConfig>) {
|
10
8
|
const themeConfig = options.config.themeConfig || {}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "valaxy-theme-yun",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.15.0",
|
4
4
|
"author": {
|
5
5
|
"email": "me@yunyoujun.cn",
|
6
6
|
"name": "YunYouJun",
|
@@ -19,12 +19,12 @@
|
|
19
19
|
"dependencies": {
|
20
20
|
"@explosions/fireworks": "^0.0.2",
|
21
21
|
"@iconify-json/ant-design": "^1.1.10",
|
22
|
-
"@iconify-json/simple-icons": "^1.1.
|
22
|
+
"@iconify-json/simple-icons": "^1.1.73",
|
23
23
|
"animejs": "^3.2.1"
|
24
24
|
},
|
25
25
|
"devDependencies": {
|
26
|
-
"@types/animejs": "^3.1.
|
27
|
-
"valaxy": "0.
|
28
|
-
"valaxy-addon-waline": "0.1.
|
26
|
+
"@types/animejs": "^3.1.8",
|
27
|
+
"valaxy": "0.15.0",
|
28
|
+
"valaxy-addon-waline": "0.1.1"
|
29
29
|
}
|
30
30
|
}
|
@@ -57,4 +57,39 @@
|
|
57
57
|
margin-bottom: 1rem;
|
58
58
|
}
|
59
59
|
}
|
60
|
+
|
61
|
+
hr {
|
62
|
+
opacity: 0.6;
|
63
|
+
height: 2px;
|
64
|
+
border-top-width: 0;
|
65
|
+
background-color: var(--va-c-text);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
.markdown-body {
|
70
|
+
div[class*='language-'].line-numbers-mode {
|
71
|
+
/*rtl:ignore*/
|
72
|
+
padding-left: 32px;
|
73
|
+
}
|
74
|
+
|
75
|
+
.line-numbers-wrapper {
|
76
|
+
position: absolute;
|
77
|
+
top: 0;
|
78
|
+
bottom: 0;
|
79
|
+
/*rtl:ignore*/
|
80
|
+
left: 0;
|
81
|
+
z-index: 3;
|
82
|
+
/*rtl:ignore*/
|
83
|
+
border-right: 1px solid var(--va-code-block-divider-color);
|
84
|
+
padding-top: 20px;
|
85
|
+
width: 32px;
|
86
|
+
text-align: center;
|
87
|
+
font-family: var(--va-font-family-mono);
|
88
|
+
line-height: var(--va-code-line-height);
|
89
|
+
font-size: var(--va-code-font-size);
|
90
|
+
color: var(--va-code-line-number-color);
|
91
|
+
transition:
|
92
|
+
border-color 0.5s,
|
93
|
+
color 0.5s;
|
94
|
+
}
|
60
95
|
}
|
package/styles/index.scss
CHANGED
package/styles/layout/index.scss
CHANGED
package/styles/vars.scss
CHANGED
@@ -5,10 +5,10 @@ $light: () !default;
|
|
5
5
|
$light: map.merge(
|
6
6
|
(
|
7
7
|
"bg-img":
|
8
|
-
url("
|
8
|
+
url("../assets/images/light/bg-img.jpg"),
|
9
9
|
"sidebar-bg-color": var(--va-c-bg-light),
|
10
10
|
"sidebar-bg-img":
|
11
|
-
url("
|
11
|
+
url("../assets/images/light/sidebar-bg-img.webp"),
|
12
12
|
),
|
13
13
|
$light
|
14
14
|
);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
$banner-animation-duration: 1s;
|
2
2
|
$char-animation-duration: 0.4s;
|
3
3
|
|
4
|
-
#banner {
|
4
|
+
#yun-banner {
|
5
5
|
--banner-line-height: 0;
|
6
6
|
--line-animation-duration: 0.4s;
|
7
7
|
|
@@ -26,6 +26,7 @@ $char-animation-duration: 0.4s;
|
|
26
26
|
flex-direction: column;
|
27
27
|
align-items: center;
|
28
28
|
height: var(--banner-line-height);
|
29
|
+
transition: height 2s;
|
29
30
|
|
30
31
|
&.bottom {
|
31
32
|
justify-content: end;
|
@@ -43,11 +44,20 @@ $char-animation-duration: 0.4s;
|
|
43
44
|
animation-duration: var(--line-animation-duration);
|
44
45
|
animation-fill-mode: forwards;
|
45
46
|
animation-timing-function: ease-in;
|
47
|
+
animation-iteration-count: 1;
|
46
48
|
}
|
47
49
|
|
48
50
|
&-bottom {
|
49
51
|
flex-direction: column-reverse;
|
50
52
|
}
|
53
|
+
|
54
|
+
&.active {
|
55
|
+
animation-name: extend-line;
|
56
|
+
animation-duration: var(--line-animation-duration);
|
57
|
+
animation-fill-mode: forwards;
|
58
|
+
animation-timing-function: ease-in;
|
59
|
+
animation-iteration-count: 1;
|
60
|
+
}
|
51
61
|
}
|
52
62
|
|
53
63
|
@keyframes extend-line {
|
package/tsconfig.json
CHANGED
@@ -2,26 +2,26 @@
|
|
2
2
|
// we need tsconfig.json to compile without
|
3
3
|
// error: This is likely not portable. A type annotation is necessary.
|
4
4
|
"compilerOptions": {
|
5
|
-
"allowJs": true,
|
6
|
-
"baseUrl": ".",
|
7
|
-
"module": "ESNext",
|
8
5
|
"target": "ESNext",
|
9
6
|
"lib": ["DOM", "ESNext"],
|
10
|
-
"strict": true,
|
11
|
-
"esModuleInterop": true,
|
12
7
|
"jsx": "preserve",
|
13
|
-
"
|
8
|
+
"module": "ESNext",
|
14
9
|
"moduleResolution": "node",
|
15
|
-
"
|
16
|
-
"noUnusedLocals": true,
|
17
|
-
"strictNullChecks": true,
|
18
|
-
"forceConsistentCasingInFileNames": true,
|
10
|
+
"baseUrl": ".",
|
19
11
|
"types": [
|
20
12
|
"vite/client",
|
21
13
|
"vue/ref-macros",
|
22
14
|
"vite-plugin-pages/client",
|
23
15
|
"vite-plugin-vue-layouts/client"
|
24
|
-
]
|
16
|
+
],
|
17
|
+
"resolveJsonModule": true,
|
18
|
+
"allowJs": true,
|
19
|
+
"esModuleInterop": true,
|
20
|
+
"forceConsistentCasingInFileNames": true,
|
21
|
+
"strict": true,
|
22
|
+
"strictNullChecks": true,
|
23
|
+
"noUnusedLocals": true,
|
24
|
+
"skipLibCheck": true
|
25
25
|
},
|
26
26
|
"exclude": ["**/dist/**", "node_modules"]
|
27
27
|
}
|
package/types/index.d.ts
CHANGED
@@ -4,7 +4,7 @@ export namespace YunTheme {
|
|
4
4
|
export type Config = ThemeConfig
|
5
5
|
export type Sidebar = any
|
6
6
|
|
7
|
-
export
|
7
|
+
export interface Banner {
|
8
8
|
/**
|
9
9
|
* 是否启用
|
10
10
|
*/
|
@@ -48,7 +48,7 @@ export interface ThemeConfig {
|
|
48
48
|
colors: {
|
49
49
|
/**
|
50
50
|
* @en primary color
|
51
|
-
*
|
51
|
+
*
|
52
52
|
* @zh 主题色
|
53
53
|
* @default '#0078E7'
|
54
54
|
*/
|
@@ -72,7 +72,7 @@ export interface ThemeConfig {
|
|
72
72
|
/**
|
73
73
|
* @en Image url
|
74
74
|
*/
|
75
|
-
url
|
75
|
+
url?: string
|
76
76
|
/**
|
77
77
|
* @en Image url when dark mode
|
78
78
|
*/
|
@@ -84,9 +84,9 @@ export interface ThemeConfig {
|
|
84
84
|
}
|
85
85
|
|
86
86
|
/**
|
87
|
-
* @en
|
87
|
+
* @en
|
88
88
|
* say something
|
89
|
-
*
|
89
|
+
*
|
90
90
|
* @zh 说点什么
|
91
91
|
* - 自定义 API 链接,如 https://el-bot-api.elpsy.cn/api/words/young
|
92
92
|
* 你可以通过在 public 下新建 json 的方式来使用, 如 public/young.json
|
package/unocss.config.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { defineConfig } from 'unocss'
|
1
|
+
import { defineConfig, presetWebFonts } from 'unocss'
|
2
2
|
|
3
3
|
export default defineConfig({
|
4
4
|
shortcuts: [
|
@@ -20,4 +20,13 @@ export default defineConfig({
|
|
20
20
|
],
|
21
21
|
],
|
22
22
|
// web fonts is so big, let the user decide
|
23
|
+
presets: [
|
24
|
+
presetWebFonts({
|
25
|
+
fonts: {
|
26
|
+
sans: 'DM Sans',
|
27
|
+
serif: 'DM Serif Display',
|
28
|
+
mono: 'DM Mono',
|
29
|
+
},
|
30
|
+
}),
|
31
|
+
],
|
23
32
|
})
|
package/utils/index.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
import noneImg from '../assets/images/none.jpg'
|
2
2
|
|
3
3
|
/**
|
4
4
|
* set default img
|
5
5
|
* @param e
|
6
6
|
*/
|
7
|
-
export function onImgError(e: Event, defaultImg =
|
7
|
+
export function onImgError(e: Event, defaultImg = noneImg) {
|
8
8
|
const targetEl = e.target as HTMLImageElement
|
9
9
|
targetEl.setAttribute('data-src', targetEl.src)
|
10
10
|
targetEl.src = defaultImg
|