clarity-theme 0.1.0 → 0.1.2

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.
Files changed (154) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +70 -4
  3. package/README.zh-CN.md +67 -4
  4. package/nuxt.config.ts +217 -214
  5. package/package.json +35 -37
  6. package/{app → src}/app.config.ts +110 -110
  7. package/{app → src}/app.vue +5 -5
  8. package/{app → src}/components/blog/BlogAside.vue +25 -25
  9. package/{app → src}/components/blog/BlogFooter.vue +25 -25
  10. package/{app → src}/components/blog/BlogHeader.global.vue +48 -48
  11. package/{app → src}/components/blog/BlogPanel.vue +43 -43
  12. package/{app → src}/components/blog/BlogSidebar.vue +53 -53
  13. package/{app → src}/components/blog/BlogWidget.vue +42 -42
  14. package/{app → src}/components/blog/Mask.vue +25 -25
  15. package/{app → src}/components/blog/SkipToContent.vue +9 -9
  16. package/{app → src}/components/blog/ThemeToggle.vue +22 -22
  17. package/{app → src}/components/content/Badge.vue +45 -45
  18. package/{app → src}/components/content/Blur.vue +14 -14
  19. package/{app → src}/components/content/CardList.vue +8 -8
  20. package/{app → src}/components/content/Chat.vue +41 -41
  21. package/{app → src}/components/content/Copy.vue +71 -71
  22. package/{app → src}/components/content/EmojiClock.vue +49 -49
  23. package/{app → src}/components/content/FeedCard.vue +96 -96
  24. package/{app → src}/components/content/FeedGroup.vue +61 -61
  25. package/{app → src}/components/content/Folding.vue +19 -19
  26. package/{app → src}/components/content/Key.vue +143 -143
  27. package/{app → src}/components/content/LinkBanner.vue +27 -27
  28. package/{app → src}/components/content/LinkCard.vue +28 -28
  29. package/{app → src}/components/content/MdTitle.vue +8 -8
  30. package/{app → src}/components/content/Mermaid.vue +99 -99
  31. package/{app → src}/components/content/MusicScore.vue +63 -63
  32. package/{app → src}/components/content/Pic.vue +49 -49
  33. package/{app → src}/components/content/Poetry.vue +27 -27
  34. package/{app → src}/components/content/ProseA.vue +24 -24
  35. package/{app → src}/components/content/ProseCode.vue +16 -16
  36. package/{app → src}/components/content/ProsePre.vue +117 -117
  37. package/{app → src}/components/content/ProseTable.vue +23 -23
  38. package/{app → src}/components/content/Quote.vue +21 -21
  39. package/{app → src}/components/content/Tab.vue +32 -32
  40. package/{app → src}/components/content/Timeline.vue +33 -33
  41. package/{app → src}/components/content/Tip.vue +40 -40
  42. package/{app → src}/components/content/VideoEmbed.vue +112 -112
  43. package/{app → src}/components/partial/Button.vue +36 -36
  44. package/{app → src}/components/partial/DlGroup.vue +31 -31
  45. package/{app → src}/components/partial/Dropdown.vue +21 -21
  46. package/{app → src}/components/partial/Error.vue +39 -39
  47. package/{app → src}/components/partial/Expand.vue +26 -26
  48. package/{app → src}/components/partial/IconNavList.vue +24 -24
  49. package/{app → src}/components/partial/Pagination.vue +62 -62
  50. package/{app → src}/components/partial/RadioGroup.vue +30 -30
  51. package/{app → src}/components/partial/Secret.vue +11 -11
  52. package/{app → src}/components/partial/Slider.vue +48 -48
  53. package/{app → src}/components/partial/Toggle.vue +17 -17
  54. package/{app → src}/components/popover/Lightbox.vue +44 -44
  55. package/{app → src}/components/popover/Search.vue +143 -143
  56. package/{app → src}/components/popover/SearchItem.vue +28 -28
  57. package/{app → src}/components/post/Archive.vue +40 -40
  58. package/{app → src}/components/post/Article.vue +49 -49
  59. package/{app → src}/components/post/Comment.vue +119 -119
  60. package/{app → src}/components/post/Excerpt.vue +40 -40
  61. package/{app → src}/components/post/OrderToggle.vue +70 -70
  62. package/{app → src}/components/post/PostFooter.vue +52 -52
  63. package/{app → src}/components/post/PostHeader.vue +66 -66
  64. package/{app → src}/components/post/PostSurround.vue +51 -51
  65. package/{app → src}/components/post/Slide.vue +85 -85
  66. package/{app → src}/components/util/Date.vue +62 -62
  67. package/{app → src}/components/util/HydrateSafe.vue +10 -10
  68. package/{app → src}/components/util/Img.vue +38 -38
  69. package/{app → src}/components/util/Link.vue +11 -11
  70. package/{app → src}/components/widget/BlogLog.vue +23 -23
  71. package/{app → src}/components/widget/BlogStats.vue +38 -38
  72. package/{app → src}/components/widget/BlogTech.vue +57 -57
  73. package/{app → src}/components/widget/Empty.vue +3 -3
  74. package/{app → src}/components/widget/Toc.vue +67 -67
  75. package/{app → src}/composables/useArticle.ts +115 -115
  76. package/{app → src}/composables/useAvoid.ts +87 -87
  77. package/{app → src}/composables/useCopy.ts +21 -21
  78. package/{app → src}/composables/usePagination.ts +70 -70
  79. package/{app → src}/composables/useShiki.ts +129 -129
  80. package/{app → src}/composables/useToc.ts +57 -57
  81. package/{app → src}/composables/useWidgets.ts +56 -56
  82. package/{config → src/config}/app.ts +62 -62
  83. package/{config → src/config}/content.ts +75 -75
  84. package/{config → src/config}/define.ts +3 -3
  85. package/src/config/feeds.empty.ts +6 -0
  86. package/{config → src/config}/index.ts +1 -1
  87. package/{config → src/config}/schema.ts +89 -89
  88. package/{app → src}/error.vue +31 -31
  89. package/{img → src/img}/index.ts +2 -2
  90. package/{app → src}/layouts/default.vue +25 -25
  91. package/{modules → src/modules}/clarity-config/index.ts +168 -162
  92. package/src/modules/clarity-source-layout/index.ts +78 -0
  93. package/{app → src}/pages/[...slug].vue +59 -59
  94. package/{app → src}/pages/archive.vue +129 -129
  95. package/{app → src}/pages/index.vue +76 -76
  96. package/{app → src}/pages/link.vue +62 -62
  97. package/{app → src}/pages/preview.vue +46 -46
  98. package/{app → src}/plugins/easter-egg.ts +23 -23
  99. package/{app → src}/plugins/init.ts +6 -6
  100. package/{app → src}/plugins/tippy.ts +22 -22
  101. package/{public → src/public}/assets/atom.css +274 -274
  102. package/{public → src/public}/assets/atom.xsl +109 -109
  103. package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.ts +58 -58
  104. package/{remark-plugins → src/remark-plugins}/remark-code-component.ts +41 -41
  105. package/{server → src/server}/api/stats.get.ts +84 -83
  106. package/{server → src/server}/routes/atom.xml.get.ts +85 -85
  107. package/{server → src/server}/routes/subscriptions.opml.get.ts +64 -64
  108. package/{shared → src/shared}/utils/clarity.ts +24 -24
  109. package/{shared → src/shared}/utils/icon.ts +218 -218
  110. package/{shared → src/shared}/utils/link.ts +52 -52
  111. package/{shared → src/shared}/utils/str.ts +88 -88
  112. package/{shared → src/shared}/utils/time.ts +115 -115
  113. package/{app → src}/shiki.config.ts +8 -8
  114. package/{app → src}/stores/layout.ts +34 -34
  115. package/{app → src}/stores/search.ts +37 -37
  116. package/{app → src}/types/article.ts +16 -16
  117. package/src/types/feed.ts +1 -0
  118. package/{app → src}/types/index.ts +14 -14
  119. package/{app → src}/types/nav.ts +11 -11
  120. package/{app → src}/utils/anim.ts +37 -37
  121. package/{app → src}/utils/img.ts +83 -83
  122. package/app/types/feed.ts +0 -1
  123. package/config/feeds.empty.ts +0 -4
  124. package/{app → src}/assets/css/_variable.scss +0 -0
  125. package/{app → src}/assets/css/animation.scss +0 -0
  126. package/{app → src}/assets/css/article.scss +0 -0
  127. package/{app → src}/assets/css/color.scss +0 -0
  128. package/{app → src}/assets/css/font.scss +0 -0
  129. package/{app → src}/assets/css/main.scss +0 -0
  130. package/{app → src}/assets/css/reusable.scss +0 -0
  131. package/{app → src}/assets/icons/solar-rewind-back-bold-duotone.svg +0 -0
  132. package/{app → src}/assets/icons/solar-rewind-forward-bold-duotone.svg +0 -0
  133. package/{app → src}/components/content/Alert.vue +0 -0
  134. package/{config → src/config}/content.d.mts +0 -0
  135. package/{config → src/config}/content.mjs +0 -0
  136. package/{config → src/config}/define.mjs +0 -0
  137. package/{config → src/config}/feed.ts +0 -0
  138. package/{config → src/config}/index.d.mts +0 -0
  139. package/{config → src/config}/index.mjs +0 -0
  140. package/{config → src/config}/public.ts +1 -1
  141. package/{config → src/config}/schema.d.mts +0 -0
  142. package/{config → src/config}/schema.mjs +0 -0
  143. package/{config → src/config}/server.ts +1 -1
  144. /package/{img → src/img}/index.d.mts +0 -0
  145. /package/{img → src/img}/index.mjs +0 -0
  146. /package/{modules → src/modules}/clarity-config/anti-mirror-client.ts +0 -0
  147. /package/{public → src/public}/fonts/AlimamaFangYuanTi.woff2 +0 -0
  148. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.d.mts +0 -0
  149. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.mjs +0 -0
  150. /package/{remark-plugins → src/remark-plugins}/remark-code-component.d.mts +0 -0
  151. /package/{remark-plugins → src/remark-plugins}/remark-code-component.mjs +0 -0
  152. /package/{server → src/server}/utils/clarity.ts +0 -0
  153. /package/{shared → src/shared}/types/content.ts +0 -0
  154. /package/{app → src}/types/modal.ts +0 -0
