Skip to content

Commit 0b94815

Browse files
committed
feat: sidebar supports the use of icons
1 parent b63eb6b commit 0b94815

22 files changed

+207
-126
lines changed

.github/workflows/deploy.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ jobs:
2727
- name: 项目打包构建
2828
env:
2929
NODE_OPTIONS: --max_old_space_size=4096
30-
run: |
31-
pnpm build && pnpm docs:build
30+
run: pnpm docs:build
3231

3332
- name: 部署产物到服务器
3433
uses: appleboy/scp-action@master

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"cli:build": "pnpm -F create-mild-theme run build",
1818
"demo:build": "pnpm -F demo run build",
1919
"tem:build": "pnpm -F template run build",
20-
"docs:build": "pnpm -F docs run build",
20+
"docs:build": "pnpm build && pnpm -F docs run build",
2121
"release": "pnpm -F vitepress-theme-mild run release && pnpm -F create-mild-theme run release",
2222
"release:cli": "pnpm -F create-mild-theme run release",
2323
"demo:preview": "pnpm -F demo run preview",

packages/docs/guide/intro/deploy.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
---
2+
sidebar:
3+
sort: 3
4+
icon: meteor-icons:laptop
25
---
36

47
# 部署

packages/docs/guide/intro/index.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
---
2-
sidebar:
2+
group:
3+
icon: meteor-icons:sparkles
4+
text: 简介
35
sort: 1
4-
title: 简介
5-
text: 概述
6+
sidebar:
7+
icon: meteor-icons:grid
68
---
79

810
# 概述
@@ -31,6 +33,7 @@ sidebar:
3133
- [x] 图片预览
3234
- [x] BiliBili视频播放器
3335
- [x] Github Task Checkbox, 与Github用法一致的任务列表复选框
36+
- [x] Tabs 面板
3437
- [ ] Youtube视频播放器
3538
- [ ] 内置视频播放器
3639

@@ -39,6 +42,7 @@ sidebar:
3942
- [x] 文章列表
4043
- [x] 文章基础信息: 发表时间、字数、阅读时间
4144
- [x] 自动侧边栏
45+
- [x] 侧边栏支持展示图标
4246
- [x] 页面加载进度条
4347
- [x] RSS订阅
4448
- [x] 文章评论

packages/docs/guide/intro/quick-start.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar:
33
sort: 1
4+
icon: meteor-icons:coffee
45
---
56

67
# 快速开始

packages/docs/guide/layout/blog.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar:
33
sort: 1
4+
icon: material-symbols:account-circle-outline
45
---
56

67
# 博客页

packages/docs/guide/layout/index.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
---
2-
sidebar:
2+
group:
33
sort: 2
4-
title: 布局
4+
text: 布局
5+
icon: meteor-icons:layout
6+
sidebar:
7+
icon: meteor-icons:grid
58
---
69

710
# 概述

packages/docs/guide/layout/tag.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
---
2+
sidebar:
3+
icon: meteor-icons:tag
4+
---
5+
16
# 标签页
27

38
待补充

packages/docs/guide/support/bilibili.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
---
2+
sidebar:
3+
icon: simple-icons:bilibili
4+
---
5+
16
# BiliBili 视频播放
27

38
`Mild Theme` 提供了BiliBili视频播放的能力, 你可以在 markdown 文档中使用全局组件 `<Bili/>`, 这将会在文档中加入一个B站视频播放器.

packages/docs/guide/support/comment.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
---
2+
sidebar:
3+
icon: meteor-icons:comment
4+
---
5+
16
# 评论
27

