@fuxishi/vitepress-theme 2.0.0-alpha.4 → 2.0.0-alpha.6

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/README_EN.md ADDED
@@ -0,0 +1,140 @@
1
+ # @fuxishi/vitepress-theme
2
+
3
+ A beautiful VitePress theme extension with frosted glass effects, purple-blue gradients, rich animations, and ready to use.
4
+
5
+ [中文](./README.md)
6
+
7
+ ## Features
8
+
9
+ - 🎵 **Floating Music Ball** — Frosted glass floating player with single/playlist mode, purple-blue gradient progress ring and four playback animations
10
+ - 🎊 **Click Confetti** — Global click confetti effect with gold stars, colored paper pieces, and custom Emoji
11
+ - 🎨 **Hero Image Color** — Auto-extract colors from hero image for background glow, title gradient, code block beams, and music ball
12
+ - ✨ **Feature Glow** — Mouse-tracking glow effect on homepage Feature cards
13
+ - ✨ **Beam Border** — Purple-cyan brand color beam border on code blocks, code groups, and navigation in dark mode on hover
14
+ - 🔄 **Theme Switch Animation** — Circular spread animation for light/dark mode switching using View Transition API
15
+ - 📜 **Smooth Scroll** — Smooth scrolling when clicking outline navigation with silky marker transition
16
+ - 🎨 **Heading Decorations** — Different colored underlines for h1-h4 headings
17
+ - 📦 **Code Block Fold** — Auto-fold code blocks exceeding a line threshold with frosted glass blur overlay, configurable fold line count
18
+ - ⚙️ **Type-safe** — Complete TypeScript type definitions, IDE friendly
19
+
20
+ > **Note:** This version (v2.x) is built on VitePress `2.0` and is not backward compatible with VitePress 1.x.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @fuxishi/vitepress-theme
26
+ ```
27
+
28
+ Install peerDependencies:
29
+
30
+ ```bash
31
+ npm install vitepress
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### 1. Register Theme
37
+
38
+ Create `.vitepress/theme/index.ts`:
39
+
40
+ ```ts
41
+ import FxTheme from "@fuxishi/vitepress-theme"
42
+ import "@fuxishi/vitepress-theme/style.css"
43
+
44
+ export default FxTheme
45
+ ```
46
+
47
+ ### 2. Extend Configuration
48
+
49
+ Create `.vitepress/config.mts`:
50
+
51
+ ```ts
52
+ import { defineConfig } from "vitepress"
53
+ import type { DefaultTheme } from "vitepress"
54
+ import fxConfig from "@fuxishi/vitepress-theme/config"
55
+ import type { FxThemeCustomConfig } from "@fuxishi/vitepress-theme"
56
+
57
+ type ThemeConfig = DefaultTheme.Config & FxThemeCustomConfig
58
+
59
+ export default defineConfig<ThemeConfig>({
60
+ extends: fxConfig,
61
+ lang: "en",
62
+ title: "My Docs",
63
+ themeConfig: {
64
+ nav: [{ text: "Guide", link: "/guide/" }],
65
+ sidebar: {
66
+ "/guide/": [{ text: "Getting Started", link: "/guide/" }],
67
+ },
68
+ // Enable music ball
69
+ musicBall: {
70
+ enable: true,
71
+ autoplay: false,
72
+ loop: true,
73
+ src: "/music/my-song.mp3",
74
+ },
75
+ // Enable confetti
76
+ confetti: true,
77
+ // Colored paper pieces
78
+ // confetti: { shape: "colored-paper" },
79
+ // Custom Emoji
80
+ // confetti: { shapes: ["🌸", "🎀"] },
81
+ // Enable hero image color extraction
82
+ heroImageColor: true,
83
+ // Enable smooth scroll
84
+ smoothScroll: true,
85
+ // Code block fold (enabled by default, 10 lines threshold)
86
+ codeBlockFold: true,
87
+ // Custom fold line threshold
88
+ // codeBlockFold: { lines: 20 },
89
+ },
90
+ })
91
+ ```
92
+
93
+ ### 3. Start Dev Server
94
+
95
+ ```bash
96
+ npx vitepress dev
97
+ ```
98
+
99
+ ## Configuration
100
+
101
+ ### FxThemeCustomConfig
102
+
103
+ Extends VitePress `DefaultTheme.Config` via intersection type `DefaultTheme.Config & FxThemeCustomConfig`, adding the following fields:
104
+
105
+ | Option | Type | Default | Description |
106
+ | --------------------- | --------------------------- | -------- | ----------------------------------------------------------------- |
107
+ | `musicBall.enable` | `boolean` | `true` | Enable music ball |
108
+ | `musicBall.visible` | `boolean` | `true` | Visibility |
109
+ | `musicBall.autoplay` | `boolean` | `false` | Autoplay |
110
+ | `musicBall.loop` | `boolean` | `true` | Loop playback (single mode) |
111
+ | `musicBall.src` | `string` | `''` | Single music URL |
112
+ | `musicBall.list` | `MusicItem[]` | — | Multi-song playlist |
113
+ | `confetti` | `boolean \| object` | `true` | Enable confetti |
114
+ | `confetti.shape` | `'star' \| 'colored-paper'` | `'star'` | Preset shape |
115
+ | `confetti.shapes` | `string \| string[]` | — | Custom Emoji shapes |
116
+ | `confetti.secondary` | `boolean` | `true` | Whether to add secondary particles |
117
+ | `heroImageColor` | `boolean` | `false` | Auto-extract hero image colors (affects glow, title, beams, ball) |
118
+ | `smoothScroll` | `boolean` | `false` | Enable smooth scrolling and outline marker transition |
119
+ | `codeBlockFold` | `boolean \| object` | `true` | Enable code block folding |
120
+ | `codeBlockFold.lines` | `number` | `10` | Line count threshold for folding |
121
+
122
+ ## Exports
123
+
124
+ | Export Path | Description |
125
+ | ------------------------------------ | ---------------------------- |
126
+ | `@fuxishi/vitepress-theme` | Theme (Layout + Styles) |
127
+ | `@fuxishi/vitepress-theme/config` | VitePress base config preset |
128
+ | `@fuxishi/vitepress-theme/style.css` | Standalone style file |
129
+
130
+ ## Documentation
131
+
132
+ Full documentation:
133
+
134
+ [https://fuxishi-vitepress-theme.fuxizjxzy.cn](https://fuxishi-vitepress-theme.fuxizjxzy.cn)
135
+
136
+ ## License
137
+
138
+ [MIT](./LICENSE)
139
+
140
+ > Will be open-sourced on GitHub in the future.
@@ -117,24 +117,7 @@ var C = e({
117
117
  function T() {
118
118
  return m.value.fxHeroImages || {};
119
119
  }
120
- async function E() {
121
- try {
122
- let e = await import(
123
- /* @vite-ignore */
124
- [h.value.base, "index.md"].join("")
125
- ), t = (typeof e.__pageData == "string" ? JSON.parse(e.__pageData) : e.__pageData)?.frontmatter?.hero?.image;
126
- return t ? typeof t == "string" ? {
127
- light: t,
128
- dark: t
129
- } : {
130
- light: t.light,
131
- dark: t.dark
132
- } : null;
133
- } catch {
134
- return null;
135
- }
136
- }
137
- async function D(e, t) {
120
+ async function E(e, t) {
138
121
  let n = e.src;
139
122
  if (n) try {
140
123
  let e = b(await c.from(n).quality(1).getPalette());
@@ -143,7 +126,7 @@ var C = e({
143
126
  r[t] = e, r.images ||= {}, r.images[t] = n, d(r);
144
127
  } catch {}
145
128
  }
146
- async function O(e, t) {
129
+ async function D(e, t) {
147
130
  try {
148
131
  let n = b(await c.from(e).quality(1).getPalette()), r = u() || {};
149
132
  return r[t] = n, r.images ||= {}, r.images[t] = e, d(r), n;
@@ -151,15 +134,15 @@ var C = e({
151
134
  return null;
152
135
  }
153
136
  }
154
- async function k(e, t = !0) {
137
+ async function O(e, t = !0) {
155
138
  if (!e.light && !e.dark) return;
156
139
  let n = {};
157
140
  if (e.light) {
158
- let r = await O(t ? s(e.light) : e.light, "light");
141
+ let r = await D(t ? s(e.light) : e.light, "light");
159
142
  r && (n.light = r);
160
143
  }
161
144
  if (e.dark) {
162
- let r = await O(t ? s(e.dark) : e.dark, "dark");
145
+ let r = await D(t ? s(e.dark) : e.dark, "dark");
163
146
  r && (n.dark = r);
164
147
  }
165
148
  if (n.light || n.dark) {
@@ -167,38 +150,28 @@ var C = e({
167
150
  e && x(e);
168
151
  }
169
152
  }
170
- async function A() {
153
+ async function k() {
171
154
  let e = T();
172
155
  if (e.light || e.dark) {
173
- await k(e);
174
- return;
175
- }
176
- let t = await E();
177
- if (t) {
178
- await k(t);
156
+ await O(e);
179
157
  return;
180
158
  }
181
159
  try {
182
- let e = await (await fetch(window.location.origin + "/")).text(), t = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".VPHero .image-container img.VPImage"), n = {};
160
+ let e = await (await fetch(window.location.origin + h.value.base)).text(), t = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".VPHero .image-container img.VPImage"), n = {};
183
161
  t.forEach((e) => {
184
162
  let t = e.getAttribute("src");
185
163
  t && (e.classList.contains("dark") ? n.dark = t : n.light = t);
186
- }), (n.light || n.dark) && await k(n, !1);
164
+ }), (n.light || n.dark) && await O(n, !1);
187
165
  } catch {}
188
166
  }
189
- function j() {
167
+ function A() {
190
168
  let e = document.querySelector(".VPHero .image-container img.VPImage.light"), t = document.querySelector(".VPHero .image-container img.VPImage.dark");
191
- e || t ? (e && e.complete && e.naturalWidth > 0 ? D(e, "light") : e && e.addEventListener("load", () => D(e, "light"), { once: !0 }), t && t.complete && t.naturalWidth > 0 ? D(t, "dark") : t && t.addEventListener("load", () => D(t, "dark"), { once: !0 })) : (g(), w(), E().then((e) => {
192
- if (e && (e.light || e.dark)) {
193
- let t = u();
194
- t?.images && (e.light && t.images.light && e.light !== t.images.light || e.dark && t.images.dark && e.dark !== t.images.dark) && (localStorage.removeItem(l), k(e));
195
- }
196
- }), A());
169
+ e || t ? (e && e.complete && e.naturalWidth > 0 ? E(e, "light") : e && e.addEventListener("load", () => E(e, "light"), { once: !0 }), t && t.complete && t.naturalWidth > 0 ? E(t, "dark") : t && t.addEventListener("load", () => E(t, "dark"), { once: !0 })) : (g(), w(), k());
197
170
  }
198
171
  r(() => {
199
- v(T()), C() || A(), j(), f = new MutationObserver((e) => {
172
+ v(T()), C() || k(), A(), f = new MutationObserver((e) => {
200
173
  for (let t of e) if (t.attributeName === "class") {
201
- w(), j();
174
+ w(), A();
202
175
  break;
203
176
  }
204
177
  }), f.observe(document.documentElement, {
@@ -206,7 +179,7 @@ var C = e({
206
179
  attributeFilter: ["class"]
207
180
  });
208
181
  }), i(() => p.path, () => {
209
- t(j);
182
+ t(A);
210
183
  }), n(() => {
211
184
  f?.disconnect(), g();
212
185
  });
package/dist/index.d.ts CHANGED
@@ -1,2 +1,12 @@
1
- export type { FxThemeCustomConfig, MusicItem, ConfettiShape, ConfettiConfig } from './config'
2
- export { default as configPreset } from './config'
1
+ import type { Theme } from "vitepress";
2
+
3
+ declare const theme: Theme;
4
+ export default theme;
5
+
6
+ export type {
7
+ FxThemeCustomConfig,
8
+ MusicItem,
9
+ ConfettiShape,
10
+ ConfettiConfig,
11
+ } from "./config";
12
+ export { default as configPreset } from "./config";
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var d = {
32
32
  }, t = document.querySelector(".VPNavBarTitle .title");
33
33
  t && (x = new ResizeObserver(e), x.observe(t)), e();
34
34
  }), a(() => x?.disconnect());
35
- let w = c(() => import("./chunk/FxMusicBall-CTP51ydo.js"), [{ style: { display: g.musicBall && g.musicBall.visible ? "" : "none" } }]), T = c(() => import("./chunk/FxHomeFeatureBefore-Cd9m9wit.js")), E = c(() => import("./chunk/FxConfetti-DFRD5KKS.js"), [{ confetti: g.confetti }]), D = c(() => import("./chunk/FxHeroImageBg-AXhVm0se.js"), [{ heroImageColor: g.heroImageColor }]), O = c(() => import("./chunk/FxCodeBlockFold-BZw-1rNl.js"), [{ lines: C }]);
35
+ let w = c(() => import("./chunk/FxMusicBall-CTP51ydo.js"), [{ style: { display: g.musicBall && g.musicBall.visible ? "" : "none" } }]), T = c(() => import("./chunk/FxHomeFeatureBefore-Cd9m9wit.js")), E = c(() => import("./chunk/FxConfetti-DFRD5KKS.js"), [{ confetti: g.confetti }]), D = c(() => import("./chunk/FxHeroImageBg-DyiZOKOD.js"), [{ heroImageColor: g.heroImageColor }]), O = c(() => import("./chunk/FxCodeBlockFold-BZw-1rNl.js"), [{ lines: C }]);
36
36
  return () => [
37
37
  S && n("style", `:root{--fx-code-fold-lines:${C || 10}}`),
38
38
  g.heroImageColor && n("style", ":root{--vp-home-hero-image-background-image:none;--vp-home-hero-name-color:transparent;--vp-home-hero-name-background:none}"),
@@ -1 +1 @@
1
- export {}
1
+ export {}
package/package.json CHANGED
@@ -2,40 +2,41 @@
2
2
  "name": "@fuxishi/vitepress-theme",
3
3
  "displayName": "@fuxishi/vitepress-theme",
4
4
  "private": false,
5
- "version": "2.0.0-alpha.4",
5
+ "version": "2.0.0-alpha.6",
6
6
  "license": "MIT",
7
7
  "description": "一款精美的 VitePress 主题",
8
8
  "keywords": [
9
9
  "vitepress",
10
- "theme",
11
10
  "vitepress-theme",
12
11
  "@fuxishi/vitepress-theme",
13
12
  "fuxishi"
14
13
  ],
15
14
  "type": "module",
16
15
  "files": [
17
- "dist"
16
+ "dist",
17
+ "LICENSE",
18
+ "README.md",
19
+ "README_EN.md",
20
+ "package.json"
18
21
  ],
19
22
  "main": "./dist/index.js",
20
23
  "module": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
21
25
  "exports": {
22
26
  ".": {
23
- "import": {
24
- "types": "./dist/index.d.ts",
25
- "default": "./dist/index.js"
26
- }
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
27
30
  },
28
31
  "./config": {
29
- "import": {
30
- "types": "./dist/config.d.ts",
31
- "default": "./dist/config.js"
32
- }
32
+ "types": "./dist/config.d.ts",
33
+ "import": "./dist/config.js",
34
+ "default": "./dist/config.js"
33
35
  },
34
36
  "./style.css": {
35
- "import": {
36
- "types": "./dist/style.css.d.ts",
37
- "default": "./dist/style.css"
38
- }
37
+ "types": "./dist/style.css.d.ts",
38
+ "import": "./dist/style.css",
39
+ "default": "./dist/style.css"
39
40
  }
40
41
  },
41
42
  "scripts": {