@@ -1,110 +1,110 @@
1
- /**
2
- * Clarity Theme UI 默认配置。
3
- *
4
- * 站点数据(标题、作者、文章分类等)来自根目录 clarity.config.ts,
5
- * 由 modules/clarity-config 注入到 appConfig.clarity 中。
6
- *
7
- * 消费项目可在自己的 app/app.config.ts 中按需覆盖任意键。
8
- */
9
- export default defineAppConfig({
10
- clarity: {
11
- // @keep-sorted
12
- component: {
13
- alert: {
14
- /** 默认使用卡片风格还是扁平风格 */
15
- defaultStyle: 'card' as 'card' | 'flat',
16
- },
17
-
18
- codeblock: {
19
- /** 代码块触发折叠的行数 */
20
- triggerRows: 32,
21
- /** 代码块折叠后的行数 */
22
- collapsedRows: 16,
23
- /** 启用代码块缩进导航会关闭空格渲染 */
24
- enableIndentGuide: true,
25
- /** 代码块缩进导航竖线匹配空格数 */
26
- indent: 4,
27
- /** tab 渲染宽度 */
28
- tabSize: 3,
29
- },
30
-
31
- /** 文章开头摘要 */
32
- excerpt: {
33
- animation: true,
34
- caret: '_',
35
- },
36
-
37
- /** 精选文章 Slide */
38
- slide: {
39
- /** 适合封面图无字时启用 */
40
- showTitle: true,
41
- },
42
-
43
- stats: {
44
- /** 归档页面每年标题对应的年龄,无需展示时可留空 */
45
- birthYear: undefined as number | undefined,
46
- },
47
- },
48
-
49
- // @keep-sorted
50
- footer: {
51
- /** 侧边栏底部图标导航 */
52
- iconNav: [] as { icon: string, text: string, url: string }[],
53
- /** 页脚站点地图 */
54
- nav: [] as {
55
- title: string
56
- items: { icon: string, text: string, url: string }[]
57
- }[],
58
- },
59
-
60
- /** 左侧栏顶部 Logo */
61
- header: {
62
- /** 展示标题文本,否则展示纯 Logo */
63
- showTitle: true,
64
- emojiTail: [] as string[],
65
- },
66
-
67
- /** 友链页面 */
68
- link: {
69
- /** 无订阅源展示静音图标 */
70
- remindNoFeed: true,
71
- /** 友链分组内随机排序 */
72
- randomInGroup: true,
73
- },
74
-
75
- /** 左侧栏导航 */
76
- nav: [
77
- {
78
- title: '',
79
- items: [
80
- { icon: 'tabler:files', text: '文章', url: '/' },
81
- { icon: 'tabler:link', text: '友链', url: '/link' },
82
- { icon: 'tabler:archive', text: '归档', url: '/archive' },
83
- ],
84
- },
85
- ],
86
-
87
- pagination: {
88
- perPage: 10,
89
- /** 默认排序方式,需要是 article.order 中的键名 */
90
- sortOrder: 'date',
91
- /** 允许(普通/预览/归档)文章列表正序,开启后排序方式左侧图标可切换顺序 */
92
- allowAscending: false,
93
- },
94
-
95
- themes: {
96
- light: {
97
- icon: 'tabler:sun',
98
- tip: '浅色模式',
99
- },
100
- system: {
101
- icon: 'tabler:device-desktop',
102
- tip: '跟随系统',
103
- },
104
- dark: {
105
- icon: 'tabler:moon',
106
- tip: '深色模式',
107
- },
108
- },
109
- },
110
- })
1
+ /**
2
+ * Clarity Theme UI 默认配置。
3
+ *
4
+ * 站点数据(标题、作者、文章分类等)来自根目录 clarity.config.ts,
5
+ * 由 src/modules/clarity-config 注入到 appConfig.clarity 中。
6
+ *
7
+ * 消费项目可在自己的 app/app.config.ts 中按需覆盖任意键。
8
+ */
9
+ export default defineAppConfig({
10
+ clarity: {
11
+ // @keep-sorted
12
+ component: {
13
+ alert: {
14
+ /** 默认使用卡片风格还是扁平风格 */
15
+ defaultStyle: 'card' as 'card' | 'flat',
16
+ },
17
+
18
+ codeblock: {
19
+ /** 代码块触发折叠的行数 */
20
+ triggerRows: 32,
21
+ /** 代码块折叠后的行数 */
22
+ collapsedRows: 16,
23
+ /** 启用代码块缩进导航会关闭空格渲染 */
24
+ enableIndentGuide: true,
25
+ /** 代码块缩进导航竖线匹配空格数 */
26
+ indent: 4,
27
+ /** tab 渲染宽度 */
28
+ tabSize: 3,
29
+ },
30
+
31
+ /** 文章开头摘要 */
32
+ excerpt: {
33
+ animation: true,
34
+ caret: '_',
35
+ },
36
+
37
+ /** 精选文章 Slide */
38
+ slide: {
39
+ /** 适合封面图无字时启用 */
40
+ showTitle: true,
41
+ },
42
+
43
+ stats: {
44
+ /** 归档页面每年标题对应的年龄,无需展示时可留空 */
45
+ birthYear: undefined as number | undefined,
46
+ },
47
+ },
48
+
49
+ // @keep-sorted
50
+ footer: {
51
+ /** 侧边栏底部图标导航 */
52
+ iconNav: [] as { icon: string, text: string, url: string }[],
53
+ /** 页脚站点地图 */
54
+ nav: [] as {
55
+ title: string
56
+ items: { icon: string, text: string, url: string }[]
57
+ }[],
58
+ },
59
+
60
+ /** 左侧栏顶部 Logo */
61
+ header: {
62
+ /** 展示标题文本,否则展示纯 Logo */
63
+ showTitle: true,
64
+ emojiTail: [] as string[],
65
+ },
66
+
67
+ /** 友链页面 */
68
+ link: {
69
+ /** 无订阅源展示静音图标 */
70
+ remindNoFeed: true,
71
+ /** 友链分组内随机排序 */
72
+ randomInGroup: true,
73
+ },
74
+
75
+ /** 左侧栏导航 */
76
+ nav: [
77
+ {
78
+ title: '',
79
+ items: [
80
+ { icon: 'tabler:files', text: '文章', url: '/' },
81
+ { icon: 'tabler:link', text: '友链', url: '/link' },
82
+ { icon: 'tabler:archive', text: '归档', url: '/archive' },
83
+ ],
84
+ },
85
+ ],
86
+
87
+ pagination: {
88
+ perPage: 10,
89
+ /** 默认排序方式,需要是 article.order 中的键名 */
90
+ sortOrder: 'date',
91
+ /** 允许(普通/预览/归档)文章列表正序,开启后排序方式左侧图标可切换顺序 */
92
+ allowAscending: false,
93
+ },
94
+
95
+ themes: {
96
+ light: {
97
+ icon: 'tabler:sun',
98
+ tip: '浅色模式',
99
+ },
100
+ system: {
101
+ icon: 'tabler:device-desktop',
102
+ tip: '跟随系统',
103
+ },
104
+ dark: {
105
+ icon: 'tabler:moon',
106
+ tip: '深色模式',
107
+ },
108
+ },
109
+ },
110
+ })
@@ -1,5 +1,5 @@
1
- <template>
2
- <NuxtLayout>
3
- <NuxtPage />
4
- </NuxtLayout>
5
- </template>
1
+ <template>
2
+ <NuxtLayout>
3
+ <NuxtPage />
4
+ </NuxtLayout>
5
+ </template>
@@ -1,27 +1,27 @@
1
- <script setup lang="ts">
2
- const layoutStore = useLayoutStore()
3
-
4
- // 页面完成异步 setup 后才注册具名插槽;水合前等待,避免重复插入 SSR 侧栏。
5
- const layoutSlots = inject<{ ready: Promise<void> }>(Symbol.for('dxup:layout-slots'))
6
- if (layoutSlots)
7
- await layoutSlots.ready
8
- </script>
9
-
10
- <template>
11
- <BlogMask
12
- :show="layoutStore.state === 'aside'"
13
- class="widescreen-only"
14
- @click="layoutStore.close()"
15
- />
16
-
17
- <!-- 不能用 Transition 实现弹出收起动画,因为宽屏状态始终显示 -->
18
- <!-- 如果为空数组则隐藏 -->
19
- <aside id="blog-aside" :class="{ show: layoutStore.state === 'aside' }">
20
- <slot />
21
- </aside>
22
- </template>
23
-
24
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const layoutStore = useLayoutStore()
3
+
4
+ // 页面完成异步 setup 后才注册具名插槽;水合前等待,避免重复插入 SSR 侧栏。
5
+ const layoutSlots = inject<{ ready: Promise<void> }>(Symbol.for('dxup:layout-slots'))
6
+ if (layoutSlots)
7
+ await layoutSlots.ready
8
+ </script>
9
+
10
+ <template>
11
+ <BlogMask
12
+ :show="layoutStore.state === 'aside'"
13
+ class="widescreen-only"
14
+ @click="layoutStore.close()"
15
+ />
16
+
17
+ <!-- 不能用 Transition 实现弹出收起动画,因为宽屏状态始终显示 -->
18
+ <!-- 如果为空数组则隐藏 -->
19
+ <aside id="blog-aside" :class="{ show: layoutStore.state === 'aside' }">
20
+ <slot />
21
+ </aside>
22
+ </template>
23
+
24
+ <style lang="scss" scoped>
25
25
  #blog-aside {
