valaxy-theme-hairy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. package/@types/markdown-it.d.ts +1 -0
  2. package/@types/markdown-toc.d.ts +1 -0
  3. package/@types/types.d.ts +1 -0
  4. package/@types/valaxy.d.ts +10 -0
  5. package/LICENSE +21 -0
  6. package/components/HairyAlgoliaSearchBox.vue +118 -0
  7. package/components/HairyArticleImage.vue +17 -0
  8. package/components/HairyArticleImageDefault.vue +127 -0
  9. package/components/HairyArticleImageSmall.vue +49 -0
  10. package/components/HairyArticleSeries.vue +73 -0
  11. package/components/HairyArticleText.vue +38 -0
  12. package/components/HairyArticleTop.vue +0 -0
  13. package/components/HairyBackToTop.vue +72 -0
  14. package/components/HairyBody.vue +55 -0
  15. package/components/HairyBreadcrumb.vue +51 -0
  16. package/components/HairyBreadcrumbItem.vue +14 -0
  17. package/components/HairyCarousel.vue +45 -0
  18. package/components/HairyDivider.vue +0 -0
  19. package/components/HairyHeader.vue +31 -0
  20. package/components/HairyImage.vue +14 -0
  21. package/components/HairyImageGroup.vue +69 -0
  22. package/components/HairyImageViewer.vue +22 -0
  23. package/components/HairyLayout.vue +29 -0
  24. package/components/HairyLink.vue +10 -0
  25. package/components/HairyMenu.vue +15 -0
  26. package/components/HairyMenuItem.vue +40 -0
  27. package/components/HairyMeting.vue +19 -0
  28. package/components/HairyNav.vue +39 -0
  29. package/components/HairyNavBackground.vue +7 -0
  30. package/components/HairyNavSearch.vue +265 -0
  31. package/components/HairyNavTitle.vue +13 -0
  32. package/components/HairyNavToggleDark.vue +16 -0
  33. package/components/HairyPostImageList.vue +28 -0
  34. package/components/HairyPostList.vue +24 -0
  35. package/components/HairyPostTitle.vue +33 -0
  36. package/components/HairySocialLinks.vue +27 -0
  37. package/components/HairyTimelinePostItem.vue +40 -0
  38. package/components/HairyToc.vue +135 -0
  39. package/components/HairyUserCard.vue +30 -0
  40. package/components/HairyUserNav.vue +68 -0
  41. package/components/HairyUserTab.vue +40 -0
  42. package/components/HairyWaline.vue +25 -0
  43. package/components/HairyWaves.vue +67 -0
  44. package/components/ValaxyMain.vue +45 -0
  45. package/hooks/useCategory.ts +18 -0
  46. package/hooks/useCategoryPost.ts +21 -0
  47. package/hooks/useContext.ts +16 -0
  48. package/hooks/useHeaderHeight.ts +9 -0
  49. package/hooks/usePostLayout.ts +9 -0
  50. package/hooks/useYearArchives.ts +43 -0
  51. package/images.json +101 -0
  52. package/index.d.ts +54 -0
  53. package/layouts/archives.vue +11 -0
  54. package/layouts/categories.vue +6 -0
  55. package/layouts/default.vue +9 -0
  56. package/layouts/home.vue +23 -0
  57. package/layouts/month.vue +6 -0
  58. package/layouts/post.vue +26 -0
  59. package/layouts/tag.vue +6 -0
  60. package/layouts/tags.vue +6 -0
  61. package/layouts/year.vue +6 -0
  62. package/locales/en.yml +1 -0
  63. package/locales/zh-CN.yml +3 -0
  64. package/modules/context.ts +5 -0
  65. package/modules/loading.scss +531 -0
  66. package/modules/loading.ts +42 -0
  67. package/modules/scroll.ts +21 -0
  68. package/node/addon-hairy.ts +18 -0
  69. package/node/addon-images.ts +61 -0
  70. package/node/addon-meting.ts +13 -0
  71. package/node/addon-statistics.ts +19 -0
  72. package/node/addon-toc.ts +20 -0
  73. package/node/utils.ts +20 -0
  74. package/package.json +47 -0
  75. package/pages/archives/[year]/[month]/index.vue +52 -0
  76. package/pages/archives/[year]/index.vue +73 -0
  77. package/pages/archives/index.vue +53 -0
  78. package/pages/categories/[...categories].vue +115 -0
  79. package/pages/index.vue +14 -0
  80. package/pages/tags/[tag].vue +40 -0
  81. package/pages/tags/index.vue +31 -0
  82. package/setup/main.ts +11 -0
  83. package/shims.d.ts +8 -0
  84. package/styles/css-vars.scss +161 -0
  85. package/styles/element-plus/index.scss +2 -0
  86. package/styles/element-plus/tabs.scss +26 -0
  87. package/styles/element-plus/timeline.scss +19 -0
  88. package/styles/font-face.scss +20 -0
  89. package/styles/fonts/FrederickatheGreat.ttf +0 -0
  90. package/styles/fonts/Modesty.ttf +0 -0
  91. package/styles/fonts/MountainsofChristmas-Bold.ttf +0 -0
  92. package/styles/fonts/MountainsofChristmas-Regular.ttf +0 -0
  93. package/styles/fonts/Seto.ttf +0 -0
  94. package/styles/index.scss +65 -0
  95. package/styles/markdown.scss +60 -0
  96. package/styles/scrollbar.scss +26 -0
  97. package/unocss.config.ts +29 -0
  98. package/utils/createContext.ts +40 -0
  99. package/utils/index.ts +28 -0
  100. package/utils/loading.scss +531 -0
  101. package/utils/loading.ts +30 -0
  102. package/valaxy.config.ts +26 -0
