* feat(plugin-md-power): refactor file-tree container * chore: tweak * chore: tweak
This commit is contained in:
parent
7e255412c1
commit
599e43fd3c
@ -1,66 +1,149 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`fileTreePlugin > should work with default options 1`] = `
|
exports[`fileTree > parseFileTreeRawContent > should work 1`] = `
|
||||||
"<div class="vp-file-tree"><ul>
|
[
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-docs"></VPIcon><span class="name">docs</span></span>
|
{
|
||||||
<ul>
|
"children": [
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="flat-color-icons:info"></VPIcon><span class="name">README.md</span></span></FileTreeItem>
|
{
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-markdown"></VPIcon><span class="name">foo.md</span></span></FileTreeItem>
|
"children": [],
|
||||||
</ul>
|
"info": "README.md",
|
||||||
</FileTreeItem>
|
"level": 1,
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-src"></VPIcon><span class="name">src</span></span>
|
},
|
||||||
<ul>
|
{
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-client"></VPIcon><span class="name">client</span></span>
|
"children": [],
|
||||||
<ul>
|
"info": "foo.md",
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-component"></VPIcon><span class="name">components</span></span>
|
"level": 1,
|
||||||
<ul>
|
},
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-vue"></VPIcon><span class="name focus"><strong>Navbar.vue</strong></span></span></FileTreeItem>
|
],
|
||||||
</ul>
|
"info": "docs",
|
||||||
</FileTreeItem>
|
"level": 0,
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-typescript"></VPIcon><span class="name">index.ts</span><span class="comment"># comment</span></span></FileTreeItem>
|
},
|
||||||
</ul>
|
{
|
||||||
</FileTreeItem>
|
"children": [
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:default-folder"></VPIcon><span class="name">node</span></span>
|
{
|
||||||
<ul>
|
"children": [
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-typescript"></VPIcon><span class="name">index.ts</span></span></FileTreeItem>
|
{
|
||||||
</ul>
|
"children": [
|
||||||
</FileTreeItem>
|
{
|
||||||
</ul>
|
"children": [],
|
||||||
</FileTreeItem>
|
"info": "**Navbar.vue**",
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-git"></VPIcon><span class="name">.gitignore</span></span></FileTreeItem>
|
"level": 3,
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:file-type-node"></VPIcon><span class="name">package.json</span></span></FileTreeItem>
|
},
|
||||||
</ul>
|
],
|
||||||
</div><div class="vp-file-tree"><p class="vp-file-tree-title">files</p><ul>
|
"info": "components",
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-src"></VPIcon><span class="name">src</span></span>
|
"level": 2,
|
||||||
<ul>
|
},
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:default-folder"></VPIcon><span class="name">js</span></span>
|
{
|
||||||
<ul>
|
"children": [],
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><span class="name">…</span></span></FileTreeItem>
|
"info": "index.ts # comment",
|
||||||
</ul>
|
"level": 2,
|
||||||
</FileTreeItem>
|
},
|
||||||
<FileTreeItem type="folder" :expanded="false" :empty="true"><span class="tree-node folder"><VPIcon name="vscode-icons:default-folder"></VPIcon><span class="name">vue</span></span></FileTreeItem>
|
],
|
||||||
<FileTreeItem type="folder" :expanded="false" :empty="true"><span class="tree-node folder"><VPIcon name="vscode-icons:folder-type-css"></VPIcon><span class="name">css</span></span></FileTreeItem>
|
"info": "client",
|
||||||
</ul>
|
"level": 1,
|
||||||
</FileTreeItem>
|
},
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="flat-color-icons:info"></VPIcon><span class="name">README.md</span></span></FileTreeItem>
|
{
|
||||||
</ul>
|
"children": [
|
||||||
</div><div class="vp-file-tree"><ul>
|
{
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:default-file"></VPIcon><span class="name">docs</span></span></FileTreeItem>
|
"children": [],
|
||||||
<FileTreeItem type="folder" :expanded="true" :empty="false"><span class="tree-node folder"><VPIcon name="vscode-icons:default-folder"></VPIcon><span class="name">src</span></span>
|
"info": "index.ts",
|
||||||
<ul>
|
"level": 2,
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:default-file"></VPIcon><span class="name">a.js</span></span></FileTreeItem>
|
},
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:default-file"></VPIcon><span class="name">b.ts</span></span></FileTreeItem>
|
],
|
||||||
</ul>
|
"info": "node",
|
||||||
</FileTreeItem>
|
"level": 1,
|
||||||
<FileTreeItem type="file" :expanded="false" :empty="true"><span class="tree-node file"><VPIcon name="vscode-icons:default-file"></VPIcon><span class="name">README.md</span></span></FileTreeItem>
|
},
|
||||||
</ul>
|
],
|
||||||
</div><div class="vp-file-tree"><ul>
|
"info": "src",
|
||||||
<li></li>
|
"level": 0,
|
||||||
<li>
|
},
|
||||||
<ul>
|
{
|
||||||
<li></li>
|
"children": [],
|
||||||
</ul>
|
"info": ".gitignore",
|
||||||
</li>
|
"level": 0,
|
||||||
</ul>
|
},
|
||||||
</div><div class="vp-file-tree"></div>"
|
{
|
||||||
|
"children": [],
|
||||||
|
"info": "package.json",
|
||||||
|
"level": 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`fileTreePlugin > should work with default options 1`] = `
|
||||||
|
"<div class="vp-file-tree"><FileTreeNode expanded type="folder" filename="docs">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-docs" /></template><FileTreeNode type="file" filename="README.md">
|
||||||
|
<template #icon><VPIcon name="flat-color-icons:info" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="foo.md">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-markdown" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode expanded type="folder" filename="src">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="client">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-client" /></template><FileTreeNode expanded type="folder" filename="components">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-component" /></template><FileTreeNode focus type="file" filename="Navbar.vue">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-vue" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="index.ts">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-typescript" /></template><template #comment># comment</template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode expanded type="folder" filename="node">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="index.ts">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-typescript" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename=".gitignore">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-git" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="package.json">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:file-type-node" /></template>
|
||||||
|
</FileTreeNode></div>
|
||||||
|
<div class="vp-file-tree"><p class="vp-file-tree-title">files</p><FileTreeNode expanded type="folder" filename="src">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="js">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…">
|
||||||
|
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="folder" filename="vue">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…">
|
||||||
|
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="folder" filename="css">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:folder-type-css" /></template><FileTreeNode type="file" filename="…">
|
||||||
|
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="README.md">
|
||||||
|
<template #icon><VPIcon name="flat-color-icons:info" /></template>
|
||||||
|
</FileTreeNode></div>
|
||||||
|
<div class="vp-file-tree"><FileTreeNode type="file" filename="docs">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode expanded type="folder" filename="src">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="a.js">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="b.ts">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode type="file" filename="README.md">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode></div>
|
||||||
|
<div class="vp-file-tree"><FileTreeNode type="file" filename="">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
<FileTreeNode expanded type="folder" filename="">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="">
|
||||||
|
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||||
|
</FileTreeNode>
|
||||||
|
</FileTreeNode></div>
|
||||||
|
<div class="vp-file-tree"></div>
|
||||||
|
"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,7 +1,59 @@
|
|||||||
import type { FileTreeOptions } from '../src/shared/fileTree.js'
|
import type { FileTreeOptions } from '../src/shared/fileTree.js'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { fileTreePlugin } from '../src/node/container/fileTree.js'
|
import { fileTreePlugin, parseFileTreeNodeInfo, parseFileTreeRawContent } from '../src/node/container/fileTree.js'
|
||||||
|
|
||||||
|
describe('fileTree > parseFileTreeRawContent', () => {
|
||||||
|
it('should work', () => {
|
||||||
|
const content = `\
|
||||||
|
- docs
|
||||||
|
- README.md
|
||||||
|
- foo.md
|
||||||
|
|
||||||
|
- src
|
||||||
|
- client
|
||||||
|
- components
|
||||||
|
- **Navbar.vue**
|
||||||
|
- index.ts # comment
|
||||||
|
- node
|
||||||
|
- index.ts
|
||||||
|
- .gitignore
|
||||||
|
- package.json
|
||||||
|
`
|
||||||
|
const nodes = parseFileTreeRawContent(content)
|
||||||
|
expect(nodes).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('fileTree > parseFileTreeNodeInfo', () => {
|
||||||
|
it('should work', () => {
|
||||||
|
expect(parseFileTreeNodeInfo('README.md'))
|
||||||
|
.toEqual({ filename: 'README.md', comment: '', focus: false, expanded: true, type: 'file' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('README.md # comment'))
|
||||||
|
.toEqual({ filename: 'README.md', comment: '# comment', focus: false, expanded: true, type: 'file' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('**Navbar.vue**'))
|
||||||
|
.toEqual({ filename: 'Navbar.vue', comment: '', focus: true, expanded: true, type: 'file' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('**Navbar.vue** # comment'))
|
||||||
|
.toEqual({ filename: 'Navbar.vue', comment: '# comment', focus: true, expanded: true, type: 'file' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with expanded', () => {
|
||||||
|
expect(parseFileTreeNodeInfo('folder/'))
|
||||||
|
.toEqual({ filename: 'folder', comment: '', focus: false, expanded: false, type: 'folder' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('folder/ # comment'))
|
||||||
|
.toEqual({ filename: 'folder', comment: '# comment', focus: false, expanded: false, type: 'folder' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('**folder/**'))
|
||||||
|
.toEqual({ filename: 'folder', comment: '', focus: true, expanded: false, type: 'folder' })
|
||||||
|
|
||||||
|
expect(parseFileTreeNodeInfo('**folder/** # comment'))
|
||||||
|
.toEqual({ filename: 'folder', comment: '# comment', focus: true, expanded: false, type: 'folder' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
function createMarkdown(options?: FileTreeOptions) {
|
function createMarkdown(options?: FileTreeOptions) {
|
||||||
return new MarkdownIt().use(fileTreePlugin, options)
|
return new MarkdownIt().use(fileTreePlugin, options)
|
||||||
|
|||||||
@ -1,183 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, onUnmounted, ref } from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
type: 'file' | 'folder'
|
|
||||||
expanded: boolean
|
|
||||||
empty: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const active = ref(!!props.expanded)
|
|
||||||
const el = ref<HTMLElement>()
|
|
||||||
|
|
||||||
function toggle(e: HTMLElementEventMap['click']) {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
if (target.matches('.comment') || e.currentTarget === target)
|
|
||||||
return
|
|
||||||
active.value = !active.value
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (!el.value || props.type !== 'folder')
|
|
||||||
return
|
|
||||||
|
|
||||||
el.value.querySelector('.tree-node.folder')?.addEventListener(
|
|
||||||
'click',
|
|
||||||
toggle as EventListener,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (!el.value || props.type !== 'folder')
|
|
||||||
return
|
|
||||||
|
|
||||||
el.value.querySelector('.tree-node.folder')?.removeEventListener(
|
|
||||||
'click',
|
|
||||||
toggle as EventListener,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<li ref="el" class="file-tree-item" :class="{ expanded: active }">
|
|
||||||
<slot />
|
|
||||||
<ul v-if="props.type === 'folder' && props.empty">
|
|
||||||
<li class="file-tree-item">
|
|
||||||
<span class="tree-node file">
|
|
||||||
<span class="name">…</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.vp-file-tree {
|
|
||||||
width: fit-content;
|
|
||||||
max-width: 100%;
|
|
||||||
padding: 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: var(--vp-c-bg-safe);
|
|
||||||
border: solid 1px var(--vp-c-divider);
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: border var(--vp-t-color), background-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-file-tree .vp-file-tree-title {
|
|
||||||
padding-left: 16px;
|
|
||||||
margin: -16px -16px 0;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--vp-c-text-1);
|
|
||||||
border-bottom: solid 1px var(--vp-c-divider);
|
|
||||||
transition: color var(--vp-t-color), border-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-file-tree ul {
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
list-style: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item {
|
|
||||||
margin-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-file-tree .file-tree-item {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node .name {
|
|
||||||
font-family: var(--vp-font-family-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node.folder {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node.folder > .name {
|
|
||||||
color: var(--vp-c-text-1);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node.folder > .name:hover {
|
|
||||||
color: var(--vp-c-brand-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node.folder::before {
|
|
||||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M5.536 21.886a1 1 0 0 0 1.033-.064l13-9a1 1 0 0 0 0-1.644l-13-9A1 1 0 0 0 5 3v18a1 1 0 0 0 .536.886'/%3E%3C/svg%3E");
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: 7px;
|
|
||||||
left: -14px;
|
|
||||||
display: block;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
color: var(--vp-c-text-3);
|
|
||||||
cursor: pointer;
|
|
||||||
content: "";
|
|
||||||
background-color: currentcolor;
|
|
||||||
-webkit-mask: var(--icon) no-repeat;
|
|
||||||
mask: var(--icon) no-repeat;
|
|
||||||
-webkit-mask-size: 100% 100%;
|
|
||||||
mask-size: 100% 100%;
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node .name.focus {
|
|
||||||
position: relative;
|
|
||||||
padding: 0 4px;
|
|
||||||
margin: 0 -4px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--vp-c-bg);
|
|
||||||
background-color: var(--vp-c-brand-2);
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: color var(--vp-t-color), background-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node .name.focus:hover {
|
|
||||||
color: var(--vp-c-bg);
|
|
||||||
background-color: var(--vp-c-brand-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node .comment {
|
|
||||||
margin-left: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
color: var(--vp-c-text-3);
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node [class*="vpi-"] {
|
|
||||||
width: 1.2em;
|
|
||||||
height: 1.2em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item .tree-node.folder [class*="vpi-"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-file-tree .file-tree-item > ul {
|
|
||||||
padding-left: 8px !important;
|
|
||||||
margin: 0 0 0 6px !important;
|
|
||||||
border-left: solid 1px var(--vp-c-divider);
|
|
||||||
transition: border-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item:not(.expanded) > ul {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-tree-item.expanded > .tree-node.folder::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
211
plugins/plugin-md-power/src/client/components/FileTreeNode.vue
Normal file
211
plugins/plugin-md-power/src/client/components/FileTreeNode.vue
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import { FadeInExpandTransition } from '@vuepress/helper/client'
|
||||||
|
import { inject, ref } from 'vue'
|
||||||
|
|
||||||
|
import '@vuepress/helper/transition/fade-in-height-expand.css'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
type: 'file' | 'folder'
|
||||||
|
filename: string
|
||||||
|
expanded?: boolean
|
||||||
|
focus?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const activeFileTreeNode = inject<Ref<string>>('active-file-tree-node', ref(''))
|
||||||
|
|
||||||
|
const onNodeClick = inject<
|
||||||
|
(filename: string, type: 'file' | 'folder') => void
|
||||||
|
>('on-file-tree-node-click', () => {})
|
||||||
|
|
||||||
|
const active = ref(props.expanded)
|
||||||
|
|
||||||
|
function nodeClick() {
|
||||||
|
if (props.filename === '…' || props.filename === '...')
|
||||||
|
return
|
||||||
|
|
||||||
|
onNodeClick(props.filename, props.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(ev: MouseEvent) {
|
||||||
|
if (props.type === 'folder') {
|
||||||
|
const el = ev.target as HTMLElement
|
||||||
|
if (!el.matches('.comment, .comment *')) {
|
||||||
|
active.value = !active.value
|
||||||
|
nodeClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nodeClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="vp-file-tree-node">
|
||||||
|
<p
|
||||||
|
class="vp-file-tree-info" :class="{
|
||||||
|
[type]: true,
|
||||||
|
focus,
|
||||||
|
expanded: type === 'folder' ? active : false,
|
||||||
|
active: type === 'file' ? activeFileTreeNode === filename : false,
|
||||||
|
}"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<slot name="icon" />
|
||||||
|
<span class="name" :class="[type]">{{ filename }}</span>
|
||||||
|
<span v-if="$slots.comment" class="comment"><slot name="comment" /></span>
|
||||||
|
</p>
|
||||||
|
<FadeInExpandTransition>
|
||||||
|
<div v-if="type === 'folder'" v-show="active" class="group">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</FadeInExpandTransition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vp-file-tree {
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto hidden;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: var(--vp-c-bg-safe);
|
||||||
|
border: solid 1px var(--vp-c-divider);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: border var(--vp-t-color), background-color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-title {
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin: -16px -16px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
border-bottom: solid 1px var(--vp-c-divider);
|
||||||
|
transition: color var(--vp-t-color), border-color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
height: 28px;
|
||||||
|
padding: 2px 0;
|
||||||
|
margin: 0 0 0 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
left: -16px;
|
||||||
|
z-index: 0;
|
||||||
|
display: block;
|
||||||
|
pointer-events: none;
|
||||||
|
content: "";
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background-color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.active::after,
|
||||||
|
.vp-file-tree .vp-file-tree-info:hover::after {
|
||||||
|
background-color: var(--vp-c-default-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.folder {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.folder::before {
|
||||||
|
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' d='m5.157 13.069l4.611-4.685a.546.546 0 0 0 0-.768L5.158 2.93a.55.55 0 0 1 0-.771a.53.53 0 0 1 .759 0l4.61 4.684a1.65 1.65 0 0 1 0 2.312l-4.61 4.684a.53.53 0 0 1-.76 0a.55.55 0 0 1 0-.771'/%3E%3C/svg%3E");
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: -16px;
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
cursor: pointer;
|
||||||
|
content: "";
|
||||||
|
background-color: currentcolor;
|
||||||
|
-webkit-mask: var(--icon) no-repeat;
|
||||||
|
mask: var(--icon) no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
transition: color var(--vp-t-color), transform var(--vp-t-color);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.folder.expanded::before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info .name {
|
||||||
|
position: relative;
|
||||||
|
font-family: var(--vp-font-family-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.folder .name {
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
transition: color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.focus .name {
|
||||||
|
padding: 0 4px;
|
||||||
|
margin: 0 -4px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--vp-c-bg);
|
||||||
|
background-color: var(--vp-c-brand-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: color var(--vp-t-color), background-color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info.active .name {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info:not(.focus).folder .name:hover {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-info .comment {
|
||||||
|
display: inline-block;
|
||||||
|
flex: 1 2;
|
||||||
|
height: 28px;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-left: 20px;
|
||||||
|
margin: -2px 0;
|
||||||
|
color: var(--vp-c-text-3);
|
||||||
|
cursor: auto;
|
||||||
|
transition: color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-node .group {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree .vp-file-tree-node .group::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -4px;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
content: "";
|
||||||
|
background-color: var(--vp-c-divider);
|
||||||
|
transition: background-color var(--vp-t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-file-tree [class*="vpi-"] {
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1.2em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,17 +1,14 @@
|
|||||||
import type { Markdown } from 'vuepress/markdown'
|
import type { Markdown } from 'vuepress/markdown'
|
||||||
import type { FileTreeIconMode, FileTreeOptions } from '../../shared/index.js'
|
import type { FileTreeIconMode, FileTreeOptions } from '../../shared/index.js'
|
||||||
import container from 'markdown-it-container'
|
import { removeEndingSlash } from 'vuepress/shared'
|
||||||
import Token from 'markdown-it/lib/token.mjs'
|
|
||||||
import { removeEndingSlash, removeLeadingSlash } from 'vuepress/shared'
|
|
||||||
import { defaultFile, defaultFolder, getFileIcon } from '../fileIcons/index.js'
|
import { defaultFile, defaultFolder, getFileIcon } from '../fileIcons/index.js'
|
||||||
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
import { stringifyAttrs } from '../utils/stringifyAttrs.js'
|
||||||
|
import { createContainerSyntaxPlugin } from './createContainer.js'
|
||||||
|
|
||||||
interface FileTreeNode {
|
interface FileTreeNode {
|
||||||
filename: string
|
info: string
|
||||||
type: 'folder' | 'file'
|
level: number
|
||||||
expanded: boolean
|
children: FileTreeNode[]
|
||||||
focus: boolean
|
|
||||||
empty: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileTreeAttrs {
|
interface FileTreeAttrs {
|
||||||
@ -19,11 +16,63 @@ interface FileTreeAttrs {
|
|||||||
icon?: FileTreeIconMode
|
icon?: FileTreeIconMode
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = 'file-tree'
|
export function parseFileTreeRawContent(content: string): FileTreeNode[] {
|
||||||
const closeType = `container_${type}_close`
|
const root: FileTreeNode = { info: '', level: -1, children: [] }
|
||||||
const componentName = 'FileTreeItem'
|
const stack: FileTreeNode[] = [root]
|
||||||
const itemOpen = 'file_tree_item_open'
|
const lines = content.trim().split('\n')
|
||||||
const itemClose = 'file_tree_item_close'
|
for (const line of lines) {
|
||||||
|
const match = line.match(/^(\s*)-(.*)$/)
|
||||||
|
if (!match)
|
||||||
|
continue
|
||||||
|
|
||||||
|
const level = Math.floor(match[1].length / 2) // 每两个空格为一个层级
|
||||||
|
const info = match[2].trim()
|
||||||
|
|
||||||
|
// 检索当前层级的父节点
|
||||||
|
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = stack[stack.length - 1]
|
||||||
|
const node: FileTreeNode = { info, level, children: [] }
|
||||||
|
parent.children.push(node)
|
||||||
|
stack.push(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.children
|
||||||
|
}
|
||||||
|
|
||||||
|
const RE_FOCUS = /^\*\*(.*)\*\*(?:$|\s+)/
|
||||||
|
|
||||||
|
export function parseFileTreeNodeInfo(info: string) {
|
||||||
|
let filename = ''
|
||||||
|
let comment = ''
|
||||||
|
let focus = false
|
||||||
|
let expanded: boolean | undefined = true
|
||||||
|
let type: 'folder' | 'file' = 'file'
|
||||||
|
|
||||||
|
info = info.replace(RE_FOCUS, (_, matched) => {
|
||||||
|
filename = matched
|
||||||
|
focus = true
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
if (filename === '' && !focus) {
|
||||||
|
const spaceIndex = info.indexOf(' ')
|
||||||
|
filename = info.slice(0, spaceIndex === -1 ? info.length : spaceIndex)
|
||||||
|
info = spaceIndex === -1 ? '' : info.slice(spaceIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
comment = info.trim()
|
||||||
|
|
||||||
|
if (filename.endsWith('/')) {
|
||||||
|
type = 'folder'
|
||||||
|
expanded = false
|
||||||
|
filename = removeEndingSlash(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { filename, comment, focus, expanded, type }
|
||||||
|
}
|
||||||
|
|
||||||
export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
||||||
const getIcon = (filename: string, type: 'folder' | 'file', mode?: FileTreeIconMode): string => {
|
const getIcon = (filename: string, type: 'folder' | 'file', mode?: FileTreeIconMode): string => {
|
||||||
@ -33,166 +82,44 @@ export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
|||||||
return getFileIcon(filename, type)
|
return getFileIcon(filename, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = (tokens: Token[], idx: number): string => {
|
const renderFileTree = (nodes: FileTreeNode[], meta: FileTreeAttrs): string =>
|
||||||
const { attrs } = resolveAttrs<FileTreeAttrs>(tokens[idx].info.slice(type.length - 1))
|
nodes.map((node) => {
|
||||||
|
const { info, level, children } = node
|
||||||
|
const { filename, comment, focus, expanded, type } = parseFileTreeNodeInfo(info)
|
||||||
|
const isOmit = filename === '…' || filename === '...' /* fallback */
|
||||||
|
|
||||||
if (tokens[idx].nesting === 1) {
|
if (children.length === 0 && type === 'folder') {
|
||||||
const hasRes: number[] = [] // level stack
|
children.push({ info: '…', level: level + 1, children: [] })
|
||||||
for (
|
|
||||||
let i = idx + 1;
|
|
||||||
!(tokens[i].nesting === -1
|
|
||||||
&& tokens[i].type === closeType);
|
|
||||||
++i
|
|
||||||
) {
|
|
||||||
const token = tokens[i]
|
|
||||||
if (token.type === 'list_item_open') {
|
|
||||||
const result = resolveTreeNodeInfo(tokens, token, i)
|
|
||||||
if (result) {
|
|
||||||
hasRes.push(token.level)
|
|
||||||
const [info, inline] = result
|
|
||||||
const { filename, type, expanded, empty } = info
|
|
||||||
const icon = getIcon(filename, type, attrs.icon)
|
|
||||||
|
|
||||||
token.type = itemOpen
|
|
||||||
token.tag = componentName
|
|
||||||
token.attrSet('type', type)
|
|
||||||
token.attrSet(':expanded', expanded ? 'true' : 'false')
|
|
||||||
token.attrSet(':empty', empty ? 'true' : 'false')
|
|
||||||
updateInlineToken(inline, info, icon)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hasRes.push(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (token.type === 'list_item_close') {
|
|
||||||
if (token.level === hasRes.pop()) {
|
|
||||||
token.type = itemClose
|
|
||||||
token.tag = componentName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const title = attrs.title
|
|
||||||
return `<div class="vp-file-tree">${title ? `<p class="vp-file-tree-title">${title}</p>` : ''}`
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return '</div>'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
md.use(container, type, { render })
|
const nodeType = children.length > 0 ? 'folder' : type
|
||||||
}
|
const renderedComment = comment
|
||||||
|
? `<template #comment>${md.renderInline(comment.replaceAll('#', '\#'))}</template>`
|
||||||
export function resolveTreeNodeInfo(
|
: ''
|
||||||
tokens: Token[],
|
const renderedIcon = !isOmit
|
||||||
current: Token,
|
? `<template #icon><VPIcon name="${getIcon(filename, nodeType, meta.icon)}" /></template>`
|
||||||
idx: number,
|
: ''
|
||||||
): [FileTreeNode, Token] | undefined {
|
const props = {
|
||||||
let hasInline = false
|
expanded: nodeType === 'folder' ? expanded : false,
|
||||||
let hasChildren = false
|
focus,
|
||||||
let inline!: Token
|
type: nodeType,
|
||||||
for (
|
filename,
|
||||||
let i = idx + 1;
|
}
|
||||||
!(tokens[i].level === current.level && tokens[i].type === 'list_item_close');
|
return `<FileTreeNode${stringifyAttrs(props)}>
|
||||||
++i
|
${renderedIcon}${renderedComment}${children.length > 0 ? renderFileTree(children, meta) : ''}
|
||||||
) {
|
</FileTreeNode>`
|
||||||
if (tokens[i].type === 'inline' && !hasInline) {
|
}).join('\n')
|
||||||
inline = tokens[i]
|
|
||||||
hasInline = true
|
return createContainerSyntaxPlugin(
|
||||||
}
|
md,
|
||||||
else if (tokens[i].tag === 'ul') {
|
'file-tree',
|
||||||
hasChildren = true
|
(tokens, index) => {
|
||||||
}
|
const token = tokens[index]
|
||||||
|
const nodes = parseFileTreeRawContent(token.content)
|
||||||
if (hasInline && hasChildren)
|
const meta = token.meta as FileTreeAttrs
|
||||||
break
|
return `<div class="vp-file-tree">${
|
||||||
}
|
meta.title ? `<p class="vp-file-tree-title">${meta.title}</p>` : ''
|
||||||
|
}${renderFileTree(nodes, meta)}</div>\n`
|
||||||
if (!hasInline)
|
},
|
||||||
return undefined
|
)
|
||||||
|
|
||||||
const children = inline.children!.filter(token => (token.type === 'text' && token.content) || token.tag === 'strong')
|
|
||||||
const filename = children.filter(token => token.type === 'text').map(token => token.content).join(' ').split(/\s+/)[0]
|
|
||||||
const focus = children[0]?.tag === 'strong'
|
|
||||||
const type = hasChildren || filename.endsWith('/') ? 'folder' : 'file'
|
|
||||||
const info: FileTreeNode = {
|
|
||||||
filename: removeLeadingSlash(removeEndingSlash(filename)),
|
|
||||||
type,
|
|
||||||
focus,
|
|
||||||
empty: !hasChildren,
|
|
||||||
expanded: type === 'folder' && !filename.endsWith('/'),
|
|
||||||
}
|
|
||||||
|
|
||||||
return [info, inline] as const
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateInlineToken(inline: Token, info: FileTreeNode, icon: string) {
|
|
||||||
const children = inline.children!
|
|
||||||
|
|
||||||
const tokens: Token[] = []
|
|
||||||
const wrapperOpen = new Token('span_open', 'span', 1)
|
|
||||||
const wrapperClose = new Token('span_close', 'span', -1)
|
|
||||||
|
|
||||||
wrapperOpen.attrSet('class', `tree-node ${info.type}`)
|
|
||||||
tokens.push(wrapperOpen)
|
|
||||||
|
|
||||||
if (info.filename !== '...' && info.filename !== '…') {
|
|
||||||
const iconOpen = new Token('vp_iconify_open', 'VPIcon', 1)
|
|
||||||
iconOpen.attrSet('name', icon)
|
|
||||||
const iconClose = new Token('vp_iconify_close', 'VPIcon', -1)
|
|
||||||
|
|
||||||
tokens.push(iconOpen, iconClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileOpen = new Token('span_open', 'span', 1)
|
|
||||||
fileOpen.attrSet('class', `name${info.focus ? ' focus' : ''}`)
|
|
||||||
tokens.push(fileOpen)
|
|
||||||
|
|
||||||
let isStrongTag = false
|
|
||||||
while (children.length) {
|
|
||||||
const token = children.shift()!
|
|
||||||
if (token.type === 'text' && token.content) {
|
|
||||||
if (token.content.includes(' ')) {
|
|
||||||
const [first, ...other] = token.content.split(' ')
|
|
||||||
const text = new Token('text', '', 0)
|
|
||||||
text.content = removeEndingSlash(first)
|
|
||||||
tokens.push(text)
|
|
||||||
const comment = new Token('text', '', 0)
|
|
||||||
comment.content = other.join(' ')
|
|
||||||
children.unshift(comment)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
token.content = removeEndingSlash(token.content)
|
|
||||||
tokens.push(token)
|
|
||||||
}
|
|
||||||
if (!isStrongTag)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
else if (token.tag === 'strong') {
|
|
||||||
token.content = removeEndingSlash(token.content)
|
|
||||||
tokens.push(token)
|
|
||||||
if (token.nesting === 1) {
|
|
||||||
isStrongTag = true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tokens.push(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileClose = new Token('span_close', 'span', -1)
|
|
||||||
tokens.push(fileClose)
|
|
||||||
|
|
||||||
if (children.filter(token => token.type === 'text' && token.content.trim()).length) {
|
|
||||||
const commentOpen = new Token('span_open', 'span', 1)
|
|
||||||
commentOpen.attrSet('class', 'comment')
|
|
||||||
const commentClose = new Token('span_close', 'span', -1)
|
|
||||||
|
|
||||||
tokens.push(commentOpen, ...children, commentClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens.push(wrapperClose)
|
|
||||||
inline.children = tokens
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,8 +71,8 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.fileTree) {
|
if (options.fileTree) {
|
||||||
imports.add(`import FileTreeItem from '${CLIENT_FOLDER}components/FileTreeItem.vue'`)
|
imports.add(`import FileTreeNode from '${CLIENT_FOLDER}components/FileTreeNode.vue'`)
|
||||||
enhances.add(`app.component('FileTreeItem', FileTreeItem)`)
|
enhances.add(`app.component('FileTreeNode', FileTreeNode)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.artPlayer) {
|
if (options.artPlayer) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user