26
26
  display: flex;
27
27
  flex-direction: column;
@@ -60,4 +60,4 @@ if (layoutSlots)
60
60
  display: none;
61
61
  }
62
62
  }
63
- </style>
63
+ </style>
@@ -1,27 +1,27 @@
1
- <script setup lang="ts">
2
- const clarity = useClarityConfig()
3
- </script>
4
-
5
- <template>
6
- <footer class="blog-footer">
7
- <nav class="footer-nav">
8
- <div v-for="(group, groupIndex) in clarity.footer.nav" :key="groupIndex">
9
- <hgroup class="text-creative" v-text="group.title" />
10
- <menu>
11
- <li v-for="(item, itemIndex) in group.items" :key="itemIndex">
12
- <UtilLink :to="item.url">
13
- <Icon :name="item.icon" />
14
- <span class="nav-text">{{ item.text }}</span>
15
- </UtilLink>
16
- </li>
17
- </menu>
18
- </div>
19
- </nav>
20
- <p v-html="clarity.footer.copyright" />
21
- </footer>
22
- </template>
23
-
24
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const clarity = useClarityConfig()
3
+ </script>
4
+
5
+ <template>
6
+ <footer class="blog-footer">
7
+ <nav class="footer-nav">
8
+ <div v-for="(group, groupIndex) in clarity.footer.nav" :key="groupIndex">
9
+ <hgroup class="text-creative" v-text="group.title" />
10
+ <menu>
11
+ <li v-for="(item, itemIndex) in group.items" :key="itemIndex">
12
+ <UtilLink :to="item.url">
13
+ <Icon :name="item.icon" />
14
+ <span class="nav-text">{{ item.text }}</span>
15
+ </UtilLink>
16
+ </li>
17
+ </menu>
18
+ </div>
19
+ </nav>
20
+ <p v-html="clarity.footer.copyright" />
21
+ </footer>
22
+ </template>
23
+
24
+ <style lang="scss" scoped>
25
25
  .blog-footer {
26
26
  margin: 3rem 1rem;
27
27
  font-size: 0.9em;
@@ -58,4 +58,4 @@ const clarity = useClarityConfig()
58
58
  margin: 0.5em;
59
59
  }
