valaxy-theme-yun 0.3.11 → 0.6.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.
@@ -0,0 +1,151 @@
1
+ <script setup lang="ts">
2
+ import '@docsearch/css'
3
+ import docsearch from '@docsearch/js'
4
+ import type { DocSearchHit } from '@docsearch/react/dist/esm/types'
5
+ import { onMounted } from 'vue'
6
+ import type { AlgoliaSearchOptions } from 'valaxy'
7
+ import { useConfig } from 'valaxy'
8
+ import { useRoute, useRouter } from 'vue-router'
9
+
10
+ const router = useRouter()
11
+ const route = useRoute()
12
+ const config = useConfig()
13
+
14
+ onMounted(() => {
15
+ initialize(config.value.search.algolia)
16
+ setTimeout(poll, 16)
17
+ })
18
+
19
+ /**
20
+ * poll until open
21
+ */
22
+ function poll() {
23
+ // programmatically open the search box after initialize
24
+ const e = new Event('keydown') as any
25
+
26
+ e.key = 'k'
27
+ e.metaKey = true
28
+
29
+ window.dispatchEvent(e)
30
+
31
+ setTimeout(() => {
32
+ if (!document.querySelector('.DocSearch-Modal'))
33
+ poll()
34
+ }, 16)
35
+ }
36
+
37
+ function initialize(userOptions: AlgoliaSearchOptions) {
38
+ // note: multi-lang search support is removed since the theme
39
+ // doesn't support multiple locales as of now.
40
+ const options = Object.assign({}, userOptions, {
41
+ container: '#docsearch',
42
+ navigator: {
43
+ navigate({ itemUrl }: { itemUrl: string }) {
44
+ const { pathname: hitPathname } = new URL(
45
+ window.location.origin + itemUrl,
46
+ )
47
+ // router doesn't handle same-page navigation so we use the native
48
+ // browser location API for anchor navigation
49
+ if (route.path === hitPathname)
50
+ window.location.assign(window.location.origin + itemUrl)
51
+
52
+ else
53
+ router.push(itemUrl)
54
+ },
55
+ },
56
+ transformItems(items: DocSearchHit[]) {
57
+ return items.map((item) => {
58
+ return Object.assign({}, item, {
59
+ url: getRelativePath(item.url),
60
+ })
61
+ })
62
+ },
63
+ hitComponent({ hit, children }: { hit: DocSearchHit; children: any }) {
64
+ const relativeHit = hit.url.startsWith('http')
65
+ ? getRelativePath(hit.url as string)
66
+ : hit.url
67
+ return {
68
+ __v: null,
69
+ type: 'a',
70
+ ref: undefined,
71
+ constructor: undefined,
72
+ key: undefined,
73
+ props: {
74
+ href: hit.url,
75
+ onClick(event: MouseEvent) {
76
+ if (isSpecialClick(event))
77
+ return
78
+
79
+ // we rely on the native link scrolling when user is already on
80
+ // the right anchor because Router doesn't support duplicated
81
+ // history entries.
82
+ if (route.path === relativeHit)
83
+ return
84
+
85
+ // if the hits goes to another page, we prevent the native link
86
+ // behavior to leverage the Router loading feature.
87
+ if (route.path !== relativeHit)
88
+ event.preventDefault()
89
+
90
+ router.push(relativeHit)
91
+ },
92
+ children,
93
+ },
94
+ }
95
+ },
96
+ })
97
+ docsearch(options)
98
+ }
99
+
100
+ function isSpecialClick(event: MouseEvent) {
101
+ return (
102
+ event.button === 1
103
+ || event.altKey
104
+ || event.ctrlKey
105
+ || event.metaKey
106
+ || event.shiftKey
107
+ )
108
+ }
109
+
110
+ function getRelativePath(absoluteUrl: string) {
111
+ const { pathname, hash } = new URL(absoluteUrl)
112
+ return pathname + hash
113
+ }
114
+ </script>
115
+
116
+ <template>
117
+ <div id="docsearch" class="hidden" />
118
+ </template>
119
+
120
+ <style lang="scss">
121
+ .DocSearch {
122
+ --docsearch-primary-color: var(--va-c-primary);
123
+ --docsearch-highlight-color: var(--docsearch-primary-color);
124
+ --docsearch-text-color: var(--va-c-text-primary);
125
+ --docsearch-muted-color: var(--va-c-text-light);
126
+ --docsearch-searchbox-focus-background: transparent;
127
+ --docsearch-searchbox-shadow: none;
128
+
129
+ --docsearch-key-gradient: transparent;
130
+ --docsearch-key-shadow: none;
131
+
132
+ --docsearch-modal-background: var(--va-c-bg-light);
133
+ --docsearch-footer-background: var(--va-c-bg);
134
+
135
+ input {
136
+ &::placeholder {
137
+ color: var(--va-c-text-dark);
138
+ }
139
+ }
140
+ }
141
+
142
+ .dark .DocSearch {
143
+ --docsearch-modal-shadow: none;
144
+ --docsearch-footer-shadow: none;
145
+ --docsearch-hit-shadow: none;
146
+ }
147
+
148
+ .DocSearch-Form {
149
+ border: 1px solid var(--va-c-primary);
150
+ }
151
+ </style>
@@ -0,0 +1,74 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from 'vue-i18n'
3
+ import type { Post } from 'valaxy'
4
+ import { useAppStore } from 'valaxy/client/stores/app'
5
+ defineProps<{ frontmatter: Post }>()
6
+ const { t } = useI18n()
7
+
8
+ const app = useAppStore()
9
+ </script>
10
+
11
+ <template>
12
+ <button
13
+ class="xl:hidden toc-btn shadow fixed yun-icon-btn z-350"
14
+ opacity="75" right="2" bottom="19"
15
+ @click="app.toggleRightSidebar()"
16
+ >
17
+ <div i-ri-file-list-line />
18
+ </button>
19
+
20
+ <ValaxyOverlay :show="app.isRightSidebarOpen" @click="app.toggleRightSidebar()" />
21
+
22
+ <aside class="right-sidebar fixed va-card" :class="app.isRightSidebarOpen && 'open'" m="l-4" text="center">
23
+ <h2 v-if="frontmatter.toc !== false" m="t-6 b-2" font="serif black">
24
+ {{ t('sidebar.toc') }}
25
+ </h2>
26
+
27
+ <div class="right-sidebar-container">
28
+ <!-- <ValaxyToc v-if="frontmatter.toc !== false" /> -->
29
+ <YunToc v-if="frontmatter.toc !== false" />
30
+
31
+ <div v-if="$slots.custom" class="custom-container">
32
+ <slot name="custom" />
33
+ </div>
34
+ </div>
35
+ </aside>
36
+ </template>
37
+
38
+ <style lang="scss">
39
+ @use '~/styles/mixins' as *;
40
+
41
+ @include xl {
42
+ .right-sidebar {
43
+ transform: translateX(0) !important;
44
+ }
45
+ }
46
+
47
+ .right-sidebar {
48
+ width: var(--va-sidebar-width-mobile);
49
+
50
+ position: fixed;
51
+ top: 0;
52
+ bottom: 0;
53
+ right: 0;
54
+
55
+ transform: translateX(100%);
56
+
57
+ transition: box-shadow var(--va-transition-duration),
58
+ background-color var(--va-transition-duration), opacity 0.25s,
59
+ transform var(--va-transition-duration) cubic-bezier(0.19, 1, 0.22, 1);
60
+
61
+ &.open {
62
+ z-index: 10;
63
+ transform: translateX(0);
64
+ }
65
+ }
66
+ .right-sidebar-container {
67
+ top: 1rem;
68
+ }
69
+
70
+ .toc-btn {
71
+ color: var(--va-c-primary);
72
+ background-color: white;
73
+ }
74
+ </style>
@@ -1,5 +1,11 @@
1
+ <script lang="ts" setup>
2
+ defineProps<{ cover?: string }>()
3
+ </script>
4
+
1
5
  <template>
