Merge pull request #162 from pengzhanbo/file-tree
feat(plugin-md-power): add support file-tree container
This commit is contained in:
commit
3316ee5ebd
@ -35,7 +35,7 @@ export async function run(mode: Mode, root?: string) {
|
||||
}
|
||||
|
||||
const cdCommand = mode === Mode.create ? colors.green(`cd ${data.root}`) : ''
|
||||
const runCommand = colors.green(pm === 'yarn' ? 'yarn dev' : `${pm} run dev`)
|
||||
const runCommand = colors.green(pm === 'yarn' ? 'yarn docs:dev' : `${pm} run docs:dev`)
|
||||
const installCommand = colors.green(pm === 'yarn' ? 'yarn' : `${pm} install`)
|
||||
|
||||
progress.stop(t('spinner.stop'))
|
||||
|
||||
@ -166,6 +166,73 @@ tags:
|
||||
4. 结束
|
||||
::::
|
||||
|
||||
## 文件树
|
||||
|
||||
在 Markdown 中,你可以使用 `file-tree` 容器 来显示带有文件图标和可折叠子目录的目录结构。
|
||||
|
||||
在 `::: file-tree` 容器,使用内置的 **Markdown 无序列表语法** 指定文件和目录的组织结构。
|
||||
使用嵌套的列表项创建子目录;若希望某个目录不显示具体内容,只需在列表项末尾添加斜杠 `/` 即可。
|
||||
|
||||
以下语法可用于自定义文件树的外观:
|
||||
|
||||
- 通过加粗文件名或目录名来突出显示,例如 `**README.md**`
|
||||
- 通过在名称后添加更多文本来为文件或目录添加注释
|
||||
- 使用 `...` 或 `…` 作为名称来添加占位符文件和目录。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- .vuepress
|
||||
- config.ts
|
||||
- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
- composables
|
||||
- useNavbar.ts
|
||||
- styles
|
||||
- navbar.css
|
||||
- config.ts
|
||||
- node/
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
- .gitignore
|
||||
- README.md
|
||||
- …
|
||||
:::
|
||||
```
|
||||
|
||||
**输出:**
|
||||
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- .vuepress
|
||||
- config.ts
|
||||
- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- client
|
||||
- components
|
||||
- **Navbar.vue**
|
||||
- composables
|
||||
- useNavbar.ts
|
||||
- styles
|
||||
- navbar.css
|
||||
- config.ts
|
||||
- node/
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
- .gitignore
|
||||
- README.md
|
||||
- …
|
||||
:::
|
||||
|
||||
## 选项组
|
||||
|
||||
在 Markdown 中支持选项卡。
|
||||
|
||||
@ -9,36 +9,39 @@ tags:
|
||||
- 快速开始
|
||||
---
|
||||
|
||||
## 介绍
|
||||
|
||||
==vuepress-theme-plume== 是一个基于 VuePress 的主题,适用于 博客、文档 和 知识笔记 。
|
||||
|
||||
主题针对 文本内容、图片内容 的表现做了大量的优化,特别是针对 Markdown 内容的语法 做了 丰富的扩展,
|
||||
你可以很轻松的利用这些特性编写 漂亮、美观、易读、表现力丰富 的内容。
|
||||
|
||||
::: details 不了解 VuePress ?
|
||||
|
||||
VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) 。
|
||||
专为构建快速、以内容为中心的站点而设计。
|
||||
简而言之,VuePress 获取用 Markdown 编写的内容,对其应用主题,并生成可以轻松部署到任何地方的静态 HTML 页面。
|
||||
|
||||
::: tip
|
||||
|
||||
本主题 基于 [vuepress-next](https://github.com/vuepress/vuepress-next), 目前处于 RC 阶段。
|
||||
|
||||
当前主题 功能和 API 趋于稳定,但在未来的更新中仍有小概率出现破坏更改。
|
||||
|
||||
如果您在使用过程中遇到问题,或者有新的想法,
|
||||
请在 [Issues](https://github.com/pengzhanbo/vuepress-theme-plume/issues) 里提出,
|
||||
也欢迎 通过 [PR](https://github.com/pengzhanbo/vuepress-theme-plume/pulls) 帮助完善主题。
|
||||
|
||||
:::
|
||||
|
||||
## 优势
|
||||
|
||||
与 vuepress 默认主题相比:
|
||||
|
||||
- 大幅度优化了界面、交互,更具美观度,更好的用户体验。
|
||||
- 同时,还添加了大量的丰富实用的功能,如 代码分组、提示容器、任务列表、数学公式、代码演示、
|
||||
内容搜索、文章评论、加密 等。
|
||||
- 新增编译缓存,加快启动速度。
|
||||
- 支持使用单独的主题配置文件,避免修改配置导致频繁重启 VuePress 服务。
|
||||
- 大幅度简化了配置,更易于使用,同时还保留了丰富灵活的配置项,满足个性化的需求。
|
||||
- **更好的用户体验**
|
||||
|
||||
大幅度优化了界面、交互,更为美观、简洁,易用。
|
||||
|
||||
- **更多的功能**
|
||||
|
||||
代码分组、提示容器、任务列表、数学公式、代码演示、内容搜索、文章评论、加密、文件树 等。
|
||||
|
||||
- **更好的开发体验**
|
||||
|
||||
增加编译缓存,缓存 markdown 文件编译、缓存 复杂 代码块 内容解析结果。
|
||||
|
||||
- **更好的配置**
|
||||
|
||||
支持使用单独的主题配置文件,避免修改配置导致频繁重启 VuePress 服务。
|
||||
|
||||
大幅度简化了配置,更易于使用,同时还保留了丰富灵活的配置项,满足个性化的需求。
|
||||
|
||||
==plume 主题== 尽可能的内置你可能需要的功能,以及搭建站点所需要的一般性配置,您无需关注这些细节。
|
||||
目的是,让您更专注于 内容的创作,更好的表达你的想法,享受 Markdown 增强语法带来的便利。
|
||||
@ -52,7 +55,19 @@ VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_
|
||||
- 🔑 支持 全站加密、部分加密
|
||||
- 👀 支持 搜索、文章评论
|
||||
- 👨💻 支持 浅色/深色 主题 (包括代码高亮)
|
||||
- 📠 markdown 增强,支持 代码块分组、提示容器、任务列表、数学公式、代码演示 等
|
||||
- 📠 markdown 增强,支持 代码块分组、提示容器、任务列表、数学公式、代码演示、文件树 等
|
||||
- 📚 代码演示,支持 CodePen, Replit, JSFiddle, CodeSandbox
|
||||
- 📊 嵌入图标,支持 chart.js,Echarts,Mermaid,flowchart 等
|
||||
- 🎛 资源嵌入,支持 PDF, bilibili视频,youtube视频等
|
||||
|
||||
::: tip
|
||||
|
||||
本主题 基于 [vuepress-next](https://github.com/vuepress/vuepress-next), 目前处于 RC 阶段。
|
||||
|
||||
当前主题 功能和 API 趋于稳定,但在未来的更新中仍有小概率出现破坏更改。
|
||||
|
||||
如果您在使用过程中遇到问题,或者有新的想法,
|
||||
请在 [Issues](https://github.com/pengzhanbo/vuepress-theme-plume/issues) 里提出,
|
||||
也欢迎 通过 [PR](https://github.com/pengzhanbo/vuepress-theme-plume/pulls) 帮助完善主题。
|
||||
|
||||
:::
|
||||
|
||||
@ -13,14 +13,15 @@ tags:
|
||||
|
||||
要使用内置的 i18n (国际化) 功能,需要创建类似于下面的目录结构:
|
||||
|
||||
```
|
||||
docs/
|
||||
├─ es/
|
||||
│ ├─ foo.md
|
||||
├─ fr/
|
||||
│ ├─ foo.md
|
||||
├─ foo.md
|
||||
```
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- en
|
||||
- foo.md
|
||||
- fr
|
||||
- foo.md
|
||||
- foo.md
|
||||
:::
|
||||
|
||||
## vuepress 配置
|
||||
|
||||
|
||||
@ -18,14 +18,63 @@ const vuepressVersion = __VUEPRESS_VERSION__
|
||||
- [Node.js v18.20.0+](https://nodejs.org/)
|
||||
- [pnpm 8+](https://pnpm.io/zh/) 或 [Yarn 2+](https://yarnpkg.com/)
|
||||
|
||||
## 命令行安装
|
||||
|
||||
主题提供了一个 命令行工具,帮助您构建一个基本项目。您可以通过运行以下命令,启动 安装向导。
|
||||
|
||||
::: code-tabs
|
||||
|
||||
@tab pnpm
|
||||
|
||||
```sh
|
||||
pnpm create vuepress-theme-plume@latest
|
||||
```
|
||||
|
||||
@tab yarn
|
||||
|
||||
```sh
|
||||
yarn create vuepress-theme-plume@latest
|
||||
```
|
||||
|
||||
@tab npm
|
||||
|
||||
```sh
|
||||
npm init vuepress-theme-plume@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
启动向导后,您只需要回答几个简单的问题:
|
||||
|
||||
<!-- @include: ../snippet/create.snippet.md ---->
|
||||
|
||||
::: details 怎么使用命令行工具?
|
||||
|
||||
以 Windows 系统为例,你可以使用以下方法来启动 CMD 命令行工具:
|
||||
|
||||
1. 按下 `Win + R` 键打开 “运行” 对话框。
|
||||
2. 输入 `cmd` 并按下 Enter 键。 (也可以输入 `powershell` 来打开 PowerShell)
|
||||
|
||||
注意此时 `cmd` 可能不在你期望的目录位置,你可以使用如下命令来切换到正确的目录:
|
||||
|
||||
```sh
|
||||
D: # 此命令将切换到 D: 分区,进入其他分区请按照实际情况修改
|
||||
cd open-source # 进入 D: 分区下的 open-source 目录
|
||||
```
|
||||
|
||||
此时,你就可以在这里输入 `pnpm create vuepress-theme-plume@latest` 来创建一个基本的项目了。
|
||||
|
||||
创建的项目将位于 `D:\open-source\my-project` 目录下。
|
||||
:::
|
||||
|
||||
## 手动安装
|
||||
|
||||
::: info 提示
|
||||
|
||||
- 使用 [pnpm](https://pnpm.io/zh/) 时,你需要安装 `vue` 作为 peer-dependencies 。
|
||||
- 使用 [Yarn 2+](https://yarnpkg.com/) 时,你需要在 `.yarnrc.yml` 文件中设置 `nodeLinker: 'node-modules'` 。
|
||||
:::
|
||||
|
||||
## 安装
|
||||
|
||||
使用本主题,你需要首先新建一个项目,并安装`vuepress@next`以及本主题
|
||||
|
||||
:::: steps
|
||||
@ -110,8 +159,8 @@ const vuepressVersion = __VUEPRESS_VERSION__
|
||||
``` json :no-line-numbers
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vuepress dev docs",
|
||||
"build": "vuepress build docs"
|
||||
"docs:dev": "vuepress dev docs",
|
||||
"docs:build": "vuepress build docs"
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -188,19 +237,19 @@ const vuepressVersion = __VUEPRESS_VERSION__
|
||||
@tab pnpm
|
||||
|
||||
```sh :no-line-numbers
|
||||
pnpm dev
|
||||
pnpm docs:dev
|
||||
```
|
||||
|
||||
@tab yarn
|
||||
|
||||
``` sh :no-line-numbers
|
||||
yarn dev
|
||||
yarn docs:dev
|
||||
```
|
||||
|
||||
@tab npm
|
||||
|
||||
``` sh :no-line-numbers
|
||||
npm run dev
|
||||
npm docs:run dev
|
||||
```
|
||||
|
||||
:::
|
||||
@ -210,3 +259,34 @@ const vuepressVersion = __VUEPRESS_VERSION__
|
||||
- ### 完成
|
||||
|
||||
::::
|
||||
|
||||
## 文件结构
|
||||
|
||||
使用命令行工具创建的项目,它的文件结构是这样的。(如果你是手动创建的,也可以参考此文件结构管理您的项目)
|
||||
|
||||
::: file-tree
|
||||
|
||||
- .git/
|
||||
- docs \# 文档源目录
|
||||
- .vuepress
|
||||
- public/ \# 静态资源目录
|
||||
- client.ts
|
||||
- config.ts \# vuepress 配值文件
|
||||
- navbar.ts \# 导航栏配置
|
||||
- notes.ts \# notes 配置
|
||||
- plume.config.ts \# 主题配置文件
|
||||
- notes
|
||||
- demo
|
||||
- foo.md
|
||||
- bar.md
|
||||
- preview
|
||||
- markdown.md
|
||||
- README.md \# 首页
|
||||
- package.json
|
||||
- package-lock.json
|
||||
- .gitignore
|
||||
- README.md
|
||||
:::
|
||||
|
||||
在 `docs` 目录中, 除 `.vuepress` 目录外,目录中的 所有 markdown 文件都会被识别为文档。
|
||||
其中,除 `notes` 目录外的 `markdown` 文件会被识别为 博客文章,而 `notes` 目录下 `markdown` 文件会被识别为 文档笔记。
|
||||
|
||||
@ -22,14 +22,16 @@ tags:
|
||||
|
||||
示例:
|
||||
|
||||
``` :no-line-numbers
|
||||
{sourceDir}/
|
||||
├─ notes/
|
||||
│ ├─ typescript/
|
||||
│ │ └─ foo.md
|
||||
│ └─ rust/
|
||||
│ └─ foo.md
|
||||
```
|
||||
::: file-tree
|
||||
|
||||
- \{sourceDir\}
|
||||
- notes
|
||||
- typescript
|
||||
- foo.md
|
||||
- rust
|
||||
- foo.md
|
||||
|
||||
:::
|
||||
|
||||
其中,`typescript` 和 `rust` 为目录名,各自独立保存与之相关的 markdown 文件。
|
||||
|
||||
|
||||
@ -40,15 +40,16 @@ const dir = /\d+\.[\s\S]+/
|
||||
|
||||
__example:__
|
||||
|
||||
``` txt :no-line-numbers
|
||||
.{sourceDir}
|
||||
- 1.前端
|
||||
- 1.html
|
||||
- 2.css
|
||||
- 3.javascript
|
||||
- 2.后端
|
||||
- 运维
|
||||
```
|
||||
::: file-tree
|
||||
|
||||
- \{sourceDir\}
|
||||
- 1.前端/
|
||||
- 1.html/
|
||||
- 2.css/
|
||||
- 3.javascript/
|
||||
- 2.后端/
|
||||
- 运维/
|
||||
:::
|
||||
|
||||
## 文章写作
|
||||
|
||||
|
||||
@ -10,8 +10,7 @@ tags:
|
||||
---
|
||||
|
||||
::: tip
|
||||
此文档 fork 自 [vuepress official doc](https://v2.vuepress.vuejs.org/zh/guide/deployment.html),
|
||||
仅为了方便阅读。
|
||||
此文档 fork 自 [vuepress official doc](https://v2.vuepress.vuejs.org/zh/guide/deployment.html)。
|
||||
:::
|
||||
|
||||
下述的指南基于以下条件:
|
||||
@ -64,7 +63,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
# 选择要使用的 pnpm 版本
|
||||
version: 8
|
||||
|
||||
42
docs/notes/theme/snippet/create.snippet.md
Normal file
42
docs/notes/theme/snippet/create.snippet.md
Normal file
@ -0,0 +1,42 @@
|
||||
```ansi :no-line-numbers :collapsed-lines
|
||||
[0;90m┌[0m [0;36;1mWelcome to VuePress and vuepress-theme-plume ![0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m Select a language to display / 选择显示语言[0m
|
||||
[0;90m│[0m[0m [0;2m简体中文[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 您想在哪里初始化 VuePress?[0m
|
||||
[0;90m│[0m[0m [0;2m./my-project[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 站点名称:[0m
|
||||
[0;90m│[0m[0m [0;2mMy Vuepress Site[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 站点描述信息:[0m
|
||||
[0;90m│[0m[0m [0;2mMy Vuepress Site Description[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 是否使用多语言?[0m
|
||||
[0;90m│[0m[0m [0;2mNo[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 请选择站点默认语言[0m
|
||||
[0;90m│[0m[0m [0;2m简体中文[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 是否使用 TypeScript?[0m
|
||||
[0;90m│[0m[0m [0;2mYes[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 请选择打包工具[0m
|
||||
[0;90m│[0m[0m [0;2mVite[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 部署方式:[0m
|
||||
[0;90m│[0m[0m [0;2mCustom[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 是否初始化 git 仓库?[0m
|
||||
[0;90m│[0m[0m [0;2mYes[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 是否安装依赖?[0m
|
||||
[0;90m│[0m[0m [0;2mYes[0m[0m
|
||||
[0;90m│[0m[0m
|
||||
[0;32m◇[0m 🎉 创建成功![0m
|
||||
[0;90m│[0m[0m
|
||||
[0;90m└[0m[0m 🔨 执行以下命令即可启动:[0m
|
||||
[0;32mcd ./my-project[0m
|
||||
[0;32mpnpm run docs:dev[0m
|
||||
```
|
||||
@ -12,7 +12,7 @@
|
||||
"vuepress": "2.0.0-rc.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.242",
|
||||
"@iconify/json": "^2.2.243",
|
||||
"@simonwep/pickr": "^1.9.1",
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.15",
|
||||
"chart.js": "^4.4.4",
|
||||
|
||||
@ -30,3 +30,4 @@ readingTime: false
|
||||
| ---- | ---------- | ------ | ------------------ |
|
||||
| **锋 | 2024-04-18 | 6.88 | 支持下作者,加油! |
|
||||
| *杰 | 2024-05-25 | 6.00 | 请你喝杯茶,加油 |
|
||||
| **党 | 2024-08-22 | 8.80 | 感谢开源,加油 |
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^9.9.1",
|
||||
"husky": "^9.1.5",
|
||||
"lint-staged": "^15.2.9",
|
||||
"lint-staged": "^15.2.10",
|
||||
"rimraf": "^6.0.1",
|
||||
"stylelint": "^16.9.0",
|
||||
"tsconfig-vuepress": "^5.0.0",
|
||||
|
||||
@ -44,8 +44,8 @@
|
||||
"@vueuse/core": "^11.0.3",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.14.1",
|
||||
"tm-grammars": "^1.17.8",
|
||||
"shiki": "^1.15.2",
|
||||
"tm-grammars": "^1.17.11",
|
||||
"tm-themes": "^1.8.1",
|
||||
"vue": "^3.4.38"
|
||||
},
|
||||
|
||||
151
plugins/plugin-md-power/src/client/components/FileTreeItem.vue
Normal file
151
plugins/plugin-md-power/src/client/components/FileTreeItem.vue
Normal file
@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
type: 'file' | 'folder'
|
||||
expanded: boolean
|
||||
empty: boolean
|
||||
}>()
|
||||
|
||||
const active = ref(!!props.expanded)
|
||||
const el = ref<HTMLElement>()
|
||||
|
||||
onMounted(() => {
|
||||
if (!el.value || props.type !== 'folder')
|
||||
return
|
||||
|
||||
el.value.querySelector('.tree-node.folder')?.addEventListener('click', () => {
|
||||
active.value = !active.value
|
||||
})
|
||||
})
|
||||
</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(--t-color), background-color var(--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(--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);
|
||||
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(--t-color);
|
||||
}
|
||||
|
||||
.file-tree-item .tree-node.file .name.focus {
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-brand-1);
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
|
||||
.file-tree-item .tree-node .comment {
|
||||
margin-left: 20px;
|
||||
overflow: hidden;
|
||||
color: var(--vp-c-text-3);
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
|
||||
.file-tree-item .tree-node [class*="vp-fti-"] {
|
||||
display: inline-block;
|
||||
width: 0.9em;
|
||||
height: 0.9em;
|
||||
color: var(--vp-c-text-2);
|
||||
background-color: currentcolor;
|
||||
-webkit-mask: var(--icon) no-repeat;
|
||||
mask: var(--icon) no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
|
||||
.file-tree-item .tree-node.folder [class*="vp-fti-"] {
|
||||
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(--t-color);
|
||||
}
|
||||
|
||||
.file-tree-item:not(.expanded) > ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-tree-item.expanded > .tree-node.folder::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,74 @@
|
||||
import { FileIcons, definitions } from './icons.js'
|
||||
|
||||
export interface FileIcon {
|
||||
name: string
|
||||
svg: string
|
||||
}
|
||||
|
||||
export const defaultFileIcon: FileIcon = {
|
||||
name: 'default',
|
||||
svg: makeSVGIcon(FileIcons['seti:default']),
|
||||
}
|
||||
|
||||
export const folderIcon: FileIcon = {
|
||||
name: 'folder',
|
||||
svg: makeSVGIcon(FileIcons['seti:folder']),
|
||||
}
|
||||
|
||||
export function getFileIcon(fileName: string): FileIcon {
|
||||
const name = getFileIconName(fileName)
|
||||
if (!name)
|
||||
return defaultFileIcon
|
||||
|
||||
if (name in FileIcons) {
|
||||
const path = FileIcons[name as keyof typeof FileIcons]
|
||||
return {
|
||||
name: name.includes(':') ? name.split(':')[1] : name,
|
||||
svg: makeSVGIcon(path),
|
||||
}
|
||||
}
|
||||
return defaultFileIcon
|
||||
}
|
||||
|
||||
function makeSVGIcon(svg: string): string {
|
||||
svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">${svg}</svg>`
|
||||
.replace(/"/g, '\'')
|
||||
.replace(/%/g, '%25')
|
||||
.replace(/#/g, '%23')
|
||||
.replace(/\{/g, '%7B')
|
||||
.replace(/\}/g, '%7D')
|
||||
.replace(/</g, '%3C')
|
||||
.replace(/>/g, '%3E')
|
||||
return `url("data:image/svg+xml,${svg}")`
|
||||
}
|
||||
|
||||
function getFileIconName(fileName: string) {
|
||||
let icon: string | undefined = definitions.files[fileName]
|
||||
if (icon)
|
||||
return icon
|
||||
icon = getFileIconTypeFromExtension(fileName)
|
||||
if (icon)
|
||||
return icon
|
||||
for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
|
||||
if (fileName.includes(partial))
|
||||
return partialIcon
|
||||
}
|
||||
return icon
|
||||
}
|
||||
|
||||
function getFileIconTypeFromExtension(fileName: string): string | undefined {
|
||||
const firstDotIndex = fileName.indexOf('.')
|
||||
if (firstDotIndex === -1)
|
||||
return
|
||||
let extension = fileName.slice(firstDotIndex)
|
||||
while (extension !== '') {
|
||||
const icon = definitions.extensions[extension]
|
||||
if (icon)
|
||||
return icon
|
||||
const nextDotIndex = extension.indexOf('.', 1)
|
||||
if (nextDotIndex === -1)
|
||||
return
|
||||
extension = extension.slice(nextDotIndex)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
775
plugins/plugin-md-power/src/node/features/fileTree/icons.ts
Normal file
775
plugins/plugin-md-power/src/node/features/fileTree/icons.ts
Normal file
File diff suppressed because one or more lines are too long
77
plugins/plugin-md-power/src/node/features/fileTree/index.ts
Normal file
77
plugins/plugin-md-power/src/node/features/fileTree/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import fs from 'node:fs'
|
||||
import container from 'markdown-it-container'
|
||||
import type { Markdown } from 'vuepress/markdown'
|
||||
import type Token from 'markdown-it/lib/token.mjs'
|
||||
import type { App } from 'vuepress/core'
|
||||
import { resolveTreeNodeInfo, updateInlineToken } from './resolveTreeNodeInfo.js'
|
||||
import { type FileIcon, folderIcon, getFileIcon } from './findIcon.js'
|
||||
|
||||
const type = 'file-tree'
|
||||
const closeType = `container_${type}_close`
|
||||
const componentName = 'FileTreeItem'
|
||||
const itemOpen = 'file_tree_item_open'
|
||||
const itemClose = 'file_tree_item_close'
|
||||
const classPrefix = 'vp-fti-'
|
||||
const styleFilepath = 'internal/md-power/file-tree.css'
|
||||
|
||||
export async function fileTreePlugin(app: App, md: Markdown) {
|
||||
const validate = (info: string): boolean => info.trim().startsWith(type)
|
||||
const render = (tokens: Token[], idx: number): string => {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
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) {
|
||||
const [info, inline] = result
|
||||
const { filename, type, expanded, empty } = info
|
||||
const icon = type === 'file' ? getFileIcon(filename) : folderIcon
|
||||
|
||||
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, `${classPrefix}${icon.name}`)
|
||||
addIcon(icon)
|
||||
}
|
||||
}
|
||||
else if (token.type === 'list_item_close') {
|
||||
token.type = itemClose
|
||||
token.tag = componentName
|
||||
}
|
||||
}
|
||||
return '<div class="vp-file-tree">'
|
||||
}
|
||||
else {
|
||||
return '</div>'
|
||||
}
|
||||
}
|
||||
|
||||
let timer: NodeJS.Timeout | null = null
|
||||
const icons: Record<string, FileIcon> = {}
|
||||
|
||||
function addIcon(icon: FileIcon) {
|
||||
icons[icon.name] = icon
|
||||
|
||||
if (timer)
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(async () => {
|
||||
let content = ''
|
||||
for (const icon of Object.values(icons)) {
|
||||
content += `.${classPrefix}${icon.name} { --icon: ${icon.svg}; }\n`
|
||||
}
|
||||
await app.writeTemp(styleFilepath, content)
|
||||
}, 150)
|
||||
}
|
||||
|
||||
md.use(container, type, { validate, render })
|
||||
|
||||
if (!fs.existsSync(app.dir.temp(styleFilepath)))
|
||||
await app.writeTemp(styleFilepath, '')
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
import { removeLeadingSlash } from 'vuepress/shared'
|
||||
import Token from 'markdown-it/lib/token.mjs'
|
||||
|
||||
interface FileTreeNode {
|
||||
filename: string
|
||||
type: 'folder' | 'file'
|
||||
expanded: boolean
|
||||
focus: boolean
|
||||
empty: boolean
|
||||
}
|
||||
|
||||
export function resolveTreeNodeInfo(
|
||||
tokens: Token[],
|
||||
current: Token,
|
||||
idx: number,
|
||||
): [FileTreeNode, Token] | undefined {
|
||||
let hasInline = false
|
||||
let hasChildren = false
|
||||
let inline!: Token
|
||||
for (
|
||||
let i = idx + 1;
|
||||
!(tokens[i].level === current.level && tokens[i].type === 'list_item_close');
|
||||
++i
|
||||
) {
|
||||
if (tokens[i].type === 'inline' && !hasInline) {
|
||||
inline = tokens[i]
|
||||
hasInline = true
|
||||
}
|
||||
else if (tokens[i].tag === 'ul') {
|
||||
hasChildren = true
|
||||
}
|
||||
|
||||
if (hasInline && hasChildren)
|
||||
break
|
||||
}
|
||||
|
||||
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(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
|
||||
if (!children)
|
||||
return
|
||||
|
||||
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('span_open', 'span', 1)
|
||||
iconOpen.attrSet('class', icon)
|
||||
const iconClose = new Token('span_close', 'span', -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 = first
|
||||
tokens.push(text)
|
||||
const comment = new Token('text', '', 0)
|
||||
comment.content = other.join(' ')
|
||||
children.unshift(comment)
|
||||
}
|
||||
else {
|
||||
tokens.push(token)
|
||||
}
|
||||
if (!isStrongTag)
|
||||
break
|
||||
}
|
||||
else if (token.tag === 'strong') {
|
||||
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
|
||||
}
|
||||
@ -14,6 +14,7 @@ import { jsfiddlePlugin } from './features/jsfiddle.js'
|
||||
import { plotPlugin } from './features/plot.js'
|
||||
import { langReplPlugin } from './features/langRepl.js'
|
||||
import { prepareConfigFile } from './prepareConfigFile.js'
|
||||
import { fileTreePlugin } from './features/fileTree/index.js'
|
||||
|
||||
export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): Plugin {
|
||||
return (app) => {
|
||||
@ -89,12 +90,17 @@ export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): P
|
||||
options.plot === true
|
||||
|| (typeof options.plot === 'object' && options.plot.tag !== false)
|
||||
) {
|
||||
// =|plot|=
|
||||
// !!plot!!
|
||||
md.use(plotPlugin)
|
||||
}
|
||||
|
||||
if (options.repl)
|
||||
await langReplPlugin(app, md, options.repl)
|
||||
|
||||
if (options.fileTree) {
|
||||
// ::: file-tree
|
||||
await fileTreePlugin(app, md)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +54,12 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
|
||||
enhances.add(`app.component('CanIUseViewer', CanIUse)`)
|
||||
}
|
||||
|
||||
if (options.fileTree) {
|
||||
imports.add(`import FileTreeItem from '${CLIENT_FOLDER}components/FileTreeItem.vue'`)
|
||||
imports.add(`import '@internal/md-power/file-tree.css'`)
|
||||
enhances.add(`app.component('FileTreeItem', FileTreeItem)`)
|
||||
}
|
||||
|
||||
return app.writeTemp(
|
||||
'md-power/config.js',
|
||||
`\
|
||||
|
||||
@ -26,6 +26,7 @@ export interface MarkdownPowerPluginOptions {
|
||||
|
||||
// container
|
||||
repl?: false | ReplOptions
|
||||
fileTree?: boolean
|
||||
|
||||
caniuse?: boolean | CanIUseOptions
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ export default defineConfig(() => {
|
||||
entry: ['./src/node/index.ts'],
|
||||
outDir: './lib/node',
|
||||
target: 'node18',
|
||||
external: ['markdown-it'],
|
||||
},
|
||||
// client
|
||||
...config.map(({ dir, files }) => ({
|
||||
|
||||
@ -36,8 +36,8 @@
|
||||
"vuepress": "2.0.0-rc.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"@shikijs/transformers": "^1.14.1",
|
||||
"@shikijs/twoslash": "^1.14.1",
|
||||
"@shikijs/transformers": "^1.15.2",
|
||||
"@shikijs/twoslash": "^1.15.2",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@vuepress/helper": "2.0.0-rc.42",
|
||||
"@vueuse/core": "^11.0.3",
|
||||
@ -46,7 +46,7 @@
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"mdast-util-to-hast": "^13.2.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.14.1",
|
||||
"shiki": "^1.15.2",
|
||||
"twoslash": "^0.2.9",
|
||||
"twoslash-vue": "^0.2.9"
|
||||
},
|
||||
|
||||
91
pnpm-lock.yaml
generated
91
pnpm-lock.yaml
generated
@ -51,8 +51,8 @@ importers:
|
||||
specifier: ^9.1.5
|
||||
version: 9.1.5
|
||||
lint-staged:
|
||||
specifier: ^15.2.9
|
||||
version: 15.2.9
|
||||
specifier: ^15.2.10
|
||||
version: 15.2.10
|
||||
rimraf:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
@ -96,8 +96,8 @@ importers:
|
||||
docs:
|
||||
dependencies:
|
||||
'@iconify/json':
|
||||
specifier: ^2.2.242
|
||||
version: 2.2.242
|
||||
specifier: ^2.2.243
|
||||
version: 2.2.243
|
||||
'@simonwep/pickr':
|
||||
specifier: ^1.9.1
|
||||
version: 1.9.1
|
||||
@ -163,11 +163,11 @@ importers:
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
shiki:
|
||||
specifier: ^1.14.1
|
||||
version: 1.14.1
|
||||
specifier: ^1.15.2
|
||||
version: 1.15.2
|
||||
tm-grammars:
|
||||
specifier: ^1.17.8
|
||||
version: 1.17.8
|
||||
specifier: ^1.17.11
|
||||
version: 1.17.11
|
||||
tm-themes:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1
|
||||
@ -218,11 +218,11 @@ importers:
|
||||
plugins/plugin-shikiji:
|
||||
dependencies:
|
||||
'@shikijs/transformers':
|
||||
specifier: ^1.14.1
|
||||
version: 1.14.1
|
||||
specifier: ^1.15.2
|
||||
version: 1.15.2
|
||||
'@shikijs/twoslash':
|
||||
specifier: ^1.14.1
|
||||
version: 1.14.1(typescript@5.5.4)
|
||||
specifier: ^1.15.2
|
||||
version: 1.15.2(typescript@5.5.4)
|
||||
'@types/hast':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4
|
||||
@ -248,8 +248,8 @@ importers:
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
shiki:
|
||||
specifier: ^1.14.1
|
||||
version: 1.14.1
|
||||
specifier: ^1.15.2
|
||||
version: 1.15.2
|
||||
twoslash:
|
||||
specifier: ^0.2.9
|
||||
version: 0.2.9(typescript@5.5.4)
|
||||
@ -375,8 +375,8 @@ importers:
|
||||
version: link:../plugins/plugin-md-power
|
||||
devDependencies:
|
||||
'@iconify/json':
|
||||
specifier: ^2.2.242
|
||||
version: 2.2.242
|
||||
specifier: ^2.2.243
|
||||
version: 2.2.243
|
||||
|
||||
packages:
|
||||
|
||||
@ -1010,8 +1010,8 @@ packages:
|
||||
resolution: {integrity: sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
'@iconify/json@2.2.242':
|
||||
resolution: {integrity: sha512-cS6eYdx1C1GhqaZm25ztH5yoghCaTXGJBeseUkS259GxxX9obtGLLk0yy+twxpNCD5/F9gjbgxh46BjNWsHtwg==}
|
||||
'@iconify/json@2.2.243':
|
||||
resolution: {integrity: sha512-92pXEKdjDmH8sV3fZwJTxifKpJtp3ie8KD7oCqHRJh/Z+CoGm3bzDcK5UnWs5G5EDbu00kYVcSGM4jRnY9HgGQ==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
@ -1551,14 +1551,14 @@ packages:
|
||||
'@sec-ant/readable-stream@0.4.1':
|
||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||
|
||||
'@shikijs/core@1.14.1':
|
||||
resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==}
|
||||
'@shikijs/core@1.15.2':
|
||||
resolution: {integrity: sha512-hi6XZuwHYn6bU4wtXZxST8ynM55aiU2+rVU9aPIrSxqKmEKl4d65puwGsggwcZWTET+7zGXKe7AUj46iQ8Aq8w==}
|
||||
|
||||
'@shikijs/transformers@1.14.1':
|
||||
resolution: {integrity: sha512-JJqL8QBVCJh3L61jqqEXgFq1cTycwjcGj7aSmqOEsbxnETM9hRlaB74QuXvY/fVJNjbNt8nvWo0VwAXKvMSLRg==}
|
||||
'@shikijs/transformers@1.15.2':
|
||||
resolution: {integrity: sha512-J+3kFFXb4hN3esMzdDBGb2GhBsMPX8UC3o/U9G4Jognb8k0ADQAzZkShTARwS76O0g2VFoMu4vnIchiVE6x/uw==}
|
||||
|
||||
'@shikijs/twoslash@1.14.1':
|
||||
resolution: {integrity: sha512-b0krVIqVCpdh9Gji+gTSJp0n2KyepPmnjKEDs+dUb765MUcyfN9qK/vRr7fA/YdAJxab8IDpz1GbLl0GuzAyFQ==}
|
||||
'@shikijs/twoslash@1.15.2':
|
||||
resolution: {integrity: sha512-xGMsTd5QYL1cP7bjAbEl2YckyC7D8EOhsRy/ie13izjszjPPTSWl0fJs6cb5bEOLxa8ivFwUtwepr8RwWlE6yw==}
|
||||
|
||||
'@sideway/address@4.1.5':
|
||||
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
|
||||
@ -4005,8 +4005,8 @@ packages:
|
||||
linkify-it@5.0.0:
|
||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||
|
||||
lint-staged@15.2.9:
|
||||
resolution: {integrity: sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==}
|
||||
lint-staged@15.2.10:
|
||||
resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
@ -4363,10 +4363,6 @@ packages:
|
||||
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
micromatch@4.0.7:
|
||||
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
micromatch@4.0.8:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
@ -5080,8 +5076,8 @@ packages:
|
||||
shell-quote@1.8.1:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
|
||||
shiki@1.14.1:
|
||||
resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==}
|
||||
shiki@1.15.2:
|
||||
resolution: {integrity: sha512-M+7QZQZiZw/cZeizrC/yryG3eeG8pTUhu7ZaHxVyzPNFIRIlN46YBciquoNPCiXiwLnx6JB62f3lSuSYQrus1w==}
|
||||
|
||||
side-channel@1.0.6:
|
||||
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
|
||||
@ -5366,8 +5362,8 @@ packages:
|
||||
tinyexec@0.2.0:
|
||||
resolution: {integrity: sha512-au8dwv4xKSDR+Fw52csDo3wcDztPdne2oM1o/7LFro4h6bdFmvyUAeAfX40pwDtzHgRFqz1XWaUqgKS2G83/ig==}
|
||||
|
||||
tm-grammars@1.17.8:
|
||||
resolution: {integrity: sha512-Qw67JNutL9LCt8FFw5RfsogeQ40iSeqrTHDSp0ecnY/b+ZweK8izlw6y/ZMje2+I6DMtTiBOCgmXEf+2oH11jQ==}
|
||||
tm-grammars@1.17.11:
|
||||
resolution: {integrity: sha512-qxTMAxbmY9NqBxTSaeFkI7ZCofhp+SVtrCLdf5OkcX6iiazUTL5LTLpzw0ZSFUW0RRr0Z2kMepIup/WDBha4sQ==}
|
||||
|
||||
tm-themes@1.8.1:
|
||||
resolution: {integrity: sha512-jTUfDRn5TysYhkxxEWBQDo1C1n4yoHcnfNNqXkVxIMGQCgal/9poGuMBsfbnZCPEmFVcN2rtrUwaOJ8s2hVQXg==}
|
||||
@ -6440,7 +6436,7 @@ snapshots:
|
||||
|
||||
'@hutson/parse-repository-url@5.0.0': {}
|
||||
|
||||
'@iconify/json@2.2.242':
|
||||
'@iconify/json@2.2.243':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
pathe: 1.1.2
|
||||
@ -6903,17 +6899,17 @@ snapshots:
|
||||
|
||||
'@sec-ant/readable-stream@0.4.1': {}
|
||||
|
||||
'@shikijs/core@1.14.1':
|
||||
'@shikijs/core@1.15.2':
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
'@shikijs/transformers@1.14.1':
|
||||
'@shikijs/transformers@1.15.2':
|
||||
dependencies:
|
||||
shiki: 1.14.1
|
||||
shiki: 1.15.2
|
||||
|
||||
'@shikijs/twoslash@1.14.1(typescript@5.5.4)':
|
||||
'@shikijs/twoslash@1.15.2(typescript@5.5.4)':
|
||||
dependencies:
|
||||
'@shikijs/core': 1.14.1
|
||||
'@shikijs/core': 1.15.2
|
||||
twoslash: 0.2.9(typescript@5.5.4)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -9774,7 +9770,7 @@ snapshots:
|
||||
dependencies:
|
||||
uc.micro: 2.1.0
|
||||
|
||||
lint-staged@15.2.9:
|
||||
lint-staged@15.2.10:
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
commander: 12.1.0
|
||||
@ -9782,7 +9778,7 @@ snapshots:
|
||||
execa: 8.0.1
|
||||
lilconfig: 3.1.2
|
||||
listr2: 8.2.4
|
||||
micromatch: 4.0.7
|
||||
micromatch: 4.0.8
|
||||
pidtree: 0.6.0
|
||||
string-argv: 0.3.2
|
||||
yaml: 2.5.0
|
||||
@ -10432,11 +10428,6 @@ snapshots:
|
||||
braces: 3.0.2
|
||||
picomatch: 2.3.1
|
||||
|
||||
micromatch@4.0.7:
|
||||
dependencies:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
micromatch@4.0.8:
|
||||
dependencies:
|
||||
braces: 3.0.3
|
||||
@ -11145,9 +11136,9 @@ snapshots:
|
||||
|
||||
shell-quote@1.8.1: {}
|
||||
|
||||
shiki@1.14.1:
|
||||
shiki@1.15.2:
|
||||
dependencies:
|
||||
'@shikijs/core': 1.14.1
|
||||
'@shikijs/core': 1.15.2
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
side-channel@1.0.6:
|
||||
@ -11458,7 +11449,7 @@ snapshots:
|
||||
|
||||
tinyexec@0.2.0: {}
|
||||
|
||||
tm-grammars@1.17.8: {}
|
||||
tm-grammars@1.17.11: {}
|
||||
|
||||
tm-themes@1.8.1: {}
|
||||
|
||||
|
||||
@ -109,6 +109,6 @@
|
||||
"vuepress-plugin-md-power": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.242"
|
||||
"@iconify/json": "^2.2.243"
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,6 +131,9 @@ export function getPlugins({
|
||||
if (pluginOptions.markdownPower !== false) {
|
||||
plugins.push(markdownPowerPlugin({
|
||||
caniuse: pluginOptions.caniuse,
|
||||
fileTree: true,
|
||||
plot: true,
|
||||
icons: true,
|
||||
...pluginOptions.markdownPower || {},
|
||||
repl: pluginOptions.markdownPower?.repl
|
||||
? { theme: shikiTheme, ...pluginOptions.markdownPower?.repl }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Plugin } from 'vuepress/core'
|
||||
import type { MarkdownEnv } from 'vuepress/markdown'
|
||||
|
||||
const REG_HEADING = /^#\s*?(\S.*)?\n/
|
||||
const REG_HEADING = /^#\s*?([^#\s].*)?\n/
|
||||
|
||||
export function markdownTitlePlugin(): Plugin {
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user