38
`Mild Theme` 使用 [giscus](https://giscus.app/zh-CN) 评论系统, 用于让阅读者能够对文章进行评论.

packages/docs/guide/support/group-icons.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar:
3+
icon: meteor-icons:hexagon
34
order: 9
45
---
56

packages/docs/guide/support/index.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
2-
sidebar:
3-
title: 主题能力
2+
group:
3+
icon: meteor-icons:microchip
4+
text: 主题能力
45
sort: 3
5-
publish: false
6+
collapsed: true
67
---
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
---
2+
sidebar:
3+
icon: hugeicons:cursor-loading-01
4+
---
5+
16
# 加载进度条
27

38
待补充

packages/docs/guide/support/rss.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
---
2+
sidebar:
3+
icon: material-symbols:rss-feed
4+
---
5+
16
# RSS 订阅
27

38
待补充

packages/docs/guide/support/sidebar.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar:
33
sort: 1
4+
icon: pajamas:sidebar
45
---
56

67
# 自动侧边栏

packages/theme/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { setup } from '@css-render/vue3-ssr';
44
import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client';
55
import { MotionPlugin } from '@vueuse/motion';
66
import { NImageGroup } from 'naive-ui';
7+
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client';
78
import VPTheme from 'vitepress/theme';
89
import Bili from './src/client/components/Bili.vue';
910
import BlogPage from './src/client/components/BlogPage.vue';
@@ -20,6 +21,8 @@ const MildTheme: Theme = {
2021
extends: VPTheme,
2122
Layout,
2223
enhanceApp({ app, router, siteData }) {
24+
enhanceAppWithTabs(app);
25+
2326
const themeConfig: ThemeConfig = siteData.value.themeConfig;
2427
const originalConsoleError = console.error;
2528

packages/theme/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"typed.js": "^2.1.0",
5757
"vitepress-plugin-group-icons": "^1.3.2",
5858
"vitepress-plugin-rss": "^0.3.0",
59+
"vitepress-plugin-tabs": "^0.5.0",
5960
"vue": "^3.4.21",
6061
"vue-easy-lightbox": "^1.19.0"
6162
},

packages/theme/src/client/components/SidebarItem.vue

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { DefaultTheme } from 'vitepress';
3+
import { Icon } from '@iconify/vue';
34
import { computed } from 'vue';
45
import { useSidebarControl } from '../hooks/useSidebarControl';
56
import VPLink from './Link.vue';
@@ -69,7 +70,7 @@ function onCaretClick() {
6970
"
7071
>
7172
<div class="indicator" />
72-
73+
<Icon v-if="item.icon && item.items" :icon="item.icon" style="margin-right: 5px;" />
7374
<VPLink
7475
v-if="item.link"
7576
:tag="linkTag"
@@ -78,6 +79,11 @@ function onCaretClick() {
7879
:rel="item.rel"
7980
:target="item.target"
8081
>
82+
<Icon
83+
v-if="item.icon && item.link"
84+
:icon="item.icon"
85+
class="text text-icon"
86+
/>
8187
<component :is="textTag" class="text" v-html="item.text" />
8288
</VPLink>
8389
<component
@@ -126,6 +132,8 @@ function onCaretClick() {
126132
position: relative;
127133
display: flex;
128134
width: 100%;
135+
display: flex;
136+
align-items: center;
129137
}
130138
131139
.VPSidebarItem.collapsible > .item {
@@ -162,7 +170,11 @@ function onCaretClick() {
162170
font-size: 14px;
163171
transition: color 0.25s;
164172
}
165-
173+
.text-icon {
174+
flex-grow: 0;
175+
padding: 0;
176+
margin-right: 4px;
177+
}
166178
.VPSidebarItem.level-0 .text {
167179
font-weight: 700;
168180
color: var(--vp-c-text-1);

packages/theme/src/client/datas/base.ts

+2-113
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import path from 'node:path';
2-
import { isObject } from '@vueuse/core';
3-
import directoryTree from 'directory-tree';
42
import fs from 'fs-extra';
53
import matter from 'gray-matter';
64
import { glob, type GlobOptions } from 'tinyglobby';
75
import { createMarkdownRenderer, type SiteConfig } from 'vitepress';
6+
import { handleAutoSidebar } from '../utils/loader/auto.sidebar';
87
import { dateToUnixTimestamp } from '../utils/node/date';
98
import { getLastCommitInfo } from '../utils/node/git';
10-
import { ensureIndexMd, getPattern, normalizePath } from '../utils/node/path';
9+
import { getPattern, normalizePath } from '../utils/node/path';
1110

1211
export interface ContentData {
1312
url: string
@@ -71,116 +70,6 @@ export interface ContentOptions<T = ContentData[]> {
7170
globOptions?: GlobOptions
7271
}
7372

74-
function filterHiddenItems(items: any[]): any[] {
75-
return items
76-
.filter(item => !item.hide) // 过滤掉 hide 为 true 的对象
77-
.map((item: any) => {
78-
if (item.items) {
79-
// 如果有子项,递归过滤子项
80-
return {
81-
...item,
82-
items: filterHiddenItems(item.items),
83-
};
84-
}
85-
return item;
86-
});
87-
}
88-
89-
// sidebar排序, 越小越靠前
90-
function sortSidebar(sidebar: any[]) {
91-
sidebar.forEach(item => {
92-
if (item.items) {
93-
item.items = sortSidebar(item.items);
94-
}
95-
});
96-
97-
// 排序函数
98-
return sidebar.sort((a, b) => {
99-
// 判断 path 是否以 index.md 结尾
100-
const aIsIndex = a.path.endsWith('index.md');
101-
const bIsIndex = b.path.endsWith('index.md');
102-
103-
if (aIsIndex && !bIsIndex) {
104-
return -1;
105-
}
106-
if (!aIsIndex && bIsIndex) {
107-
return 1;
108-
}
109-
110-
if (a.sort === undefined && b.sort !== undefined) {
111-
return 1;
112-
}
113-
else if (a.sort !== undefined && b.sort === undefined) {
114-
return -1;
115-
}
116-
117-
// 根据 sort 排序
118-
if (a.sort !== b.sort) {
119-
return a.sort - b.sort;
120-
}
121-
122-
// 根据 text 的首个字符排序
123-
return a.text.localeCompare(b.text);
124-
});
125-
}
126-
// Handle auto sidebar
127-
function formatSidebarItems(item: any, PATH: string, config: SiteConfig, data: Map<string, ContentData>) {
128-
const link = `/${
129-
normalizePath(path.relative(config.srcDir, PATH))
130-
.replace(/(^|\/)index\.md$/, '$1')
131-
.replace(/\.md$/, config.cleanUrls ? '' : '.html')}`;
132-
const filename = link.split('/')[link.split('/').length - 1].split('.')[0] || 'index';
133-
const article = data.get(ensureIndexMd(PATH));
134-
const content = matter(article?.src || '').content;
135-
const match = content.match(/^(#+)\s+(.+)/m);
136-
const title = match?.[2] || filename;
137-
const { sidebar, publish } = article?.frontmatter || {};
138-
139-
item.sort = sidebar?.sort;
140-
item.hide = sidebar === false;
141-
142-
if (!item.children) {
143-
item.link = link;
144-
item.text = sidebar?.text || title;
145-
item.hide = publish === false;
146-
}
147-
else {
148-
item.items = item.children;
149-
item.text = sidebar?.title || title;
150-
item.collapsed = sidebar?.collapsed;
151-
delete item.children;
152-
}
153-
}
154-
155-
function handleAutoSidebar(config: SiteConfig, data: Map<string, ContentData>) {
156-
const sidebar: any = (global as any).VITEPRESS_CONFIG.userConfig.themeConfig?.sidebar;
157-
const autoPaths: string[] = [];
158-
const autoSidebar: Record<string, any> = {};
159-
if (isObject(sidebar)) {
160-
Object.keys(sidebar).forEach(key => {
161-
if (((sidebar as any)[key] as string) === 'auto') {
162-
autoPaths.push(key);
163-
}
164-
});
165-
autoPaths.forEach(item => {
166-
const dirPath = path.join(config.srcDir, item);
167-
const filteredTree: any = directoryTree(dirPath, {
168-
exclude: /(node_modules|\.vitepress)$/,
169-
extensions: /\.md/,
170-
normalizePath: true
171-
}, (item, PATH) => formatSidebarItems(item, PATH, config, data), (item, PATH) => formatSidebarItems(item, PATH, config, data));
172-
173-
if (!data.get(ensureIndexMd(filteredTree.path))) {
174-
autoSidebar[item] = sortSidebar(filterHiddenItems(filteredTree.items));
175-
}
176-
else {
177-
autoSidebar[item] = sortSidebar(filterHiddenItems([filteredTree]));
178-
}
179-
});
180-
}
181-
return autoSidebar;
182-
}
183-
18473
export function createBaseDataLoader<T = {
18574
list: ContentData[]
18675
autoSidebar: any

0 commit comments

Comments
 (0)