@@ -0,0 +1,45 @@
1
+ <script lang="ts" setup>
2
+ import images from '@hairy:images:home'
3
+ import { Swiper, SwiperSlide } from 'swiper/vue'
4
+ import { Autoplay, EffectFade } from 'swiper'
5
+ import 'swiper/css'
6
+ import 'swiper/css/autoplay'
7
+ import 'swiper/css/effect-fade'
8
+
9
+ images.sort(() => Math.random() - 0.5)
10
+ </script>
11
+
12
+ <template>
13
+ <swiper
14
+ effect="fade" :speed="2000" :fade-effect="{ crossFade: true }" :modules="[Autoplay, EffectFade]"
15
+ :slides-per-view="1" :space-between="50" :autoplay="{
16
+ delay: 4000,
17
+ }" class="HairyCarousel swiper-no-swiping"
18
+ >
19
+ <swiper-slide v-for="(item, index) in images" :key="index" class="w-full h-full">
20
+ <img class="w-full h-full object-cover" :src="item" />
21
+ </swiper-slide>
22
+ </swiper>
23
+ </template>
24
+
25
+ <style lang="scss">
26
+ .HairyCarousel {
27
+ &::before {
28
+ content: '';
29
+ display: block;
30
+ position: absolute;
31
+ top: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 100%;
35
+ background-color: rgba(0, 0, 0, .2);
36
+ z-index: 2;
37
+ transition: all .2s ease-in-out 0s;
38
+ }
39
+ }
40
+ .dark {
41
+ .HairyCarousel::before {
42
+ background-color: rgba(0,0,0,.4);
43
+ }
44
+ }
45
+ </style>
File without changes
@@ -0,0 +1,31 @@
1
+ <script lang="ts" setup>
2
+ import { useContext } from '../hooks/useContext'
3
+
4
+ defineProps<{
5
+ headline?: String
6
+ title?: String
7
+ description?: string
8
+ }>()
9
+
10
+ const { headerRef } = useContext()
11
+ </script>
12
+
13
+ <template>
14
+ <header ref="headerRef" class="HairyHeader relative">
15
+ <div class="h-30vh lt-md:h-60vh min-h-80 flex-center">
16
+ <HairyPostTitle v-if="title" class="relative z-2" :title="title" v-bind="$props">
17
+ <template #description>
18
+ <slot name="description" />
19
+ </template>
20
+ </HairyPostTitle>
21
+ </div>
22
+ <HairyCarousel class="inset-0" style="position: absolute" />
23
+ <HairyWaves class="relative z-10" />
24
+ </header>
25
+ </template>
26
+
27
+ <style>
28
+ .HairyHeader {
29
+ position: relative;
30
+ }
31
+ </style>
@@ -0,0 +1,14 @@
1
+ <script lang="ts" setup>
2
+ import { ElImage, imageProps } from 'element-plus/es/components/image/index'
3
+ import 'element-plus/es/components/image/style/index'
4
+ import { inject } from 'vue'
5
+ const props = defineProps(imageProps)
6
+
7
+ const preview = inject<(url: string) => void>('HairyImageGroup:preview')
8
+ </script>
9
+
10
+ <template>
11
+ <ElImage v-bind="props" class="HairyImage" :class="[preview && 'cursor-pointer rounded']" @click="preview?.(src)" />
12
+ </template>
13
+
14
+ <style lang="scss" scoped></style>
@@ -0,0 +1,69 @@
1
+ <script lang="ts" setup>
2
+ import { computed, provide, useCssVars, useSlots } from 'vue'
3
+ import { executeOverlay } from 'unoverlay-vue'
4
+ import type { ImageViewerProps } from 'element-plus/es/components/image-viewer/index'
5
+ import type { AtWillNumber } from '@hairy/libcore'
6
+ import { atWillToUnit } from '@hairy/libcore'
7
+ import HairyImageViewer from './HairyImageViewer.vue'
8
+
9
+ const props = withDefaults(defineProps<{
10
+ row?: AtWillNumber
11
+ col?: AtWillNumber
12
+ gap?: AtWillNumber
13
+ justify?: string
14
+ align?: string
15
+ }>(),
16
+ {
17
+ row: 'auto',
18
+ col: 'auto',
19
+ gap: 10,
20
+ justify: 'space-evenly',
21
+ align: 'initial',
22
+ })
23
+
24
+ useCssVars(() => ({
25
+ width: atWillToUnit(props.row),
26
+ height: atWillToUnit(props.col),
27
+ gap: atWillToUnit(props.gap),
28
+ justify: props.justify,
29
+ align: props.align,
30
+ }))
31
+
32
+ const slots = useSlots()
33
+ const paths = computed(() => slots
34
+ .default?.()
35
+ .map(v => v.props?.src)
36
+ .filter(Boolean) as string[],
37
+ )
38
+
39
+ const preview = (url: string) => {
40
+ const initialIndex = paths.value.findIndex(v => v === url) || 0
41
+ executeOverlay<Partial<ImageViewerProps>>(HairyImageViewer, {
42
+ props: {
43
+ urlList: paths.value,
44
+ initialIndex,
45
+ },
46
+ })
47
+ }
48
+
49
+ provide('HairyImageGroup:preview', preview)
50
+ </script>
51
+
52
+ <template>
53
+ <div class="HairyImageGroup">
54
+ <slot></slot>
55
+ </div>
56
+ </template>
57
+
58
+ <style lang="scss" scoped>
59
+ .HairyImageGroup {
60
+ display: grid;
61
+ grid-template-columns: repeat(auto-fill, var(--width, auto));
62
+ gap: var(--gap);
63
+ justify-content: var(--justify);
64
+ align-items: var(--align);
65
+ :deep(.HairyImage) {
66
+ height: var(--height, auto);
67
+ }
68
+ }
69
+ </style>
@@ -0,0 +1,22 @@
1
+ <script lang="ts" setup>
2
+ import { useOverlayMeta } from 'unoverlay-vue'
3
+ import { ElImageViewer, imageViewerProps } from 'element-plus/es/components/image-viewer/index'
4
+ import 'element-plus/es/components/image-viewer/style/index'
5
+ import { onMounted, onUnmounted } from 'vue'
6
+ const props = defineProps(imageViewerProps)
7
+
8
+ const { visible, confirm } = useOverlayMeta()
9
+
10
+ onMounted(() => {
11
+ document.body.style.overflow = 'hidden'
12
+ })
13
+ onUnmounted(() => {
14
+ document.body.style.overflow = ''
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <div class="HairyImageViewer fixed inset-0 z-2000">
20
+ <ElImageViewer v-if="visible" v-bind="props" @close="confirm()"></ElImageViewer>
21
+ </div>
22
+ </template>
@@ -0,0 +1,29 @@
1
+ <script lang="ts" setup>
2
+ defineProps<{
3
+ header?: {
4
+ title?: string
5
+ headline?: string
6
+ description?: string
7
+ }
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <div class="HairyLayout">
13
+ <HairyNav />
14
+ <HairyHeader v-bind="header">
15
+ <template #description>
16
+ <slot name="header-description" />
17
+ </template>
18
+ </HairyHeader>
19
+ <HairyBody>
20
+ <slot />
21
+
22
+ <template v-if="$slots['body-slide']" #slide>
23
+ <slot name="body-slide" />
24
+ </template>
25
+ </HairyBody>1
26
+ <HairyBackToTop />
27
+ <HairyMeting />
28
+ </div>
29
+ </template>
@@ -0,0 +1,10 @@
1
+ <script lang="ts" setup>
2
+ </script>
3
+
4
+ <template>
5
+ <span class="HairyLink cursor-pointer border-b border-dashed hover:border-primary hover:text-primary transition-all">
6
+ <slot />
7
+ </span>
8
+ </template>
9
+
10
+ <style lang="scss" scoped></style>
@@ -0,0 +1,15 @@
1
+ <script lang="ts" setup>
2
+ import type { HairyTheme } from 'valaxy-theme-hairy'
3
+ import { useThemeConfig } from 'valaxy'
4
+ import { computed } from 'vue'
5
+ const themeConfig = useThemeConfig<HairyTheme>()
6
+ const nav = computed(() => themeConfig.value.nav || [])
7
+ </script>
8
+
9
+ <template>
10
+ <div class="flex items-center h-12.5">
11
+ <HairyMenuItem v-for="(item, index) in nav" :key="index" :item="item" />
12
+ </div>
13
+ </template>
14
+
15
+ <style lang="scss" scoped></style>
@@ -0,0 +1,40 @@
1
+ <script lang="ts" setup>
2
+ import type { NavItem } from 'valaxy-theme-hairy'
3
+ import { computed } from 'vue'
4
+ import { useRoute, useRouter } from 'vue-router'
5
+ import { ejectWindow } from '../utils'
6
+ const props = defineProps<{
7
+ item: NavItem
8
+ }>()
9
+ const urlReg = /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/
10
+ const isLink = computed(() => urlReg.test(props.item?.link || ''))
11
+ const isPointer = computed(() => Boolean(props.item.link) || isLink.value)
12
+ const router = useRouter()
13
+ const route = useRoute()
14
+ const toLink = () => {
15
+ if (isLink.value)
16
+ return ejectWindow(props.item.link!)
17
+ if (props.item.link)
18
+ router.push(props.item.link)
19
+ }
20
+ const isActive = computed(() => {
21
+ if (isLink.value)
22
+ return false
23
+ if (props.item.link === '/')
24
+ return route.path === props.item.link
25
+ return route.path.includes(props.item.link!)
26
+ })
27
+ </script>
28
+
29
+ <template>
30
+ <div class="px-2.5 HairyMenuItem" :class="[isPointer ? 'cursor-pointer' : 'select-none', isActive && 'text-primary active']">
31
+ <div class="flex items-center hover:text-primary" @click="toLink">
32
+ <div v-if="item.icon" class="mr-1 icon" :class="item.icon" />
33
+ <div class="question">
34
+ {{ item.text }}
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <style lang="scss" scoped></style>
@@ -0,0 +1,19 @@
1
+ <script lang="ts" setup>
2
+ import { useAplayer, useThemeConfig } from 'valaxy'
3
+
4
+ import type { HairyTheme } from 'valaxy-theme-hairy'
5
+
6
+ const config = useThemeConfig<HairyTheme>()
7
+
8
+ if (config.value.meting)
9
+ useAplayer()
10
+ </script>
11
+
12
+ <template>
13
+ <meting-js
14
+ v-if="config.meting && config.meting.fixed !== false"
15
+ v-bind="config.meting"
16
+ :fixed="true"
17
+ >
18
+ </meting-js>
19
+ </template>
@@ -0,0 +1,39 @@
1
+ <script lang="ts" setup>
2
+ import { useScroll, whenever } from '@vueuse/core'
3
+ import { computed, ref } from 'vue'
4
+ import { useHeaderHeight } from '../hooks/useHeaderHeight'
5
+
6
+ const headerHeight = useHeaderHeight()
7
+ const cache = ref<'top' | 'bottom'>('top')
8
+ const scroll = useScroll(document)
9
+
10
+ whenever(() => scroll.directions.top, () => {
11
+ cache.value = 'top'
12
+ })
13
+ whenever(() => scroll.directions.bottom, () => {
14
+ cache.value = 'bottom'
15
+ })
16
+
17
+ const show = computed(() => {
18
+ return scroll.y.value < (headerHeight.value / 2) || cache.value === 'top'
19
+ })
20
+ </script>
21
+
22
+ <template>
23
+ <div class="HairyNav fixed w-full h-3.125rem top-0 z-20 opacity-0 transition-opacity duration-200" :class="[show && 'opacity-100']">
24
+ <div class="mx-auto breakpoint flex relative z-1">
25
+ <div class="flex-1 flex items-center">
26
+ <HairyNavTitle class="lt-sm:hidden" />
27
+ <HairyMenu />
28
+ </div>
29
+ <div class="flex-center lt-sm:hidden">
30
+ <HairyNavToggleDark />
31
+ <HairyNavSearch />
32
+ </div>
33
+ </div>
34
+ <HairyNavBackground />
35
+ </div>
36
+ </template>
37
+
38
+ <style>
39
+ </style>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <div class="HairyNavBackground transition-colors absolute inset-0 bg-white bg-opacity-80 dark:bg-black dark:bg-opacity-80 blur-5" />
3
+ </template>
4
+
5
+ <style>
6
+
7
+ </style>
@@ -0,0 +1,265 @@
1
+ <script lang="ts" setup>
2
+ import '@docsearch/css'
3
+ import { computed, defineAsyncComponent, onMounted, onUnmounted, ref } from 'vue'
4
+ import { useConfig } from 'valaxy'
5
+
6
+ const HairyAlgoliaSearchBox = __ALGOLIA__
7
+ ? defineAsyncComponent(() => import('./HairyAlgoliaSearchBox.vue'))
8
+ : () => null
9
+
10
+ const config = useConfig()
11
+ const search = computed(() => config.value.search)
12
+ const enable = computed(() => search.value.algolia.enable)
13
+
14
+ // to avoid loading the docsearch js upfront (which is more than 1/3 of the
15
+ // payload), we delay initializing it until the user has actually clicked or
16
+ // hit the hotkey to invoke it.
17
+ const loaded = ref(false)
18
+
19
+ const metaKey = ref()
20
+
21
+ onMounted(() => {
22
+ if (!search.value.enable || !search.value.algolia.enable)
23
+ return
24
+
25
+ // meta key detect (same logic as in @docsearch/js)
26
+ metaKey.value.textContent = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
27
+ ? '⌘'
28
+ : 'Ctrl'
29
+
30
+ const handleSearchHotKey = (e: KeyboardEvent) => {
31
+ if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
32
+ e.preventDefault()
33
+ load()
34
+ remove()
35
+ }
36
+ }
37
+ function remove() {
38
+ window.removeEventListener('keydown', handleSearchHotKey)
39
+ }
40
+
41
+ window.addEventListener('keydown', handleSearchHotKey)
42
+ onUnmounted(remove)
43
+ })
44
+ function load() {
45
+ if (!loaded.value)
46
+ loaded.value = true
47
+ }
48
+ </script>
49
+
50
+ <template>
51
+ <div v-if="enable" class="VPNavBarSearch">
52
+ <HairyAlgoliaSearchBox v-if="loaded" />
53
+ <div v-else id="docsearch" @click="load">
54
+ <button
55
+ type="button"
56
+ class="DocSearch DocSearch-Button"
57
+ aria-label="Search"
58
+ >
59
+ <span class="DocSearch-Button-Container">
60
+ <svg
61
+ class="DocSearch-Search-Icon"
62
+ width="20"
63
+ height="20"
64
+ viewBox="0 0 20 20"
65
+ >
66
+ <path
67
+ d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z"
68
+ stroke="currentColor"
69
+ fill="none"
70
+ fill-rule="evenodd"
71
+ stroke-linecap="round"
72
+ stroke-linejoin="round"
73
+ />
74
+ </svg>
75
+ <span class="DocSearch-Button-Placeholder">Search</span>
76
+ </span>
77
+ <span class="DocSearch-Button-Keys">
78
+ <kbd ref="metaKey" class="DocSearch-Button-Key">Meta</kbd>
79
+ <kbd class="DocSearch-Button-Key">K</kbd>
80
+ </span>
81
+ </button>
82
+ </div>
83
+ </div>
84
+ </template>
85
+
86
+ <style>
87
+ .VPNavBarSearch {
88
+ display: flex;
89
+ align-items: center;
90
+ }
91
+
92
+ @media (min-width: 768px) {
93
+ .VPNavBarSearch {
94
+ flex-grow: 1;
95
+ padding-left: 24px;
96
+ }
97
+ }
98
+
99
+ @media (min-width: 960px) {
100
+ .VPNavBarSearch {
101
+ padding-left: 32px;
102
+ }
103
+ }
104
+
105
+ .DocSearch {
106
+ --docsearch-primary-color: var(--hy-c-brand);
107
+ --docsearch-highlight-color: var(--docsearch-primary-color);
108
+ --docsearch-text-color: var(--hy-c-text-1);
109
+ --docsearch-muted-color: var(--hy-c-text-2);
110
+ --docsearch-searchbox-shadow: none;
111
+ --docsearch-searchbox-focus-background: transparent;
112
+ --docsearch-key-gradient: transparent;
113
+ --docsearch-key-shadow: none;
114
+ --docsearch-modal-background: var(--hy-c-bg-soft);
115
+ --docsearch-footer-background: var(--hy-c-bg);
116
+ }
117
+
118
+ .dark .DocSearch {
119
+ --docsearch-modal-shadow: none;
120
+ --docsearch-footer-shadow: none;
121
+ --docsearch-logo-color: var(--hy-c-text-2);
122
+ --docsearch-hit-background: var(--hy-c-bg-mute);
123
+ --docsearch-hit-color: var(--hy-c-text-2);
124
+ --docsearch-hit-shadow: none;
125
+ }
126
+
127
+ .DocSearch-Button {
128
+ display: flex;
129
+ justify-content: center;
130
+ align-items: center;
131
+ margin: 0;
132
+ padding: 0;
133
+ width: 32px;
134
+ height: 55px;
135
+ background: transparent;
136
+ transition: border-color 0.25s;
137
+ }
138
+
139
+ .DocSearch-Button:hover {
140
+ background: transparent;
141
+ }
142
+
143
+ .DocSearch-Button:focus {
144
+ outline: 1px dotted;
145
+ outline: 5px auto -webkit-focus-ring-color;
146
+ }
147
+
148
+ .DocSearch-Button:focus:not(:focus-visible) {
149
+ outline: none !important;
150
+ }
151
+
152
+ @media (min-width: 768px) {
153
+ .DocSearch-Button {
154
+ justify-content: flex-start;
155
+ border: 1px solid transparent;
156
+ border-radius: 8px;
157
+ padding: 0 10px 0 12px;
158
+ width: 100%;
159
+ height: 40px;
160
+ /* background-color: var(--hy-c-bg-alt); */
161
+ }
162
+
163
+ .DocSearch-Button:hover {
164
+ border-color: var(--hy-c-brand);
165
+ /* background: var(--hy-c-bg-alt); */
166
+ }
167
+ }
168
+
169
+ .DocSearch-Button .DocSearch-Button-Container {
170
+ display: flex;
171
+ align-items: center;
172
+ }
173
+
174
+ .DocSearch-Button .DocSearch-Search-Icon {
175
+ position: relative;
176
+ width: 16px;
177
+ height: 16px;
178
+ color: var(--hy-c-text-1);
179
+ fill: currentColor;
180
+ transition: color 0.5s;
181
+ }
182
+
183
+ .DocSearch-Button:hover .DocSearch-Search-Icon {
184
+ color: var(--hy-c-text-1);
185
+ }
186
+
187
+ @media (min-width: 768px) {
188
+ .DocSearch-Button .DocSearch-Search-Icon {
189
+ top: 1px;
190
+ margin-right: 8px;
191
+ width: 14px;
192
+ height: 14px;
193
+ color: var(--hy-c-text-2);
194
+ }
195
+ }
196
+
197
+ .DocSearch-Button .DocSearch-Button-Placeholder {
198
+ display: none;
199
+ margin-top: 2px;
200
+ padding: 0 16px 0 0;
201
+ font-size: 13px;
202
+ font-weight: 500;
203
+ color: var(--hy-c-text-2);
204
+ transition: color 0.5s;
205
+ }
206
+
207
+ .DocSearch-Button:hover .DocSearch-Button-Placeholder {
208
+ color: var(--hy-c-text-1);
209
+ }
210
+
211
+ @media (min-width: 768px) {
212
+ .DocSearch-Button .DocSearch-Button-Placeholder {
213
+ display: inline-block;
214
+ }
215
+ }
216
+
217
+ .DocSearch-Button .DocSearch-Button-Keys {
218
+ display: none;
219
+ min-width: auto;
220
+ }
221
+
222
+ @media (min-width: 768px) {
223
+ .DocSearch-Button .DocSearch-Button-Keys {
224
+ display: flex;
225
+ align-items: center;
226
+ }
227
+ }
228
+
229
+ .DocSearch-Button .DocSearch-Button-Key {
230
+ display: block;
231
+ margin: 2px 0 0 0;
232
+ border: 1px solid var(--hy-c-divider);
233
+ border-right: none;
234
+ border-radius: 4px 0 0 4px;
235
+ padding-left: 6px;
236
+ min-width: 0;
237
+ width: auto;
238
+ height: 22px;
239
+ line-height: 22px;
240
+ font-size: 12px;
241
+ font-weight: 500;
242
+ transition: color 0.5s, border-color 0.5s;
243
+ }
244
+
245
+ .DocSearch-Button .DocSearch-Button-Key + .DocSearch-Button-Key {
246
+ border-right: 1px solid var(--hy-c-divider);
247
+ border-left: none;
248
+ border-radius: 0 4px 4px 0;
249
+ padding-left: 2px;
250
+ padding-right: 6px;
251
+ }
252
+
253
+ .dark .DocSearch-Footer {
254
+ border-top: 1px solid var(--hy-c-divider);
255
+ }
256
+
257
+ .DocSearch-Form {
258
+ border: 1px solid var(--hy-c-brand);
259
+ background-color: var(--hy-c-white);
260
+ }
261
+
262
+ .dark .DocSearch-Form {
263
+ background-color: var(--hy-c-bg-mute);
264
+ }
265
+ </style>
@@ -0,0 +1,13 @@
1
+ <script lang="ts" setup>
2
+ import { useConfig } from 'valaxy'
3
+ const config = useConfig()
4
+ const hrefToUrl = () => {
5
+ location.href = config.value.url
6
+ }
7
+ </script>
8
+
9
+ <template>
10
+ <div class="px-2.5">
11
+ <span class="cursor-pointer hover:text-primary" @click="hrefToUrl">{{ config.title }}</span>
12
+ </div>
13
+ </template>
@@ -0,0 +1,16 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from 'vue-i18n'
3
+ import { computed } from 'vue'
4
+ import { isDark, toggleDark } from 'valaxy'
5
+
6
+ const { t } = useI18n()
7
+ const themeTitle = computed(() => {
8
+ return isDark.value ? t('button.toggle_light') : t('button.toggle_dark')
9
+ })
10
+ </script>
11
+
12
+ <template>
13
+ <button class="yun-icon-btn" :title="themeTitle" :style="{ color: isDark ? '' : '#f1cb64' }" @click="toggleDark()">
14
+ <div i="ri-sun-line dark:ri-moon-line" />
15
+ </button>
16
+ </template>
@@ -0,0 +1,28 @@
1
+ <script lang="ts" setup>
2
+ import type { Ref } from 'vue'
3
+ import { computed } from 'vue'
4
+ import type { Post } from 'valaxy'
5
+ import { usePostList } from 'valaxy'
6
+
7
+ import { usePostLayout } from '../hooks/usePostLayout'
8
+
9
+ const props = withDefaults(defineProps<{
10
+ type?: string
11
+ posts?: Post[]
12
+ }>(), {
13
+ })
14
+
15
+ const routes = usePostList() as any as Ref<Post[]>
16
+ const posts = computed(() => props.posts || routes.value)
17
+ const layout = usePostLayout()
18
+ const reverse = computed(() => layout.value.includes('reverse'))
19
+ </script>
20
+
21
+ <template>
22
+ <ul class="divide-y divide-gray-200 dark:divide-gray-700">
23
+ <Transition v-for="post, i in posts" :key="i" name="fade">
24
+ <HairyArticleImage :post="post" :reverse="reverse && (i % 2) === 0" />
25
+ </Transition>
26
+ </ul>
27
+ </template>
28
+