vitepress-plugin-file-tree 0.2.0 → 0.4.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/dist/client/browser/index.d.ts +41 -11
- package/dist/client/browser/index.js +66 -16
- package/dist/client/ssr/index.d.ts +41 -11
- package/dist/client/ssr/index.js +63 -12
- package/dist/client/style.css +6 -4
- package/dist/node/index.d.ts +171 -36
- package/dist/node/index.js +171 -39
- package/package.json +7 -3
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { InjectionKey, Ref } from "vue";
|
|
1
2
|
import { EnhanceAppContext } from "vitepress/client";
|
|
2
3
|
|
|
3
4
|
//#region src/client/VPFileTree.vue.d.ts
|
|
4
5
|
type __VLS_Props$1 = {
|
|
5
|
-
title?: string;
|
|
6
|
-
text
|
|
6
|
+
/** Optional title displayed above the file tree. / 显示在文件树上方的可选标题。 */title?: string; /** URL-encoded plain-text representation of the file tree for the copy button. / 文件树的 URL 编码纯文本,供复制按钮使用。 */
|
|
7
|
+
text?: string;
|
|
7
8
|
};
|
|
8
|
-
declare var
|
|
9
|
+
declare var __VLS_1$1: {}, __VLS_8: {};
|
|
9
10
|
type __VLS_Slots$1 = {} & {
|
|
10
|
-
|
|
11
|
+
title?: (props: typeof __VLS_1$1) => any;
|
|
12
|
+
} & {
|
|
13
|
+
default?: (props: typeof __VLS_8) => any;
|
|
11
14
|
};
|
|
12
15
|
declare const __VLS_base$1: import("vue").DefineComponent<__VLS_Props$1, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props$1> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
16
|
declare const __VLS_export$1: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
|
|
@@ -20,12 +23,12 @@ type __VLS_WithSlots$1<T, S> = T & {
|
|
|
20
23
|
//#endregion
|
|
21
24
|
//#region src/client/VPFileTreeNode.vue.d.ts
|
|
22
25
|
type __VLS_Props = {
|
|
23
|
-
type: 'file' | 'folder';
|
|
24
|
-
filename: string;
|
|
25
|
-
level: number;
|
|
26
|
-
diff?: 'add' | 'remove';
|
|
27
|
-
expanded?: boolean;
|
|
28
|
-
focus?: boolean;
|
|
26
|
+
/** Node type, either a folder or a file. / 节点类型,文件夹或文件。 */type: 'file' | 'folder'; /** Display name of the file or folder. / 文件或文件夹的显示名称。 */
|
|
27
|
+
filename: string; /** Indentation depth level, used for visual offset. / 缩进层级,用于视觉偏移。 */
|
|
28
|
+
level: number; /** Diff marker indicating the node was added or removed. / 差异标记,表示节点为新增或删除。 */
|
|
29
|
+
diff?: 'add' | 'remove'; /** Whether a folder node is expanded by default. / 文件夹节点是否默认展开。 */
|
|
30
|
+
expanded?: boolean; /** Whether this node is visually highlighted as focused. / 是否作为聚焦节点高亮显示。 */
|
|
31
|
+
focus?: boolean; /** Full file path used for active state matching. / 用于匹配激活状态的完整文件路径。 */
|
|
29
32
|
filepath?: string;
|
|
30
33
|
};
|
|
31
34
|
declare var __VLS_1: {}, __VLS_3: {};
|
|
@@ -43,9 +46,36 @@ type __VLS_WithSlots<T, S> = T & {
|
|
|
43
46
|
};
|
|
44
47
|
};
|
|
45
48
|
//#endregion
|
|
49
|
+
//#region src/client/constants.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* File tree node click event
|
|
52
|
+
*
|
|
53
|
+
* 文件树节点点击事件
|
|
54
|
+
*/
|
|
55
|
+
declare const ON_NODE_CLICK: InjectionKey<(filename: string, type: 'folder' | 'file') => void>;
|
|
56
|
+
/**
|
|
57
|
+
* Active file tree node
|
|
58
|
+
*
|
|
59
|
+
* 当前激活的文件树节点
|
|
60
|
+
*/
|
|
61
|
+
declare const ACTIVE_NODE_KEY: InjectionKey<Ref<string>>;
|
|
62
|
+
//#endregion
|
|
46
63
|
//#region src/client/index.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Register file tree components globally during VitePress app enhancement.
|
|
66
|
+
*
|
|
67
|
+
* 在 VitePress 应用增强阶段全局注册文件树组件。
|
|
68
|
+
*
|
|
69
|
+
* Called automatically by the `virtual:enhance-app` module when the plugin
|
|
70
|
+
* is loaded via `vitepress-tuck`'s `defineConfig`. Registers `VPFileTree`
|
|
71
|
+
* and `VPFileTreeNode` as global Vue components so they can be used in
|
|
72
|
+
* markdown-rendered HTML without explicit imports.
|
|
73
|
+
*
|
|
74
|
+
* @param ctx - VitePress enhance app context / VitePress 应用增强上下文
|
|
75
|
+
* @param ctx.app - The VitePress Vue app instance / VitePress 的 Vue 应用实例
|
|
76
|
+
*/
|
|
47
77
|
declare function enhanceAppWithFileTree({
|
|
48
78
|
app
|
|
49
79
|
}: EnhanceAppContext): void;
|
|
50
80
|
//#endregion
|
|
51
|
-
export { _default as VPFileTree, _default$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
81
|
+
export { ACTIVE_NODE_KEY, ON_NODE_CLICK, _default as VPFileTree, _default$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
@@ -1,30 +1,47 @@
|
|
|
1
1
|
import "../style.css";
|
|
2
|
-
import { computed, createCommentVNode, createElementBlock, createElementVNode,
|
|
2
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, inject, mergeProps, normalizeClass, openBlock, ref, renderSlot, toDisplayString, unref, vShow, withDirectives } from "vue";
|
|
3
3
|
import { VPCopyButton } from "vitepress-plugin-toolkit/client";
|
|
4
4
|
//#region src/client/VPFileTree.vue
|
|
5
|
-
const _hoisted_1$1 = {
|
|
6
|
-
const _hoisted_2$1 = {
|
|
5
|
+
const _hoisted_1$1 = {
|
|
7
6
|
key: 0,
|
|
8
7
|
class: "vp-file-tree-title"
|
|
9
8
|
};
|
|
10
|
-
const
|
|
9
|
+
const _hoisted_2$1 = { class: "file-tree-content" };
|
|
10
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
11
11
|
__name: "VPFileTree",
|
|
12
12
|
props: {
|
|
13
13
|
title: {},
|
|
14
14
|
text: {}
|
|
15
15
|
},
|
|
16
16
|
setup(__props) {
|
|
17
|
-
const content = computed(() => decodeURIComponent(__props.text));
|
|
17
|
+
const content = computed(() => __props.text ? decodeURIComponent(__props.text) : "");
|
|
18
18
|
return (_ctx, _cache) => {
|
|
19
|
-
return openBlock(), createElementBlock("div",
|
|
20
|
-
__props.title ? (openBlock(), createElementBlock("p",
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["vp-file-tree", { "has-copy": __props.text }]) }, [
|
|
20
|
+
renderSlot(_ctx.$slots, "title", {}, () => [__props.title ? (openBlock(), createElementBlock("p", _hoisted_1$1, toDisplayString(__props.title), 1)) : createCommentVNode("v-if", true)]),
|
|
21
|
+
__props.text ? (openBlock(), createBlock(unref(VPCopyButton), {
|
|
22
|
+
key: 0,
|
|
23
|
+
text: content.value
|
|
24
|
+
}, null, 8, ["text"])) : createCommentVNode("v-if", true),
|
|
25
|
+
createElementVNode("div", _hoisted_2$1, [renderSlot(_ctx.$slots, "default")])
|
|
26
|
+
], 2);
|
|
24
27
|
};
|
|
25
28
|
}
|
|
26
29
|
});
|
|
27
30
|
//#endregion
|
|
31
|
+
//#region src/client/constants.ts
|
|
32
|
+
/**
|
|
33
|
+
* File tree node click event
|
|
34
|
+
*
|
|
35
|
+
* 文件树节点点击事件
|
|
36
|
+
*/
|
|
37
|
+
const ON_NODE_CLICK = Symbol(import.meta.env.DEV ? "on-file-tree-node-click" : "");
|
|
38
|
+
/**
|
|
39
|
+
* Active file tree node
|
|
40
|
+
*
|
|
41
|
+
* 当前激活的文件树节点
|
|
42
|
+
*/
|
|
43
|
+
const ACTIVE_NODE_KEY = Symbol(import.meta.env.DEV ? "active-file-tree-node" : "");
|
|
44
|
+
//#endregion
|
|
28
45
|
//#region src/client/VPFileTreeNode.vue
|
|
29
46
|
const _hoisted_1 = { class: "vp-file-tree-node" };
|
|
30
47
|
const _hoisted_2 = ["data-filename"];
|
|
@@ -36,7 +53,7 @@ const _hoisted_4 = {
|
|
|
36
53
|
key: 0,
|
|
37
54
|
class: "group"
|
|
38
55
|
};
|
|
39
|
-
const _sfc_main$1 =
|
|
56
|
+
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
40
57
|
__name: "VPFileTreeNode",
|
|
41
58
|
props: {
|
|
42
59
|
type: {},
|
|
@@ -48,13 +65,33 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
|
48
65
|
filepath: {}
|
|
49
66
|
},
|
|
50
67
|
setup(__props) {
|
|
51
|
-
const activeFileTreeNode = inject(
|
|
52
|
-
const onNodeClick = inject(
|
|
68
|
+
const activeFileTreeNode = inject(ACTIVE_NODE_KEY, ref(""));
|
|
69
|
+
const onNodeClick = inject(ON_NODE_CLICK, () => {});
|
|
53
70
|
const active = ref(__props.expanded);
|
|
71
|
+
/**
|
|
72
|
+
* Notify the parent of a node click event via the injected callback.
|
|
73
|
+
*
|
|
74
|
+
* 通过注入的回调通知父组件节点点击事件。
|
|
75
|
+
*
|
|
76
|
+
* Ellipsis placeholders (`…` or `...`) are ignored to avoid triggering
|
|
77
|
+
* selection on empty folder markers.
|
|
78
|
+
*/
|
|
54
79
|
function nodeClick() {
|
|
55
80
|
if (__props.filename === "…" || __props.filename === "...") return;
|
|
56
81
|
onNodeClick(__props.filepath || __props.filename, __props.type);
|
|
57
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Handle click events on the node, toggling folder expansion or firing
|
|
85
|
+
* the node click callback for files.
|
|
86
|
+
*
|
|
87
|
+
* 处理节点的点击事件,切换文件夹展开状态或触发文件节点的点击回调。
|
|
88
|
+
*
|
|
89
|
+
* For folders, clicks on the comment area are ignored so that inline
|
|
90
|
+
* comments remain interactive. For files, the click callback fires
|
|
91
|
+
* directly.
|
|
92
|
+
*
|
|
93
|
+
* @param ev - Native mouse event / 原生鼠标事件
|
|
94
|
+
*/
|
|
58
95
|
function toggle(ev) {
|
|
59
96
|
if (__props.type === "folder") {
|
|
60
97
|
if (!ev.target.matches(".comment, .comment *")) {
|
|
@@ -92,9 +129,22 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
|
92
129
|
});
|
|
93
130
|
//#endregion
|
|
94
131
|
//#region src/client/index.ts
|
|
132
|
+
/**
|
|
133
|
+
* Register file tree components globally during VitePress app enhancement.
|
|
134
|
+
*
|
|
135
|
+
* 在 VitePress 应用增强阶段全局注册文件树组件。
|
|
136
|
+
*
|
|
137
|
+
* Called automatically by the `virtual:enhance-app` module when the plugin
|
|
138
|
+
* is loaded via `vitepress-tuck`'s `defineConfig`. Registers `VPFileTree`
|
|
139
|
+
* and `VPFileTreeNode` as global Vue components so they can be used in
|
|
140
|
+
* markdown-rendered HTML without explicit imports.
|
|
141
|
+
*
|
|
142
|
+
* @param ctx - VitePress enhance app context / VitePress 应用增强上下文
|
|
143
|
+
* @param ctx.app - The VitePress Vue app instance / VitePress 的 Vue 应用实例
|
|
144
|
+
*/
|
|
95
145
|
function enhanceAppWithFileTree({ app }) {
|
|
96
|
-
app.component("VPFileTree", _sfc_main);
|
|
97
|
-
app.component("VPFileTreeNode", _sfc_main$1);
|
|
146
|
+
if (!app._context.components.VPFileTree) app.component("VPFileTree", _sfc_main);
|
|
147
|
+
if (!app._context.components.VPFileTreeNode) app.component("VPFileTreeNode", _sfc_main$1);
|
|
98
148
|
}
|
|
99
149
|
//#endregion
|
|
100
|
-
export { _sfc_main as VPFileTree, _sfc_main$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
150
|
+
export { ACTIVE_NODE_KEY, ON_NODE_CLICK, _sfc_main as VPFileTree, _sfc_main$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { InjectionKey, Ref } from "vue";
|
|
1
2
|
import { EnhanceAppContext } from "vitepress/client";
|
|
2
3
|
|
|
3
4
|
//#region src/client/VPFileTree.vue.d.ts
|
|
4
5
|
type __VLS_Props$1 = {
|
|
5
|
-
title?: string;
|
|
6
|
-
text
|
|
6
|
+
/** Optional title displayed above the file tree. / 显示在文件树上方的可选标题。 */title?: string; /** URL-encoded plain-text representation of the file tree for the copy button. / 文件树的 URL 编码纯文本,供复制按钮使用。 */
|
|
7
|
+
text?: string;
|
|
7
8
|
};
|
|
8
|
-
declare var
|
|
9
|
+
declare var __VLS_1$1: {}, __VLS_8: {};
|
|
9
10
|
type __VLS_Slots$1 = {} & {
|
|
10
|
-
|
|
11
|
+
title?: (props: typeof __VLS_1$1) => any;
|
|
12
|
+
} & {
|
|
13
|
+
default?: (props: typeof __VLS_8) => any;
|
|
11
14
|
};
|
|
12
15
|
declare const __VLS_base$1: import("vue").DefineComponent<__VLS_Props$1, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props$1> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
16
|
declare const __VLS_export$1: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
|
|
@@ -20,12 +23,12 @@ type __VLS_WithSlots$1<T, S> = T & {
|
|
|
20
23
|
//#endregion
|
|
21
24
|
//#region src/client/VPFileTreeNode.vue.d.ts
|
|
22
25
|
type __VLS_Props = {
|
|
23
|
-
type: 'file' | 'folder';
|
|
24
|
-
filename: string;
|
|
25
|
-
level: number;
|
|
26
|
-
diff?: 'add' | 'remove';
|
|
27
|
-
expanded?: boolean;
|
|
28
|
-
focus?: boolean;
|
|
26
|
+
/** Node type, either a folder or a file. / 节点类型,文件夹或文件。 */type: 'file' | 'folder'; /** Display name of the file or folder. / 文件或文件夹的显示名称。 */
|
|
27
|
+
filename: string; /** Indentation depth level, used for visual offset. / 缩进层级,用于视觉偏移。 */
|
|
28
|
+
level: number; /** Diff marker indicating the node was added or removed. / 差异标记,表示节点为新增或删除。 */
|
|
29
|
+
diff?: 'add' | 'remove'; /** Whether a folder node is expanded by default. / 文件夹节点是否默认展开。 */
|
|
30
|
+
expanded?: boolean; /** Whether this node is visually highlighted as focused. / 是否作为聚焦节点高亮显示。 */
|
|
31
|
+
focus?: boolean; /** Full file path used for active state matching. / 用于匹配激活状态的完整文件路径。 */
|
|
29
32
|
filepath?: string;
|
|
30
33
|
};
|
|
31
34
|
declare var __VLS_1: {}, __VLS_3: {};
|
|
@@ -43,9 +46,36 @@ type __VLS_WithSlots<T, S> = T & {
|
|
|
43
46
|
};
|
|
44
47
|
};
|
|
45
48
|
//#endregion
|
|
49
|
+
//#region src/client/constants.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* File tree node click event
|
|
52
|
+
*
|
|
53
|
+
* 文件树节点点击事件
|
|
54
|
+
*/
|
|
55
|
+
declare const ON_NODE_CLICK: InjectionKey<(filename: string, type: 'folder' | 'file') => void>;
|
|
56
|
+
/**
|
|
57
|
+
* Active file tree node
|
|
58
|
+
*
|
|
59
|
+
* 当前激活的文件树节点
|
|
60
|
+
*/
|
|
61
|
+
declare const ACTIVE_NODE_KEY: InjectionKey<Ref<string>>;
|
|
62
|
+
//#endregion
|
|
46
63
|
//#region src/client/index.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Register file tree components globally during VitePress app enhancement.
|
|
66
|
+
*
|
|
67
|
+
* 在 VitePress 应用增强阶段全局注册文件树组件。
|
|
68
|
+
*
|
|
69
|
+
* Called automatically by the `virtual:enhance-app` module when the plugin
|
|
70
|
+
* is loaded via `vitepress-tuck`'s `defineConfig`. Registers `VPFileTree`
|
|
71
|
+
* and `VPFileTreeNode` as global Vue components so they can be used in
|
|
72
|
+
* markdown-rendered HTML without explicit imports.
|
|
73
|
+
*
|
|
74
|
+
* @param ctx - VitePress enhance app context / VitePress 应用增强上下文
|
|
75
|
+
* @param ctx.app - The VitePress Vue app instance / VitePress 的 Vue 应用实例
|
|
76
|
+
*/
|
|
47
77
|
declare function enhanceAppWithFileTree({
|
|
48
78
|
app
|
|
49
79
|
}: EnhanceAppContext): void;
|
|
50
80
|
//#endregion
|
|
51
|
-
export { _default as VPFileTree, _default$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
81
|
+
export { ACTIVE_NODE_KEY, ON_NODE_CLICK, _default as VPFileTree, _default$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
package/dist/client/ssr/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { computed, defineComponent, inject, mergeProps, ref, unref, useSSRContex
|
|
|
2
2
|
import { ssrInterpolate, ssrRenderAttr, ssrRenderAttrs, ssrRenderClass, ssrRenderComponent, ssrRenderSlot, ssrRenderStyle } from "vue/server-renderer";
|
|
3
3
|
import { VPCopyButton } from "vitepress-plugin-toolkit/client";
|
|
4
4
|
//#region src/client/VPFileTree.vue
|
|
5
|
-
const _sfc_main =
|
|
5
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
6
6
|
__name: "VPFileTree",
|
|
7
7
|
__ssrInlineRender: true,
|
|
8
8
|
props: {
|
|
@@ -10,14 +10,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
10
10
|
text: {}
|
|
11
11
|
},
|
|
12
12
|
setup(__props) {
|
|
13
|
-
const content = computed(() => decodeURIComponent(__props.text));
|
|
13
|
+
const content = computed(() => __props.text ? decodeURIComponent(__props.text) : "");
|
|
14
14
|
return (_ctx, _push, _parent, _attrs) => {
|
|
15
|
-
_push(`<div${ssrRenderAttrs(mergeProps({ class: "vp-file-tree has-copy" }, _attrs))}>`);
|
|
16
|
-
|
|
15
|
+
_push(`<div${ssrRenderAttrs(mergeProps({ class: ["vp-file-tree", { "has-copy": __props.text }] }, _attrs))}>`);
|
|
16
|
+
ssrRenderSlot(_ctx.$slots, "title", {}, () => {
|
|
17
|
+
if (__props.title) _push(`<p class="vp-file-tree-title">${ssrInterpolate(__props.title)}</p>`);
|
|
18
|
+
else _push(`<!---->`);
|
|
19
|
+
}, _push, _parent);
|
|
20
|
+
if (__props.text) _push(ssrRenderComponent(unref(VPCopyButton), { text: content.value }, null, _parent));
|
|
17
21
|
else _push(`<!---->`);
|
|
18
|
-
_push(
|
|
22
|
+
_push(`<div class="file-tree-content">`);
|
|
19
23
|
ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent);
|
|
20
|
-
_push(`</div>`);
|
|
24
|
+
_push(`</div></div>`);
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
27
|
});
|
|
@@ -28,8 +32,22 @@ _sfc_main.setup = (props, ctx) => {
|
|
|
28
32
|
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
|
|
29
33
|
};
|
|
30
34
|
//#endregion
|
|
35
|
+
//#region src/client/constants.ts
|
|
36
|
+
/**
|
|
37
|
+
* File tree node click event
|
|
38
|
+
*
|
|
39
|
+
* 文件树节点点击事件
|
|
40
|
+
*/
|
|
41
|
+
const ON_NODE_CLICK = Symbol(import.meta.env.DEV ? "on-file-tree-node-click" : "");
|
|
42
|
+
/**
|
|
43
|
+
* Active file tree node
|
|
44
|
+
*
|
|
45
|
+
* 当前激活的文件树节点
|
|
46
|
+
*/
|
|
47
|
+
const ACTIVE_NODE_KEY = Symbol(import.meta.env.DEV ? "active-file-tree-node" : "");
|
|
48
|
+
//#endregion
|
|
31
49
|
//#region src/client/VPFileTreeNode.vue
|
|
32
|
-
const _sfc_main$1 =
|
|
50
|
+
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
33
51
|
__name: "VPFileTreeNode",
|
|
34
52
|
__ssrInlineRender: true,
|
|
35
53
|
props: {
|
|
@@ -42,13 +60,33 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
|
42
60
|
filepath: {}
|
|
43
61
|
},
|
|
44
62
|
setup(__props) {
|
|
45
|
-
const activeFileTreeNode = inject(
|
|
46
|
-
const onNodeClick = inject(
|
|
63
|
+
const activeFileTreeNode = inject(ACTIVE_NODE_KEY, ref(""));
|
|
64
|
+
const onNodeClick = inject(ON_NODE_CLICK, () => {});
|
|
47
65
|
const active = ref(__props.expanded);
|
|
66
|
+
/**
|
|
67
|
+
* Notify the parent of a node click event via the injected callback.
|
|
68
|
+
*
|
|
69
|
+
* 通过注入的回调通知父组件节点点击事件。
|
|
70
|
+
*
|
|
71
|
+
* Ellipsis placeholders (`…` or `...`) are ignored to avoid triggering
|
|
72
|
+
* selection on empty folder markers.
|
|
73
|
+
*/
|
|
48
74
|
function nodeClick() {
|
|
49
75
|
if (__props.filename === "…" || __props.filename === "...") return;
|
|
50
76
|
onNodeClick(__props.filepath || __props.filename, __props.type);
|
|
51
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Handle click events on the node, toggling folder expansion or firing
|
|
80
|
+
* the node click callback for files.
|
|
81
|
+
*
|
|
82
|
+
* 处理节点的点击事件,切换文件夹展开状态或触发文件节点的点击回调。
|
|
83
|
+
*
|
|
84
|
+
* For folders, clicks on the comment area are ignored so that inline
|
|
85
|
+
* comments remain interactive. For files, the click callback fires
|
|
86
|
+
* directly.
|
|
87
|
+
*
|
|
88
|
+
* @param ev - Native mouse event / 原生鼠标事件
|
|
89
|
+
*/
|
|
52
90
|
function toggle(ev) {
|
|
53
91
|
if (__props.type === "folder") {
|
|
54
92
|
if (!ev.target.matches(".comment, .comment *")) {
|
|
@@ -99,9 +137,22 @@ _sfc_main$1.setup = (props, ctx) => {
|
|
|
99
137
|
};
|
|
100
138
|
//#endregion
|
|
101
139
|
//#region src/client/index.ts
|
|
140
|
+
/**
|
|
141
|
+
* Register file tree components globally during VitePress app enhancement.
|
|
142
|
+
*
|
|
143
|
+
* 在 VitePress 应用增强阶段全局注册文件树组件。
|
|
144
|
+
*
|
|
145
|
+
* Called automatically by the `virtual:enhance-app` module when the plugin
|
|
146
|
+
* is loaded via `vitepress-tuck`'s `defineConfig`. Registers `VPFileTree`
|
|
147
|
+
* and `VPFileTreeNode` as global Vue components so they can be used in
|
|
148
|
+
* markdown-rendered HTML without explicit imports.
|
|
149
|
+
*
|
|
150
|
+
* @param ctx - VitePress enhance app context / VitePress 应用增强上下文
|
|
151
|
+
* @param ctx.app - The VitePress Vue app instance / VitePress 的 Vue 应用实例
|
|
152
|
+
*/
|
|
102
153
|
function enhanceAppWithFileTree({ app }) {
|
|
103
|
-
app.component("VPFileTree", _sfc_main);
|
|
104
|
-
app.component("VPFileTreeNode", _sfc_main$1);
|
|
154
|
+
if (!app._context.components.VPFileTree) app.component("VPFileTree", _sfc_main);
|
|
155
|
+
if (!app._context.components.VPFileTreeNode) app.component("VPFileTreeNode", _sfc_main$1);
|
|
105
156
|
}
|
|
106
157
|
//#endregion
|
|
107
|
-
export { _sfc_main as VPFileTree, _sfc_main$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
|
158
|
+
export { ACTIVE_NODE_KEY, ON_NODE_CLICK, _sfc_main as VPFileTree, _sfc_main$1 as VPFileTreeNode, enhanceAppWithFileTree };
|
package/dist/client/style.css
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
.vp-file-tree {
|
|
13
13
|
position: relative;
|
|
14
14
|
max-width: 100%;
|
|
15
|
-
padding: 16px;
|
|
16
15
|
overflow: auto hidden;
|
|
17
16
|
font-size: 14px;
|
|
18
17
|
background-color: var(--vp-file-tree-bg);
|
|
@@ -22,11 +21,10 @@
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
.vp-file-tree .vp-file-tree-title {
|
|
25
|
-
padding-block: 8px;
|
|
26
24
|
padding-inline: 16px;
|
|
27
|
-
margin-block:
|
|
28
|
-
margin-inline: -16px;
|
|
25
|
+
margin-block: 0;
|
|
29
26
|
font-weight: bold;
|
|
27
|
+
line-height: 47px;
|
|
30
28
|
color: var(--vp-c-text-1);
|
|
31
29
|
border-bottom: solid 1px var(--vp-c-divider);
|
|
32
30
|
transition: color 0.25s ease, border-color 0.25s ease;
|
|
@@ -36,6 +34,10 @@
|
|
|
36
34
|
top: calc(45px + 1em) !important;
|
|
37
35
|
}
|
|
38
36
|
|
|
37
|
+
.vp-file-tree .file-tree-content {
|
|
38
|
+
padding: 16px;
|
|
39
|
+
}
|
|
40
|
+
|
|
39
41
|
.vp-file-tree .vp-file-tree-info {
|
|
40
42
|
position: relative;
|
|
41
43
|
display: flex;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,72 +1,190 @@
|
|
|
1
1
|
import { PluginSimple } from "markdown-it";
|
|
2
2
|
|
|
3
|
-
//#region src/node/
|
|
3
|
+
//#region src/node/plugin.d.ts
|
|
4
4
|
/**
|
|
5
|
+
* VitePress plugin for rendering file tree diagrams in markdown.
|
|
6
|
+
*
|
|
7
|
+
* VitePress 插件,用于在 markdown 中渲染文件树图。
|
|
8
|
+
*
|
|
9
|
+
* Registers the `fileTreeMarkdownPlugin` with markdown-it to parse both
|
|
10
|
+
* container syntax (`::: file-tree`) and fence syntax (` ```tree ` or
|
|
11
|
+
* ` ```file-tree `) into interactive collapsible file trees. On the client
|
|
12
|
+
* side, the `VPFileTree` and `VPFileTreeNode` Vue components are injected
|
|
13
|
+
* globally via `virtual:enhance-app`.
|
|
14
|
+
*
|
|
15
|
+
* The rendered tree supports inline comments, focus highlights, diff markers
|
|
16
|
+
* for added or removed entries, and a copy button that outputs the plain-text
|
|
17
|
+
* `tree` command representation.
|
|
18
|
+
*
|
|
5
19
|
* @example
|
|
20
|
+
* `.vitepress/config.ts`
|
|
6
21
|
* ```ts
|
|
7
|
-
* import {
|
|
8
|
-
* import
|
|
22
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
23
|
+
* import fileTree from 'vitepress-plugin-file-tree'
|
|
24
|
+
*
|
|
9
25
|
* export default defineConfig({
|
|
10
|
-
*
|
|
11
|
-
* config: (md) => {
|
|
12
|
-
* md.use(fileTreeMarkdownPlugin)
|
|
13
|
-
* },
|
|
14
|
-
* },
|
|
26
|
+
* plugins: [fileTree()],
|
|
15
27
|
* })
|
|
16
28
|
* ```
|
|
17
29
|
*/
|
|
18
|
-
declare const
|
|
30
|
+
declare const fileTree: (options?: unknown) => import("vitepress-tuck").VitepressPlugin;
|
|
19
31
|
//#endregion
|
|
20
32
|
//#region src/node/types.d.ts
|
|
21
33
|
/**
|
|
22
|
-
* File tree node structure
|
|
34
|
+
* File tree node structure.
|
|
35
|
+
*
|
|
36
|
+
* 文件树节点结构。
|
|
23
37
|
*
|
|
24
|
-
*
|
|
38
|
+
* Represents a single entry in the file tree. A node can be either a file or
|
|
39
|
+
* a folder, with optional metadata such as inline comments, focus highlight,
|
|
40
|
+
* expansion state, and diff markers used for before/after comparisons.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const node: FileTreeNode = {
|
|
45
|
+
* filename: 'src',
|
|
46
|
+
* type: 'folder',
|
|
47
|
+
* level: 0,
|
|
48
|
+
* children: [
|
|
49
|
+
* { filename: 'index.ts', type: 'file', level: 1, children: [] },
|
|
50
|
+
* ],
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
25
53
|
*/
|
|
26
54
|
interface FileTreeNode {
|
|
55
|
+
/** Display name of the file or folder. / 文件或文件夹的显示名称。 */
|
|
27
56
|
filename: string;
|
|
57
|
+
/** Optional inline comment rendered next to the filename. / 渲染在文件名旁的可选内联注释。 */
|
|
28
58
|
comment?: string;
|
|
59
|
+
/** Whether this node is visually highlighted as focused. / 是否作为聚焦节点高亮显示。 */
|
|
29
60
|
focus?: boolean;
|
|
61
|
+
/** Whether a folder node is expanded by default. / 文件夹节点是否默认展开。 */
|
|
30
62
|
expanded?: boolean;
|
|
63
|
+
/** Node type, either a folder or a file. / 节点类型,文件夹或文件。 */
|
|
31
64
|
type: 'folder' | 'file';
|
|
65
|
+
/** Diff marker indicating the node was added or removed. / 差异标记,表示节点为新增或删除。 */
|
|
32
66
|
diff?: 'add' | 'remove';
|
|
67
|
+
/** Indentation depth level, starting from 0 at the root's direct children. / 缩进层级,根节点的直接子节点为 0。 */
|
|
33
68
|
level: number;
|
|
69
|
+
/** Child nodes, only populated for folder nodes. / 子节点数组,仅文件夹节点会有子节点。 */
|
|
34
70
|
children: FileTreeNode[];
|
|
35
71
|
}
|
|
36
72
|
/**
|
|
37
|
-
* File tree container attributes
|
|
73
|
+
* File tree container attributes.
|
|
38
74
|
*
|
|
39
|
-
*
|
|
75
|
+
* 文件树容器属性。
|
|
76
|
+
*
|
|
77
|
+
* Defines the optional attributes parsed from the `::: file-tree` container
|
|
78
|
+
* syntax, used to configure the outer wrapper of the rendered file tree.
|
|
40
79
|
*/
|
|
41
80
|
interface FileTreeAttrs {
|
|
81
|
+
/** Optional title displayed above the file tree. / 显示在文件树上方的可选标题。 */
|
|
42
82
|
title?: string;
|
|
43
83
|
}
|
|
44
84
|
//#endregion
|
|
45
|
-
//#region src/node/
|
|
85
|
+
//#region src/node/fileTreeToCMDText.d.ts
|
|
46
86
|
/**
|
|
47
|
-
*
|
|
87
|
+
* Convert file tree nodes into `tree` command-style plain text.
|
|
48
88
|
*
|
|
49
|
-
*
|
|
89
|
+
* 将文件树节点转换为 `tree` 命令风格的纯文本。
|
|
50
90
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
91
|
+
* Produces a string representation using `├──` and `└──` branch markers with
|
|
92
|
+
* `│ ` and ` ` indentation, matching the output of the Unix `tree`
|
|
93
|
+
* command. Ellipsis placeholder children (`…`) are filtered out so empty
|
|
94
|
+
* folders render cleanly. The result is used by the copy button on the
|
|
95
|
+
* rendered file tree.
|
|
96
|
+
*
|
|
97
|
+
* 使用 `├──` 和 `└──` 分支标记以及 `│` 和空格缩进生成字符串表示,与 Unix `tree` 命令的输出一致。
|
|
98
|
+
* 省略号占位符子项(`…`)会被过滤掉,使得空文件夹显示整洁。该结果用于渲染文件树上的复制按钮。
|
|
99
|
+
*
|
|
100
|
+
* @param nodes - File tree nodes to convert / 要转换的文件树节点数组
|
|
101
|
+
* @param prefix - Line prefix for recursive indentation / 递归缩进使用的行前缀
|
|
102
|
+
* @returns CMD-style text representation / CMD 风格的文本表示
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const nodes = [
|
|
107
|
+
* { filename: 'src', type: 'folder', level: 0, children: [
|
|
108
|
+
* { filename: 'index.ts', type: 'file', level: 1, children: [] },
|
|
109
|
+
* ]},
|
|
110
|
+
* ]
|
|
111
|
+
* fileTreeToCMDText(nodes)
|
|
112
|
+
* // .
|
|
113
|
+
* // └── src
|
|
114
|
+
* // └── index.ts
|
|
115
|
+
* ```
|
|
53
116
|
*/
|
|
54
|
-
declare function
|
|
117
|
+
declare function fileTreeToCMDText(nodes: FileTreeNode[], prefix?: string): string;
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/node/markdown.d.ts
|
|
120
|
+
/**
|
|
121
|
+
* markdown-it plugin for rendering file tree diagrams.
|
|
122
|
+
*
|
|
123
|
+
* 用于渲染文件树图的 markdown-it 插件。
|
|
124
|
+
*
|
|
125
|
+
* Supports two syntaxes:
|
|
126
|
+
* - Container syntax: `::: file-tree` blocks with `- filename` indented entries
|
|
127
|
+
* - Fence syntax: ` ```tree ` or ` ```file-tree ` code blocks with `tree` command output
|
|
128
|
+
*
|
|
129
|
+
* The plugin converts the parsed tree structure into nested `<VPFileTreeNode>`
|
|
130
|
+
* components wrapped by a `<VPFileTree>` container, with a URL-encoded
|
|
131
|
+
* plain-text representation for the copy button.
|
|
132
|
+
*
|
|
133
|
+
* 支持两种语法:
|
|
134
|
+
* - 容器语法:`::: file-tree` 块,其中包含以 `- 文件名` 缩进的条目
|
|
135
|
+
* - 围栏语法:` ```tree ` 或 ` ```file-tree ` 代码块,其中包含 `tree` 命令的输出
|
|
136
|
+
*
|
|
137
|
+
* 该插件将解析后的树结构转换为由 `<VPFileTree>` 容器包裹的嵌套 `<VPFileTreeNode>` 组件,并附带一个 URL 编码的纯文本表示,用于复制按钮。
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { fileTreeMarkdownPlugin } from 'vitepress-plugin-file-tree'
|
|
142
|
+
* import { defineConfig } from 'vitepress'
|
|
143
|
+
* export default defineConfig({
|
|
144
|
+
* markdown: {
|
|
145
|
+
* config: (md) => {
|
|
146
|
+
* md.use(fileTreeMarkdownPlugin)
|
|
147
|
+
* },
|
|
148
|
+
* },
|
|
149
|
+
* })
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
declare const fileTreeMarkdownPlugin: PluginSimple;
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/node/parseContentWithContainer.d.ts
|
|
55
155
|
/**
|
|
56
|
-
*
|
|
156
|
+
* Parse the content from the `::: file-tree` container into a node tree structure.
|
|
57
157
|
*
|
|
58
|
-
*
|
|
158
|
+
* 从 `::: file-tree` 容器中解析内容为节点树结构。
|
|
59
159
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
160
|
+
* Accepts indented list-style text where each entry starts with `- ` and
|
|
161
|
+
* indentation is measured in two-space increments. The first line's leading
|
|
162
|
+
* whitespace is stripped to normalize the root level. Each entry is passed
|
|
163
|
+
* to `parseNodeInfo` to extract the filename, comment, type, and other
|
|
164
|
+
* metadata.
|
|
165
|
+
*
|
|
166
|
+
* @param content - Raw file tree text content / 文件树的原始文本内容
|
|
167
|
+
* @returns File tree node array / 文件树节点数组
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* const content = `
|
|
172
|
+
* - src/ # source folder
|
|
173
|
+
* - index.ts
|
|
174
|
+
* - utils.ts
|
|
175
|
+
* - package.json
|
|
176
|
+
* `
|
|
177
|
+
* parseContentWithContainer(content)
|
|
178
|
+
* ```
|
|
63
179
|
*/
|
|
64
|
-
declare function
|
|
180
|
+
declare function parseContentWithContainer(content: string): FileTreeNode[];
|
|
65
181
|
//#endregion
|
|
66
|
-
//#region src/node/
|
|
182
|
+
//#region src/node/parseContentWithFence.d.ts
|
|
67
183
|
/**
|
|
68
184
|
* Parse `tree` command output format into a structured file tree node array.
|
|
69
185
|
*
|
|
186
|
+
* 将 `tree` 命令行输出格式解析为结构化的文件树节点数组。
|
|
187
|
+
*
|
|
70
188
|
* Converts text like:
|
|
71
189
|
* ```
|
|
72
190
|
* .
|
|
@@ -77,9 +195,10 @@ declare function fileTreeToCMDText(nodes: FileTreeNode[], prefix?: string): stri
|
|
|
77
195
|
* ```
|
|
78
196
|
*
|
|
79
197
|
* into a `FileTreeNode[]` tree structure, with support for inline comments
|
|
80
|
-
* (text after `#`) on each entry.
|
|
81
|
-
*
|
|
82
|
-
*
|
|
198
|
+
* (text after `#`) on each entry. The leading root marker line (`.`) is
|
|
199
|
+
* skipped if present. Indentation depth is derived from the 4-character
|
|
200
|
+
* prefix segments, and any parent node that gains children is automatically
|
|
201
|
+
* promoted to the `folder` type.
|
|
83
202
|
*
|
|
84
203
|
* @param content - Raw `tree` command output text / `tree` 命令输出的原始文本
|
|
85
204
|
* @returns Structured file tree node array / 结构化的文件树节点数组
|
|
@@ -88,16 +207,32 @@ declare function parseContentWithFence(content: string): FileTreeNode[];
|
|
|
88
207
|
//#endregion
|
|
89
208
|
//#region src/node/parseNodeInfo.d.ts
|
|
90
209
|
/**
|
|
91
|
-
* Parse single node info string,
|
|
210
|
+
* Parse a single node info string, extracting filename, comment, type, and
|
|
211
|
+
* other metadata.
|
|
212
|
+
*
|
|
213
|
+
* 解析单个节点的 info 字符串,提取文件名、注释、类型等属性。
|
|
92
214
|
*
|
|
93
|
-
*
|
|
215
|
+
* Recognizes the following syntax within the info string:
|
|
216
|
+
* - `++filename` — marks the node as a diff addition
|
|
217
|
+
* - `--filename` — marks the node as a diff removal
|
|
218
|
+
* - `**filename**` — highlights the node as focused
|
|
219
|
+
* - `filename # comment` — attaches an inline comment after `#`
|
|
220
|
+
* - `filename/` — treats the entry as a folder (trailing slash removed)
|
|
94
221
|
*
|
|
95
222
|
* @param info - Node description string / 节点描述字符串
|
|
96
|
-
* @returns File tree node
|
|
223
|
+
* @returns File tree node properties excluding `children` and `level` / 文件树节点属性(不含 `children` 和 `level`)
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* parseNodeInfo('src/ # source folder')
|
|
228
|
+
* // { filename: 'src', comment: '# source folder', focus: false,
|
|
229
|
+
* // expanded: false, type: 'folder', diff: undefined }
|
|
230
|
+
*
|
|
231
|
+
* parseNodeInfo('**index.ts**')
|
|
232
|
+
* // { filename: 'index.ts', comment: '', focus: true,
|
|
233
|
+
* // expanded: true, type: 'file', diff: undefined }
|
|
234
|
+
* ```
|
|
97
235
|
*/
|
|
98
236
|
declare function parseNodeInfo(info: string): Omit<FileTreeNode, 'children' | 'level'>;
|
|
99
237
|
//#endregion
|
|
100
|
-
|
|
101
|
-
declare const _default: (option?: unknown) => import("vitepress-tuck").VitepressPlugin;
|
|
102
|
-
//#endregion
|
|
103
|
-
export { FileTreeAttrs, FileTreeNode, _default as default, fileTreeMarkdownPlugin, fileTreeToCMDText, parseContentWithContainer, parseContentWithFence, parseNodeInfo };
|
|
238
|
+
export { FileTreeAttrs, FileTreeNode, fileTree as default, fileTree, fileTreeMarkdownPlugin, fileTreeToCMDText, parseContentWithContainer, parseContentWithFence, parseNodeInfo };
|
package/dist/node/index.js
CHANGED
|
@@ -1,20 +1,85 @@
|
|
|
1
1
|
import { definePlugin } from "vitepress-tuck";
|
|
2
2
|
import { createContainerSyntaxPlugin, stringifyAttrs } from "vitepress-plugin-toolkit";
|
|
3
3
|
import { removeTrailingSlash } from "@pengzhanbo/utils";
|
|
4
|
+
//#region src/node/fileTreeToCMDText.ts
|
|
5
|
+
/**
|
|
6
|
+
* Convert file tree nodes into `tree` command-style plain text.
|
|
7
|
+
*
|
|
8
|
+
* 将文件树节点转换为 `tree` 命令风格的纯文本。
|
|
9
|
+
*
|
|
10
|
+
* Produces a string representation using `├──` and `└──` branch markers with
|
|
11
|
+
* `│ ` and ` ` indentation, matching the output of the Unix `tree`
|
|
12
|
+
* command. Ellipsis placeholder children (`…`) are filtered out so empty
|
|
13
|
+
* folders render cleanly. The result is used by the copy button on the
|
|
14
|
+
* rendered file tree.
|
|
15
|
+
*
|
|
16
|
+
* 使用 `├──` 和 `└──` 分支标记以及 `│` 和空格缩进生成字符串表示,与 Unix `tree` 命令的输出一致。
|
|
17
|
+
* 省略号占位符子项(`…`)会被过滤掉,使得空文件夹显示整洁。该结果用于渲染文件树上的复制按钮。
|
|
18
|
+
*
|
|
19
|
+
* @param nodes - File tree nodes to convert / 要转换的文件树节点数组
|
|
20
|
+
* @param prefix - Line prefix for recursive indentation / 递归缩进使用的行前缀
|
|
21
|
+
* @returns CMD-style text representation / CMD 风格的文本表示
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const nodes = [
|
|
26
|
+
* { filename: 'src', type: 'folder', level: 0, children: [
|
|
27
|
+
* { filename: 'index.ts', type: 'file', level: 1, children: [] },
|
|
28
|
+
* ]},
|
|
29
|
+
* ]
|
|
30
|
+
* fileTreeToCMDText(nodes)
|
|
31
|
+
* // .
|
|
32
|
+
* // └── src
|
|
33
|
+
* // └── index.ts
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function fileTreeToCMDText(nodes, prefix = "") {
|
|
37
|
+
let content = prefix ? "" : ".\n";
|
|
38
|
+
for (let i = 0, l = nodes.length; i < l; i++) {
|
|
39
|
+
const { filename, children } = nodes[i];
|
|
40
|
+
content += `${prefix + (i === l - 1 ? "└── " : "├── ")}${filename}\n`;
|
|
41
|
+
const child = children.filter((n) => n.filename !== "…");
|
|
42
|
+
if (child.length) content += fileTreeToCMDText(child, prefix + (i === l - 1 ? " " : "│ "));
|
|
43
|
+
}
|
|
44
|
+
return content;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
4
47
|
//#region src/node/parseNodeInfo.ts
|
|
5
48
|
/**
|
|
6
|
-
*
|
|
49
|
+
* Regular expression for the focus marker `**filename**`.
|
|
50
|
+
*
|
|
51
|
+
* 聚焦标记 `**filename**` 的正则表达式。
|
|
7
52
|
*
|
|
8
|
-
*
|
|
53
|
+
* A filename wrapped in double asterisks is highlighted as a focused node.
|
|
54
|
+
* The marker may be followed by a space and an inline comment.
|
|
9
55
|
*/
|
|
10
56
|
const RE_FOCUS = /^\*\*(.*)\*\*(?:$|\s+)/;
|
|
11
57
|
/**
|
|
12
|
-
* Parse single node info string,
|
|
58
|
+
* Parse a single node info string, extracting filename, comment, type, and
|
|
59
|
+
* other metadata.
|
|
60
|
+
*
|
|
61
|
+
* 解析单个节点的 info 字符串,提取文件名、注释、类型等属性。
|
|
13
62
|
*
|
|
14
|
-
*
|
|
63
|
+
* Recognizes the following syntax within the info string:
|
|
64
|
+
* - `++filename` — marks the node as a diff addition
|
|
65
|
+
* - `--filename` — marks the node as a diff removal
|
|
66
|
+
* - `**filename**` — highlights the node as focused
|
|
67
|
+
* - `filename # comment` — attaches an inline comment after `#`
|
|
68
|
+
* - `filename/` — treats the entry as a folder (trailing slash removed)
|
|
15
69
|
*
|
|
16
70
|
* @param info - Node description string / 节点描述字符串
|
|
17
|
-
* @returns File tree node
|
|
71
|
+
* @returns File tree node properties excluding `children` and `level` / 文件树节点属性(不含 `children` 和 `level`)
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* parseNodeInfo('src/ # source folder')
|
|
76
|
+
* // { filename: 'src', comment: '# source folder', focus: false,
|
|
77
|
+
* // expanded: false, type: 'folder', diff: undefined }
|
|
78
|
+
*
|
|
79
|
+
* parseNodeInfo('**index.ts**')
|
|
80
|
+
* // { filename: 'index.ts', comment: '', focus: true,
|
|
81
|
+
* // expanded: true, type: 'file', diff: undefined }
|
|
82
|
+
* ```
|
|
18
83
|
*/
|
|
19
84
|
function parseNodeInfo(info) {
|
|
20
85
|
let filename = "";
|
|
@@ -56,14 +121,31 @@ function parseNodeInfo(info) {
|
|
|
56
121
|
};
|
|
57
122
|
}
|
|
58
123
|
//#endregion
|
|
59
|
-
//#region src/node/
|
|
124
|
+
//#region src/node/parseContentWithContainer.ts
|
|
60
125
|
/**
|
|
61
126
|
* Parse the content from the `::: file-tree` container into a node tree structure.
|
|
62
127
|
*
|
|
63
|
-
* 从 `::: file-tree`
|
|
128
|
+
* 从 `::: file-tree` 容器中解析内容为节点树结构。
|
|
129
|
+
*
|
|
130
|
+
* Accepts indented list-style text where each entry starts with `- ` and
|
|
131
|
+
* indentation is measured in two-space increments. The first line's leading
|
|
132
|
+
* whitespace is stripped to normalize the root level. Each entry is passed
|
|
133
|
+
* to `parseNodeInfo` to extract the filename, comment, type, and other
|
|
134
|
+
* metadata.
|
|
64
135
|
*
|
|
65
136
|
* @param content - Raw file tree text content / 文件树的原始文本内容
|
|
66
137
|
* @returns File tree node array / 文件树节点数组
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* const content = `
|
|
142
|
+
* - src/ # source folder
|
|
143
|
+
* - index.ts
|
|
144
|
+
* - utils.ts
|
|
145
|
+
* - package.json
|
|
146
|
+
* `
|
|
147
|
+
* parseContentWithContainer(content)
|
|
148
|
+
* ```
|
|
67
149
|
*/
|
|
68
150
|
function parseContentWithContainer(content) {
|
|
69
151
|
const root = {
|
|
@@ -90,29 +172,12 @@ function parseContentWithContainer(content) {
|
|
|
90
172
|
}
|
|
91
173
|
return root.children;
|
|
92
174
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Convert file tree to command line text format
|
|
95
|
-
*
|
|
96
|
-
* 将文件树转换为命令行文本格式
|
|
97
|
-
*
|
|
98
|
-
* @param nodes - File tree nodes / 文件树节点
|
|
99
|
-
* @param prefix - Line prefix / 行前缀
|
|
100
|
-
* @returns CMD text / CMD 文本
|
|
101
|
-
*/
|
|
102
|
-
function fileTreeToCMDText(nodes, prefix = "") {
|
|
103
|
-
let content = prefix ? "" : ".\n";
|
|
104
|
-
for (let i = 0, l = nodes.length; i < l; i++) {
|
|
105
|
-
const { filename, children } = nodes[i];
|
|
106
|
-
content += `${prefix + (i === l - 1 ? "└── " : "├── ")}${filename}\n`;
|
|
107
|
-
const child = children.filter((n) => n.filename !== "…");
|
|
108
|
-
if (child.length) content += fileTreeToCMDText(child, prefix + (i === l - 1 ? " " : "│ "));
|
|
109
|
-
}
|
|
110
|
-
return content;
|
|
111
|
-
}
|
|
112
175
|
//#endregion
|
|
113
|
-
//#region src/node/
|
|
176
|
+
//#region src/node/parseContentWithFence.ts
|
|
114
177
|
/**
|
|
115
|
-
*
|
|
178
|
+
* Regular expression for matching a single line of `tree` command output.
|
|
179
|
+
*
|
|
180
|
+
* 匹配 `tree` 命令输出的单行正则。
|
|
116
181
|
*
|
|
117
182
|
* Matches lines like:
|
|
118
183
|
* ├── filename
|
|
@@ -123,13 +188,13 @@ function fileTreeToCMDText(nodes, prefix = "") {
|
|
|
123
188
|
* - Group 1: prefix segments, each is either `│ ` (has next sibling) or ` ` (last sibling)
|
|
124
189
|
* - Group 2: branch marker, either `├── ` (non-last) or `└── ` (last)
|
|
125
190
|
* - Group 3: the filename with optional comment
|
|
126
|
-
*
|
|
127
|
-
* 匹配 `tree` 命令输出的单行正则
|
|
128
191
|
*/
|
|
129
192
|
const TREE_LINE_RE = /^((?:│ {3}| {4})*)([├└]── )(.+)$/u;
|
|
130
193
|
/**
|
|
131
194
|
* Parse `tree` command output format into a structured file tree node array.
|
|
132
195
|
*
|
|
196
|
+
* 将 `tree` 命令行输出格式解析为结构化的文件树节点数组。
|
|
197
|
+
*
|
|
133
198
|
* Converts text like:
|
|
134
199
|
* ```
|
|
135
200
|
* .
|
|
@@ -140,9 +205,10 @@ const TREE_LINE_RE = /^((?:│ {3}| {4})*)([├└]── )(.+)$/u;
|
|
|
140
205
|
* ```
|
|
141
206
|
*
|
|
142
207
|
* into a `FileTreeNode[]` tree structure, with support for inline comments
|
|
143
|
-
* (text after `#`) on each entry.
|
|
144
|
-
*
|
|
145
|
-
*
|
|
208
|
+
* (text after `#`) on each entry. The leading root marker line (`.`) is
|
|
209
|
+
* skipped if present. Indentation depth is derived from the 4-character
|
|
210
|
+
* prefix segments, and any parent node that gains children is automatically
|
|
211
|
+
* promoted to the `folder` type.
|
|
146
212
|
*
|
|
147
213
|
* @param content - Raw `tree` command output text / `tree` 命令输出的原始文本
|
|
148
214
|
* @returns Structured file tree node array / 结构化的文件树节点数组
|
|
@@ -175,8 +241,26 @@ function parseContentWithFence(content) {
|
|
|
175
241
|
return root.children;
|
|
176
242
|
}
|
|
177
243
|
//#endregion
|
|
178
|
-
//#region src/node/
|
|
244
|
+
//#region src/node/markdown.ts
|
|
179
245
|
/**
|
|
246
|
+
* markdown-it plugin for rendering file tree diagrams.
|
|
247
|
+
*
|
|
248
|
+
* 用于渲染文件树图的 markdown-it 插件。
|
|
249
|
+
*
|
|
250
|
+
* Supports two syntaxes:
|
|
251
|
+
* - Container syntax: `::: file-tree` blocks with `- filename` indented entries
|
|
252
|
+
* - Fence syntax: ` ```tree ` or ` ```file-tree ` code blocks with `tree` command output
|
|
253
|
+
*
|
|
254
|
+
* The plugin converts the parsed tree structure into nested `<VPFileTreeNode>`
|
|
255
|
+
* components wrapped by a `<VPFileTree>` container, with a URL-encoded
|
|
256
|
+
* plain-text representation for the copy button.
|
|
257
|
+
*
|
|
258
|
+
* 支持两种语法:
|
|
259
|
+
* - 容器语法:`::: file-tree` 块,其中包含以 `- 文件名` 缩进的条目
|
|
260
|
+
* - 围栏语法:` ```tree ` 或 ` ```file-tree ` 代码块,其中包含 `tree` 命令的输出
|
|
261
|
+
*
|
|
262
|
+
* 该插件将解析后的树结构转换为由 `<VPFileTree>` 容器包裹的嵌套 `<VPFileTreeNode>` 组件,并附带一个 URL 编码的纯文本表示,用于复制按钮。
|
|
263
|
+
*
|
|
180
264
|
* @example
|
|
181
265
|
* ```ts
|
|
182
266
|
* import { fileTreeMarkdownPlugin } from 'vitepress-plugin-file-tree'
|
|
@@ -192,9 +276,20 @@ function parseContentWithFence(content) {
|
|
|
192
276
|
*/
|
|
193
277
|
const fileTreeMarkdownPlugin = (md) => {
|
|
194
278
|
/**
|
|
195
|
-
* Recursively render file tree nodes
|
|
279
|
+
* Recursively render file tree nodes into nested `<VPFileTreeNode>` markup.
|
|
280
|
+
*
|
|
281
|
+
* 递归地将文件树节点渲染为嵌套的 `<VPFileTreeNode>` 标记。
|
|
282
|
+
*
|
|
283
|
+
* Empty folders automatically receive an ellipsis placeholder child so the
|
|
284
|
+
* folder icon is displayed correctly. Inline comments are rendered through
|
|
285
|
+
* `md.renderInline` and passed via the `#comment` slot.
|
|
286
|
+
*
|
|
287
|
+
* 空文件夹会自动获得一个省略号占位符子项,以便正确显示文件夹图标。
|
|
288
|
+
* 内联注释通过 `md.renderInline` 渲染,并通过 `#comment` 插槽传递。
|
|
196
289
|
*
|
|
197
|
-
*
|
|
290
|
+
* @param nodes - File tree nodes to render / 要渲染的文件树节点数组
|
|
291
|
+
* @param meta - Container-level attributes / 容器级属性
|
|
292
|
+
* @returns Rendered HTML string of nested nodes / 嵌套节点的 HTML 字符串
|
|
198
293
|
*/
|
|
199
294
|
const renderFileTree = (nodes, meta) => nodes.map((node) => {
|
|
200
295
|
const { level, children, filename, comment, focus, expanded, type, diff } = node;
|
|
@@ -239,8 +334,34 @@ ${renderedComment}${children.length > 0 ? renderFileTree(children, meta) : ""}
|
|
|
239
334
|
};
|
|
240
335
|
};
|
|
241
336
|
//#endregion
|
|
242
|
-
//#region src/node/
|
|
243
|
-
|
|
337
|
+
//#region src/node/plugin.ts
|
|
338
|
+
/**
|
|
339
|
+
* VitePress plugin for rendering file tree diagrams in markdown.
|
|
340
|
+
*
|
|
341
|
+
* VitePress 插件,用于在 markdown 中渲染文件树图。
|
|
342
|
+
*
|
|
343
|
+
* Registers the `fileTreeMarkdownPlugin` with markdown-it to parse both
|
|
344
|
+
* container syntax (`::: file-tree`) and fence syntax (` ```tree ` or
|
|
345
|
+
* ` ```file-tree `) into interactive collapsible file trees. On the client
|
|
346
|
+
* side, the `VPFileTree` and `VPFileTreeNode` Vue components are injected
|
|
347
|
+
* globally via `virtual:enhance-app`.
|
|
348
|
+
*
|
|
349
|
+
* The rendered tree supports inline comments, focus highlights, diff markers
|
|
350
|
+
* for added or removed entries, and a copy button that outputs the plain-text
|
|
351
|
+
* `tree` command representation.
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* `.vitepress/config.ts`
|
|
355
|
+
* ```ts
|
|
356
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
357
|
+
* import fileTree from 'vitepress-plugin-file-tree'
|
|
358
|
+
*
|
|
359
|
+
* export default defineConfig({
|
|
360
|
+
* plugins: [fileTree()],
|
|
361
|
+
* })
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
const fileTree = definePlugin(() => ({
|
|
244
365
|
name: "vitepress-plugin-file-tree",
|
|
245
366
|
client: { enhance: "enhanceAppWithFileTree" },
|
|
246
367
|
markdown: { config: (md) => {
|
|
@@ -252,4 +373,15 @@ var node_default = definePlugin(() => ({
|
|
|
252
373
|
}
|
|
253
374
|
}));
|
|
254
375
|
//#endregion
|
|
255
|
-
|
|
376
|
+
//#region src/node/index.ts
|
|
377
|
+
/**
|
|
378
|
+
* Entry point for the `vitepress-plugin-file-tree` package.
|
|
379
|
+
*
|
|
380
|
+
* `vitepress-plugin-file-tree` 插件的入口模块。
|
|
381
|
+
*
|
|
382
|
+
* Re-exports all node-side utilities, the markdown-it plugin, parsing helpers,
|
|
383
|
+
* and the main `fileTree` plugin definition as the default export.
|
|
384
|
+
*/
|
|
385
|
+
var node_default = fileTree;
|
|
386
|
+
//#endregion
|
|
387
|
+
export { node_default as default, fileTree, fileTreeMarkdownPlugin, fileTreeToCMDText, parseContentWithContainer, parseContentWithFence, parseNodeInfo };
|
package/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitepress-plugin-file-tree",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "Render file tree structure in your VitePress site.",
|
|
6
6
|
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"homepage": "https://tuck.pengzhanbo.cn/plugins/file-tree",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
11
|
"url": "git+https://github.com/pengzhanbo/vitepress-tuck.git",
|
|
11
12
|
"directory": "packages/plugin-file-tree"
|
|
12
13
|
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/pengzhanbo/vitepress-tuck/issues"
|
|
16
|
+
},
|
|
13
17
|
"keywords": [
|
|
14
18
|
"vitepress",
|
|
15
19
|
"vitepress-plugin",
|
|
@@ -35,8 +39,8 @@
|
|
|
35
39
|
"dependencies": {
|
|
36
40
|
"@pengzhanbo/utils": "^3.7.3",
|
|
37
41
|
"@vueuse/core": "^14.3.0",
|
|
38
|
-
"vitepress-plugin-toolkit": "0.
|
|
39
|
-
"vitepress-tuck": "0.
|
|
42
|
+
"vitepress-plugin-toolkit": "0.4.0",
|
|
43
|
+
"vitepress-tuck": "0.4.0"
|
|
40
44
|
},
|
|
41
45
|
"publishConfig": {
|
|
42
46
|
"access": "public",
|