@sugarat/theme 0.5.14 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/node.d.ts CHANGED
@@ -622,6 +622,24 @@ declare namespace Theme {
622
622
  * @default '🏷 标签'
623
623
  */
624
624
  title?: string;
625
+ /**
626
+ * 首页标签默认展示数量,超出部分折叠
627
+ * @default 999
628
+ */
629
+ limit?: number;
630
+ /**
631
+ * 标签排序方式
632
+ * desc: 降序
633
+ * asc: 升序
634
+ * normal: 不排序 (默认)
635
+ * @default 'normal'
636
+ */
637
+ sort?: 'desc' | 'asc' | 'normal';
638
+ /**
639
+ * 是否显示文章数量
640
+ * @default false
641
+ */
642
+ showCount?: boolean;
625
643
  }
626
644
  }
627
645
 
package/node.js CHANGED
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(node_exports);
40
40
  // src/utils/node/mdPlugins.ts
41
41
  var import_module = require("module");
42
42
 
43
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.7.3_vitepress@2.0.0-alpha.15_@types+node@24.5.2_async-validator_f00dcfd3cc5e72074cf41a606f1d799c/node_modules/vitepress-plugin-tabs/dist/node/index.js
43
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.7.3_vitepress@2.0.0-alpha.16_@types+node@24.5.2_async-validator@4.2.5_wszfouv2xxx6bizfwwcmbwut3e/node_modules/vitepress-plugin-tabs/dist/node/index.js
44
44
  function container_plugin(md, name, options) {
45
45
  function validateDefault(params) {
46
46
  return params.trim().split(" ", 2)[0] === name;
@@ -422,7 +422,8 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
422
422
  meta.tag = [meta.tag || []].flat().concat([
423
423
  .../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
424
424
  ]);
425
- meta.description = meta.description || (0, import_theme_shared2.getTextSummary)(content, 100) || excerpt;
425
+ const contentWithoutHeadings = content.replace(/^#+\s+.*$/gm, "");
426
+ meta.description = meta.description || (0, import_theme_shared2.getTextSummary)(contentWithoutHeadings, 100) || excerpt;
426
427
  meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, route);
427
428
  if (meta.publish === false) {
428
429
  meta.hidden = true;
package/node.mjs CHANGED
@@ -9,7 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  // src/utils/node/mdPlugins.ts
10
10
  import { createRequire } from "module";
11
11
 
12
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.7.3_vitepress@2.0.0-alpha.15_@types+node@24.5.2_async-validator_f00dcfd3cc5e72074cf41a606f1d799c/node_modules/vitepress-plugin-tabs/dist/node/index.js
12
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.7.3_vitepress@2.0.0-alpha.16_@types+node@24.5.2_async-validator@4.2.5_wszfouv2xxx6bizfwwcmbwut3e/node_modules/vitepress-plugin-tabs/dist/node/index.js
13
13
  function container_plugin(md, name, options) {
14
14
  function validateDefault(params) {
15
15
  return params.trim().split(" ", 2)[0] === name;
@@ -389,7 +389,8 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
389
389
  meta.tag = [meta.tag || []].flat().concat([
390
390
  .../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
391
391
  ]);
392
- meta.description = meta.description || getTextSummary(content, 100) || excerpt;
392
+ const contentWithoutHeadings = content.replace(/^#+\s+.*$/gm, "");
393
+ meta.description = meta.description || getTextSummary(contentWithoutHeadings, 100) || excerpt;
393
394
  meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, route);
394
395
  if (meta.publish === false) {
395
396
  meta.hidden = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -51,11 +51,11 @@
51
51
  "vitepress-plugin-group-icons": "1.6.5",
52
52
  "vitepress-plugin-mermaid": "2.0.13",
53
53
  "vitepress-plugin-tabs": "0.7.3",
54
- "vitepress-plugin-announcement": "0.1.7",
55
54
  "@sugarat/theme-shared": "0.0.7",
56
- "vitepress-plugin-image-preview": "0.1.0",
57
- "vitepress-plugin-rss": "0.4.2",
58
- "vitepress-plugin-pagefind": "0.4.18"
55
+ "vitepress-plugin-image-preview": "0.1.1",
56
+ "vitepress-plugin-rss": "0.4.3",
57
+ "vitepress-plugin-pagefind": "0.4.19",
58
+ "vitepress-plugin-announcement": "0.1.7"
59
59
  },
60
60
  "devDependencies": {
61
61
  "artalk": "^2.8.5",
@@ -63,7 +63,7 @@
63
63
  "pagefind": "^1.3.0",
64
64
  "typescript": "^5.4.5",
65
65
  "vite": "^5.4.9",
66
- "vitepress": "2.0.0-alpha.15",
66
+ "vitepress": "2.0.0-alpha.16",
67
67
  "vue": "^3.5.24",
68
68
  "vitepress-plugin-51la": "0.1.1"
69
69
  },
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { computed, watch } from 'vue'
2
+ import { computed, ref, watch } from 'vue'
3
3
  import { useBrowserLocation, useUrlSearchParams } from '@vueuse/core'
4
4
  import { useRoute, useRouter } from 'vitepress'
5
5
  import {
@@ -19,9 +19,47 @@ const title = computed(() => (typeof homeTagsConfig.value === 'boolean' || !home
19
19
  ? `${tagsSvgStr}标签`
20
20
  : homeTagsConfig.value?.title
21
21
  )
22
- const tags = computed(() => {
23
- return [...new Set(docs.value.map(v => v.meta.tag || []).flat(3))]
22
+
23
+ // 统计每个标签的文章数量,并按数量降序排序
24
+ const tagsWithCount = computed(() => {
25
+ const tagCountMap = new Map<string, number>()
26
+ docs.value.forEach((v) => {
27
+ const articleTags = v.meta.tag || []
28
+ const flatTags = Array.isArray(articleTags) ? articleTags.flat(3) : [articleTags]
29
+ flatTags.forEach((tag: string) => {
30
+ tagCountMap.set(tag, (tagCountMap.get(tag) || 0) + 1)
31
+ })
32
+ })
33
+ // 按文章数量降序排序
34
+ const data = [...tagCountMap.entries()]
35
+ const sort = (typeof homeTagsConfig.value === 'object' && homeTagsConfig.value?.sort) ? homeTagsConfig.value.sort : 'normal'
36
+ if (sort !== 'normal') {
37
+ data.sort((a, b) => {
38
+ return sort === 'asc' ? a[1] - b[1] : b[1] - a[1]
39
+ })
40
+ }
41
+ return data.map(([tag, count]) => ({ tag, count }))
42
+ })
43
+
44
+ // 折叠/展开功能,从配置中读取 limit,默认 10
45
+ const limit = computed(() => {
46
+ if (typeof homeTagsConfig.value === 'object' && homeTagsConfig.value?.limit) {
47
+ return homeTagsConfig.value.limit
48
+ }
49
+ return 999
50
+ })
51
+ const isExpanded = ref(false)
52
+ const displayTags = computed(() => {
53
+ if (isExpanded.value || tagsWithCount.value.length <= limit.value) {
54
+ return tagsWithCount.value
55
+ }
56
+ return tagsWithCount.value.slice(0, limit.value)
24
57
  })
58
+ const hasMore = computed(() => tagsWithCount.value.length > limit.value)
59
+
60
+ function toggleExpand() {
61
+ isExpanded.value = !isExpanded.value
62
+ }
25
63
 
26
64
  const activeTag = useActiveTag()
27
65
 
@@ -78,7 +116,7 @@ watch(
78
116
  </script>
79
117
 
80
118
  <template>
81
- <div v-if="showTags && tags.length" class="card tags" data-pagefind-ignore="all">
119
+ <div v-if="showTags && tagsWithCount.length" class="card tags" data-pagefind-ignore="all">
82
120
  <!-- 头部 -->
83
121
  <div class="card-header">
84
122
  <span class="title svg-icon" v-html="title" />
@@ -91,15 +129,33 @@ watch(
91
129
  </div>
92
130
  <!-- 标签列表 -->
93
131
  <ul class="tag-list">
94
- <li v-for="(tag, idx) in tags" :key="tag">
132
+ <li v-for="(item, idx) in displayTags" :key="item.tag">
95
133
  <Tag
96
134
  :type="tagType[idx % tagType.length] || 'primary'"
97
- @click="handleTagClick(tag, tagType[idx % tagType.length])"
135
+ @click="handleTagClick(item.tag, tagType[idx % tagType.length])"
98
136
  >
99
- {{ tag }}
137
+ {{ item.tag }}
138
+ <span v-if="typeof homeTagsConfig === 'object' && homeTagsConfig?.showCount" class="tag-count">{{ item.count }}</span>
100
139
  </Tag>
101
140
  </li>
102
141
  </ul>
142
+ <!-- 展开/收起按钮 -->
143
+ <div v-if="hasMore" class="expand-btn" @click="toggleExpand">
144
+ <span>{{ isExpanded ? '收起' : `展开全部 ${tagsWithCount.length} 个标签` }}</span>
145
+ <svg
146
+ class="expand-icon"
147
+ :class="{ 'is-expanded': isExpanded }"
148
+ viewBox="0 0 1024 1024"
149
+ xmlns="http://www.w3.org/2000/svg"
150
+ width="1em"
151
+ height="1em"
152
+ >
153
+ <path
154
+ fill="currentColor"
155
+ d="M488.832 344.32l-339.84 356.672a32 32 0 0 0 0 44.16l.384.384a29.44 29.44 0 0 0 42.688 0l320-335.872 319.872 335.872a29.44 29.44 0 0 0 42.688 0l.384-.384a32 32 0 0 0 0-44.16L535.168 344.32a32 32 0 0 0-46.336 0z"
156
+ />
157
+ </svg>
158
+ </div>
103
159
  </div>
104
160
  </template>
105
161
 
@@ -147,4 +203,43 @@ watch(
147
203
  margin-bottom: 10px;
148
204
  cursor: pointer;
149
205
  }
206
+
207
+ .tag-count {
208
+ display: inline-flex;
209
+ align-items: center;
210
+ justify-content: center;
211
+ margin-left: 4px;
212
+ min-width: 16px;
213
+ height: 16px;
214
+ padding: 0 4px;
215
+ font-size: 10px;
216
+ line-height: 1;
217
+ border-radius: 8px;
218
+ background-color: rgba(255, 255, 255, 0.3);
219
+ }
220
+
221
+ .expand-btn {
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ gap: 4px;
226
+ margin-top: 6px;
227
+ padding: 4px 0;
228
+ font-size: 12px;
229
+ color: var(--vp-c-text-2);
230
+ cursor: pointer;
231
+ transition: color 0.3s;
232
+ user-select: none;
233
+ }
234
+ .expand-btn:hover {
235
+ color: var(--vp-c-brand);
236
+ }
237
+
238
+ .expand-icon {
239
+ transition: transform 0.3s;
240
+ transform: rotate(180deg);
241
+ }
242
+ .expand-icon.is-expanded {
243
+ transform: rotate(0deg);
244
+ }
150
245
  </style>
@@ -659,5 +659,23 @@ export namespace Theme {
659
659
  * @default '🏷 标签'
660
660
  */
661
661
  title?: string
662
+ /**
663
+ * 首页标签默认展示数量,超出部分折叠
664
+ * @default 999
665
+ */
666
+ limit?: number
667
+ /**
668
+ * 标签排序方式
669
+ * desc: 降序
670
+ * asc: 升序
671
+ * normal: 不排序 (默认)
672
+ * @default 'normal'
673
+ */
674
+ sort?: 'desc' | 'asc' | 'normal'
675
+ /**
676
+ * 是否显示文章数量
677
+ * @default false
678
+ */
679
+ showCount?: boolean
662
680
  }
663
681
  }
@@ -66,8 +66,10 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
66
66
 
67
67
  // 获取摘要信息
68
68
  // TODO:摘要生成优化,支持AI?
69
+ // 先去除 Markdown 中的标题行(# 开头),避免 description 中重复出现标题文字
70
+ const contentWithoutHeadings = content.replace(/^#+\s+.*$/gm, '')
69
71
  meta.description
70
- = meta.description || getTextSummary(content, 100) || excerpt
72
+ = meta.description || getTextSummary(contentWithoutHeadings, 100) || excerpt
71
73
 
72
74
  // 获取封面图
73
75
  // TODO: 耦合信息优化