vitepress-plugin-code-collapse 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pengzhanbo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # vitepress-plugin-code-collapse
2
+
3
+ provide code block collapsed lines feature
4
+
5
+ 在 VitePress 中提供代码块折叠行功能。
6
+
7
+ ## Usage
8
+
9
+ ### With Vitepress-tuck
10
+
11
+ **Installation:**
12
+
13
+ ```bash
14
+ # npm
15
+ npm install -D vitepress-tuck vitepress-plugin-code-collapse
16
+ # pnpm
17
+ pnpm add -D vitepress-tuck vitepress-plugin-code-collapse
18
+ # yarn
19
+ yarn add -D vitepress-tuck vitepress-plugin-code-collapse
20
+ ```
21
+
22
+ **Configuration:**
23
+
24
+ ```ts
25
+ // .vitepress/config.ts
26
+ import codeCollapse from 'vitepress-plugin-code-collapse'
27
+ import { defineConfig } from 'vitepress-tuck'
28
+
29
+ export default defineConfig({
30
+ plugins: [codeCollapse()],
31
+ })
32
+ ```
33
+
34
+ ```ts
35
+ // .vitepress/theme/index.ts
36
+ import type { Theme } from 'vitepress'
37
+ import enhanceApp from 'virtual:enhance-app'
38
+ import DefaultTheme from 'vitepress/theme'
39
+
40
+ export default {
41
+ extends: DefaultTheme,
42
+ enhanceApp(ctx) {
43
+ enhanceApp(ctx)
44
+ },
45
+ } satisfies Theme
46
+ ```
47
+
48
+ ### With Vitepress
49
+
50
+ **Installation:**
51
+
52
+ ```bash
53
+ # npm
54
+ npm install -D vitepress-plugin-code-collapse
55
+ # pnpm
56
+ pnpm add -D vitepress-plugin-code-collapse
57
+ # yarn
58
+ yarn add -D vitepress-plugin-code-collapse
59
+ ```
60
+
61
+ **Configuration:**
62
+
63
+ ```ts
64
+ // .vitepress/config.ts
65
+ import { defineConfig } from 'vitepress'
66
+ import { collapsedLinesMarkdownPlugin } from 'vitepress-plugin-code-collapse'
67
+
68
+ export default defineConfig({
69
+ markdown: {
70
+ config: (md) => {
71
+ md.use(collapsedLinesMarkdownPlugin)
72
+ },
73
+ }
74
+ })
75
+ ```
76
+
77
+ ```ts
78
+ // .vitepress/theme/index.ts
79
+ import type { Theme } from 'vitepress'
80
+ import { enhanceAppWithCollapsedLines } from 'vitepress-plugin-code-collapse/client'
81
+ import DefaultTheme from 'vitepress/theme'
82
+
83
+ export default {
84
+ extends: DefaultTheme,
85
+ enhanceApp(ctx) {
86
+ enhanceAppWithCollapsedLines(ctx)
87
+ },
88
+ } satisfies Theme
89
+ ```
90
+
91
+ ## Syntax
92
+
93
+ use `:collapsed-lines` to collapse lines in code block.
94
+
95
+ ````md
96
+ ```ts :collapsed-lines
97
+ const a = 1
98
+ ```
99
+ ````
@@ -0,0 +1,31 @@
1
+ //#region src/client/setupCollapsedLines.d.ts
2
+ /**
3
+ * Setup collapsed lines functionality for code blocks
4
+ *
5
+ * 为代码块设置折叠行功能
6
+ *
7
+ * @example
8
+ * import { setupCollapsedLines } from '@vuepress/highlighter-helper/client'
9
+ *
10
+ * // Use default selector
11
+ * setupCollapsedLines()
12
+ *
13
+ * // Use custom selector
14
+ * setupCollapsedLines({
15
+ * selector: '.my-collapsed-lines',
16
+ * })
17
+ *
18
+ * @param options - Setup options / 设置选项
19
+ * @param options.selector - CSS selector for collapsed lines elements / 折叠行元素的
20
+ * CSS 选择器
21
+ */
22
+ declare function setupCollapsedLines({
23
+ selector
24
+ }?: {
25
+ selector?: string;
26
+ }): void;
27
+ //#endregion
28
+ //#region src/client/index.d.ts
29
+ declare function enhanceAppWithCollapsedLines(): void;
30
+ //#endregion
31
+ export { enhanceAppWithCollapsedLines, setupCollapsedLines };
@@ -0,0 +1,42 @@
1
+ import "../style.css";
2
+ import { useEventListener } from "@vueuse/core";
3
+ //#region src/client/setupCollapsedLines.ts
4
+ /**
5
+ * Setup collapsed lines functionality for code blocks
6
+ *
7
+ * 为代码块设置折叠行功能
8
+ *
9
+ * @example
10
+ * import { setupCollapsedLines } from '@vuepress/highlighter-helper/client'
11
+ *
12
+ * // Use default selector
13
+ * setupCollapsedLines()
14
+ *
15
+ * // Use custom selector
16
+ * setupCollapsedLines({
17
+ * selector: '.my-collapsed-lines',
18
+ * })
19
+ *
20
+ * @param options - Setup options / 设置选项
21
+ * @param options.selector - CSS selector for collapsed lines elements / 折叠行元素的
22
+ * CSS 选择器
23
+ */
24
+ function setupCollapsedLines({ selector = "div[class*=\"language-\"].has-collapsed-lines > .collapsed-lines" } = {}) {
25
+ useEventListener("click", (event) => {
26
+ const target = event.target;
27
+ if (target.matches(selector)) {
28
+ const parent = target.parentElement;
29
+ if (parent?.classList.toggle("collapsed")) parent.scrollIntoView({
30
+ block: "center",
31
+ behavior: "instant"
32
+ });
33
+ }
34
+ }, { passive: true });
35
+ }
36
+ //#endregion
37
+ //#region src/client/index.ts
38
+ function enhanceAppWithCollapsedLines() {
39
+ setupCollapsedLines();
40
+ }
41
+ //#endregion
42
+ export { enhanceAppWithCollapsedLines, setupCollapsedLines };
@@ -0,0 +1,31 @@
1
+ //#region src/client/setupCollapsedLines.d.ts
2
+ /**
3
+ * Setup collapsed lines functionality for code blocks
4
+ *
5
+ * 为代码块设置折叠行功能
6
+ *
7
+ * @example
8
+ * import { setupCollapsedLines } from '@vuepress/highlighter-helper/client'
9
+ *
10
+ * // Use default selector
11
+ * setupCollapsedLines()
12
+ *
13
+ * // Use custom selector
14
+ * setupCollapsedLines({
15
+ * selector: '.my-collapsed-lines',
16
+ * })
17
+ *
18
+ * @param options - Setup options / 设置选项
19
+ * @param options.selector - CSS selector for collapsed lines elements / 折叠行元素的
20
+ * CSS 选择器
21
+ */
22
+ declare function setupCollapsedLines({
23
+ selector
24
+ }?: {
25
+ selector?: string;
26
+ }): void;
27
+ //#endregion
28
+ //#region src/client/index.d.ts
29
+ declare function enhanceAppWithCollapsedLines(): void;
30
+ //#endregion
31
+ export { enhanceAppWithCollapsedLines, setupCollapsedLines };
@@ -0,0 +1,41 @@
1
+ import { useEventListener } from "@vueuse/core";
2
+ //#region src/client/setupCollapsedLines.ts
3
+ /**
4
+ * Setup collapsed lines functionality for code blocks
5
+ *
6
+ * 为代码块设置折叠行功能
7
+ *
8
+ * @example
9
+ * import { setupCollapsedLines } from '@vuepress/highlighter-helper/client'
10
+ *
11
+ * // Use default selector
12
+ * setupCollapsedLines()
13
+ *
14
+ * // Use custom selector
15
+ * setupCollapsedLines({
16
+ * selector: '.my-collapsed-lines',
17
+ * })
18
+ *
19
+ * @param options - Setup options / 设置选项
20
+ * @param options.selector - CSS selector for collapsed lines elements / 折叠行元素的
21
+ * CSS 选择器
22
+ */
23
+ function setupCollapsedLines({ selector = "div[class*=\"language-\"].has-collapsed-lines > .collapsed-lines" } = {}) {
24
+ useEventListener("click", (event) => {
25
+ const target = event.target;
26
+ if (target.matches(selector)) {
27
+ const parent = target.parentElement;
28
+ if (parent?.classList.toggle("collapsed")) parent.scrollIntoView({
29
+ block: "center",
30
+ behavior: "instant"
31
+ });
32
+ }
33
+ }, { passive: true });
34
+ }
35
+ //#endregion
36
+ //#region src/client/index.ts
37
+ function enhanceAppWithCollapsedLines() {
38
+ setupCollapsedLines();
39
+ }
40
+ //#endregion
41
+ export { enhanceAppWithCollapsedLines, setupCollapsedLines };
@@ -0,0 +1,75 @@
1
+ /* stylelint-disable no-descending-specificity */
2
+ :root {
3
+ --vp-collapsed-lines-icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='m18 12l-6 6l-6-6m12-6l-6 6l-6-6'/%3E%3C/svg%3E");
4
+ --vp-collapsed-lines-rotate: 0deg;
5
+ }
6
+
7
+ @property --vp-collapsed-lines-bg {
8
+ inherits: false;
9
+ initial-value: #fff;
10
+ syntax: "<color>";
11
+ }
12
+
13
+ @keyframes code-collapsed-lines {
14
+ 0% {
15
+ opacity: 0.3;
16
+ transform: translateY(-2px) rotate(var(--vp-collapsed-lines-rotate));
17
+ }
18
+
19
+ 100% {
20
+ opacity: 1;
21
+ transform: translateY(2px) rotate(var(--vp-collapsed-lines-rotate));
22
+ }
23
+ }
24
+
25
+ div[class*="language-"].has-collapsed-lines .collapsed-lines {
26
+ --vp-collapsed-lines-bg: var(--vp-code-block-bg);
27
+
28
+ position: absolute;
29
+ right: 0;
30
+ bottom: 0;
31
+ left: 0;
32
+ z-index: 4;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ height: 28px;
37
+ cursor: pointer;
38
+ background: linear-gradient(to bottom, transparent 0%, var(--vp-collapsed-lines-bg) 55%, var(--vp-collapsed-lines-bg) 100%);
39
+ transition: --vp-collapsed-lines-bg 0.2s ease-in-out;
40
+ }
41
+
42
+ div[class*="language-"].has-collapsed-lines .collapsed-lines:hover {
43
+ --vp-collapsed-lines-bg: var(--vp-c-default-soft);
44
+ }
45
+
46
+ div[class*="language-"].has-collapsed-lines .collapsed-lines::before {
47
+ display: inline-block;
48
+ width: 24px;
49
+ height: 24px;
50
+ pointer-events: none;
51
+ content: "";
52
+ background-color: var(--vp-code-block-color);
53
+ mask-image: var(--vp-collapsed-lines-icon);
54
+ mask-repeat: no-repeat;
55
+ mask-position: 50%;
56
+ mask-size: 20px;
57
+ animation: 1.2s ease-in-out infinite alternate-reverse code-collapsed-lines;
58
+ }
59
+
60
+ div[class*="language-"].has-collapsed-lines[data-highlighter="shiki"] .collapsed-lines {
61
+ --vp-collapsed-lines-bg: var(--vp-code-block-bg, var(--shiki-light-bg));
62
+ }
63
+
64
+ [data-theme="dark"] div[class*="language-"].has-collapsed-lines[data-highlighter="shiki"] .collapsed-lines {
65
+ --vp-collapsed-lines-bg: var(--vp-code-block-bg, var(--shiki-dark-bg));
66
+ }
67
+
68
+ div[class*="language-"].has-collapsed-lines.collapsed {
69
+ height: calc(var(--vp-collapsed-lines) * var(--vp-code-line-height) * var(--vp-code-font-size) + 48px);
70
+ overflow-y: hidden;
71
+ }
72
+
73
+ div[class*="language-"].has-collapsed-lines:not(.collapsed) .collapsed-lines {
74
+ --vp-collapsed-lines-rotate: 180deg;
75
+ }
@@ -0,0 +1,62 @@
1
+ import { PluginWithOptions } from "markdown-it";
2
+
3
+ //#region src/node/types.d.ts
4
+ /**
5
+ * Whether to collapse code blocks when they exceed a certain number of lines
6
+ *
7
+ * - If `number`, collapse starts from line `number`.
8
+ * - If `true`, collapse starts from line 15 by default.
9
+ * - If `false`, do not enable code block collapsing globally, but you can
10
+ * enable it for individual code blocks using `:collapsed-lines`
11
+ * - If `'disable'`, Completely disable code block collapsing
12
+ *
13
+ * 当代码块超过一定行数时是否折叠
14
+ *
15
+ * - 如果是 `number`,从第 `number` 行开始折叠
16
+ * - 如果是 `true`,默认从第 15 行开始折叠
17
+ * - 如果是 `false`,不全局启用代码块折叠,但你可以为单个代码块使用 `:collapsed-lines` 启用
18
+ * - 如果是 `'disable'`,完全禁用代码块折叠
19
+ *
20
+ * @default false
21
+ */
22
+ type CollapsedLinesOptions = boolean | number | 'disable';
23
+ //#endregion
24
+ //#region src/node/codeCollapsePlugin.d.ts
25
+ /**
26
+ * Add collapsed lines functionality to code blocks in markdown-it
27
+ *
28
+ * 为 markdown-it 中的代码块添加折叠行功能
29
+ *
30
+ * @param md - MarkdownIt instance / MarkdownIt 实例
31
+ * @param options - Plugin options / 插件选项
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { collapsedLinesMarkdownPlugin } from 'vitepress-code-collapse'
36
+ * import { defineConfig } from 'vitepress'
37
+ * export default defineConfig({
38
+ * markdown: {
39
+ * config(md) {
40
+ * md.use(collapsedLinesMarkdownPlugin)
41
+ * }
42
+ * }
43
+ * })
44
+ * ```
45
+ */
46
+ declare const collapsedLinesMarkdownPlugin: PluginWithOptions<CollapsedLinesOptions>;
47
+ //#endregion
48
+ //#region src/node/index.d.ts
49
+ /**
50
+ * @example
51
+ * ```ts
52
+ * import collapsedLines from 'vitepress-code-collapse'
53
+ * import { defineConfig } from 'vitepress-tuck'
54
+ * export default defineConfig({
55
+ * plugins: [collapsedLines()]
56
+ * })
57
+ * ```
58
+ * ```
59
+ */
60
+ declare const _default: (option?: number | boolean | "disable" | undefined) => import("vitepress-tuck").VitepressPlugin;
61
+ //#endregion
62
+ export { collapsedLinesMarkdownPlugin, _default as default };
@@ -0,0 +1,116 @@
1
+ import { definePlugin } from "vitepress-tuck";
2
+ //#region src/node/resolveCollapsedLine.ts
3
+ /**
4
+ * Regular expression to match `:collapsed-lines` directive in code block info
5
+ *
6
+ * 匹配代码块信息中 `:collapsed-lines` 指令的正则表达式
7
+ */
8
+ const COLLAPSED_LINES_REGEXP = /:collapsed-lines\b/u;
9
+ /**
10
+ * Regular expression to match `:collapsed-lines=num` directive in code block
11
+ * info
12
+ *
13
+ * 匹配代码块信息中 `:collapsed-lines=num` 指令的正则表达式
14
+ */
15
+ const COLLAPSED_LINES_START_REGEXP = /:collapsed-lines=(\d+)\b/u;
16
+ /**
17
+ * Regular expression to match `:no-collapsed-lines` directive in code block
18
+ * info
19
+ *
20
+ * 匹配代码块信息中 `:no-collapsed-lines` 指令的正则表达式
21
+ */
22
+ const NO_COLLAPSED_LINES_REGEXP = /:no-collapsed-lines\b/u;
23
+ /**
24
+ * Resolve the `:collapsed-lines` `:collapsed-lines=num` / `:no-collapsed-lines`
25
+ * mark from token info
26
+ *
27
+ * 从 token 信息中解析 `:collapsed-lines` `:collapsed-lines=num` /
28
+ * `:no-collapsed-lines` 标记
29
+ *
30
+ * @example
31
+ * resolveCollapsedLines('js :collapsed-lines') // true
32
+ * resolveCollapsedLines('js :collapsed-lines=20') // 20
33
+ * resolveCollapsedLines('js :no-collapsed-lines') // false
34
+ * resolveCollapsedLines('js') // null
35
+ *
36
+ * @param info - Code block info string / 代码块信息字符串
37
+ * @returns Collapsed lines configuration / 折叠行配置
38
+ *
39
+ * - `number` - Start line number for collapsing / 折叠起始行号
40
+ * - `true` - Enable collapsed lines / 启用折叠行
41
+ * - `false` - Disable collapsed lines / 禁用折叠行
42
+ * - `null` - No configuration found / 未找到配置
43
+ */
44
+ function resolveCollapsedLines(info) {
45
+ const lines = COLLAPSED_LINES_START_REGEXP.exec(info)?.[1];
46
+ if (lines) return Number(lines);
47
+ if (COLLAPSED_LINES_REGEXP.test(info)) return true;
48
+ if (NO_COLLAPSED_LINES_REGEXP.test(info)) return false;
49
+ return null;
50
+ }
51
+ //#endregion
52
+ //#region src/node/codeCollapsePlugin.ts
53
+ /**
54
+ * Add collapsed lines functionality to code blocks in markdown-it
55
+ *
56
+ * 为 markdown-it 中的代码块添加折叠行功能
57
+ *
58
+ * @param md - MarkdownIt instance / MarkdownIt 实例
59
+ * @param options - Plugin options / 插件选项
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * import { collapsedLinesMarkdownPlugin } from 'vitepress-code-collapse'
64
+ * import { defineConfig } from 'vitepress'
65
+ * export default defineConfig({
66
+ * markdown: {
67
+ * config(md) {
68
+ * md.use(collapsedLinesMarkdownPlugin)
69
+ * }
70
+ * }
71
+ * })
72
+ * ```
73
+ */
74
+ const collapsedLinesMarkdownPlugin = (md, options = false) => {
75
+ if (options === "disable") return;
76
+ const rawFence = md.renderer.rules.fence;
77
+ md.renderer.rules.fence = (...args) => {
78
+ const [tokens, index] = args;
79
+ const token = tokens[index];
80
+ const info = token.info ? md.utils.unescapeAll(token.info).trim() : "";
81
+ const code = rawFence(...args);
82
+ const collapsedLinesInfo = resolveCollapsedLines(info) ?? options;
83
+ if (collapsedLinesInfo === false) return code;
84
+ const lines = code.slice(code.indexOf("<code>"), code.indexOf("</code>")).split("\n").length;
85
+ const startLines = typeof collapsedLinesInfo === "number" ? collapsedLinesInfo : 15;
86
+ if (lines < startLines) return code;
87
+ const collapsedLinesCode = `<div class="collapsed-lines"></div>`;
88
+ const styles = `--vp-collapsed-lines:${startLines};`;
89
+ return code.replace(/<\/div>$/u, `${collapsedLinesCode}</div>`).replace(/"(language-[^"]*)"/u, "\"$1 has-collapsed-lines collapsed\"").replace(/^<div[^>]*>/u, (match) => {
90
+ if (!match.includes("style=")) return `${match.slice(0, -1)} style="${styles}">`;
91
+ return match.replace(/(style=")/u, `$1${styles}`);
92
+ });
93
+ };
94
+ };
95
+ //#endregion
96
+ //#region src/node/index.ts
97
+ /**
98
+ * @example
99
+ * ```ts
100
+ * import collapsedLines from 'vitepress-code-collapse'
101
+ * import { defineConfig } from 'vitepress-tuck'
102
+ * export default defineConfig({
103
+ * plugins: [collapsedLines()]
104
+ * })
105
+ * ```
106
+ * ```
107
+ */
108
+ var node_default = definePlugin((options) => ({
109
+ name: "vitepress-plugin-code-collapse",
110
+ client: { enhance: "enhanceAppWithCollapsedLines" },
111
+ markdown: { config: (md) => {
112
+ md.use(collapsedLinesMarkdownPlugin, options);
113
+ } }
114
+ }));
115
+ //#endregion
116
+ export { collapsedLinesMarkdownPlugin, node_default as default };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "vitepress-plugin-code-collapse",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "description": "provide code block collapsed lines feature",
6
+ "author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "vitepress",
10
+ "vitepress-plugin",
11
+ "code-collapse",
12
+ "collapsed-lines"
13
+ ],
14
+ "exports": {
15
+ ".": "./dist/node/index.js",
16
+ "./client": {
17
+ "browser": "./dist/client/browser/index.js",
18
+ "default": "./dist/client/ssr/index.js"
19
+ },
20
+ "./style.css": "./dist/client/style.css"
21
+ },
22
+ "module": "./dist/node/index.js",
23
+ "types": "./dist/node/index.d.ts",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "peerDependencies": {
28
+ "vitepress": "^1.6.4 || ^2.0.0-alpha.17",
29
+ "vue": "^3.5.0"
30
+ },
31
+ "dependencies": {
32
+ "@vueuse/core": "^14.3.0",
33
+ "vitepress-plugin-toolkit": "0.1.0",
34
+ "vitepress-tuck": "0.1.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "clean": "rimraf --glob ./dist",
41
+ "dev": "pnpm '/(tsdown|copy):watch/'",
42
+ "build": "pnpm tsdown && pnpm copy",
43
+ "copy": "cpx \"src/**/*.css\" dist",
44
+ "copy:watch": "pnpm copy -w",
45
+ "tsdown": "tsdown --config-loader unrun",
46
+ "tsdown:watch": "pnpm tsdown -w"
47
+ }
48
+ }