60
60
  }
61
- </style>
61
+ </style>
@@ -1,50 +1,50 @@
1
- <script setup lang="ts">
2
- withDefaults(defineProps<{
3
- tag?: string
4
- }>(), {
5
- tag: 'div',
6
- })
7
- const clarity = useClarityConfig()
8
- </script>
9
-
10
- <template>
11
- <UtilLink class="blog-header">
12
- <div v-if="clarity.header.emojiTail" class="emoji-tail">
13
- <span
14
- v-for="(emoji, emojiIndex) in clarity.header.emojiTail"
15
- :key="emojiIndex"
16
- class="split-char"
17
- :style="getFixedDelay(emojiIndex * .6 - 3)"
18
- v-text="emoji"
19
- />
20
- </div>
21
-
22
- <NuxtImg
23
- :src="clarity.header.logo"
24
- class="blog-logo round-cobblestone"
25
- :class="{ circle: clarity.header.showTitle }"
26
- :alt="clarity.site.title"
27
- />
28
-
29
- <div v-if="clarity.header.showTitle" class="blog-text">
30
- <component :is="tag" class="header-title">
31
- <span
32
- v-for="(char, charIndex) in clarity.site.title"
33
- :key="charIndex"
34
- class="split-char"
35
- :style="getFixedDelay((charIndex + 1) * .1)"
36
- v-text="char"
37
- />
38
- </component>
39
-
40
- <div class="header-subtitle">
41
- {{ clarity.header.subtitle }}
42
- </div>
43
- </div>
44
- </UtilLink>
45
- </template>
46
-
47
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ withDefaults(defineProps<{
3
+ tag?: string
4
+ }>(), {
5
+ tag: 'div',
6
+ })
7
+ const clarity = useClarityConfig()
8
+ </script>
9
+
10
+ <template>
11
+ <UtilLink class="blog-header">
12
+ <div v-if="clarity.header.emojiTail" class="emoji-tail">
13
+ <span
14
+ v-for="(emoji, emojiIndex) in clarity.header.emojiTail"
15
+ :key="emojiIndex"
16
+ class="split-char"
17
+ :style="getFixedDelay(emojiIndex * .6 - 3)"
18
+ v-text="emoji"
19
+ />
20
+ </div>
21
+
22
+ <NuxtImg
23
+ :src="clarity.header.logo"
24
+ class="blog-logo round-cobblestone"
25
+ :class="{ circle: clarity.header.showTitle }"
26
+ :alt="clarity.site.title"
27
+ />
28
+
29
+ <div v-if="clarity.header.showTitle" class="blog-text">
30
+ <component :is="tag" class="header-title">
31
+ <span
32
+ v-for="(char, charIndex) in clarity.site.title"
33
+ :key="charIndex"
34
+ class="split-char"
35
+ :style="getFixedDelay((charIndex + 1) * .1)"
36
+ v-text="char"
37
+ />
38
+ </component>
39
+
40
+ <div class="header-subtitle">
41
+ {{ clarity.header.subtitle }}
42
+ </div>
43
+ </div>
44
+ </UtilLink>
45
+ </template>
46
+
47
+ <style lang="scss" scoped>
48
48
  .blog-header {
49
49
  contain: layout;
50
50
  display: flex;
@@ -143,4 +143,4 @@ const clarity = useClarityConfig()
143
143
  filter: blur(1px);
144
144
  }
