valaxy-theme-yun 0.12.8 → 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 CHANGED
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import { useHead } from '@vueuse/head'
3
- import { useConfig } from 'valaxy'
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 config = useConfig()
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="config.themeConfig.bg_image.enable" />
21
+ <YunBg v-if="themeConfig.bg_image.enable" />
20
22
  </slot>
21
- <YunSearch v-if="config.search.enable" />
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 { useConfig, usePostTitle } from 'valaxy'
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
- const config = useConfig()
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 = config.value.runtime.addons['valaxy-addon-waline']
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
- // const YunTwikoo = useAddonWaline()
25
- // ? defineAsyncComponent(() => import('./YunTwikoo.vue'))
26
- // : () => null
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="config.comment.enable && frontmatter.comment !== false" name="comment">
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
- <!-- <YunTwikoo /> -->
65
+ <YunTwikoo />
64
66
  </YunCard>
65
67
  </slot>
66
68
 
@@ -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="theme.banner.cloud.color" />
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" opacity="0.7" />
9
- <use xlink:href="#gentle-wave" x="48" y="3" opacity="0.5" />
10
- <use xlink:href="#gentle-wave" x="48" y="5" opacity="0.3" />
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
  }
@@ -1,12 +1,13 @@
1
1
  <script lang="ts" setup>
2
2
  import { capitalize, computed } from 'vue'
3
- import { useConfig } from 'valaxy'
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 = useConfig()
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>{{ config.author.name }}</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 { useConfig } from 'valaxy'
2
+ import { useSiteConfig } from 'valaxy'
3
3
  import { useRouter } from 'vue-router'
4
4
 
5
- const config = useConfig()
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="config.author.avatar" alt="avatar">
14
- <span class="site-author-status">{{ config.author.status.emoji }}</span>
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
- {{ config.author.name }}
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
- {{ config.title }}
22
+ {{ siteConfig.title }}
23
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 }}
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="config.description" class="site-description my-1">
29
- {{ config.description }}
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 { useConfig } from 'valaxy'
2
+ import { useSiteConfig } from 'valaxy'
3
3
 
4
- const config = useConfig()
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 config.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}`">
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 { useConfig } from 'valaxy'
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 config = useConfig()
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 config.sponsor.methods" :key="i"
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"
@@ -1,8 +1,7 @@
1
1
  <script lang="ts" setup>
2
- // import { useConfig, useTwikoo } from 'valaxy'
2
+ import { useTwikooWithOptions } from 'valaxy-addon-twikoo'
3
3
 
4
- // const config = useConfig()
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 { useConfig } from 'valaxy'
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 config = useConfig()
9
+ const runtimeConfig = useRuntimeConfig()
10
10
 
11
- const addonWaline = computed(() => config.value.runtime.addons['valaxy-addon-waline'])
11
+ const addonWaline = computed(() => runtimeConfig.value.addons['valaxy-addon-waline'])
12
12
 
13
13
  const { t } = useI18n()
14
14
  </script>
@@ -1,5 +1,5 @@
1
1
  import { computed } from 'vue'
2
- import { useConfig } from 'valaxy'
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 = useConfig<ThemeConfig>()
10
+ const config = useValaxyConfig<ThemeConfig>()
11
11
  return computed(() => config!.value.themeConfig)
12
12
  }
@@ -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 { useConfig, useFrontmatter, useFullUrl } from 'valaxy'
3
+ import { useFrontmatter, useFullUrl, useSiteConfig } from 'valaxy'
4
4
 
5
- const config = useConfig()
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 config.value.sponsor.enable
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 || config.license.enabled" :url="url" m="y-4" />
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/layouts/tags.vue CHANGED
@@ -46,6 +46,8 @@ const displayTag = (tag: string) => {
46
46
  }
47
47
 
48
48
  const title = usePostTitle(frontmatter)
49
+
50
+ // use flex to fix `overflow-wrap: break-words;` not working in Safari
49
51
  </script>
50
52
 
51
53
  <template>
@@ -62,9 +64,15 @@ const title = usePostTitle(frontmatter)
62
64
  {{ t('counter.tags', Array.from(tags).length) }}
63
65
  </div>
64
66
 
65
- <div text="center" class="break-words">
66
- <span v-for="[key, tag] in Array.from(tags).sort()" :key="key" class="post-tag cursor-pointer" :style="getTagStyle(tag.count)" p="1" @click="displayTag(key.toString())">
67
- #{{ key }}<span text="xs">[{{ tag.count }}]</span>
67
+ <div class="justify-center items-end" flex="~ wrap" gap="1">
68
+ <span
69
+ v-for="[key, tag] in Array.from(tags).sort()"
70
+ :key="key"
71
+ inline-flex my="2"
72
+ class="post-tag cursor-pointer items-baseline leading-4" :style="getTagStyle(tag.count)" p="1" @click="displayTag(key.toString())"
73
+ >
74
+ <span inline-flex>#{{ key }}</span>
75
+ <span inline-flex text="xs">[{{ tag.count }}]</span>
68
76
  </span>
69
77
  </div>
70
78
 
package/node/config.ts CHANGED
@@ -11,6 +11,9 @@ export const defaultThemeConfig: ThemeConfig = {
11
11
  banner: {
12
12
  enable: true,
13
13
  title: '云游君的小站',
14
+ cloud: {
15
+ enable: true,
16
+ },
14
17
  },
15
18
 
16
19
  bg_image: {
package/node/unocss.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { ResolvedValaxyOptions } from 'valaxy'
2
- import type { ThemeUserConfig } from '../types'
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<ThemeUserConfig>) {
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.12.8",
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.12.8"
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": 8,
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 ThemeUserConfig = Partial<ThemeConfig>
148
+ export type UserThemeConfig = Partial<ThemeConfig>
package/valaxy.config.ts CHANGED
@@ -36,6 +36,5 @@ export default defineTheme<ThemeConfig>((options) => {
36
36
  unocss: {
37
37
  safelist: generateSafelist(options),
38
38
  },
39
- addons: ['valaxy-addon-waline'],
40
39
  }
41
40
  })
@@ -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>