2
6
  <div class="yun-card">
7
+ <img v-if="cover" class="object-cover" h="64 md:sm" w="full" :src="cover">
8
+
3
9
  <div v-if="$slots.header" class="yun-card-header">
4
10
  <header>
5
11
  <slot name="header" />
@@ -7,7 +7,7 @@ defineProps<{
7
7
  </script>
8
8
 
9
9
  <template>
10
- <header class="post-header">
10
+ <header class="post-header" m="t-4">
11
11
  <h1 class="post-title" p="2" text="2xl center" font="serif black" :style="`color:${color}`">
12
12
  <div v-if="icon" class="icon" m="r-1" inline-flex :class="icon" />
13
13
  <span>{{ title }}</span>
@@ -0,0 +1,130 @@
1
+ <script lang="ts" setup>
2
+ import { defineAsyncComponent, onMounted, onUnmounted, ref } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+
5
+ const YunAlgoliaSearchBox = __ALGOLIA__
6
+ ? defineAsyncComponent(() => import('./YunAlgoliaSearchBox.vue'))
7
+ : () => null
8
+
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
+ onMounted(() => {
22
+ if (!__ALGOLIA__)
23
+ return
24
+
25
+ const handleSearchHotKey = (e: KeyboardEvent) => {
26
+ if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
27
+ e.preventDefault()
28
+ load()
29
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
30
+ remove()
31
+ }
32
+ }
33
+
34
+ const remove = () => {
35
+ window.removeEventListener('keydown', handleSearchHotKey)
36
+ }
37
+
38
+ window.addEventListener('keydown', handleSearchHotKey)
39
+
40
+ onUnmounted(remove)
41
+ })
42
+
43
+ const trigger = () => {
44
+ const e = new Event('keydown') as any
45
+
46
+ e.key = 'k'
47
+ e.metaKey = true
48
+
49
+ window.dispatchEvent(e)
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <div>
55
+ <button class="search-btn popup-trigger yun-icon-btn" :title="t('menu.search')" @click="trigger">
56
+ <div i-ri-search-line />
57
+ <!-- <div v-else text="!2xl" i-ri-close-line /> -->
58
+ </button>
59
+
60
+ <YunAlgoliaSearchBox v-if="loaded" />
61
+ </div>
62
+ </template>
63
+
64
+ <style lang="scss">
65
+ @use 'sass:map';
66
+ @use '~/styles/vars' as *;
67
+
68
+ .search-btn {
69
+ position: fixed;
70
+ top: 0.6rem;
71
+ right: 0.8rem;
72
+
73
+ color: var(--va-c-primary);
74
+ z-index: var(--yun-z-search-btn);
75
+ }
76
+
77
+ .search-popup {
78
+ position: fixed;
79
+ top: 0;
80
+ left: 0;
81
+ width: 100%;
82
+ height: 100%;
83
+
84
+ backdrop-filter: blur(30px);
85
+ -webkit-backdrop-filter: blur(30px);
86
+
87
+ text-align: center;
88
+ padding-top: 3.5rem;
89
+ margin: 0;
90
+ z-index: var(--yun-z-search-popup);
91
+ transition: 0.6s;
92
+ }
93
+
94
+ .search-header {
95
+ .close-icon {
96
+ position: fixed;
97
+ top: 0.6rem;
98
+ right: 0.8rem;
99
+ }
100
+ }
101
+
102
+ .search-input {
103
+ background: transparent;
104
+ color: var(--va-c-text);
105
+ font-size: 1.5rem;
106
+ border-radius: 3rem;
107
+ padding: 1rem 1.5rem;
108
+ border: 1px solid var(--va-c-gray);
109
+ box-sizing: border-box;
110
+ width: 90%;
111
+ max-width: 800px;
112
+ font-family: var(--va-font-serif);
113
+ font-weight: 900;
114
+ text-align: center;
115
+ }
116
+
117
+ .popup {
118
+ .search-icon, .close-icon {
119
+ display: inline-block;
120
+ width: 2rem;
121
+ height: 2rem;
122
+ padding: 0.5rem;
123
+
124
+ .icon {
125
+ width: 2rem;
126
+ height: 2rem;
127
+ }
128
+ }
129
+ }
130
+ </style>
@@ -0,0 +1,154 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+ import { useI18n } from 'vue-i18n'
5
+ import { useFrontmatter } from '~/composables'
6
+ import {
7
+ resolveHeaders,
8
+ useActiveAnchor,
9
+ useOutline,
10
+ } from '~/composables/outline'
11
+ import { useThemeConfig } from '~/config'
12
+
13
+ const route = useRoute()
14
+ const frontmatter = useFrontmatter()
15
+ const themeConfig = useThemeConfig()
16
+
17
+ const { hasOutline } = useOutline()
18
+
19
+ const { locale } = useI18n()
20
+ const container = ref()
21
+ const marker = ref()
22
+
23
+ useActiveAnchor(container, marker)
24
+
25
+ const resolvedHeaders = computed(() => {
26
+ return resolveHeaders(route.meta.headers || [])
27
+ })
28
+
29
+ function handleClick({ target: el }: Event) {
30
+ const id = `#${(el as HTMLAnchorElement).href!.split('#')[1]}`
31
+ const heading = document.querySelector(decodeURIComponent(id)) as HTMLAnchorElement
32
+ heading?.focus()
33
+ }
34
+ </script>
35
+
36
+ <template>
37
+ <div ref="container" class="VPDocAsideOutline" :class="{ 'has-outline': hasOutline }">
38
+ <div class="content">
39
+ <div class="outline-title">
40
+ {{ themeConfig.outlineTitle || 'On this page' }}
41
+ </div>
42
+
43
+ <div ref="marker" class="outline-marker" />
44
+
45
+ <nav aria-labelledby="doc-outline-aria-label">
46
+ <span id="doc-outline-aria-label" class="visually-hidden">
47
+ Table of Contents for current page
48
+ </span>
49
+
50
+ <ul class="root va-toc">
51
+ <li
52
+ v-for="{ text, link, children, hidden, lang } in resolvedHeaders"
53
+ v-show="!hidden"
54
+ :key="link"
55
+ class="va-toc-item"
56
+ :lang="lang || locale"
57
+ >
58
+ <a class="outline-link" :href="link" @click="handleClick">
59
+ {{ text }}
60
+ </a>
61
+ <ul v-if="children && frontmatter.outline === 'deep'">
62
+ <li v-for="{ text, link, hidden } in children" v-show="!hidden" :key="link" :lang="lang || locale">
63
+ <a class="outline-link nested" :href="link" @click="handleClick">
64
+ {{ text }}
65
+ </a>
66
+ </li>
67
+ </ul>
68
+ </li>
69
+ </ul>
70
+ </nav>
71
+ </div>
72
+ </div>
73
+ </template>
74
+
75
+ <style lang="scss" scoped>
76
+ .va-toc {
77
+ text-align: left;
78
+
79
+ .va-toc-item {
80
+ color: var(--va-c-text-light);
81
+ }
82
+ }
83
+
84
+ .VPDocAsideOutline {
85
+ display: none;
86
+ }
87
+
88
+ .VPDocAsideOutline.has-outline {
89
+ display: block;
90
+ }
91
+
92
+ .content {
93
+ position: relative;
94
+ padding-left: 16px;
95
+ font-size: 14px;
96
+ text-align: left;
97
+ }
98
+
99
+ .outline-marker {
100
+ position: absolute;
101
+ top: 32px;
102
+ left: -2px;
103
+ z-index: 0;
104
+ opacity: 0;
105
+ width: 4px;
106
+ height: 18px;
107
+ background-color: var(--va-c-brand);
108
+ transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), background-color 0.5s, opacity 0.25s;
109
+ border-top-right-radius: 2px;
110
+ border-bottom-right-radius: 2px;
111
+ }
112
+
113
+ .outline-title {
114
+ letter-spacing: 0.4px;
115
+ line-height: 28px;
116
+ font-size: 14px;
117
+ font-weight: 600;
118
+ }
119
+
120
+ .outline-link {
121
+ display: block;
122
+ line-height: 28px;
123
+ color: var(--vp-c-text-2);
124
+ white-space: nowrap;
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ transition: color 0.5s;
128
+ }
129
+
130
+ .outline-link:hover,
131
+ .outline-link.active {
132
+ color: var(--va-c-brand);
133
+ transition: color 0.25s;
134
+ }
135
+
136
+ .outline-link.nested {
137
+ padding-left: 13px;
138
+ }
139
+
140
+ .root {
141
+ position: relative;
142
+ z-index: 1;
143
+ }
144
+
145
+ .visually-hidden {
146
+ position: absolute;
147
+ width: 1px;
148
+ height: 1px;
149
+ white-space: nowrap;
150
+ clip: rect(0 0 0 0);
151
+ clip-path: inset(50%);
152
+ overflow: hidden;
153
+ }
154
+ </style>
package/config/index.ts CHANGED
@@ -1,9 +1,20 @@
1
1
  export const anonymousImage = 'https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg'