145
145
  }
146
- </style>
146
+ </style>
@@ -1,45 +1,45 @@
1
- <script setup lang="ts">
2
- defineProps<{
3
- hasAside?: boolean
4
- }>()
5
-
6
- const layoutStore = useLayoutStore()
7
- const { avoidTargets } = storeToRefs(layoutStore)
8
-
9
- const panelRef = useTemplateRef('blog-panel')
10
- const { transform } = useAvoidTransform(panelRef, avoidTargets)
11
- </script>
12
-
13
- <template>
14
- <div
15
- id="blog-panel"
16
- ref="blog-panel"
17
- :class="{ 'has-active': layoutStore.state !== 'none' }"
18
- :style="{ transform }"
19
- >
20
- <button
21
- v-if="hasAside"
22
- class="toggle-aside widescreen-only"
23
- :class="{ active: layoutStore.state === 'aside' }"
24
- aria-label="切换侧边栏"
25
- @click="layoutStore.toggle('aside')"
26
- >
27
- <Icon class="rtl-flip" name="tabler:align-right" />
28
- </button>
29
-
30
- <Icon v-show="false" name="tabler:layout-sidebar-filled" />
31
- <button
32
- class="toggle-sidebar mobile-only"
33
- :class="{ active: layoutStore.state === 'sidebar' }"
34
- aria-label="切换菜单"
35
- @click="layoutStore.toggle('sidebar')"
36
- >
37
- <Icon class="rtl-flip" :name="layoutStore.state === 'sidebar' ? 'tabler:layout-sidebar-filled' : 'tabler:layout-sidebar'" />
38
- </button>
39
- </div>
40
- </template>
41
-
42
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ hasAside?: boolean
4
+ }>()
5
+
6
+ const layoutStore = useLayoutStore()
7
+ const { avoidTargets } = storeToRefs(layoutStore)
8
+
9
+ const panelRef = useTemplateRef('blog-panel')
10
+ const { transform } = useAvoidTransform(panelRef, avoidTargets)
11
+ </script>
12
+
13
+ <template>
14
+ <div
15
+ id="blog-panel"
16
+ ref="blog-panel"
17
+ :class="{ 'has-active': layoutStore.state !== 'none' }"
18
+ :style="{ transform }"
19
+ >
20
+ <button
21
+ v-if="hasAside"
22
+ class="toggle-aside widescreen-only"
23
+ :class="{ active: layoutStore.state === 'aside' }"
24
+ aria-label="切换侧边栏"
25
+ @click="layoutStore.toggle('aside')"
26
+ >
27
+ <Icon class="rtl-flip" name="tabler:align-right" />
28
+ </button>
29
+
30
+ <Icon v-show="false" name="tabler:layout-sidebar-filled" />
31
+ <button
32
+ class="toggle-sidebar mobile-only"
33
+ :class="{ active: layoutStore.state === 'sidebar' }"
34
+ aria-label="切换菜单"
35
+ @click="layoutStore.toggle('sidebar')"
36
+ >
37
+ <Icon class="rtl-flip" :name="layoutStore.state === 'sidebar' ? 'tabler:layout-sidebar-filled' : 'tabler:layout-sidebar'" />
38
+ </button>
39
+ </div>
40
+ </template>
41
+
42
+ <style lang="scss" scoped>
43
43
  #blog-panel {
