@wot-ui/vitepress-theme 2.0.0-alpha.10

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 (57) hide show
  1. package/README.md +153 -0
  2. package/dist/config.js +118 -0
  3. package/dist/index.js +68 -0
  4. package/dist/locales/md-component-links.js +15 -0
  5. package/dist/plugins/md-component-links.js +34 -0
  6. package/dist/plugins/md-scss-vars.js +73 -0
  7. package/dist/plugins/md-version-badge.js +51 -0
  8. package/dist/plugins/virtual-version-data.js +83 -0
  9. package/dist/src/config.d.ts +3 -0
  10. package/dist/src/index.d.ts +21 -0
  11. package/dist/src/locales/md-component-links.d.ts +13 -0
  12. package/dist/src/plugins/md-component-links.d.ts +3 -0
  13. package/dist/src/plugins/md-scss-vars.d.ts +3 -0
  14. package/dist/src/plugins/md-version-badge.d.ts +6 -0
  15. package/dist/src/plugins/virtual-version-data.d.ts +3 -0
  16. package/dist/src/theme/composables/adSponsor.d.ts +30 -0
  17. package/dist/src/theme/composables/adsData.d.ts +16 -0
  18. package/dist/src/theme/composables/banner.d.ts +16 -0
  19. package/dist/src/theme/composables/cases.d.ts +16 -0
  20. package/dist/src/theme/composables/friendly.d.ts +19 -0
  21. package/dist/src/theme/composables/sponsor.d.ts +3 -0
  22. package/dist/src/theme/composables/team.d.ts +28 -0
  23. package/dist/src/theme/options.d.ts +3 -0
  24. package/dist/src/types.d.ts +86 -0
  25. package/dist/theme/Layout.vue +45 -0
  26. package/dist/theme/components/AsideSponsors.vue +105 -0
  27. package/dist/theme/components/Banner.vue +377 -0
  28. package/dist/theme/components/CustomFooter.vue +123 -0
  29. package/dist/theme/components/ExternalLink.vue +36 -0
  30. package/dist/theme/components/HomeCases.vue +122 -0
  31. package/dist/theme/components/HomeFriendly.vue +96 -0
  32. package/dist/theme/components/HomeTeam.vue +250 -0
  33. package/dist/theme/components/QrCode.vue +45 -0
  34. package/dist/theme/components/SidebarAds.vue +86 -0
  35. package/dist/theme/components/SpecialSponsor.vue +115 -0
  36. package/dist/theme/components/SvgImage.vue +22 -0
  37. package/dist/theme/components/VPContent.vue +92 -0
  38. package/dist/theme/components/VPDoc.vue +222 -0
  39. package/dist/theme/components/VPFeature.vue +150 -0
  40. package/dist/theme/components/VPIframe.vue +445 -0
  41. package/dist/theme/components/VPLocalNav.vue +151 -0
  42. package/dist/theme/components/VPNavBar.vue +253 -0
  43. package/dist/theme/components/VPSidebar.vue +120 -0
  44. package/dist/theme/components/VPSidebarItem.vue +271 -0
  45. package/dist/theme/components/WwAds.vue +74 -0
  46. package/dist/theme/composables/adSponsor.js +29 -0
  47. package/dist/theme/composables/adsData.js +35 -0
  48. package/dist/theme/composables/banner.js +35 -0
  49. package/dist/theme/composables/cases.js +43 -0
  50. package/dist/theme/composables/friendly.js +35 -0
  51. package/dist/theme/composables/sponsor.js +35 -0
  52. package/dist/theme/composables/team.js +48 -0
  53. package/dist/theme/options.js +4 -0
  54. package/dist/theme/styles/custom.css +206 -0
  55. package/dist/theme/styles/vars.css +136 -0
  56. package/dist/types.js +0 -0
  57. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # @wot-ui/vitepress-theme
