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,132 +1,132 @@
1
- <script setup lang="ts">
2
- import type { ArticleProps } from '../types/article'
3
- import { groupBy } from 'es-toolkit/array'
4
- import { sumBy } from 'es-toolkit/math'
5
- import { mapValues } from 'es-toolkit/object'
6
-
7
- const clarity = useClarityConfig()
8
- useSeoMeta({
9
- title: '归档',
10
- description: `${clarity.site.title}的所有文章归档。`,
11
- })
12
- const birthYear = computed(() => clarity.component.stats.birthYear)
13
- const showTuning = ref(false)
14
- const spacing = ref(0)
15
- const column = ref(1)
16
-
17
- const tuningRef = useTemplateRef('tuning-panel')
18
- useAvoidTarget(tuningRef, showTuning)
19
-
20
- const { data: listRaw } = await useAsyncData('posts:index', () => getArticleIndexOptions(), { default: () => [] })
21
- const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw)
22
- const { category, categories, listCategorized } = useCategory(listSorted)
23
-
24
- const listGrouped = computed(() => {
25
- const groupList = Object.entries(groupBy(listCategorized.value, getArticleYear))
26
- return isAscending.value ? groupList : groupList.reverse()
27
- })
28
-
29
- // 不能使用 /api/stats,因为可能切换分组方式
30
- const yearlyWordCount = computed(() =>
31
- mapValues(Object.fromEntries(listGrouped.value), (articles) => {
32
- const total = sumBy(articles, a => a.readingTime?.words ?? 0)
33
- return formatNumber(total)
34
- }),
35
- )
36
-
37
- function getArticleYear(article: ArticleProps) {
38
- try {
1
+ <script setup lang="ts">
2
+ import type { ArticleProps } from '../types/article'
3
+ import { groupBy } from 'es-toolkit/array'
4
+ import { sumBy } from 'es-toolkit/math'
5
+ import { mapValues } from 'es-toolkit/object'
6
+
7
+ const clarity = useClarityConfig()
8
+ useSeoMeta({
9
+ title: '归档',
10
+ description: `${clarity.site.title}的所有文章归档。`,
11
+ })
12
+ const birthYear = computed(() => clarity.component.stats.birthYear)
13
+ const showTuning = ref(false)
14
+ const spacing = ref(0)
15
+ const column = ref(1)
16
+
17
+ const tuningRef = useTemplateRef('tuning-panel')
18
+ useAvoidTarget(tuningRef, showTuning)
19
+
20
+ const { data: listRaw } = await useAsyncData('posts:index', () => getArticleIndexOptions(), { default: () => [] })
21
+ const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw)
22
+ const { category, categories, listCategorized } = useCategory(listSorted)
23
+
24
+ const listGrouped = computed(() => {
25
+ const groupList = Object.entries(groupBy(listCategorized.value, getArticleYear))
26
+ return isAscending.value ? groupList : groupList.reverse()
27
+ })
28
+
29
+ // 不能使用 /api/stats,因为可能切换分组方式
30
+ const yearlyWordCount = computed(() =>
31
+ mapValues(Object.fromEntries(listGrouped.value), (articles) => {
32
+ const total = sumBy(articles, a => a.readingTime?.words ?? 0)
33
+ return formatNumber(total)
34
+ }),
35
+ )
36
+
37
+ function getArticleYear(article: ArticleProps) {
38
+ try {
39
39
  return toZonedTemporal((article as any)[sortOrder.value] as string, clarity.site.timezone).year.toString()
40
- }
41
- catch {
42
- return ''
43
- }
44
- }
45
- </script>
46
-
47
- <template>
48
- <template #aside>
49
- <WidgetBlogStats />
50
- <WidgetBlogLog />
51
- </template>
52
-
53
- <div class="archive proper-height">
54
- <PostOrderToggle
55
- v-model:is-ascending="isAscending"
56
- v-model:sort-order="sortOrder"
57
- v-model:category="category"
58
- :categories
59
- >
60
- <ZSecret>
61
- <ZToggle
62
- v-model="showTuning"
63
- label="密度调节"
64
- />
65
- </ZSecret>
66
- </PostOrderToggle>
67
-
68
- <section
69
- v-for="[year, yearGroup] in listGrouped"
70
- :key="year"
71
- class="archive-group"
72
- :class="{ 'hide-info': column > 1 }"
73
- :style="{
74
- '--archive-item-gap': `${spacing}em`,
75
- '--archive-item-column': column,
76
- }"
77
- >
78
- <div class="archive-title">
79
- <h2 class="archive-year">
80
- {{ year }}
81
- </h2>
82
-
83
- <div v-if="birthYear" class="archive-age">
84
- <span>{{ Number(year) - birthYear }}</span>
85
- <span class="age-label">岁</span>
86
- </div>
87
-
88
- <div class="archive-info">
89
- <span>{{ yearlyWordCount[year] }}字</span>
90
- <span>{{ yearGroup?.length }}篇</span>
91
- </div>
92
- </div>
93
-
94
- <TransitionGroup tag="menu" class="archive-list" name="float-in">
95
- <PostArchive
96
- v-for="article, index in yearGroup"
97
- :key="article.path"
98
- v-bind="article"
99
- :to="article.path"
100
- :show-category="column < 3"
101
- :use-updated="sortOrder === 'updated'"
102
- :style="getFixedDelay(index * 0.03)"
103
- />
104
- </TransitionGroup>
105
- </section>
106
-
107
- <div v-if="showTuning" ref="tuning-panel" class="archive-tuning card">
108
- <ZSlider
109
- v-model="spacing"
110
- label="间距"
111
- :spring-min="-0.4"
112
- :spring-max="0.1"
113
- :list="['-0.3', '0']"
114
- min="-1"
115
- max=".2"
116
- step=".1"
117
- />
118
-
119
- <ZSlider
120
- v-model="column"
121
- label="列数"
122
- min="1"
123
- max="8"
124
- />
125
- </div>
126
- </div>
127
- </template>
128
-
129
- <style lang="scss" scoped>
40
+ }
41
+ catch {
42
+ return ''
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <template>
48
+ <template #aside>
49
+ <WidgetBlogStats />
50
+ <WidgetBlogLog />
51
+ </template>
52
+
53
+ <div class="archive proper-height">
54
+ <PostOrderToggle
55
+ v-model:is-ascending="isAscending"
56
+ v-model:sort-order="sortOrder"
57
+ v-model:category="category"
58
+ :categories
59
+ >
60
+ <ZSecret>
61
+ <ZToggle
62
+ v-model="showTuning"
63
+ label="密度调节"
64
+ />
65
+ </ZSecret>
66
+ </PostOrderToggle>
67
+
68
+ <section
69
+ v-for="[year, yearGroup] in listGrouped"
70
+ :key="year"
71
+ class="archive-group"
72
+ :class="{ 'hide-info': column > 1 }"
73
+ :style="{
74
+ '--archive-item-gap': `${spacing}em`,
75
+ '--archive-item-column': column,
76
+ }"
77
+ >
78
+ <div class="archive-title">
79
+ <h2 class="archive-year">
80
+ {{ year }}
81
+ </h2>
82
+
83
+ <div v-if="birthYear" class="archive-age">
84
+ <span>{{ Number(year) - birthYear }}</span>
85
+ <span class="age-label">岁</span>
86
+ </div>
87
+
88
+ <div class="archive-info">
89
+ <span>{{ yearlyWordCount[year] }}字</span>
90
+ <span>{{ yearGroup?.length }}篇</span>
91
+ </div>
92
+ </div>
93
+
94
+ <TransitionGroup tag="menu" class="archive-list" name="float-in">
95
+ <PostArchive
96
+ v-for="article, index in yearGroup"
97
+ :key="article.path"
98
+ v-bind="article"
99
+ :to="article.path"
100
+ :show-category="column < 3"
101
+ :use-updated="sortOrder === 'updated'"
102
+ :style="getFixedDelay(index * 0.03)"
103
+ />
104
+ </TransitionGroup>
105
+ </section>
106
+
107
+ <div v-if="showTuning" ref="tuning-panel" class="archive-tuning card">
108
+ <ZSlider
109
+ v-model="spacing"
110
+ label="间距"
111
+ :spring-min="-0.4"
112
+ :spring-max="0.1"
113
+ :list="['-0.3', '0']"
114
+ min="-1"
115
+ max=".2"
116
+ step=".1"
117
+ />
118
+
119
+ <ZSlider
120
+ v-model="column"
121
+ label="列数"
122
+ min="1"
123
+ max="8"
124
+ />
125
+ </div>
126
+ </div>
127
+ </template>
128
+
129
+ <style lang="scss" scoped>
130
130
  .archive {
131
131
  padding: 1rem; // 防止内部 outline 被 mask
132
132
  mask-image: linear-gradient(#FFF 50%, #FFF7);
@@ -204,4 +204,4 @@ function getArticleYear(article: ArticleProps) {
204
204
  column-gap: 0.5em;
205
205
  }
206
206
  }
207
- </style>
207
+ </style>
@@ -1,78 +1,78 @@
1
- <script setup lang="ts">
2
- import { orderBy } from 'es-toolkit/array'
3
-
4
- const clarity = useClarityConfig()
5
- useSeoMeta({
6
- description: clarity.site.description,
7
- ogImage: clarity.site.author.avatar,
8
- })
9
-
10
- const { data: listRaw } = await useAsyncData('posts:index', () => getArticleIndexOptions(), { default: () => [] })
11
- const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw, { bindDirectionQuery: 'asc', bindOrderQuery: 'sort' })
12
- const { category, categories, listCategorized } = useCategory(listSorted, { bindQuery: 'category' })
13
- const { page, totalPages, listPaged } = usePagination(listCategorized, { bindQuery: 'page' })
14
-
15
- watch(category, () => {
16
- page.value = 1
17
- })
18
-
19
- useSeoMeta({ title: () => (page.value > 1 ? `第${page.value}页` : '') })
20
-
21
- const listRecommended = computed(() => orderBy(
22
- listRaw.value.filter(item => item.recommend !== null),
23
- ['recommend', 'date'],
24
- ['desc'],
25
- ))
26
-
27
- const { data: previewCount } = useAsyncData(
28
- 'previews:count',
29
- () => queryCollection('content').where('stem', 'LIKE', 'previews/%').count(),
30
- )
31
- </script>
32
-
33
- <template>
34
- <template #aside>
35
- <WidgetBlogStats />
36
- <WidgetBlogTech />
37
- </template>
38
-
39
- <BlogHeader class="mobile-only" to="/" tag="h1" />
40
-
41
- <UtilHydrateSafe>
42
- <PostSlide v-if="listRecommended.length && page === 1 && !category" :list="listRecommended" />
43
-
44
- <div class="post-list">
45
- <PostOrderToggle
46
- v-model:is-ascending="isAscending"
47
- v-model:sort-order="sortOrder"
48
- v-model:category="category"
49
- :categories
50
- >
51
- <ZSecret>
52
- <UtilLink v-if="previewCount" to="/preview" class="preview-entrance">
53
- <Icon name="tabler:shield-lock" />
54
- 查看预览文章
55
- </UtilLink>
56
- </ZSecret>
57
- </PostOrderToggle>
58
-
59
- <TransitionGroup tag="menu" class="proper-height" name="float-in">
60
- <PostArticle
61
- v-for="article, index in listPaged"
62
- :key="article.path"
63
- v-bind="article"
64
- :to="article.path"
65
- :use-updated="sortOrder === 'updated'"
66
- :style="getFixedDelay(index * 0.05)"
67
- />
68
- </TransitionGroup>
69
-
70
- <ZPagination v-model="page" sticky avoid :total-pages="totalPages" />
71
- </div>
72
- </UtilHydrateSafe>
73
- </template>
74
-
75
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import { orderBy } from 'es-toolkit/array'
3
+
4
+ const clarity = useClarityConfig()
5
+ useSeoMeta({
6
+ description: clarity.site.description,
7
+ ogImage: clarity.site.author.avatar,
8
+ })
9
+
10
+ const { data: listRaw } = await useAsyncData('posts:index', () => getArticleIndexOptions(), { default: () => [] })
11
+ const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw, { bindDirectionQuery: 'asc', bindOrderQuery: 'sort' })
12
+ const { category, categories, listCategorized } = useCategory(listSorted, { bindQuery: 'category' })
13
+ const { page, totalPages, listPaged } = usePagination(listCategorized, { bindQuery: 'page' })
14
+
15
+ watch(category, () => {
16
+ page.value = 1
17
+ })
18
+
19
+ useSeoMeta({ title: () => (page.value > 1 ? `第${page.value}页` : '') })
20
+
21
+ const listRecommended = computed(() => orderBy(
22
+ listRaw.value.filter(item => item.recommend !== null),
23
+ ['recommend', 'date'],
24
+ ['desc'],
25
+ ))
26
+
27
+ const { data: previewCount } = useAsyncData(
28
+ 'previews:count',
29
+ () => queryCollection('content').where('stem', 'LIKE', 'previews/%').count(),
30
+ )
31
+ </script>
32
+
33
+ <template>
34
+ <template #aside>
35
+ <WidgetBlogStats />
36
+ <WidgetBlogTech />
37
+ </template>
38
+
39
+ <BlogHeader class="mobile-only" to="/" tag="h1" />
40
+
41
+ <UtilHydrateSafe>
42
+ <PostSlide v-if="listRecommended.length && page === 1 && !category" :list="listRecommended" />
43
+
44
+ <div class="post-list">
45
+ <PostOrderToggle
46
+ v-model:is-ascending="isAscending"
47
+ v-model:sort-order="sortOrder"
48
+ v-model:category="category"
49
+ :categories
50
+ >
51
+ <ZSecret>
52
+ <UtilLink v-if="previewCount" to="/preview" class="preview-entrance">
53
+ <Icon name="tabler:shield-lock" />
54
+ 查看预览文章
55
+ </UtilLink>
56
+ </ZSecret>
57
+ </PostOrderToggle>
58
+
59
+ <TransitionGroup tag="menu" class="proper-height" name="float-in">
60
+ <PostArticle
61
+ v-for="article, index in listPaged"
62
+ :key="article.path"
63
+ v-bind="article"
64
+ :to="article.path"
65
+ :use-updated="sortOrder === 'updated'"
66
+ :style="getFixedDelay(index * 0.05)"
67
+ />
68
+ </TransitionGroup>
69
+
70
+ <ZPagination v-model="page" sticky avoid :total-pages="totalPages" />
71
+ </div>
72
+ </UtilHydrateSafe>
73
+ </template>
74
+
75
+ <style lang="scss" scoped>
76
76
  .post-list {
77
77
  margin: 1rem;
78
78
  }