44
44
  contain: paint;
45
45
  position: fixed;
@@ -76,4 +76,4 @@ button {
76
76
  color: var(--c-primary);
77
77
  }
78
78
  }
79
- </style>
79
+ </style>
@@ -1,55 +1,55 @@
1
- <script setup lang="ts">
2
- const clarity = useClarityConfig()
3
- const layoutStore = useLayoutStore()
4
- const searchStore = useSearchStore()
5
-
6
- const { text } = useTextSelection()
7
- const debouncedSelection = refDebounced(text)
8
- </script>
9
-
10
- <template>
11
- <BlogMask
12
- :show="layoutStore.state === 'sidebar'"
13
- class="mobile-only"
14
- @click="layoutStore.close()"
15
- />
16
-
17
- <!-- 不能用 Transition 实现弹出收起动画,因为半宽屏状态始终显示 -->
18
- <aside id="blog-sidebar" :class="{ show: layoutStore.state === 'sidebar' }">
19
- <BlogHeader class="sidebar-header" to="/" />
20
-
21
- <nav class="sidebar-nav scrollcheck-y">
22
- <div class="search-btn sidebar-nav-item gradient-card" @click="layoutStore.toggle('search')">
23
- <Icon name="tabler:search" />
24
- <span class="nav-text">{{ debouncedSelection || searchStore.word || '搜索' }}</span>
25
- <Key class="keycut" code="K" cmd prevent @press="layoutStore.toggle('search')" />
26
- </div>
27
-
28
- <template v-for="(group, groupIndex) in clarity.nav" :key="groupIndex">
29
- <h3 v-if="group.title">
30
- {{ group.title }}
31
- </h3>
32
-
33
- <menu>
34
- <li v-for="(item, itemIndex) in group.items" :key="itemIndex">
35
- <UtilLink :to="item.url" class="sidebar-nav-item">
36
- <Icon :name="item.icon" />
37
- <span class="nav-text">{{ item.text }}</span>
38
- <Icon v-if="isExtLink(item.url)" class="external-tip" name="tabler:arrow-up-right" />
39
- </UtilLink>
40
- </li>
41
- </menu>
42
- </template>
43
- </nav>
44
-
45
- <footer class="sidebar-footer">
46
- <BlogThemeToggle />
47
- <ZIconNavList :list="clarity.footer.iconNav" />
48
- </footer>
49
- </aside>
50
- </template>
51
-
52
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const clarity = useClarityConfig()
3
+ const layoutStore = useLayoutStore()
4
+ const searchStore = useSearchStore()
5
+
6
+ const { text } = useTextSelection()
7
+ const debouncedSelection = refDebounced(text)
8
+ </script>
9
+
10
+ <template>
11
+ <BlogMask
12
+ :show="layoutStore.state === 'sidebar'"
13
+ class="mobile-only"
14
+ @click="layoutStore.close()"
15
+ />
16
+
17
+ <!-- 不能用 Transition 实现弹出收起动画,因为半宽屏状态始终显示 -->
18
+ <aside id="blog-sidebar" :class="{ show: layoutStore.state === 'sidebar' }">
19
+ <BlogHeader class="sidebar-header" to="/" />
20
+
21
+ <nav class="sidebar-nav scrollcheck-y">
22
+ <div class="search-btn sidebar-nav-item gradient-card" @click="layoutStore.toggle('search')">
23
+ <Icon name="tabler:search" />
24
+ <span class="nav-text">{{ debouncedSelection || searchStore.word || '搜索' }}</span>
25
+ <Key class="keycut" code="K" cmd prevent @press="layoutStore.toggle('search')" />
26
+ </div>
27
+
28
+ <template v-for="(group, groupIndex) in clarity.nav" :key="groupIndex">
29
+ <h3 v-if="group.title">
30
+ {{ group.title }}
31
+ </h3>
32
+
33
+ <menu>
34
+ <li v-for="(item, itemIndex) in group.items" :key="itemIndex">
35
+ <UtilLink :to="item.url" class="sidebar-nav-item">
36
+ <Icon :name="item.icon" />
37
+ <span class="nav-text">{{ item.text }}</span>
38
+ <Icon v-if="isExtLink(item.url)" class="external-tip" name="tabler:arrow-up-right" />
39
+ </UtilLink>
40
+ </li>
41
+ </menu>
42
+ </template>
43
+ </nav>
44
+
45
+ <footer class="sidebar-footer">
46
+ <BlogThemeToggle />
47
+ <ZIconNavList :list="clarity.footer.iconNav" />
48
+ </footer>
49
+ </aside>
50
+ </template>
51
+
52
+ <style lang="scss" scoped>
53
53
  #blog-sidebar {
54
54
  display: flex;
55
55
  flex-direction: column;
@@ -157,4 +157,4 @@ const debouncedSelection = refDebounced(text)
157
157
  text-align: center;
158
158
  color: var(--c-text-2);
159
159
  }
160
- </style>
160
+ </style>