2
2
 
3
+ export namespace YunTheme {
4
+ export type Config = ThemeConfig
5
+ export type Sidebar = any
6
+ }
7
+
3
8
  /**
4
9
  * Theme Config
5
10
  */
6
11
  export interface ThemeConfig {
12
+ /**
13
+ * toc title
14
+ * @default 'On this page'
15
+ */
16
+ outlineTitle: string
17
+
7
18
  // for unocss
8
19
  safelist: string[]
9
20
  colors: {
@@ -52,6 +63,8 @@ export interface ThemeConfig {
52
63
  color: string
53
64
  }[]
54
65
 
66
+ sidebar: YunTheme.Sidebar
67
+
55
68
  /**
56
69
  * footer
57
70
  */
@@ -121,6 +134,7 @@ export type ThemeUserConfig = Partial<ThemeConfig>
121
134
  * Default Config
122
135
  */
123
136
  export const defaultThemeConfig: ThemeConfig = {
137
+ outlineTitle: 'On this page',
124
138
  safelist: ['i-ri-clipboard-line'],
125
139
  colors: {
126
140
  primary: '#0078E7',
@@ -147,6 +161,7 @@ export const defaultThemeConfig: ThemeConfig = {
147
161
 
148
162
  pages: [],
149
163
 
164
+ sidebar: null,
150
165
  footer: {
151
166
  since: 2022,
152
167
  icon: {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,19 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  declare const anonymousImage = "https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg";
4
+ declare namespace YunTheme {
5
+ type Config = ThemeConfig;
6
+ type Sidebar = any;
7
+ }
4
8
  /**
5
9
  * Theme Config
6
10
  */
7
11
  interface ThemeConfig {
12
+ /**
13
+ * toc title
14
+ * @default 'On this page'
15
+ */
16
+ outlineTitle: string;
8
17
  safelist: string[];
9
18
  colors: {
10
19
  /**
@@ -47,6 +56,7 @@ interface ThemeConfig {
47
56
  icon: string;
48
57
  color: string;
49
58
  }[];
59
+ sidebar: YunTheme.Sidebar;
50
60
  /**
51
61
  * footer
52
62
  */
@@ -124,4 +134,4 @@ interface UserOptions {
124
134
  }
125
135
  declare function yunPlugin(userOptions?: Partial<ThemeConfig>): Plugin;
126
136
 
127
- export { ThemeConfig, ThemeUserConfig, UserOptions, anonymousImage, yunPlugin as default, defaultThemeConfig, generateSafelist, yunPlugin };
137
+ export { ThemeConfig, ThemeUserConfig, UserOptions, YunTheme, anonymousImage, yunPlugin as default, defaultThemeConfig, generateSafelist, yunPlugin };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var f="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"},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(m(n));function m(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 b(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 y=b;exports.anonymousImage = f; exports.default = y; exports.defaultThemeConfig = n; exports.generateSafelist = m; exports.yunPlugin = b;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var f="https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg",n= exports.defaultThemeConfig ={outlineTitle:"On this page",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"},say:{enable:!0,api:"https://el-bot-api.vercel.app/api/words/young",hitokoto:{enable:!1,api:"https://v1.hitokoto.cn"}},pages:[],sidebar:null,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(m(n));function m(e){var t,r,a,l,s,c,u,p;let i=[],o=e.types;if(o)for(let g in o)i.push(o[g].icon);return(r=(t=e.footer)==null?void 0:t.icon)!=null&&r.name&&i.push((l=(a=e.footer)==null?void 0:a.icon)==null?void 0:l.name),(c=(s=e.menu)==null?void 0:s.custom)!=null&&c.icon&&i.push((p=(u=e.menu)==null?void 0:u.custom)==null?void 0:p.icon),i}function b(e=n){return{name:"valaxy-theme-yun",enforce:"pre",config(){var i;return{css:{preprocessorOptions:{scss:{additionalData:`$c-primary: ${((i=e.colors)==null?void 0:i.primary)||"#0078E7"} !default;`}}}}}}}var y=b;exports.anonymousImage = f; exports.default = y; exports.defaultThemeConfig = n; exports.generateSafelist = m; exports.yunPlugin = b;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var f="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"},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(m(n));function m(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 b(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 y=b;export{f as anonymousImage,y as default,n as defaultThemeConfig,m as generateSafelist,b as yunPlugin};
1
+ var f="https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg",n={outlineTitle:"On this page",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"},say:{enable:!0,api:"https://el-bot-api.vercel.app/api/words/young",hitokoto:{enable:!1,api:"https://v1.hitokoto.cn"}},pages:[],sidebar:null,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(m(n));function m(e){var t,r,a,l,s,c,u,p;let i=[],o=e.types;if(o)for(let g in o)i.push(o[g].icon);return(r=(t=e.footer)==null?void 0:t.icon)!=null&&r.name&&i.push((l=(a=e.footer)==null?void 0:a.icon)==null?void 0:l.name),(c=(s=e.menu)==null?void 0:s.custom)!=null&&c.icon&&i.push((p=(u=e.menu)==null?void 0:u.custom)==null?void 0:p.icon),i}function b(e=n){return{name:"valaxy-theme-yun",enforce:"pre",config(){var i;return{css:{preprocessorOptions:{scss:{additionalData:`$c-primary: ${((i=e.colors)==null?void 0:i.primary)||"#0078E7"} !default;`}}}}}}}var y=b;export{f as anonymousImage,y as default,n as defaultThemeConfig,m as generateSafelist,b as yunPlugin};
package/layouts/base.vue CHANGED
@@ -20,14 +20,17 @@ const title = usePostTitle(frontmatter)
20
20
 
21
21
  <main class="yun-main flex lt-md:ml-0">
22
22
  <div flex="~ 1 col" w="full" p="l-4 lt-md:0" class="content">
23
- <YunCard m="0" p="4" class="relative sm:p-6 lg:px-12 xl:px-16" :style="styles">
23
+ <YunCard :cover="frontmatter.cover" m="0" class="relative" :style="styles">
24
24
  <slot name="header">
25
- <YunPageHeader :title="title" :icon="frontmatter.icon || icon" :color="frontmatter.color || color" />
25
+ <YunPageHeader :title="title" :icon="frontmatter.icon || icon" :color="frontmatter.color || color" :cover="frontmatter.cover" />
26
26
  </slot>
27
+
27
28
  <template #content>
28
- <slot name="content">
29
- <router-view />
30
- </slot>
29
+ <div p="x-4 b-8" class="sm:px-6 lg:px-12 xl:px-16">
30
+ <slot name="content">
31
+ <router-view />
32
+ </slot>
33
+ </div>
31
34
  </template>
32
35
  </YunCard>
33
36
 
@@ -50,15 +53,17 @@ const title = usePostTitle(frontmatter)
50
53
  </div>
51
54
 
52
55
  <slot name="right-sidebar">
53
- <ValaxyRightSidebar :frontmatter="frontmatter">
56
+ <!-- <ValaxyRightSidebar > -->
57
+ <YunAside :frontmatter="frontmatter">
54
58
  <template #custom>
55
59
  <slot name="right-custom" />
56
60
  </template>
57
- </ValaxyRightSidebar>
61
+ <!-- </ValaxyRightSidebar> -->
62
+ </YunAside>
58
63
  </slot>
59
64
  </main>
60
65
 
61
- <YunAlgoliaSearch v-if="config.search.algolia.enable" />
66
+ <YunSearch v-if="config.search.enable" />
62
67
  <YunBackToTop />
63
68
  </template>
64
69
 
@@ -15,12 +15,14 @@ const curCategory = computed(() => (route.query.category as string || ''))
15
15
  const postList = usePostList()
16
16
  const posts = computed(() => {
17
17
  const list = postList.value.filter((post) => {
18
- if (post.categories) {
18
+ if (post.categories && curCategory.value !== 'Uncategorized') {
19
19
  if (typeof post.categories === 'string')
20
20
  return post.categories === curCategory.value
21
21
  else
22
22
  return post.categories.includes(curCategory.value)
23
23
  }
24
+ if (!post.categories && curCategory.value === 'Uncategorized')
25
+ return post.categories === undefined
24
26
  return false
25
27
  })
26
28
  return list
@@ -61,7 +63,7 @@ const title = usePostTitle(frontmatter)
61
63
  </template>
62
64
 
63
65
  <YunCard v-if="curCategory" ref="collapse" m="t-4" w="full">
64
- <YunPageHeader m="t-4" :title="curCategory" icon="i-ri-folder-open-line" />
66
+ <YunPageHeader :title="curCategory === 'Uncategorized' ? t('category.uncategorized') : curCategory" icon="i-ri-folder-open-line" />
65
67
  <YunPostCollapse w="full" m="b-4" p="x-20 lt-sm:x-5" :posts="posts" />
66
68
  </YunCard>
67
69
  </YunBase>
package/layouts/home.vue CHANGED
@@ -34,5 +34,5 @@ const isHome = useLayout('home')
34
34
  </ValaxyFooter>
35
35
  </main>
36
36
 
37
- <YunAlgoliaSearch v-if="config.search.algolia.enable" />
37
+ <YunSearch v-if="config.search.enable" />
38
38
  </template>
package/layouts/tags.vue CHANGED
@@ -71,7 +71,7 @@ const title = usePostTitle(frontmatter)
71
71
  </template>
72
72
 
73
73
  <YunCard v-if="curTag" ref="collapse" m="t-4" w="full">
74
- <YunPageHeader m="t-4" :title="curTag" icon="i-ri-hashtag" />
74
+ <YunPageHeader :title="curTag" icon="i-ri-hashtag" />
75
75
  <YunPostCollapse w="full" m="b-4" p="x-20 lt-sm:x-5" :posts="posts" />
76
76
  </YunCard>
77
77
  </YunBase>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-yun",
3
- "version": "0.3.11",
3
+ "version": "0.6.0",
4
4
  "author": {
5
5
  "email": "me@yunyoujun.cn",
6
6
  "name": "YunYouJun",
@@ -15,10 +15,10 @@
15
15
  "types": "dist/index.d.ts",
16
16
  "dependencies": {
17
17
  "@iconify-json/ant-design": "^1.1.2",
18
- "@iconify-json/simple-icons": "^1.1.14"
18
+ "@iconify-json/simple-icons": "^1.1.16"
19
19
  },
20
20
  "devDependencies": {
21
- "valaxy": "0.3.11"
21
+ "valaxy": "0.6.0"
22
22
  },
23
23
  "scripts": {
24
24
  "build": "tsup",
@@ -5,6 +5,24 @@
5
5
  max-width: var(--yun-post-card-max-width);
6
6
  }
7
7
 
8
+ .post-card-image {
9
+ @extend .post-card;
10
+
11
+ .post-card-image-info-text {
12
+ height: 0;
13
+ }
14
+
15
+ @include mobile {
16
+ .post-card-info {
17
+ flex-direction: column;
18
+ }
19
+
20
+ img {
21
+ width: 100%;
22
+ }
23
+ }
24
+ }
25
+
8
26
  .post-title {
9
27
  display: flex;
10
28
  justify-content: center;
@@ -75,4 +93,4 @@
75
93
  &:before {
76
94
  content: none;
77
95
  }
78
- }
96
+ }
@@ -1,227 +0,0 @@
1
- <script lang="ts" setup>
2
- import { ref, watch } from 'vue'
3
- import { useMagicKeys } from '@vueuse/core'
4
- import { useI18n } from 'vue-i18n'
5
- import { useConfig } from 'valaxy'
6
- import { useAlgoliaSearch } from '~/composables/search/algolia'
7
-
8
- const { t } = useI18n()
9
-
10
- const config = useConfig()
11
- const showPopup = ref(false)
12
-
13
- const { Escape } = useMagicKeys()
14
-
15
- useAlgoliaSearch({
16
- ...config.value.search.algolia,
17
- hits: {
18
- per_page: 8,
19
- },
20
- })
21
-
22
- watch(Escape, () => {
23
- showPopup.value = false
24
- })
25
-
26
- watch(showPopup, () => {
27
- if (showPopup.value)
28
- document.documentElement.classList.add('no-scroll')
29
- else
30
- document.documentElement.classList.remove('no-scroll')
31
- })
32
- </script>
33
-
34
- <template>
35
- <button class="search-btn popup-trigger yun-icon-btn" :title="t('menu.search')" @click="showPopup = !showPopup">
36
- <div v-if="!showPopup" i-ri-search-line />
37
- <div v-else text="!2xl" i-ri-close-line />
38
- </button>
39
-
40
- <transition>
41
- <div v-show="showPopup" class="search-popup">
42
- <div class="search-header" />
43
- <div class="search-input-container" />
44
- <div class="algolia-results">
45
- <div id="algolia-stats" />
46
- <div id="algolia-hits" />
47
- <div id="algolia-pagination" class="algolia-pagination" />
48
- </div>
49
- </div>
50
- </transition>
51
- </template>
52
-
53
- <style lang="scss">
54
- @use 'sass:map';
55
- @use '~/styles/vars' as *;
56
-
57
- .search-btn {
58
- position: fixed;
59
- top: 0.6rem;
60
- right: 0.8rem;
61
-
62
- color: var(--va-c-primary);
63
- z-index: var(--yun-z-search-btn);
64
- }
65
-
66
- .search-popup {
67
- position: fixed;
68
- top: 0;
69
- left: 0;
70
- width: 100%;
71
- height: 100%;
72
-
73
- backdrop-filter: blur(30px);
74
- -webkit-backdrop-filter: blur(30px);
75
-
76
- text-align: center;
77
- padding-top: 3.5rem;
78
- margin: 0;
79
- z-index: var(--yun-z-search-popup);
80
- transition: 0.6s;
81
- }
82
-
83
- .search-header {
84
- .close-icon {
85
- position: fixed;
86
- top: 0.6rem;
87
- right: 0.8rem;
88
- }
89
- }
90
-
91
- .search-input {
92
- background: transparent;
93
- color: var(--va-c-text);
94
- font-size: 1.5rem;
95
- border-radius: 3rem;
96
- padding: 1rem 1.5rem;
97
- border: 1px solid var(--va-c-gray);
98
- box-sizing: border-box;
99
- width: 90%;
100
- max-width: 800px;
101
- font-family: var(--va-font-serif);
102
- font-weight: 900;
103
- text-align: center;
104
- }
105
-
106
- .popup {
107
- .search-icon, .close-icon {
108
- display: inline-block;
109
- width: 2rem;
110
- height: 2rem;
111
- padding: 0.5rem;
112
-
113
- .icon {
114
- width: 2rem;
115
- height: 2rem;
116
- }
117
- }
118
- }
119
-
120
- .ais-Stats {
121
- .ais-Stats-text {
122
- display: flex;
123
- justify-content: center;
124
- align-items: center;
125
-
126
- position: relative;
127
-
128
- padding-bottom: 8px;
129
- margin-bottom: 8px;
130
- font-size: small;
131
- }
132
- }
133
-
134
- .algolia-powered {
135
- position: absolute;
136
- top: 0;
137
- right: 1rem;
138
-
139
- display: inline-flex;
140
- justify-content: center;
141
- align-items: center;
142
-
143
- img {
144
- width: 1rem;
145
- height: 1rem;
146
- display: inline-flex;
147
- }
148
- }
149
-
150
- .algolia-results {
151
- position: relative;
152
- overflow: auto;
153
- padding: 10px 30px 0 30px;
154
-
155
- hr {
156
- margin-top: 10px;
157
- margin-bottom: 0;
158
- }
159
- }
160
-
161
- .algolia-hit-item {
162
- display: flex;
163
- justify-content: center;
164
- }
165
-
166
- .algolia-hit-item-link {
167
- display: block;
168
- width: 100%;
169
- padding: 0.5rem;
170
- border-bottom: 1px dashed #ccc;
171
- font-family: var(--va-font-serif);
172
- font-weight: 900;
173
- font-size: 1.2rem;
174
- max-width: 800px;
175
-
176
- mark {
177
- color: var(--va-c-danger);
178
- font-family: 900;
179
- background-color: transparent;
180
- text-decoration: underline;
181
- }
182
-
183
- small {
184
- display: flex;
185
- font-size: 12px;
186
- justify-content: center;
187
- font-family: var(--va-font-sans);
188
- font-weight: normal;
189
- line-height: 1;
190
- margin-top: -0.2rem;
191
- }
192
- }
193
-
194
- .algolia-pagination {
195
- margin-top: 1rem;
196
- .ais-Pagination-list {
197
- display: flex;
198
- padding-inline-start: 0;
199
- }
200
-
201
- .pagination-item {
202
- display: inline-flex;
203
- list-style-type: none;
204
- }
205
-
206
- .page-number {
207
- border-top: none;
208
- }
209
-
210
- .disabled-item {
211
- display: none;
212
- }
213
-
214
- .active {
215
- .page-number {
216
- background: var(--page-btn-active-bg-color);
217
- color: var(--va-c-bg);
218
- }
219
- }
220
- }
221
-
222
- .ais-Hits-list {
223
- margin: 0;
224
- list-style-type: none;
225
- padding-inline-start: 0;
226
- }
227
- </style>