@@ -80,4 +80,4 @@ const { data: previewCount } = useAsyncData(
80
80
  .float-in-leave-to {
81
81
  position: absolute;
82
82
  }
83
- </style>
83
+ </style>
@@ -1,65 +1,65 @@
1
- <script setup lang="ts">
2
- import feeds from '#clarity/feeds'
3
-
4
- const clarity = useClarityConfig()
5
- const myFeed = useClaritySiteFeedEntry()
6
-
7
- const { data: postLink } = await useAsyncData(
8
- 'content:/link',
9
- () => queryCollection('content').path('/link').first(),
10
- )
11
-
12
- useSeoMeta({
13
- title: '友链',
14
- ogType: 'profile',
15
- description: `${clarity.site.title}的友链页面,收集了添加他为友链的网站和他订阅的网站列表。`,
16
- })
17
-
18
- const copyFields = {
19
- 博主: myFeed.author,
20
- 标题: myFeed.title,
21
- 介绍: myFeed.desc,
22
- 网址: myFeed.link,
23
- 头像: myFeed.avatar,
24
- }
25
- </script>
26
-
27
- <template>
28
- <div class="mobile-only">
29
- <BlogHeader to="/" suffix="友链" tag="h1" />
30
- </div>
31
-
32
- <FeedGroup
33
- v-for="group in feeds"
34
- :key="group.name"
35
- v-bind="group"
36
- :shuffle="clarity.link.randomInGroup"
37
- />
38
-
39
- <Tab :tabs="['我的博客信息', '申请友链']" center>
40
- <template #tab1>
41
- <div class="link-tab">
42
- <FeedCard v-bind="myFeed" />
43
- <Copy v-for="(code, prompt) in copyFields" :key="prompt" :prompt :code />
44
- </div>
45
- </template>
46
- <template #tab2>
47
- <ContentRenderer
48
- v-if="postLink"
49
- :value="postLink"
50
- class="article"
51
- />
52
- <p v-else class="text-center">
53
- 可于 link.md 配置友链补充说明。
54
- </p>
55
- </template>
56
- </Tab>
57
-
58
- <PostComment />
59
- </template>
60
-
61
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import feeds from '#clarity/feeds'
3
+
4
+ const clarity = useClarityConfig()
5
+ const myFeed = useClaritySiteFeedEntry()
6
+
7
+ const { data: postLink } = await useAsyncData(
8
+ 'content:/link',
9
+ () => queryCollection('content').path('/link').first(),
10
+ )
11
+
12
+ useSeoMeta({
13
+ title: '友链',
14
+ ogType: 'profile',
15
+ description: `${clarity.site.title}的友链页面,收集了添加他为友链的网站和他订阅的网站列表。`,
16
+ })
17
+
18
+ const copyFields = {
19
+ 博主: myFeed.author,
20
+ 标题: myFeed.title,
21
+ 介绍: myFeed.desc,
22
+ 网址: myFeed.link,
23
+ 头像: myFeed.avatar,
24
+ }
25
+ </script>
26
+
27
+ <template>
28
+ <div class="mobile-only">
29
+ <BlogHeader to="/" suffix="友链" tag="h1" />
30
+ </div>
31
+
32
+ <FeedGroup
33
+ v-for="group in feeds"
34
+ :key="group.name"
35
+ v-bind="group"
36
+ :shuffle="clarity.link.randomInGroup"
37
+ />
38
+
39
+ <Tab :tabs="['我的博客信息', '申请友链']" center>
40
+ <template #tab1>
41
+ <div class="link-tab">
42
+ <FeedCard v-bind="myFeed" />
43
+ <Copy v-for="(code, prompt) in copyFields" :key="prompt" :prompt :code />
44
+ </div>
45
+ </template>
46
+ <template #tab2>
47
+ <ContentRenderer
48
+ v-if="postLink"
49
+ :value="postLink"
50
+ class="article"
51
+ />
52
+ <p v-else class="text-center">
53
+ 可于 link.md 配置友链补充说明。
54
+ </p>
55
+ </template>
56
+ </Tab>
57
+
58
+ <PostComment />
59
+ </template>
60
+
61
+ <style lang="scss" scoped>
62
62
  .link-tab {
63
63
  margin: 1rem;
64
64
  }
65
- </style>
65
+ </style>
@@ -1,48 +1,48 @@
1
- <script setup lang="ts">
2
- const clarity = useClarityConfig()
3
- useSeoMeta({
4
- title: '预览',
5
- description: `${clarity.site.title}的文章预览。`,
6
- })
7
- const { data: listRaw } = await useAsyncData('previews:index', () => getArticleIndexOptions('previews/%'), { default: () => [] })
8
- const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw)
9
- const { category, categories, listCategorized } = useCategory(listSorted)
10
- </script>
11
-
12
- <template>
13
- <template #aside>
14
- <WidgetBlogLog />
15
- </template>
16
-
17
- <div class="preview">
18
- <div class="preview-header">
19
- <h1>
20
- <UtilLink class="mobile-only" to="/" title="返回首页">
21
- <Icon name="tabler:chevron-left" />
22
- </UtilLink>预览
23
- </h1>
24
- <PostOrderToggle
25
- v-model:is-ascending="isAscending"
26
- v-model:sort-order="sortOrder"
27
- v-model:category="category"
28
- :categories
29
- />
30
- </div>
31
- <p>勇敢的人探索世界。这里是一些还未发布的文章。</p>
32
-
33
- <menu class="proper-height">
34
- <PostArticle
35
- v-for="article in listCategorized"
36
- :key="article.path"
37
- v-bind="article"
38
- :to="article.path"
39
- :use-updated="sortOrder === 'updated'"
40
- />
41
- </menu>
42
- </div>
43
- </template>
44
-
45
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const clarity = useClarityConfig()
3
+ useSeoMeta({
4
+ title: '预览',
5
+ description: `${clarity.site.title}的文章预览。`,
6
+ })
7
+ const { data: listRaw } = await useAsyncData('previews:index', () => getArticleIndexOptions('previews/%'), { default: () => [] })
8
+ const { listSorted, isAscending, sortOrder } = useArticleSort(listRaw)
9
+ const { category, categories, listCategorized } = useCategory(listSorted)
10
+ </script>
11
+
12
+ <template>
13
+ <template #aside>
14
+ <WidgetBlogLog />
15
+ </template>
16
+
17
+ <div class="preview">
18
+ <div class="preview-header">
19
+ <h1>
20
+ <UtilLink class="mobile-only" to="/" title="返回首页">
21
+ <Icon name="tabler:chevron-left" />
22
+ </UtilLink>预览
23
+ </h1>
24
+ <PostOrderToggle
25
+ v-model:is-ascending="isAscending"
26
+ v-model:sort-order="sortOrder"
27
+ v-model:category="category"
28
+ :categories
29
+ />
30
+ </div>
31
+ <p>勇敢的人探索世界。这里是一些还未发布的文章。</p>
32
+
33
+ <menu class="proper-height">
34
+ <PostArticle
35
+ v-for="article in listCategorized"
36
+ :key="article.path"
37
+ v-bind="article"
38
+ :to="article.path"
39
+ :use-updated="sortOrder === 'updated'"
40
+ />
41
+ </menu>
42
+ </div>
43
+ </template>
44
+
45
+ <style lang="scss" scoped>
46
46
  .preview {
47
47
  margin: 1rem;
48
48
  }
@@ -56,4 +56,4 @@ const { category, categories, listCategorized } = useCategory(listSorted)
56
56
  mask-image: linear-gradient(#FFF, transparent);
57
57
  }
58
58
  }
59
- </style>
59
+ </style>