2
+
3
+ 这是一个为 [Wot UI](https://github.com/wot-ui/wot-ui) 文档量身定制的 VitePress 共享主题包。
4
+ 该主题包内聚了 VitePress 运行时配置、自定义 Vue 组件、Markdown 增强插件以及常用的业务配置(如顶部通栏、广告、赞助商等),通过配置工厂的形式暴露,支持通过选项灵活开启、关闭或定制各模块能力,使得组织内其他项目能够快速复用一致的文档风格。
5
+
6
+ ## 安装
7
+
8
+ 由于本包主要供组织内文档站点使用,推荐通过工作区 (workspace) 的方式引入:
9
+
10
+ ```bash
11
+ pnpm add @wot-ui/vitepress-theme -D
12
+ ```
13
+
14
+ ## 使用方式
15
+
16
+ ### 1. 配置 VitePress (`config.mts`)
17
+
18
+ 在 `.vitepress/config.mts` 中,使用 `createWotVitePressConfig` 来包装你的 VitePress 配置。该工厂函数将自动注入主题所需的别名、SSR 处理和必要的 Vite/Markdown 插件。
19
+
20
+ ```typescript
21
+ import { createWotVitePressConfig } from '@wot-ui/vitepress-theme/config'
22
+ import { fileURLToPath, URL } from 'node:url'
23
+
24
+ export default createWotVitePressConfig({
25
+ title: 'My Project',
26
+ description: '项目描述',
27
+ markdown: {
28
+ // 组件源码与 Demo 的链接追加
29
+ componentLinks: {
30
+ repoUrl: 'https://github.com/my-org/my-repo/tree/master',
31
+ demoSourceRoot: 'src/subPages',
32
+ componentSourceRoot: 'src/components',
33
+ },
34
+ // SCSS 变量解析插件配置
35
+ scssVars: {
36
+ componentScssRoot: fileURLToPath(new URL('../../src/components', import.meta.url)),
37
+ componentMap: {
38
+ // 自定义你的组件别名映射
39
+ table: ['my-table', 'my-table-column']
40
+ }
41
+ },
42
+ versionBadge: true, // 开启 `^(x.y.z)` 转换为 Badge 标签
43
+ virtualVersionData: {
44
+ docsRoot: fileURLToPath(new URL('../', import.meta.url))
45
+ }
46
+ },
47
+ features: {
48
+ // 是否开启大语言模型文档生成插件
49
+ llms: {
50
+ ignoreFiles: ['reward/*'],
51
+ domain: 'https://my-docs-domain.com'
52
+ },
53
+ compression: {} // 是否开启产物压缩
54
+ },
55
+ // 你原有的 VitePress 配置...
56
+ themeConfig: {
57
+ logo: '/logo.svg',
58
+ // nav, sidebar, locales ...
59
+ }
60
+ })
61
+ ```
62
+
63
+ ### 2. 主题运行时入口 (`theme/index.ts`)
64
+
65
+ 在 `.vitepress/theme/index.ts` 中,使用 `createWotVitePressTheme` 创建并导出默认主题。可以在这里传入 UI 层面的业务数据(广告、赞助商、案例等)URL 列表。
66
+
67
+ ```typescript
68
+ import { createWotVitePressTheme } from '@wot-ui/vitepress-theme'
69
+
70
+ export default createWotVitePressTheme({
71
+ // 开启百度统计路由监听
72
+ analytics: {
73
+ trackBaiduRoute: true
74
+ },
75
+ // 移动端模拟器(右侧手机Iframe)配置
76
+ demoIframe: {
77
+ assetBase: '/wxqrcode' // 静态二维码图片前缀
78
+ },
79
+ // 以下是各业务组件的数据源配置
80
+ // 不传或传 false 则代表关闭该模块
81
+ banner: {
82
+ urls: ['https://api.example.com/banner.json']
83
+ },
84
+ sponsors: {
85
+ urls: ['https://api.example.com/sponsors.json']
86
+ },
87
+ ads: {
88
+ wwadsId: '372', // 万维广告 ID
89
+ urls: ['https://api.example.com/ads.json']
90
+ },
91
+ team: {
92
+ urls: ['https://api.example.com/team.json']
93
+ },
94
+ friendly: {
95
+ urls: ['https://api.example.com/friendly.json']
96
+ },
97
+ cases: {
98
+ urls: ['https://api.example.com/cases.json']
99
+ }
100
+ })
101
+ ```
102
+
103
+ ### 3. 在 Markdown 中使用业务组件数据
104
+
105
+ 我们提供了一系列配套的 Vue Composables 供你在特定的 Markdown 页面(如赞助页、案例页)中渲染数据。直接从包名引入即可:
106
+
107
+ ```vue
108
+ <script setup>
109
+ import { useCaseData, useSponsor, useTeam, useFriendly } from '@wot-ui/vitepress-theme'
110
+
111
+ const { data: cases } = useCaseData()
112
+ const { data: sponsors } = useSponsor()
113
+ </script>
114
+
115
+ <div v-for="item in cases" :key="item.name">
116
+ {{ item.name }}
117
+ </div>
118
+ ```
119
+
120
+ ## 配置项说明
121
+
122
+ ### Markdown 增强插件 (`markdown`)
123
+ - `componentLinks`: 是否在组件文档末尾追加 `源代码 | 文档 | 组件` 的 GitHub 外部链接。传入 `false` 可关闭。
124
+ - `scssVars`: 是否在组件文档的 `## 主题定制` 下方自动解析并渲染 SCSS 变量表。传入 `false` 可关闭。
125
+ - `versionBadge`: 是否将 `^(1.2.3)` 文本自动转换为深色 `<Badge>` 标签。
126
+ - `virtualVersionData`: 从文档中扫描版本变更,提供虚拟模块供组件侧边栏显示 `New` 或 `Update` 徽标。
127
+
128
+ ### 基础设施特性 (`features`)
129
+ - `llms`: 通过 `@vitepress-plugin-llms` 生成用于大语言模型的文本产物(`llms.txt`)。
130
+ - `compression`: 通过 `vite-plugin-compression` 生成 gzip 压缩文件。
131
+
132
+ ### 主题 UI 特性 (传给 `createWotVitePressTheme`)
133
+ 所有选项均遵循 `false | Options` 的形式。如果不传入或传入 `false`,相关 UI 模块将会自动隐藏。
134
+
135
+ - `analytics.trackBaiduRoute`: 开启后,在路由变化时调用 `_hmt.push(['_trackPageview'])`。
136
+ - `demoIframe`: 配置右侧手机模拟器相关选项。
137
+ - `banner` / `sponsors` / `ads` / `team` / `friendly` / `cases`: 提供给对应功能模块的请求地址数组(支持多线路重试)。
138
+
139
+ ## 构建要求
140
+
141
+ 如果你需要修改本包的源码,请确保在根目录执行依赖安装。
142
+ 开发模式下,VitePress 站点直接引用本包的编译后产物(通过 `pnpm build` 或根目录统筹的 `pnpm dev:docs` 触发 `esbuild` 实时输出到 `dist`)。
143
+
144
+ ```bash
145
+ pnpm --filter @wot-ui/vitepress-theme build
146
+ ```
147
+
148
+ ## 发版与发布规范
149
+
150
+ `@wot-ui/vitepress-theme` 作为 Wot Design Uni 项目的官方周边共享包,它的版本号与主仓库 `wot-ui` 强绑定。
151
+
152
+ 1. **版本同步**:当你在根目录执行 `pnpm release-tag` 时,发版脚本会自动将最新的版本号同步写入 `packages/vitepress-theme/package.json`。
153
+ 2. **自动化构建发布**:我们已经在主仓库的 `.github/workflows/release.yml` 中集成了此主题包的自动构建与 npm 发布流。只要带有 `v*` 的 Tag 被推送到主分支,GitHub Action 会在发布 `wot-ui` 的同时,自动进入 `packages/vitepress-theme` 执行 `npm publish` 将最新版本的主题包发布至 npm 官方镜像。
package/dist/config.js ADDED
@@ -0,0 +1,118 @@
1
+ import { defineConfig } from "vitepress";
2
+ import viteCompression from "vite-plugin-compression";
3
+ import llmstxt from "vitepress-plugin-llms";
4
+ import { fileURLToPath, URL } from "node:url";
5
+ import { mdScssVarsPlugin } from "./plugins/md-scss-vars.js";
6
+ import { mdComponentLinksPlugin } from "./plugins/md-component-links.js";
7
+ import { mdVersionBadgePlugin } from "./plugins/md-version-badge.js";
8
+ import { virtualVersionDataPlugin } from "./plugins/virtual-version-data.js";
9
+ function normalizeAlias(alias) {
10
+ if (!alias)
11
+ return [];
12
+ return Array.isArray(alias) ? alias : Object.entries(alias).map(([find, replacement]) => ({ find, replacement }));
13
+ }
14
+ function normalizePlugins(plugins) {
15
+ return (plugins ?? []).filter(Boolean);
16
+ }
17
+ function createLlmsPlugin(options) {
18
+ if (!options)
19
+ return null;
20
+ return llmstxt({
21
+ domain: options.domain,
22
+ ignoreFiles: options.ignoreFiles ?? []
23
+ });
24
+ }
25
+ function createCompressionPlugin(options) {
26
+ if (options === false)
27
+ return null;
28
+ return viteCompression({
29
+ verbose: true,
30
+ disable: false,
31
+ threshold: 10240,
32
+ algorithm: "gzip",
33
+ ext: ".gz",
34
+ ...options ?? {}
35
+ });
36
+ }
37
+ function createInternalAliases() {
38
+ return [
39
+ {
40
+ find: /^.*\/VPSidebar\.vue$/,
41
+ replacement: fileURLToPath(new URL("./theme/components/VPSidebar.vue", import.meta.url))
42
+ },
43
+ {
44
+ find: /^.*\/VPContent\.vue$/,
45
+ replacement: fileURLToPath(new URL("./theme/components/VPContent.vue", import.meta.url))
46
+ },
47
+ {
48
+ find: /^.*\/VPDoc\.vue$/,
49
+ replacement: fileURLToPath(new URL("./theme/components/VPDoc.vue", import.meta.url))
50
+ },
51
+ {
52
+ find: /^.*\/VPLocalNav\.vue$/,
53
+ replacement: fileURLToPath(new URL("./theme/components/VPLocalNav.vue", import.meta.url))
54
+ },
55
+ {
56
+ find: /^.*\/VPNavBar\.vue$/,
57
+ replacement: fileURLToPath(new URL("./theme/components/VPNavBar.vue", import.meta.url))
58
+ },
59
+ {
60
+ find: /^.*\/VPSidebarItem\.vue$/,
61
+ replacement: fileURLToPath(new URL("./theme/components/VPSidebarItem.vue", import.meta.url))
62
+ }
63
+ ];
64
+ }
65
+ function mergeNoExternal(existing) {
66
+ const merged = /* @__PURE__ */ new Set(["element-plus"]);
67
+ const noExternal = existing?.noExternal;
68
+ if (Array.isArray(noExternal)) {
69
+ noExternal.forEach((item) => {
70
+ if (typeof item === "string") {
71
+ merged.add(item);
72
+ }
73
+ });
74
+ }
75
+ return {
76
+ ...existing,
77
+ noExternal: Array.from(merged)
78
+ };
79
+ }
80
+ function createWotVitePressConfig(options) {
81
+ const userVite = options.vite ?? {};
82
+ const internalPlugins = [
83
+ createLlmsPlugin(options.features?.llms),
84
+ options.markdown.scssVars !== false ? mdScssVarsPlugin({
85
+ componentScssRoot: options.markdown.scssVars?.componentScssRoot ?? "",
86
+ componentMap: options.markdown.scssVars?.componentMap
87
+ }) : null,
88
+ options.markdown.componentLinks !== false ? mdComponentLinksPlugin({
89
+ repoUrl: options.markdown.componentLinks?.repoUrl ?? "",
90
+ demoSourceRoot: options.markdown.componentLinks?.demoSourceRoot ?? "",
91
+ componentSourceRoot: options.markdown.componentLinks?.componentSourceRoot ?? ""
92
+ }) : null,
93
+ options.markdown.versionBadge !== false ? mdVersionBadgePlugin() : null,
94
+ options.markdown.virtualVersionData !== false ? virtualVersionDataPlugin({
95
+ docsRoot: options.markdown.virtualVersionData?.docsRoot
96
+ }) : null,
97
+ createCompressionPlugin(options.features?.compression)
98
+ ].filter(Boolean);
99
+ return defineConfig({
100
+ title: options.title,
101
+ description: options.description,
102
+ head: options.head,
103
+ locales: options.locales,
104
+ themeConfig: options.themeConfig,
105
+ vite: {
106
+ ...userVite,
107
+ plugins: [...internalPlugins, ...normalizePlugins(userVite.plugins)],
108
+ ssr: mergeNoExternal(userVite.ssr),
109
+ resolve: {
110
+ ...userVite.resolve,
111
+ alias: [...createInternalAliases(), ...normalizeAlias(userVite.resolve?.alias)]
112
+ }
113
+ }
114
+ });
115
+ }
116
+ export {
117
+ createWotVitePressConfig
118
+ };
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ import DefaultTheme, { VPBadge } from "vitepress/theme";
2
+ import ElementPlus from "element-plus";
3
+ import "element-plus/dist/index.css";
4
+ import "element-plus/theme-chalk/dark/css-vars.css";
5
+ import Layout from "./theme/Layout.vue";
6
+ import ExternalLink from "./theme/components/ExternalLink.vue";
7
+ import SvgImage from "./theme/components/SvgImage.vue";
8
+ import { wotThemeOptionsKey } from "./theme/options.js";
9
+ import "./theme/styles/vars.css";
10
+ import "./theme/styles/custom.css";
11
+ function resolveThemeOptions(options = {}) {
12
+ return {
13
+ analytics: {
14
+ trackBaiduRoute: options.analytics !== false && options.analytics?.trackBaiduRoute !== false
15
+ },
16
+ banner: options.banner ?? false,
17
+ sponsors: options.sponsors ?? false,
18
+ ads: options.ads ?? false,
19
+ team: options.team ?? false,
20
+ friendly: options.friendly ?? false,
21
+ cases: options.cases ?? false,
22
+ demoIframe: options.demoIframe ?? false
23
+ };
24
+ }
25
+ function enhanceAppWithOptions(ctx, options) {
26
+ const { app, router } = ctx;
27
+ app.component("SvgImage", SvgImage);
28
+ app.component("ExternalLink", ExternalLink);
29
+ app.component("Badge", VPBadge);
30
+ app.use(ElementPlus);
31
+ app.provide(wotThemeOptionsKey, options);
32
+ if (!options.analytics.trackBaiduRoute || typeof window === "undefined") {
33
+ return;
34
+ }
35
+ const trackPageView = (path) => {
36
+ if (window._hmt) {
37
+ window._hmt.push(["_trackPageview", path]);
38
+ }
39
+ };
40
+ router.onAfterRouteChange = (to) => {
41
+ setTimeout(() => {
42
+ trackPageView(to);
43
+ }, 100);
44
+ };
45
+ }
46
+ function createWotVitePressTheme(options = {}) {
47
+ const resolvedOptions = resolveThemeOptions(options);
48
+ return {
49
+ extends: DefaultTheme,
50
+ Layout,
51
+ enhanceApp(ctx) {
52
+ enhanceAppWithOptions(ctx, resolvedOptions);
53
+ }
54
+ };
55
+ }
56
+ const theme = createWotVitePressTheme();
57
+ var src_default = theme;
58
+ export * from "./types.js";
59
+ export * from "./theme/composables/team.js";
60
+ export * from "./theme/composables/sponsor.js";
61
+ export * from "./theme/composables/adsData.js";
62
+ export * from "./theme/composables/banner.js";
63
+ export * from "./theme/composables/cases.js";
64
+ export * from "./theme/composables/friendly.js";
65
+ export {
66
+ createWotVitePressTheme,
67
+ src_default as default
68
+ };
@@ -0,0 +1,15 @@
1
+ var md_component_links_default = {
2
+ "zh-CN": {
3
+ sourceCode: "\u6E90\u4EE3\u7801",
4
+ document: "\u6587\u6863",
5
+ component: "\u7EC4\u4EF6"
6
+ },
7
+ "en-US": {
8
+ sourceCode: "Source Code",
9
+ document: "Documentation",
10
+ component: "Component"
11
+ }
12
+ };
13
+ export {
14
+ md_component_links_default as default
15
+ };
@@ -0,0 +1,34 @@
1
+ import path from "path";
2
+ import i18n from "../locales/md-component-links.js";
3
+ function kebabToCamelCase(value) {
4
+ return value.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
5
+ }
6
+ function mdComponentLinksPlugin(options) {
7
+ return {
8
+ name: "md-component-links",
9
+ enforce: "pre",
10
+ async transform(code, id) {
11
+ if (!id.endsWith(".md"))
12
+ return;
13
+ if (!id.includes("/component"))
14
+ return;
15
+ if (id.includes("/use-"))
16
+ return;
17
+ const componentId = path.basename(id, ".md");
18
+ const componentName = `wd-${componentId}`;
19
+ const camelComponentId = kebabToCamelCase(componentId);
20
+ const demoUrl = `${options.repoUrl.replace(/\/$/, "")}/${options.demoSourceRoot.replace(/^\/+/, "").replace(/\/$/, "")}/${camelComponentId}`;
21
+ const componentUrl = `${options.repoUrl.replace(/\/$/, "")}/${options.componentSourceRoot.replace(/^\/+/, "").replace(/\/$/, "")}/${componentName}`;
22
+ const lang = id.includes("/en-US/") ? "en-US" : "zh-CN";
23
+ const { sourceCode, document, component } = i18n[lang];
24
+ return {
25
+ code: `${code}
26
+ ## ${sourceCode}
27
+ <ExternalLink href=${demoUrl}>${document}</ExternalLink> \u2022 <ExternalLink href=${componentUrl}>${component}</ExternalLink>`
28
+ };
29
+ }
30
+ };
31
+ }
32
+ export {
33
+ mdComponentLinksPlugin
34
+ };
@@ -0,0 +1,73 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ const COMPONENT_MAP = {
4
+ table: ["wd-table", "wd-table-column"],
5
+ radio: ["wd-radio", "wd-radio-group"],
6
+ checkbox: ["wd-checkbox", "wd-checkbox-group"],
7
+ collapse: ["wd-collapse", "wd-collapse-item"],
8
+ "swipe-action": ["wd-swipe-action", "wd-swipe-action-item"],
9
+ grid: ["wd-grid", "wd-grid-item"],
10
+ tabs: ["wd-tabs", "wd-tab"],
11
+ steps: ["wd-steps", "wd-step"],
12
+ sidebar: ["wd-sidebar", "wd-sidebar-item"],
13
+ "index-bar": ["wd-index-bar", "wd-index-anchor"],
14
+ form: ["wd-form", "wd-form-item"]
15
+ };
16
+ function parseScssVariables(scssContent) {
17
+ const SCSS_VAR_REGEX = /(?:\/\/\s*(.*)\n)?\$([a-zA-Z0-9-]+):\s*var\(--wot-([a-zA-Z0-9-]+),\s*(.+)\)(?:\s*!default)?\s*;/g;
18
+ const variables = [];
19
+ let match;
20
+ while ((match = SCSS_VAR_REGEX.exec(scssContent)) !== null) {
21
+ variables.push({
22
+ desc: match[1] ? match[1].trim() : "-",
23
+ name: `--wot-${match[3]}`,
24
+ defaultValue: match[4].trim()
25
+ });
26
+ }
27
+ return variables;
28
+ }
29
+ function mdScssVarsPlugin(options) {
30
+ const componentMap = options.componentMap || COMPONENT_MAP;
31
+ return {
32
+ name: "md-scss-vars",
33
+ enforce: "pre",
34
+ async transform(code, id) {
35
+ if (!id.endsWith(".md"))
36
+ return;
37
+ if (!id.includes("/component/"))
38
+ return;
39
+ if (id.includes("/use-"))
40
+ return;
41
+ const componentId = path.basename(id, ".md");
42
+ const targetComponents = componentMap[componentId] || [`wd-${componentId}`];
43
+ let allVariables = [];
44
+ for (const comp of targetComponents) {
45
+ const scssPath = path.resolve(options.componentScssRoot, comp, "index.scss");
46
+ if (fs.existsSync(scssPath)) {
47
+ const scssContent = fs.readFileSync(scssPath, "utf8");
48
+ const vars = parseScssVariables(scssContent);
49
+ allVariables = allVariables.concat(vars);
50
+ }
51
+ }
52
+ if (allVariables.length === 0) {
53
+ return { code };
54
+ }
55
+ let tableMd = "\n## \u4E3B\u9898\u5B9A\u5236\n\n### CSS \u53D8\u91CF\n\n\u7EC4\u4EF6\u63D0\u4F9B\u4E86\u4E0B\u5217 CSS \u53D8\u91CF\uFF0C\u53EF\u7528\u4E8E\u81EA\u5B9A\u4E49\u6837\u5F0F\uFF0C\u4F7F\u7528\u65B9\u6CD5\u8BF7\u53C2\u8003 [ConfigProvider \u5168\u5C40\u914D\u7F6E](/component/config-provider)\u3002\n\n| \u540D\u79F0 | \u9ED8\u8BA4\u503C | \u63CF\u8FF0 |\n| --- | --- | --- |\n";
56
+ for (const v of allVariables) {
57
+ tableMd += `| ${v.name} | ${v.defaultValue} | ${v.desc} |
58
+ `;
59
+ }
60
+ const themeSectionRegex = /\n*##\s+主题定制[\s\S]*?(?=\n##\s+|$)/;
61
+ let newCode = code;
62
+ if (themeSectionRegex.test(code)) {
63
+ newCode = code.replace(themeSectionRegex, tableMd);
64
+ } else {
65
+ newCode = `${code}${tableMd}`;
66
+ }
67
+ return { code: newCode };
68
+ }
69
+ };
70
+ }
71
+ export {
72
+ mdScssVarsPlugin
73
+ };
@@ -0,0 +1,51 @@
1
+ const VERSION_MARK_RE = /\^\((\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\)/g;
2
+ const INLINE_CODE_RE = /`[^`]*`/g;
3
+ function replaceVersionMarkerOutsideInlineCode(line) {
4
+ const ranges = [];
5
+ for (const match of line.matchAll(INLINE_CODE_RE)) {
6
+ ranges.push({
7
+ start: match.index || 0,
8
+ end: (match.index || 0) + match[0].length
9
+ });
10
+ }
11
+ return line.replace(VERSION_MARK_RE, (full, version, offset) => {
12
+ const insideInlineCode = ranges.some((range) => offset >= range.start && offset < range.end);
13
+ if (insideInlineCode)
14
+ return full;
15
+ return `<Badge type="tip" text="v${version}" />`;
16
+ });
17
+ }
18
+ function mdVersionBadgePlugin() {
19
+ return {
20
+ name: "md-version-badge",
21
+ enforce: "pre",
22
+ async transform(code, id) {
23
+ if (!id.endsWith(".md"))
24
+ return;
25
+ if (!id.includes("/docs/"))
26
+ return;
27
+ if (!/\^\([^)]+\)/.test(code))
28
+ return;
29
+ const lines = code.split("\n");
30
+ let inFence = false;
31
+ const transformed = lines.map((line) => {
32
+ const trimmed = line.trim();
33
+ if (trimmed.startsWith("```")) {
34
+ inFence = !inFence;
35
+ return line;
36
+ }
37
+ if (inFence)
38
+ return line;
39
+ if (line.includes("<Badge"))
40
+ return line;
41
+ return replaceVersionMarkerOutsideInlineCode(line);
42
+ });
43
+ return {
44
+ code: transformed.join("\n")
45
+ };
46
+ }
47
+ };
48
+ }
49
+ export {
50
+ mdVersionBadgePlugin
51
+ };
@@ -0,0 +1,83 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from "fs";
2
+ import { resolve, relative, sep } from "path";
3
+ const VERSION_DATA_ID = "virtual:wot-vitepress-theme/version-data";
4
+ const RESOLVED_VERSION_DATA_ID = "\0virtual:wot-vitepress-theme/version-data";
5
+ function extractVersionFromMarkdown(filePath) {
6
+ if (!existsSync(filePath))
7
+ return null;
8
+ try {
9
+ const content = readFileSync(filePath, "utf-8");
10
+ const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
11
+ if (frontmatterMatch) {
12
+ const frontmatter = frontmatterMatch[1];
13
+ const versionMatch = frontmatter.match(/version:\s*(['"]?)([^'"\n]+)\1/);
14
+ if (versionMatch) {
15
+ return versionMatch[2].trim();
16
+ }
17
+ }
18
+ } catch (e) {
19
+ console.warn(`Failed to read file ${filePath}:`, e);
20
+ }
21
+ return null;
22
+ }
23
+ function scanAllVersions(docsRoot) {
24
+ const versionData = {};
25
+ const docsDir = resolve(docsRoot);
26
+ if (!existsSync(docsDir)) {
27
+ console.warn("Docs directory not found:", docsDir);
28
+ return versionData;
29
+ }
30
+ function traverse(currentDir) {
31
+ try {
32
+ const files = readdirSync(currentDir);
33
+ files.forEach((file) => {
34
+ if (file.startsWith(".") || file === "public" || file === "node_modules")
35
+ return;
36
+ const fullPath = resolve(currentDir, file);
37
+ const stat = statSync(fullPath);
38
+ if (stat.isDirectory()) {
39
+ traverse(fullPath);
40
+ } else if (file.endsWith(".md")) {
41
+ const version = extractVersionFromMarkdown(fullPath);
42
+ if (version) {
43
+ const relPath = relative(docsDir, fullPath);
44
+ const normalizedPath = "/" + relPath.split(sep).join("/").replace(/\.md$/, "");
45
+ versionData[normalizedPath] = version;
46
+ console.log(`\u2705 Found version info in ${normalizedPath}:`, version);
47
+ }
48
+ }
49
+ });
50
+ } catch (e) {
51
+ console.warn(`Failed to scan directory ${currentDir}:`, e);
52
+ }
53
+ }
54
+ traverse(docsDir);
55
+ return versionData;
56
+ }
57
+ function virtualVersionDataPlugin(options = {}) {
58
+ let versionData = {};
59
+ return {
60
+ name: "virtual-version-data",
61
+ resolveId(id) {
62
+ if (id === VERSION_DATA_ID) {
63
+ return RESOLVED_VERSION_DATA_ID;
64
+ }
65
+ },
66
+ load(id) {
67
+ if (id === RESOLVED_VERSION_DATA_ID) {
68
+ return `export const versionData = ${JSON.stringify(versionData, null, 2)}`;
69
+ }
70
+ },
71
+ configResolved() {
72
+ try {
73
+ versionData = scanAllVersions(options.docsRoot ?? process.cwd());
74
+ console.log("\u2705 Version data generated successfully");
75
+ } catch (e) {
76
+ console.error("\u274C Failed to generate version data:", e);
77
+ }
78
+ }
79
+ };
80
+ }
81
+ export {
82
+ virtualVersionDataPlugin
83
+ };
@@ -0,0 +1,3 @@
1
+ import type { WotVitePressConfigOptions } from './types.js';
2
+ export declare function createWotVitePressConfig(options: WotVitePressConfigOptions): import("vitepress").UserConfig<NoInfer<import("vitepress").DefaultTheme.Config>>;
3
+ export type { WotVitePressConfigOptions } from './types.js';
@@ -0,0 +1,21 @@
1
+ import type { Theme } from 'vitepress';
2
+ import 'element-plus/dist/index.css';
3
+ import 'element-plus/theme-chalk/dark/css-vars.css';
4
+ import type { WotVitePressThemeOptions } from './types.js';
5
+ import './theme/styles/vars.css';
6
+ import './theme/styles/custom.css';
7
+ declare global {
8
+ interface Window {
9
+ _hmt: any[];
10
+ }
11
+ }
12
+ export declare function createWotVitePressTheme(options?: WotVitePressThemeOptions): Theme;
13
+ declare const theme: Theme;
14
+ export default theme;
15
+ export * from './types';
16
+ export * from './theme/composables/team.js';
17
+ export * from './theme/composables/sponsor.js';
18
+ export * from './theme/composables/adsData.js';
19
+ export * from './theme/composables/banner.js';
20
+ export * from './theme/composables/cases.js';
21
+ export * from './theme/composables/friendly.js';
@@ -0,0 +1,13 @@
1
+ declare const _default: {
2
+ 'zh-CN': {
3
+ sourceCode: string;
4
+ document: string;
5
+ component: string;
6
+ };
7
+ 'en-US': {
8
+ sourceCode: string;
9
+ document: string;
10
+ component: string;
11
+ };
12
+ };
13
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { WotMdComponentLinksOptions } from '../types.js';
3
+ export declare function mdComponentLinksPlugin(options: WotMdComponentLinksOptions): Plugin;
@@ -0,0 +1,3 @@
1
+ import { type Plugin } from 'vite';
2
+ import type { WotMdScssVarsOptions } from '../types.js';
3
+ export declare function mdScssVarsPlugin(options: WotMdScssVarsOptions): Plugin;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 将 docs 目录下 Markdown 中形如 ^(1.2.3) 的版本标记转换为 VitePress Badge 组件。
3
+ * 仅处理普通文本内容,跳过代码块、行内代码,以及已经手写了 <Badge> 的行。
4
+ */
5
+ import { type Plugin } from 'vite';
6
+ export declare function mdVersionBadgePlugin(): Plugin;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { WotVirtualVersionDataOptions } from '../types.js';
3
+ export declare function virtualVersionDataPlugin(options?: WotVirtualVersionDataOptions): Plugin;