跳转到内容

Code Tree

npm versionnpm monthly downloadsnpm sizenpm license

代码树插件,用于在 Markdown 中渲染带文件树侧边栏的代码结构,支持文件切换查看。

该插件依赖于 文件树 插件提供的文件树侧边栏组件。

安装

sh
pnpm add vitepress-plugin-code-tree
sh
npm install vitepress-plugin-code-tree
sh
bun add vitepress-plugin-code-tree
sh
deno add vitepress-plugin-code-tree
sh
yarn add vitepress-plugin-code-tree

使用

vitepress-tuck 模式 推荐

.vitepress/config.ts
ts
import { defineConfig } from 'vitepress-tuck'
import codeTree from 'vitepress-plugin-code-tree'

export default defineConfig({
  plugins: [codeTree()],
})

查看 vitepress-tuck 了解更多

传统模式

.vitepress/config.ts
ts
import { defineConfig } from 'vitepress'
import { codeTreeMarkdownPlugin } from 'vitepress-plugin-code-tree'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(codeTreeMarkdownPlugin)
    },
  },
})
.vitepress/theme/index.ts
ts
import type { Theme } from 'vitepress'
import { enhanceAppWithCodeTree } from 'vitepress-plugin-code-tree/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCodeTree(ctx)
  },
} satisfies Theme

语法

插件提供两种语法来渲染代码树:用于内联文件内容的容器语法,以及用于从目录加载文件的嵌入语法。

容器语法

使用 ::: code-tree 容器,内部包含围栏代码块。每个围栏代码块必须通过信息字符串中的 [文件名] 语法声明文件名。

md
::: code-tree title="项目结构"

```ts [index.ts]
const a = 1
```

```rs [main.rs]
fn main() {
    println!("Hello, world!");
}
```

:::

容器属性

属性说明默认值
title代码树标题-
height代码树容器高度420px
entry入口文件,默认打开-
show-sidebar是否默认显示侧边栏false

默认激活文件

在围栏代码块的信息字符串中添加 :active 标记,可以将该文件设为默认激活的文件:

md
::: code-tree

```ts [index.ts] :active
const a = 1
```

```ts [utils.ts]
export const noop = () => {}
```

:::

嵌入语法

使用 @[code-tree](dir) 嵌入一个目录作为代码树。目录中的文件会被自动加载并渲染。

md
@[code-tree](./src)

dir 支持以下路径前缀:

前缀说明
@相对于 VitePress srcDir
/相对于 VitePress 项目根目录
-相对于当前 markdown 文件所在目录

嵌入属性

md
@[code-tree title="源码" height="500px" entry="index.ts" show-sidebar=true](./src)

配置

CodeTreePluginOptions

ts
interface CodeTreePluginOptions {
  /**
   * 代码树容器默认高度
   * @default '420px'
   */
  height?: string | number

  /**
   * 忽略文件的 glob 路径模式
   * 在通过嵌入语法从目录加载文件时生效
   * node_modules 和 .DS_Store 始终会被忽略
   * @default []
   */
  ignores?: string[]

  /**
   * 资源文件加载器
   * 使用 `@[code-tree](dir)` 嵌入目录时,用于加载资源文件
   * 自定义加载器会合并到内置加载器之前,因此具有更高优先级
   * @default []
   */
  loaders?: CodeTreeFileLoader[]
}

文件加载器

加载器用于嵌入语法加载文件内容。插件内置了常见文件类型的加载器,自定义加载器会合并到内置加载器之前,因此具有更高优先级。

内置加载器覆盖以下文件类型:

  • 点文件(.git*.env*.*ignore.npmrc):以纯文本渲染
  • .XXXrc 配置文件(如 .eslintrc):以 JSON 格式渲染
  • 图片文件:渲染为 <img> 标签,并正确解析 src
  • Shiki 支持的源文件:渲染为带语法高亮的围栏代码块
ts
import codeTree, { loadCodeContent } from 'vitepress-plugin-code-tree'
import { defineConfig } from 'vitepress-tuck'

export default defineConfig({
  plugins: [
    codeTree({
      height: '500px',
      ignores: ['**/*.test.ts'],
      loaders: [
        {
          filter: ['**/*.md'],
          load: (file) => loadCodeContent(file, 'md'),
        },
      ],
    }),
  ],
})

filter 字段接受 glob 模式字符串、glob 模式数组,或接收 CodeTreeFile 并返回布尔值的断言函数。

示例

容器语法

md
:::code-tree title="代码树" show-sidebar
```ts [index.ts]
const a = 1
```

```rs [main.rs]
fn main() {
    println!("Hello, world!");
}
```
:::

代码树

index.ts

main.rs

index.ts
ts
const a = 1
main.rs
rs
fn main() {
    println!("Hello, world!");
}

嵌入语法

md
@[code-tree title="代码树" show-sidebar](@/zh)

代码树

guide

api.md

plugin-dev.md

quick-start.md

toolkit.md

wrap-plugin.md

plugins

abbr.md

caniuse.md

code-collapse.md

code-tree.md

codepen.md

collapse.md

field.md

file-tree.md

intro.md

jsfiddle.md

mermaid.md

npm-to.md

obsidian.md

pdf.md

plantuml.md

plot.md

qrcode.md

repo-card.md

steps.md

video.md

watermark.md

config.ts

index.md

guide/api.md
md
# API 参考

<NpmBadge name="vitepress-tuck" />

## vitepress-tuck

### defineConfig

`defineConfig` 是 `vitepress-tuck` 的核心函数,用于替代 VitePress 的 `defineConfig`。

```ts
import { defineConfig } from 'vitepress-tuck'

function defineConfig<ThemeConfig = DefaultTheme.Config>(
  config: UserConfig<NoInfer<ThemeConfig>> & TuckConfig,
): UserConfig<NoInfer<ThemeConfig>>
```

#### 参数

| 参数     | 类型                      | 说明                                |
| -------- | ------------------------- | ----------------------------------- |
| `config` | `UserConfig & TuckConfig` | VitePress 原始配置 + `plugins` 选项 |

`TuckConfig` 定义:

```ts
interface TuckConfig {
  /**
   * vitepress 插件列表
   */
  plugins?: VitepressPlugin[]

  /**
   * unplugin-vue-components 插件选项
   *
   * @see - https://github.com/unplugin/unplugin-vue-components
   */
  components?: ComponentsOptions
}
```

#### 返回值

返回合并后的完整 VitePress 配置对象 `UserConfig`。

#### 工作原理

1. 遍历 `plugins` 数组,合并每个插件的 `markdown`、`vite`、`vue` 等配置
2. 收集所有钩子函数(`buildEnd`、`transformHead`、`transformHtml`、`transformPageData`、`postRender`),并按照各自的策略合并:
   - `markdown.config`、`buildEnd` → 并发执行
   - `transformHead` → 并发执行后合并结果
   - `transformHtml`、`transformPageData`、`postRender` → 顺序执行,链式传递
3. 将插件中的 `client` 配置注入到内置的 `virtual-enhance-app` 虚拟模块
4. 将插件中的 `componentResolver` 收集到内置的 `unplugin-vue-components` 解析器列表
5. 最后合并用户自身的配置项

---

### definePlugin

用于定义 VitePress 插件。它是一个类型辅助函数,帮助开发者以规范化的方式创建插件。

```ts
import { definePlugin } from 'vitepress-tuck'

function definePlugin<T>(
  plugin: (option?: T) => VitepressPlugin,
): (option?: T) => VitepressPlugin
```

#### 参数

| 参数     | 类型                              | 说明                                       |
| -------- | --------------------------------- | ------------------------------------------ |
| `plugin` | `(option?: T) => VitepressPlugin` | 插件工厂函数,接收可选配置项,返回插件对象 |

---

### VitepressPlugin

插件对象的类型定义:

```ts
interface VitepressPlugin extends Pick<
  UserConfig,
  'markdown' | 'vite' | 'vue' | 'buildEnd' | 'transformHead' | 'transformHtml' | 'transformPageData' | 'postRender'
> {
  /**
   * 插件名称(必填,用于标识和调试)
   */
  name: string

  /**
   * 客户端配置,用于自动注入到 virtual:enhance-app
   */
  client?: {
    /**
     * 自定义的 import 语句列表
     *
     * @example
     * ```ts
     * imports: ['import "my-plugin/style.css"']
     * ```
     */
    imports?: string[]

    /**
     * 客户端增强函数名。
     * - 未设置: 不注入 enhanceApp 函数
     * - true: 默认函数名为 `enhanceApp`
     * - 字符串: 指定函数名
     *
     * @example
     * ```ts
     * enhance: 'enhanceAppWithMyPlugin'
     * ```
     */
    enhance?: string | boolean
  }

  /**
   * 组件解析器,用于 unplugin-vue-components 自动按需导入。
   *
   * - 字符串数组:声明的组件名将从 `<插件名>/client` 自动解析
   * - ComponentResolver 对象:自定义解析逻辑
   *
   * @example
   * ```ts
   * // 简单形式
   * componentResolver: ['MyComponent', 'OtherComponent']
   * ```
   */
  componentResolver?: string[] | ComponentResolver
}
```

#### 支持的 VitePress 配置项

| 属性                | 类型                              | 说明                                                              |
| ------------------- | --------------------------------- | ----------------------------------------------------------------- |
| `markdown`          | `UserConfig['markdown']`          | Markdown 相关配置,常用于 `markdown.config` 注册 markdown-it 插件 |
| `vite`              | `UserConfig['vite']`              | Vite 配置,用于注册 Vite 插件和优化配置                           |
| `vue`               | `UserConfig['vue']`               | Vue 应用级别的配置                                                |
| `buildEnd`          | `UserConfig['buildEnd']`          | 构建完成钩子(并发执行)                                          |
| `transformHead`     | `UserConfig['transformHead']`     | 转换 HTML 头部钩子(并发执行,合并结果)                          |
| `transformHtml`     | `UserConfig['transformHtml']`     | 转换 HTML 钩子(顺序执行,链式传递)                              |
| `transformPageData` | `UserConfig['transformPageData']` | 转换页面数据钩子(顺序执行,链式传递)                            |
| `postRender`        | `UserConfig['postRender']`        | 页面渲染后钩子(顺序执行,链式传递)                              |

---

### virtual:enhance-app

`virtual:enhance-app` 是一个虚拟模块,由 `vitepress-tuck` 内置提供。它自动收集所有插件的 `client` 配置并生成对应的代码。

使用时在主题入口中导入:

```ts [.vitepress/theme/index.ts]
import enhanceApp from 'virtual:enhance-app'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceApp(ctx)
  },
}
```

TypeScript 类型支持需要添加类型引用:

```json [tsconfig.json]
{
  "compilerOptions": {
    "types": ["vitepress-tuck/client-types"]
  }
}
```

---

### 内置插件:auto-components

`vitepress-tuck` 内置集成了 [`unplugin-vue-components`](https://github.com/unplugin/unplugin-vue-components) 插件,提供自动按需组件导入能力。该插件作为内置插件自动启用,无需手动注册。

#### 默认行为

- 扫描 `.vue` 和 `.md` 文件中的组件使用
- 类型声明文件生成到 `node_modules/.vite/components.d.ts`
- 自动收集所有插件的 `componentResolver` 声明

#### 自定义配置

通过 `defineConfig` 的 `components` 选项可以自定义 `unplugin-vue-components` 的行为:

```ts
import { defineConfig } from 'vitepress-tuck'

export default defineConfig({
  components: {
    // 自定义扫描目录
    dirs: ['src/components'],
    // 目录作为命名空间
    directoryAsNamespace: true,
    // 其他 unplugin-vue-components 选项...
  },
  plugins: [],
})
```

::: tip
插件的 `componentResolver` 声明会与用户在 `components` 中的配置合并。插件开发者只需在插件中声明 `componentResolver`,用户即可直接在 Markdown 或 Vue 文件中使用对应组件,无需手动 import。
:::
guide/plugin-dev.md
md
# 插件开发

<NpmBadge name="vitepress-tuck" />

`vitepress-tuck` 提供了 `definePlugin` 函数来帮助开发者便捷地创建 VitePress 插件。通过 `definePlugin`,你可以将原本需要用户分散配置的 `markdown`、`vite`、`vue` 等选项集中在一个插件函数中。

## 基本结构

一个基于 `vitepress-tuck` 的插件由以下部分组成:

```ts
import { definePlugin } from 'vitepress-tuck'

export default definePlugin((options?: MyPluginOptions) => ({
  name: 'vitepress-plugin-my-plugin',

  // 客户端配置:自动注入到 virtual:enhance-app
  client: {
    imports: [],
    enhance: 'enhanceAppWithMyPlugin',
  },

  // 组件解析器:声明插件提供的组件,支持自动按需导入
  componentResolver: ['MyComponent', 'OtherComponent'],

  // Markdown 配置:注册 markdown-it 插件
  markdown: {
    config: (md) => {
      md.use(myMarkdownPlugin, options?.markdownOptions)
    },
  },

  // Vite 配置
  vite: {
    plugins: [myVitePlugin(options?.viteOptions)],
  },

  // 其他 VitePress 钩子
  buildEnd: (site) => { /* ... */ },
  transformHead: (ctx) => { /* ... */ },
  transformHtml: (code, id, ctx) => { /* ... */ },
  transformPageData: (pageData, ctx) => { /* ... */ },
  postRender: (context) => { /* ... */ },
}))
```

## 核心概念

### name

每个插件必须有一个唯一的 `name`,用于标识和调试:

```ts
{
  name: 'vitepress-plugin-my-plugin'
}
```

### client

`client` 配置用于向客户端注入代码。`vitepress-tuck` 会自动将这些代码注入到 `virtual:enhance-app` 虚拟模块中。

#### client.imports

添加自定义的 import 语句,常用于导入样式文件:

```ts
{
  client: {
    imports: [
      'import "vitepress-plugin-my-plugin/style.css"',
    ],
  }
}
```

#### client.enhance

指定插件的客户端增强函数名。该函数会在 VitePress 主题的 `enhanceApp(ctx)` 中被调用。

- 设置为 `true` 时,默认函数名为 `enhanceApp`
- 设置为字符串时,函数名为该字符串

```ts
{
  client: {
    enhance: 'enhanceAppWithMyPlugin',
  }
}
```

对应的,在插件的客户端入口文件中导出同名函数:

```ts
// client/index.ts
import type { EnhanceAppContext } from 'vitepress'

export function enhanceAppWithMyPlugin({ app }: EnhanceAppContext) {
  // 注册组件、指令等
  app.component('MyComponent', MyComponent)
}
```

### componentResolver

`componentResolver` 用于声明插件提供的 Vue 组件,使其能被内置的 `unplugin-vue-components` 自动按需导入。用户在 Markdown 或 Vue 文件中直接使用组件名即可,无需手动 import。

#### 字符串数组形式

最简单的形式是传入组件名数组,这些组件会从 `<插件名>/client` 自动解析:

```ts
{
  name: 'vitepress-plugin-my-plugin',
  componentResolver: ['MyComponent', 'OtherComponent'],
}
```

对应的,在插件的客户端入口中导出这些组件:

```ts
// client/index.ts
import MyComponent from './components/MyComponent.vue'
import OtherComponent from './components/OtherComponent.vue'

export { MyComponent, OtherComponent }
```

#### 自定义 ComponentResolver

对于更复杂的解析逻辑,可以传入来自 `unplugin-vue-components` 的 `ComponentResolver` 对象:

```ts
import type { ComponentResolver } from 'unplugin-vue-components'

const myResolver: ComponentResolver = {
  type: 'component',
  resolve: (name) => {
    if (name.startsWith('My')) {
      return { name, from: 'vitepress-plugin-my-plugin/client' }
    }
  },
}

export default definePlugin(() => ({
  name: 'vitepress-plugin-my-plugin',
  componentResolver: myResolver,
}))
```

::: tip
当插件同时设置了 `client.enhance` 用于注册组件和 `componentResolver` 时,推荐使用 `componentResolver` 来实现按需导入,这样可以减少不必要的组件打包。
:::

### markdown

配置 markdown-it 插件,用于扩展 Markdown 语法:

```ts
{
  markdown: {
    config: (md) => {
      // 注册自定义容器
      md.use(containerPlugin)
      // 注册行内规则
      md.inline.ruler.before('emphasis', 'my_rule', myRule)
    },
  }
}
```

### vite

配置 Vite 插件和优化选项:

```ts
{
  vite: {
    plugins: [myVitePlugin()],
    ssr: {
      noExternal: ['my-plugin-package'],
    },
  }
}
```

### 钩子函数

`definePlugin` 支持 VitePress 的各种钩子函数:

| 钩子                | 说明           | 执行方式           |
| ------------------- | -------------- | ------------------ |
| `buildEnd`          | 构建完成时触发 | 并发执行           |
| `transformHead`     | 转换 HTML 头部 | 并发执行,合并结果 |
| `transformHtml`     | 转换 HTML 内容 | 顺序执行,链式传递 |
| `transformPageData` | 转换页面数据   | 顺序执行,链式传递 |
| `postRender`        | 页面渲染后触发 | 顺序执行,链式传递 |

## 客户端代码组织

当插件需要提供客户端代码(如 Vue 组件)时,推荐以下目录结构:

::: file-tree title="my-plugin"

- my-plugin
  - src
    - client
      - components
        - MyComponent.vue
      - index.ts # 导出 enhanceApp 函数
    - node
      - myPlugin.ts # 插件核心逻辑
      - index.ts # 插件入口,使用 definePlugin
  - package.json
  - tsconfig.json

:::

在 `package.json` 中配置 `exports`,将客户端代码单独导出:

```json
{
  "exports": {
    ".": {
      "types": "./dist/node/index.d.ts",
      "default": "./dist/node/index.js"
    },
    "./client": {
      "browser": "./dist/client/browser/index.js",
      "default": "./dist/client/ssr/index.js"
    }
  }
}
```

## 推荐使用 vitepress-plugin-toolkit

`vitepress-plugin-toolkit` 提供了丰富的工具函数来辅助插件开发,包括:

- `createContainerPlugin` - 创建 markdown-it 自定义容器
- `createContainerSyntaxPlugin` - 创建自定义语法容器
- `createEmbedRuleBlock` - 创建嵌入语法块
- `resolveAttrs` / `stringifyAttrs` - 属性解析和序列化
- `createLogger` - 日志工具
- `createLocales` - 国际化支持
- `useSize` - 响应式尺寸计算

详细 API 请参考 [Toolkit API 文档](./toolkit)。

## 完整示例

以下是一个基于 `definePlugin` 开发的简单步骤插件:

```ts [node/index.ts]
import { definePlugin } from 'vitepress-tuck'
import { createContainerPlugin } from 'vitepress-plugin-toolkit'

export const stepsPlugin = (md) => {
  createContainerPlugin(md, 'steps', {
    before: () => '<div class="vp-steps">',
  })
}

export default definePlugin(() => ({
  name: 'vitepress-plugin-steps',
  client: {
    imports: ['import "vitepress-plugin-steps/style.css"'],
  },
  markdown: {
    config: (md) => {
      md.use(stepsPlugin)
    },
  },
}))
```

用户使用时只需:

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import steps from 'vitepress-plugin-steps'

export default defineConfig({
  plugins: [steps()],
})
```
guide/quick-start.md
md
# 快速开始

## 什么是 vitepress-tuck?

`vitepress-tuck` 为 VitePress 提供了简单灵活、低门槛的插件开发与接入能力。
它通过包装 VitePress 的 `defineConfig`,提供额外的 `plugins` 选项,将插件接入的复杂度转移到插件内部,用户只需在 `plugins` 数组中添加插件。

## 安装

::: npm-to

```sh
npm install vitepress vitepress-tuck
```

:::

## 在 VitePress 站点中使用

::: steps

- ### 替换配置文件

`.vitepress/config.ts` 中的 `defineConfig` 替换为 `vitepress-tuck``defineConfig`

  ```ts [.vitepress/config.ts]
  import { defineConfig } from 'vitepress'
  import { defineConfig } from 'vitepress-tuck' // [!code ++]

  export default defineConfig({
    plugins: [
      // 在这里添加插件
    ],
    // 其他 VitePress 配置项 ...
  })
  ```

- ### 配置客户端入口

`.vitepress/theme/index.ts` 中引入 `virtual:enhance-app`

  ```ts [.vitepress/theme/index.ts]
  import type { Theme } from 'vitepress'
  import enhanceApp from 'virtual:enhance-app'
  import DefaultTheme from 'vitepress/theme'

  export default {
    extends: DefaultTheme,
    enhanceApp(ctx) {
      enhanceApp(ctx) // [!code ++]
    },
  } satisfies Theme
  ```

  `virtual:enhance-app` 是一个虚拟模块,`vitepress-tuck` 会自动将插件的客户端代码注入其中,无需手动配置。

- ### TypeScript 支持

  如果项目使用 TypeScript,在 `tsconfig.json` 中添加类型引用:

  ```json
  {
    "compilerOptions": {
      "types": ["vitepress-tuck/client-types"]
    }
  }
  ```

:::

## 使用插件

插件使用非常简单,只需在 `plugins` 中导入并调用即可:

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import steps from 'vitepress-plugin-steps'
import mermaid from 'vitepress-plugin-mermaid-tuck'

export default defineConfig({
  plugins: [
    steps(),
    mermaid({
      // 可以传入插件配置项
    }),
  ],
})
```

每个插件的配置项请参考对应的插件文档。
guide/toolkit.md
md
# Toolkit API

<NpmBadge name="vitepress-plugin-toolkit" />

`vitepress-plugin-toolkit` 是 VitePress 插件开发工具包,提供了丰富的工具函数来辅助插件开发。

安装:

::: npm-to

```sh
npm install vitepress-plugin-toolkit
```

:::

## Node 端 API

从 `vitepress-plugin-toolkit` 导入:

```ts
import {
  createContainerPlugin,
  createContainerSyntaxPlugin,
  createEmbedRuleBlock,
  resolveAttrs,
  resolveAttr,
  stringifyAttrs,
  createLogger,
  createLocales,
  getVitepressConfig,
  getLocaleWithPath,
  resolveRouteLink,
  parseRect,
  slugify,
  treatAsHtml,
} from 'vitepress-plugin-toolkit'
```

---

### createContainerPlugin

创建 markdown-it 自定义容器插件。用于处理 `::: type` 语法,内容会被 markdown-it 正常解析。

```ts
function createContainerPlugin(
  md: MarkdownIt,
  type: string,
  options?: ContainerOptions,
): void
```

**ContainerOptions:**

```ts
interface ContainerOptions {
  /**
   * 渲染容器起始标签时的回调
   */
  before?: (info: string, tokens: Token[], index: number, options: Options, env: any) => string
  /**
   * 渲染容器结束标签时的回调
   */
  after?: (info: string, tokens: Token[], index: number, options: Options, env: any) => string
}
```

**使用示例:**

```ts
import { createContainerPlugin } from 'vitepress-plugin-toolkit'

function myPlugin(md) {
  createContainerPlugin(md, 'steps', {
    before: () => '<div class="vp-steps">',
  })
}
```

---

### createContainerSyntaxPlugin

创建自定义语法容器插件。与 `createContainerPlugin` 不同,内容不会被 markdown-it 解析,需要自行处理。

```ts
function createContainerSyntaxPlugin(
  md: MarkdownIt,
  type: string,
  render?: RenderRule,
): void
```

容器内的原始内容通过 `token.content` 获取,元数据通过 `token.meta` 获取。

**使用示例:**

```ts
import { createContainerSyntaxPlugin } from 'vitepress-plugin-toolkit'

function myPlugin(md) {
  createContainerSyntaxPlugin(md, 'file-tree', (tokens, index) => {
    const { content, meta } = tokens[index]
    return `<div class="file-tree">${content}</div>`
  })
}
```

---

### createEmbedRuleBlock

创建嵌入规则块,用于处理 `@[type info](source)` 语法。

```ts
function createEmbedRuleBlock<Meta extends Record<string, any>>(
  md: MarkdownIt,
  options: EmbedRuleBlockOptions<Meta>,
): void
```

**EmbedRuleBlockOptions:**

```ts
interface EmbedRuleBlockOptions<Meta extends Record<string, any>> {
  /** 嵌入类型,如 'pdf'、'qrcode' */
  type: string
  /** Token 名称,默认与 type 相同 */
  name?: string
  /** 插入到哪个规则之前,默认 'code' */
  beforeName?: string
  /** 规则选项 */
  ruleOptions?: RuleOptions
  /** 解析 `@[type info](source)` 中的 `info` 和 `source`,转换为元数据对象 */
  meta: (info: string, source: string) => Meta
  /** 从元数据生成内容 */
  content?: (meta: Meta, env: MarkdownEnv) => string
  /** 渲染函数 */
  render?: (tokens: Token[], index: number, env: MarkdownEnv) => string
}
```

**使用示例:**

```ts
import { createEmbedRuleBlock, resolveAttrs } from 'vitepress-plugin-toolkit'

function myPlugin(md) {
  createEmbedRuleBlock(md, {
    type: 'pdf',
    meta: (info, source) => ({
      attrs: info,
      src: source,
    }),
    content: (meta) => {
      return `<VPPdf src="${meta.src}" ${meta.attrs} />`
    },
  })
}
```

---

### resolveAttrs

将属性字符串解析为对象。

```ts
function resolveAttrs<T extends Record<string, any> = Record<string, any>>(info: string): T
```

**示例:**

```ts
resolveAttrs('width="100%" height="400" dark')
// => { width: '100%', height: '400', dark: true }
```

---

### resolveAttr

从信息字符串中解析单个属性值。

```ts
function resolveAttr(info: string, key: string): string | undefined
```

---

### stringifyAttrs

将属性对象序列化为 HTML 属性字符串。

```ts
function stringifyAttrs<T extends object = object>(
  attrs: T,
  withUndefinedOrNull?: boolean,
  forceStringify?: (keyof T)[],
): string
```

**示例:**

```ts
stringifyAttrs({ width: '100%', height: 400, dark: true })
// => ' width="100%" :height="400" dark'
```

---

### createLogger

创建日志记录器实例。

```ts
function createLogger(
  prefix: string,
  defaultLevel?: LogLevel,
): Logger
```

**LogLevel:**

```ts
type LogLevel = 'info' | 'warn' | 'error' | 'debug' | 'silent'
```

**示例:**

```ts
const logger = createLogger('my-plugin', 'info')

logger.info('插件已加载')       // [my-plugin] 插件已加载
logger.warn('可能存在问题')     // [my-plugin] 可能存在问题
logger.error('发生了错误')     // [my-plugin] 发生了错误
logger.debug('调试信息', true)  // [my-plugin] 调试信息
```

---

### createLocales

创建多语言配置,自动匹配 VitePress 的语言设置。

```ts
function createLocales<LocaleData extends Record<string, unknown>>(
  builtinLocales: BuiltinLocales<LocaleData>,
  userLocales?: Record<string, LocaleData>,
): Record<string, LocaleData>
```

**示例:**

```ts
const locales = createLocales(
  [
    [['en', 'en-US'], { chart: 'Chart', source: 'Source' }],
    [['zh', 'zh-CN'], { chart: '图表', source: '源码' }],
  ],
  userLocales,
)
```

---

### getVitepressConfig

获取当前的 VitePress 站点配置。

```ts
function getVitepressConfig(): SiteConfig
```

---

### getLocaleWithPath

根据文件路径获取语言信息。

```ts
function getLocaleWithPath(path: string): { lang: string, locale: string }
```

---

### resolveRouteLink

将相对路径转换为 VitePress 的路由链接。

```ts
function resolveRouteLink(url: string, env: MarkdownEnv): string
```

---

### parseRect

解析尺寸字符串,如果传入数字则自动添加单位。

```ts
function parseRect(str: string, unit?: string): string
```

**示例:**

```ts
parseRect('400')     // => '400px'
parseRect('50%')     // => '50%'
parseRect('10', 'rem') // => '10rem'
```

---

### slugify

将字符串转换为 URL 友好的 slug 格式。

```ts
function slugify(str: string): string
```

---

### treatAsHtml

判断文件名是否应作为 HTML 处理(非已知资源扩展名即为 HTML)。

```ts
function treatAsHtml(filename: string): boolean
```

---

## Client 端 API

从 `vitepress-plugin-toolkit/client` 导入:

```ts
import {
  VPCopyButton,
  VPLoading,
  useSize,
  isiPhone,
  isWindows,
  isiPad,
  isIOS,
  isMacOS,
  isMobile,
  isSafari,
} from 'vitepress-plugin-toolkit/client'
```

### VPCopyButton

复制按钮组件。

```vue
<template>
  <VPCopyButton :text="code" />
</template>
```

### VPLoading

加载状态组件。

```vue
<template>
  <VPLoading />
</template>
```

### useSize

响应式尺寸计算 composable。

```ts
function useSize<T extends HTMLElement>(
  el: TemplateRef<T>,
  options: ToRefs<SizeOptions>,
  extraHeight?: MaybeRef<number>,
): {
  width: Ref<string>
  height: Ref<string>
  resize: () => void
}
```

**SizeOptions:**

```ts
interface SizeOptions {
  width?: string
  height?: string
  ratio?: number | string
}
```

### 设备检测工具

| 函数          | 说明                     |
| ------------- | ------------------------ |
| `isIPhone()`  | 检测是否为 iPhone        |
| `isIPad()`    | 检测是否为 iPad          |
| `isIOS()`     | 检测是否为 iOS 设备      |
| `isMacOS()`   | 检测是否为 macOS         |
| `isWindows()` | 检测是否为 Windows       |
| `isMobile()`  | 检测是否为移动设备       |
| `isSafari()`  | 检测是否为 Safari 浏览器 |

---

## Shared 工具

从 `vitepress-plugin-toolkit` 或 `vitepress-plugin-toolkit/client` 均可导入:

```ts
import { isExternal, isLinkWithProtocol } from 'vitepress-plugin-toolkit'
```

### isExternal

判断链接是否为外部链接。

```ts
function isExternal(path: string): boolean
```

### isLinkWithProtocol

判断链接是否包含协议前缀。

```ts
function isLinkWithProtocol(link: string): boolean
```

---

## CSS 过渡动画

`vitepress-plugin-toolkit` 提供了预定义的 CSS 过渡动画,可在插件中导入:

```css
@import 'vitepress-plugin-toolkit/styles/transition/fade-in.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-up.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-down.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-left.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-right.css';
@import 'vitepress-plugin-toolkit/styles/transition/slide-in-up.css';
@import 'vitepress-plugin-toolkit/styles/transition/slide-in-down.css';
@import 'vitepress-plugin-toolkit/styles/transition/slide-in-left.css';
@import 'vitepress-plugin-toolkit/styles/transition/slide-in-right.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-scale-up.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-width-expand.css';
@import 'vitepress-plugin-toolkit/styles/transition/fade-in-height-expand.css';
```
guide/wrap-plugin.md
md
# 包装现有插件

如果你的 VitePress 站点已经在使用一些插件,或者你有一个尚未封装为 `vitepress-tuck` 模式的插件,你可以轻松地将其包装为兼容的插件形式。

## 为什么需要包装?

传统的 VitePress 插件通常需要用户在配置文件中多处配置:

```ts
// 传统的接入方式 —— 配置散落在多处
import { defineConfig } from 'vitepress'
import { someMarkdownPlugin } from 'some-plugin'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(someMarkdownPlugin)
    },
  },
  vite: {
    plugins: [someVitePlugin()],
  },
})
```

通过包装为 `vitepress-tuck` 插件,用户只需在 `plugins` 数组中添加,所有配置由插件内部处理。

## 包装方法

### 使用 definePlugin 包装

对于任何已有的插件逻辑,使用 `definePlugin` 进行包装:

```ts
import { definePlugin } from 'vitepress-tuck'
import { someMarkdownPlugin } from 'some-plugin'
import { someVitePlugin } from 'some-plugin/vite'

export default definePlugin((options?: MyOptions) => ({
  name: 'vitepress-plugin-some-plugin',
  markdown: {
    config: (md) => {
      md.use(someMarkdownPlugin, options)
    },
  },
  vite: {
    plugins: [someVitePlugin(options)],
  },
}))
```

### 包装仅有 markdown 配置的插件

如果插件只涉及 markdown-it 扩展,包装非常简单:

```ts
import { definePlugin } from 'vitepress-tuck'

export default definePlugin(() => ({
  name: 'vitepress-plugin-my-plugin',
  markdown: {
    config: (md) => {
      md.use(myMarkdownItPlugin)
    },
  },
}))
```

### 包装仅有 vite 配置的插件

如果插件只需要 Vite 配置:

```ts
import { definePlugin } from 'vitepress-tuck'
import myVitePlugin from 'vite-plugin-my'

export default definePlugin((options?: MyOptions) => ({
  name: 'vitepress-plugin-my-plugin',
  vite: {
    plugins: [myVitePlugin(options)],
  },
}))
```

### 包装需要客户端注入的插件

如果插件需要在客户端注入组件或样式,需要配置 `client` 选项:

```ts
import { definePlugin } from 'vitepress-tuck'

export default definePlugin(() => ({
  name: 'vitepress-plugin-my-plugin',
  client: {
    imports: [
      // 注入样式
      'import "my-plugin/style.css"',
    ],
    enhance: 'enhanceAppWithMyPlugin',
  },
  markdown: {
    config: (md) => {
      md.use(myMarkdownPlugin)
    },
  },
  vite: {
    ssr: {
      // 确保服务端渲染时能正确打包
      noExternal: ['my-plugin'],
    },
  },
}))
```

并在客户端入口文件中导出 enhance 函数:

```ts
// client/index.ts
import type { EnhanceAppContext } from 'vitepress'
import MyComponent from './components/MyComponent.vue'

export function enhanceAppWithMyPlugin({ app }: EnhanceAppContext) {
  app.component('MyComponent', MyComponent)
}
```

## 实际案例

以下是将 `vitepress-plugin-group-icons` 包装为 `vitepress-tuck` 插件的示例:

```ts
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
import { definePlugin } from 'vitepress-tuck'

export default definePlugin(() => ({
  name: 'vitepress-plugin-group-icons',
  client: {
    imports: ['import \'virtual:group-icons.css\''],
  },
  markdown: {
    config: (md) => {
      md.use(groupIconMdPlugin)
    },
  },
  vite: {
    plugins: [
      groupIconVitePlugin(),
    ],
    ssr: {
      noExternal: [
        'vitepress-plugin-group-icons',
      ],
    },
  },
}))

```

## 保持兼容性

包装后的插件仍然可以独立使用(不依赖 `vitepress-tuck`)。只需在插件的 README 中同时提供两种使用方式:

```ts
// 方式一:vitepress-tuck 模式(推荐)
import { defineConfig } from 'vitepress-tuck'
import myPlugin from 'my-plugin'

export default defineConfig({
  plugins: [myPlugin()],
})

// 方式二:传统 VitePress 模式
import { defineConfig } from 'vitepress'
import { myPlugin } from 'my-plugin/raw'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(myPlugin)
    },
  },
})
```
plugins/abbr.md
md
# 缩写词

<NpmBadge name="vitepress-plugin-abbr" />

缩写词插件,为 Markdown 中的缩写词添加可交互的提示框,鼠标悬停或聚焦时显示其完整描述。

基于 [`markdown-it-abbr`](https://github.com/markdown-it/markdown-it-abbr) 分叉并修改。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-abbr
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import abbr from 'vitepress-plugin-abbr'

export default defineConfig({
  plugins: [abbr()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { abbrMarkdownPlugin } from 'vitepress-plugin-abbr' // [!code ++]

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(abbrMarkdownPlugin, {
        HTML: 'HyperText Markup Language',
        W3C: 'World Wide Web Consortium',
      })
    },
  },
})
```

在主题中注册组件并引入样式:

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithAbbr } from 'vitepress-plugin-abbr/client' // [!code ++]
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithAbbr(ctx) // [!code ++]
  },
} satisfies Theme
```

## 语法

使用 `*[缩写]: 完整描述` 语法定义缩写词,定义后该缩写词在文档中出现的所有位置都会被自动识别并渲染为带有提示框的文本。

```md
HTML 规范由 W3C 维护。

*[HTML]: HyperText Markup Language
*[W3C]: World Wide Web Consortium
```

**渲染结果:**

HTML 规范由 W3C 维护。

*[HTML]: HyperText Markup Language
*[W3C]: World Wide Web Consortium

### 描述支持内联 Markdown

缩写词的完整描述支持内联 Markdown 语法,例如加粗、斜体、链接和代码等。

```md
**HTML** 规范由 W3C 维护。

*[HTML]: HyperText Markup Language
*[W3C]: World [Wide Web](https://www.w3.org/) Consortium
```

**渲染结果:**

**HTML** 规范由 W3C 维护。

*[HTML]: HyperText Markup Language
*[W3C]: World [Wide Web](https://www.w3.org/) Consortium

## 全局缩写词

除了在每个 Markdown 文件中内联定义缩写词外,还可以通过插件选项配置全局缩写词预设。当全局定义与内联定义存在相同的缩写词时,内联定义优先。

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import abbr from 'vitepress-plugin-abbr'

export default defineConfig({
  plugins: [
    abbr({
      HTML: 'HyperText Markup Language',
      W3C: 'World Wide Web Consortium',
    }),
  ],
})
```

配置后,所有页面中出现的 `HTML` 和 `W3C` 都会自动渲染为缩写词,无需在每个文件中重复定义。

## 示例

鼠标移动或聚焦到下方缩写词上,查看完整描述:

```md
HTML W3C API CSS

*[HTML]: HyperText Markup Language
*[W3C]: World Wide Web Consortium
*[API]: Application Programming Interface
*[CSS]: Cascading Style Sheets
```

HTML W3C API CSS

*[HTML]: HyperText Markup Language
*[W3C]: World Wide Web Consortium
*[API]: Application Programming Interface
*[CSS]: Cascading Style Sheets
plugins/caniuse.md
md
# Can I Use

<NpmBadge name="vitepress-plugin-caniuse" />

嵌入 [caniuse.com](https://caniuse.com/) 的浏览器兼容性数据,在页面中展示 CSS/JS 特性的浏览器支持情况。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-caniuse
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import caniuse from 'vitepress-plugin-caniuse'

export default defineConfig({
  plugins: [caniuse()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { caniuseMarkdownPlugin } from 'vitepress-plugin-caniuse' // [!code ++]

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(caniuseMarkdownPlugin) // [!code ++]
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithCaniuse } from 'vitepress-plugin-caniuse/client' // [!code ++]
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCaniuse(ctx) // [!code ++]
  },
} satisfies Theme
```

## 语法

使用 `@[caniuse]()` 嵌入浏览器兼容性数据:

```md
@[caniuse](feature_name)
```

### Baseline 模式

使用 baseline 模式显示特性支持概览:

```md
@[caniuse baseline](feature_name)
```

### 自定义版本范围

通过 `{}` 指定版本范围:

```md
@[caniuse{-2,4}](feature_name)
@[caniuse baseline{-3,2}](feature_name)
```

- `{past, future}`: past 表示回溯的版本数,future 表示前瞻的版本数
- 默认值为 `{5, 2}`,即回溯 5 个版本,前瞻 3 个版本

### 获取 特性名称

在 [caniuse.com](https://caniuse.com/) 中搜索需要展示的特性,点击卡片左侧的 `#` ,即可在浏览器地址栏中获得特性名称。

::: details 不知道哪个部分为特性名称?
**以 css `grid` 为例**

在 caniuse.com 中搜索 `grid`,然后点击第一个卡片的 `#` ,浏览器地址栏地址变更为 `https://caniuse.com/css-grid`。
则特性名称为 `css-grid`。

在此处进行验证: <https://caniuse.com/?search=grid>
:::

## 示例

### 显示 `fetch` 特性的浏览器支持情况

```md
@[caniuse](fetch)
```

@[caniuse](fetch)

### 显示 `fetch` 特性的 baseline

```md
@[caniuse baseline](fetch)
```

@[caniuse baseline](fetch)
plugins/code-collapse.md
md
# Code Collapse

<NpmBadge name="vitepress-plugin-code-collapse" />

代码块折叠插件,当代码块超过指定行数时自动折叠,提升页面阅读体验。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-code-collapse
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import collapsedLines from 'vitepress-plugin-code-collapse'

export default defineConfig({
  plugins: [
    collapsedLines(),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { collapsedLinesMarkdownPlugin } from 'vitepress-plugin-code-collapse'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(collapsedLinesMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithCollapsedLines } from 'vitepress-plugin-code-collapse/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCollapsedLines(ctx)
  },
} satisfies Theme
```

## 语法

### 全局折叠

当在配置中设置 `options` 为数字时,或者为 `true`(默认为 15 行)时,所有超过该行数的代码块都会自动折叠。

### 单个代码块折叠

可以在代码块的 info 字符串中使用 `:collapsed-lines={N}` 语法单独控制:

````md
```ts :collapsed-lines
// 使用默认值折叠(15行)
```

```ts :collapsed-lines=10
// 从第10行开始折叠
```
````

## 配置

```ts
/**
   * 折叠行数配置
   * - `true`: 超过 15 行时折叠
   * - `number`: 超过指定行数时折叠
   * - `false`: 不全局启用,允许单代码块控制 (默认值)
   * - `'disable'`: 完全禁用插
   * @default 'false'
   */
type options = boolean | number | 'disable'
```

## 示例

````md
```ts :collapsed-lines=10
const a1 = 1
const a2 = 2
const a3 = 1
const a4 = 1
const a5 = 1
const a6 = 2
const a7 = 1
const a8 = 1
const a9 = 1
const a10 = 2
const a11 = 1
const a12 = 1
```
````

```ts :line-numbers=1 :collapsed-lines=10
const a1 = 1
const a2 = 2
const a3 = 1
const a4 = 1
const a5 = 1
const a6 = 2
const a7 = 1
const a8 = 1
const a9 = 1
const a10 = 2
const a11 = 1
const a12 = 1
```
plugins/code-tree.md
md
# Code Tree

<NpmBadge name="vitepress-plugin-code-tree" />

代码树插件,用于在 Markdown 中渲染带文件树侧边栏的代码结构,支持文件切换查看。

该插件依赖于 [文件树](./file-tree.md) 插件提供的文件树侧边栏组件。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-code-tree
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import codeTree from 'vitepress-plugin-code-tree'

export default defineConfig({
  plugins: [codeTree()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { codeTreeMarkdownPlugin } from 'vitepress-plugin-code-tree'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(codeTreeMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithCodeTree } from 'vitepress-plugin-code-tree/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCodeTree(ctx)
  },
} satisfies Theme
```

## 语法

插件提供两种语法来渲染代码树:用于内联文件内容的容器语法,以及用于从目录加载文件的嵌入语法。

### 容器语法

使用 `::: code-tree` 容器,内部包含围栏代码块。每个围栏代码块必须通过信息字符串中的 `[文件名]` 语法声明文件名。

````md
::: code-tree title="项目结构"

```ts [index.ts]
const a = 1
```

```rs [main.rs]
fn main() {
    println!("Hello, world!");
}
```

:::
````

#### 容器属性

| 属性           | 说明               | 默认值  |
| -------------- | ------------------ | ------- |
| `title`        | 代码树标题         | -       |
| `height`       | 代码树容器高度     | `420px` |
| `entry`        | 入口文件,默认打开 | -       |
| `show-sidebar` | 是否默认显示侧边栏 | `false` |

#### 默认激活文件

在围栏代码块的信息字符串中添加 `:active` 标记,可以将该文件设为默认激活的文件:

````md
::: code-tree

```ts [index.ts] :active
const a = 1
```

```ts [utils.ts]
export const noop = () => {}
```

:::
````

### 嵌入语法

使用 `@[code-tree](dir)` 嵌入一个目录作为代码树。目录中的文件会被自动加载并渲染。

```md
@[code-tree](./src)
```

`dir` 支持以下路径前缀:

| 前缀 | 说明                             |
| ---- | -------------------------------- |
| `@`  | 相对于 VitePress `srcDir`        |
| `/`  | 相对于 VitePress 项目根目录      |
| -    | 相对于当前 markdown 文件所在目录 |

#### 嵌入属性

````md
@[code-tree title="源码" height="500px" entry="index.ts" show-sidebar=true](./src)
````

## 配置

### CodeTreePluginOptions

```ts
interface CodeTreePluginOptions {
  /**
   * 代码树容器默认高度
   * @default '420px'
   */
  height?: string | number

  /**
   * 忽略文件的 glob 路径模式
   * 在通过嵌入语法从目录加载文件时生效
   * node_modules 和 .DS_Store 始终会被忽略
   * @default []
   */
  ignores?: string[]

  /**
   * 资源文件加载器
   * 使用 `@[code-tree](dir)` 嵌入目录时,用于加载资源文件
   * 自定义加载器会合并到内置加载器之前,因此具有更高优先级
   * @default []
   */
  loaders?: CodeTreeFileLoader[]
}
```

### 文件加载器

加载器用于嵌入语法加载文件内容。插件内置了常见文件类型的加载器,自定义加载器会合并到内置加载器之前,因此具有更高优先级。

内置加载器覆盖以下文件类型:

- 点文件(`.git*`、`.env*`、`.*ignore`、`.npmrc`):以纯文本渲染
- `.XXXrc` 配置文件(如 `.eslintrc`):以 JSON 格式渲染
- 图片文件:渲染为 `<img>` 标签,并正确解析 `src`
- Shiki 支持的源文件:渲染为带语法高亮的围栏代码块

```ts
import codeTree, { loadCodeContent } from 'vitepress-plugin-code-tree'
import { defineConfig } from 'vitepress-tuck'

export default defineConfig({
  plugins: [
    codeTree({
      height: '500px',
      ignores: ['**/*.test.ts'],
      loaders: [
        {
          filter: ['**/*.md'],
          load: (file) => loadCodeContent(file, 'md'),
        },
      ],
    }),
  ],
})
```

`filter` 字段接受 glob 模式字符串、glob 模式数组,或接收 `CodeTreeFile` 并返回布尔值的断言函数。

## 示例

### 容器语法

````md
:::code-tree title="代码树" show-sidebar
```ts [index.ts]
const a = 1
```

```rs [main.rs]
fn main() {
    println!("Hello, world!");
}
```
:::
````

:::code-tree title="代码树" show-sidebar

```ts [index.ts]
const a = 1
```

```rs [main.rs]
fn main() {
    println!("Hello, world!");
}
```

:::

### 嵌入语法

```md
@[code-tree title="代码树" show-sidebar](@/zh)
```

@[code-tree title="代码树" show-sidebar](@/zh)
plugins/codepen.md
md
# CodePen

<NpmBadge name="vitepress-plugin-codepen" />

将 [CodePen](https://codepen.io/) 项目嵌入到 VitePress 页面中。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-codepen
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import codepen from 'vitepress-plugin-codepen'

export default defineConfig({
  plugins: [codepen()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { codepenPlugin } from 'vitepress-plugin-codepen'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(codepenPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithCodepen } from 'vitepress-plugin-codepen/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCodepen(ctx)
  },
} satisfies Theme
```

## 语法

### 基本用法

```md
@[codepen](user/slash)
```

### 带配置的用法

```md
@[codepen preview editable title="示例" height="400px" tab="css,result" theme="dark"](leimapapa/RwOZQOW)
```

### 属性说明

| 属性       | 类型      | 默认值     | 说明                         |
| ---------- | --------- | ---------- | ---------------------------- |
| `title`    | `string`  | -          | 标题                         |
| `user`     | `string`  | -          | CodePen 用户名(从链接解析) |
| `slash`    | `string`  | -          | Pen 标识符(从链接解析)     |
| `tab`      | `string`  | `'result'` | 默认显示的标签页             |
| `theme`    | `string`  | -          | 主题                         |
| `preview`  | `boolean` | `false`    | 预览模式                     |
| `editable` | `boolean` | `false`    | 可编辑模式                   |
| `width`    | `string`  | `'100%'`   | 宽度                         |
| `height`   | `string`  | -          | 高度                         |

## 示例

```md
@[codepen](leimapapa/RwOZQOW)
```

@[codepen](leimapapa/RwOZQOW)
plugins/collapse.md
md
# Collapse

<NpmBadge name="vitepress-plugin-collapse" />

折叠面板容器插件,用于在 Markdown 中创建可折叠的内容区块,支持手风琴模式、卡片样式和单项展开控制。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-collapse
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import collapse from 'vitepress-plugin-collapse'

export default defineConfig({
  plugins: [collapse()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { collapseMarkdownPlugin } from 'vitepress-plugin-collapse'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(collapseMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithCollapse(ctx)
  },
} satisfies Theme
```

## 语法

使用 `::: collapse` 容器配合列表来创建可折叠区块。每个列表项为一个折叠面板:
第一行为标题,后续缩进内容为面板正文。

### 基础用法

```md
::: collapse

- 标题 1

  内容 1

- 标题 2

  内容 2

:::
```

**渲染结果:**

::: collapse

- 标题 1

  内容 1

- 标题 2

  内容 2

:::

### 手风琴模式

添加 `accordion` 启用手风琴模式 —— 同时只能展开一项,展开新项会自动收起其他项。
配合 `expand` 可在没有显式 `:+` 标记时默认展开第一项。

```md
::: collapse accordion expand

- 问题 1

  问题 1 的回答

- 问题 2

  问题 2 的回答

:::
```

**渲染结果:**

::: collapse accordion expand

- 问题 1

  问题 1 的回答

- 问题 2

  问题 2 的回答

:::

### 卡片样式

添加 `card` 以带边框、圆角的卡片样式渲染容器,可与 `accordion` 组合使用。

```md
::: collapse accordion card

- 问题 1

  问题 1 的回答

- 问题 2

  问题 2 的回答

:::
```

**渲染结果:**

::: collapse accordion card

- 问题 1

  问题 1 的回答

- 问题 2

  问题 2 的回答

- 问题 3

  问题 3 的回答

:::

### 单项展开标记

在标题前添加 `:+` 展开该项,或 `:-` 收起该项。手风琴模式下只有第一个 `:+` 标记生效;
未添加标记的项将遵循容器的 `expand` 属性。

```md
::: collapse

- :+ 默认展开

  内容

- :- 默认收起

  内容

- 无标记,遵循容器 expand 属性

  内容

:::
```

**渲染结果:**

::: collapse

- :+ 默认展开

  内容

- :- 默认收起

  内容

- 无标记,遵循容器 expand 属性

  内容

:::

## 容器属性

| 属性        | 类型      | 说明                               |
| ----------- | --------- | ---------------------------------- |
| `accordion` | `boolean` | 启用手风琴模式(同时只能展开一项) |
| `card`      | `boolean` | 使用卡片样式(带边框和圆角)       |
| `expand`    | `boolean` | 默认展开状态                       |

## 标记说明

| 标记 | 说明                                 |
| ---- | ------------------------------------ |
| `:+` | 展开该项(手风琴模式下仅第一个生效) |
| `:-` | 收起该项                             |
plugins/field.md
md
# Field

<NpmBadge name="vitepress-plugin-field" />

字段容器插件,用于在 Markdown 中渲染结构化的 API 字段/属性文档,支持字段分组和 JSDoc 风格的标签注解。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-field
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import field from 'vitepress-plugin-field'

export default defineConfig({
  plugins: [field()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { fieldMarkdownPlugin } from 'vitepress-plugin-field'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(fieldMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithField } from 'vitepress-plugin-field/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithField(ctx)
  },
} satisfies Theme
```

## 语法

使用 `::: field` 容器来记录 API 字段/属性,配合 JSDoc 风格标签描述元数据。

### 基础字段

```md
::: field count
@type Number
@default 0
用户总数。
:::
```

### 字段标签

支持 `@name`、`@type`、`@default`、`@required`、`@deprecated`、`@optional`、`@description` 标签。

```md
::: field userName
@type String
@required
用户的唯一标识名称。
:::

::: field email
@type String
@optional
@default [email protected]
联系邮箱。
:::

::: field oldField
@type String
@deprecated
此字段已弃用,请使用新字段替代。
:::
```

**渲染结果:**

::: field userName
@type String
@required
用户的唯一标识名称。
:::

::: field email
@type String
@optional
@default [email protected]
联系邮箱。
:::

::: field oldField
@type String
@deprecated
此字段已弃用,请使用新字段替代。
:::

### 标签说明

| 标签           | 描述                                             |
| -------------- | ------------------------------------------------ |
| `@name`        | 覆盖字段名称(默认使用 `info` 中的名称)         |
| `@type`        | 字段类型注解                                     |
| `@default`     | 字段默认值                                       |
| `@required`    | 标记为必填字段                                   |
| `@optional`    | 标记为可选字段                                   |
| `@deprecated`  | 标记为已弃用字段                                 |
| `@description` | 显式描述文本,任何非标签行也会被纳入描述         |

### 字段分组

使用 `::: field-group` 容器将相关字段组合在一起。

```md
:::: field-group

::: field id
@type Number
@required
唯一标识符。
:::

::: field name
@type String
@optional
显示名称。
:::

::: field createdAt
@type Date
@default Date.now()
创建时间。
:::

::::
```

**渲染结果:**

:::: field-group

::: field id
@type Number
@required
唯一标识符。
:::

::: field name
@type String
@optional
显示名称。
:::

::: field createdAt
@type Date
@default Date.now()
创建时间。
:::

::::

### 显式描述

使用 `@description` 标签明确指定描述文本。任何非标签行也会自动作为描述内容。

```md
::: field count
@description 用户总数。此字段表示当前系统中的活跃用户数量。
@type Number
@default 0
:::
```
plugins/file-tree.md
md
# File Tree

<NpmBadge name="vitepress-plugin-file-tree" />

文件树展示插件,用于在 Markdown 中渲染文件目录结构。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-file-tree
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import fileTree from 'vitepress-plugin-file-tree'

export default defineConfig({
  plugins: [fileTree()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { fileTreeMarkdownPlugin } from 'vitepress-plugin-file-tree'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(fileTreeMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithFileTree } from 'vitepress-plugin-file-tree/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithFileTree(ctx)
  },
} satisfies Theme
```

## 语法

### 容器语法

使用 `::: file-tree` 容器,通过缩进表示文件层级:

```md
::: file-tree title="项目结构"

- src/
  - components/
    - Button.vue
    - Nav.vue
  - index.ts
- package.json
- tsconfig.json

:::
```

### 围栏代码语法

你也可以使用 `tree` 或 `file-tree` 作为语言标识符的围栏代码块,
内容采用 `tree` 命令行工具的输出格式(使用 Unicode 制表符):

````md
```tree
.
├── src/
│   ├── components/
│   │   ├── Button.vue
│   │   └── Nav.vue
│   └── index.ts
├── package.json
└── tsconfig.json
```
````

当你已有 `tree` 命令的输出内容时,可以直接粘贴到 Markdown 中而无需重新格式化,非常方便。

### 节点标注

两种语法均支持以下节点标注:

| 语法            | 说明                 |
| --------------- | -------------------- |
| `**文件名**`    | 高亮聚焦该文件       |
| `-- 文件名`     | 标记为删除           |
| `++ 文件名`     | 标记为新增           |
| `文件名 # 注释` | 添加注释             |
| `folder/`       | 标记为文件夹, 并折叠 |
| `…`             | 省略标记             |

容器语法示例(含标注):

```md
::: file-tree title="变更的文件"

- src/
  - -- old-file.ts
  - ++ new-file.ts
  - **main.ts** # 核心入口
  - …

:::
```

围栏代码语法示例(含标注):

````md
```tree
.
├── src/
│   ├── -- old-file.ts
│   ├── ++ new-file.ts
│   └── **main.ts** # 核心入口
└── …
```
````

## 示例

```md
::: file-tree

- docs
  - .vitepress
    - ++ config.ts
  - -- page1.md
  - index.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
  - .vitepress
    - ++ config.ts
  - -- page1.md
  - index.md
- theme  # 一个 **主题** 目录
  - client
    - components
      - **Navbar.vue**
    - composables
      - useNavbar.ts
    - styles
      - navbar.css
    - config.ts
  - node/
- package.json
- pnpm-lock.yaml
- .gitignore
- README.md
- …
:::
plugins/intro.md
md
# 插件总览

`vitepress-tuck` 生态提供了丰富的插件,涵盖 Markdown 语法扩展、内容嵌入、代码增强等场景。

所有插件均支持两种使用方式:

1. **vitepress-tuck 模式(推荐)**:通过 `plugins` 配置一键接入
2. **传统模式**:手动在 VitePress 配置中接入

## 插件列表

| 插件                             | 包名                             | 说明                                               |
| -------------------------------- | -------------------------------- | -------------------------------------------------- |
| [Steps](./steps)                 | `vitepress-plugin-steps`         | 步骤容器,用于创建步骤引导内容                     |
| [Obsidian](./obsidian)           | `vitepress-plugin-obsidian`      | Obsidian 语法支持,包括 Wiki 链接、Callout、注释等 |
| [Video](./video)                 | `vitepress-plugin-video`         | 视频嵌入,支持 Bilibili、YouTube、AcFun、ArtPlayer |
| [Mermaid](./mermaid)             | `vitepress-plugin-mermaid-tuck`  | Mermaid 图表支持                                   |
| [PlantUML](./plantuml)           | `vitepress-plugin-plantuml`      | PlantUML 图表支持                                  |
| [Field](./field)                 | `vitepress-plugin-field`         | API 字段文档,支持 JSDoc 标签                      |
| [折叠面板](./collapse)           | `vitepress-plugin-collapse`      | 折叠面板容器,支持手风琴模式                       |
| [Npm To](./npm-to)               | `vitepress-plugin-npm-to`        | npm 命令自动转换为其他包管理器命令                 |
| [Repo Card](./repo-card)         | `vitepress-plugin-repo-card`     | github / gitee 仓库卡片展示                        |
| [File Tree](./file-tree)         | `vitepress-plugin-file-tree`     | 文件树展示                                         |
| [Code Tree](./code-tree)         | `vitepress-plugin-code-tree`     | 代码树,带文件树侧边栏                             |
| [PDF](./pdf)                     | `vitepress-plugin-pdf`           | PDF 文件嵌入                                       |
| [QRCode](./qrcode)               | `vitepress-plugin-qrcode`        | 二维码生成                                         |
| [缩写词](./abbr)                 | `vitepress-plugin-abbr`          | 缩写词提示框,悬停或聚焦显示完整描述               |
| [Plot](./plot)                   | `vitepress-plugin-plot`          | 隐文遮罩,点击或悬停显示隐藏文本                   |
| [Can I Use](./caniuse)           | `vitepress-plugin-caniuse`       | caniuse 浏览器兼容性数据嵌入                       |
| [Code Collapse](./code-collapse) | `vitepress-plugin-code-collapse` | 代码块折叠                                         |
| [CodePen](./codepen)             | `vitepress-plugin-codepen`       | CodePen 嵌入                                       |
| [JSFiddle](./jsfiddle)           | `vitepress-plugin-jsfiddle`      | JSFiddle 嵌入                                      |
plugins/jsfiddle.md
md
# JSFiddle

<NpmBadge name="vitepress-plugin-jsfiddle" />

将 [JSFiddle](https://jsfiddle.net/) 项目嵌入到 VitePress 页面中。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-jsfiddle
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import jsfiddle from 'vitepress-plugin-jsfiddle'

export default defineConfig({
  plugins: [jsfiddle()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { jsfiddleMarkdownPlugin } from 'vitepress-plugin-jsfiddle'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(jsfiddleMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithJsFiddle } from 'vitepress-plugin-jsfiddle/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithJsFiddle(ctx)
  },
} satisfies Theme
```

## 语法

### 基本用法

```md
@[jsfiddle](user/id)
```

### 带配置的用法

```md
@[jsfiddle title="JS Fiddle" theme="dark" tab="js,css,html,result" height="400px"](pengzhanbo/1xbwz2p9)
```

### 属性说明

| 属性     | 类型               | 默认值                 | 说明                                   |
| -------- | ------------------ | ---------------------- | -------------------------------------- |
| `title`  | `string`           | `'JS Fiddle'`          | 标题                                   |
| `theme`  | `string`           | -                      | 主题(如 `dark`)                      |
| `tab`    | `string`           | `'js,css,html,result'` | 显示的标签页(用逗号分隔,会去除空格) |
| `width`  | `string`           | `'100%'`               | 宽度                                   |
| `height` | `string`           | -                      | 高度                                   |
| `ratio`  | `number \| string` | -                      | 宽高比                                 |

## 示例

```md
@[jsfiddle](pengzhanbo/1xbwz2p9)
```

@[jsfiddle](pengzhanbo/1xbwz2p9)
plugins/mermaid.md
md
# Mermaid

<NpmBadge name="vitepress-plugin-mermaid-tuck" />

Mermaid 图表插件,支持在 Markdown 中渲染 Mermaid 图表。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-mermaid-tuck
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import mermaid from 'vitepress-plugin-mermaid-tuck'

export default defineConfig({
  plugins: [
    mermaid(),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { mermaidMarkdownPlugin, mermaidVitePlugin } from 'vitepress-plugin-mermaid-tuck'

export default defineConfig({
  vite: {
    plugins: [mermaidVitePlugin({
      options: { theme: 'default' },
    })],
  },
  markdown: {
    config: (md) => {
      md.use(mermaidMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithMermaid } from 'vitepress-plugin-mermaid-tuck/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithMermaid(ctx)
  },
} satisfies Theme
```

## 语法

使用 `mermaid` 语言标记的代码块:

````md
```mermaid
flowchart LR
  Start --> Stop
```
````

````md
```mermaid
sequenceDiagram
  Alice->>John: Hello John, how are you?
  John-->>Alice: Great!
  Alice->>John: See you later!
```
````

## 配置

### MermaidPluginOptions

```ts
interface MermaidPluginOptions {
  /**
   * Mermaid 配置项(排除 startOnLoad 和 themeVariables)
   */
  options?: Omit<MermaidConfig, 'startOnLoad' | 'themeVariables'> & {
    themeVariables?: MermaidThemeVariables
  }

  /**
   * 多语言配置
   */
  locales?: Record<string, MermaidLocaleData>
}
```

### MermaidThemeVariables

支持对 Mermaid 各类图表的主题变量进行自定义,涵盖:

- 基础变量(背景色、文字颜色、线条颜色等)
- C4、Class、ER 图变量
- Flowchart 变量
- Gantt 图变量
- Git 图变量
- Journey 图变量
- Pie 图变量
- Requirement 图变量
- State 图变量
- Sequence 图变量

### MermaidLocaleData

```ts
interface MermaidLocaleData {
  chart?: string       // 默认 'Chart'
  source?: string      // 默认 'Source'
  fullscreen?: string  // 默认 'Fullscreen'
  download?: string    // 默认 'Download'
}
```

## 内置语言

插件内置了以下语言的支持:

- English (en, en-US)
- 简体中文 (zh, zh-CN)
- 日本語 (ja)
- 한국어 (ko)
- Español (es)
- Français (fr)
- Русский (ru)
- Deutsch (de)
- Português (pt)

## 示例

```mermaid
---
title: Flowchart
---
flowchart TB
    c1-->a2
    subgraph one
    a1-->a2
    end
    subgraph two
    b1-->b2
    end
    subgraph three
    c1-->c2
    end
    one --> two
    three --> two
    two --> c2
```

```mermaid
---
title: Sequence Diagram
---
sequenceDiagram
  Alice ->> Bob: Hello Bob, how are you?
  Bob-->>John: How about you John?
  Bob--x Alice: I am good thanks!
  Bob-x John: I am good thanks!
  Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.

  Bob-->Alice: Checking with John...
  Alice->John: Yes... John, how are you?
```

```mermaid
---
title: Animal Example
---
classDiagram
  note "From Duck till Zebra"
  Animal <|-- Duck
  note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
  Animal <|-- Fish
  Animal <|-- Zebra
  Animal : +int age
  Animal : +String gender
  Animal: +isMammal()
  Animal: +mate()
  class Duck{
    +String beakColor
    +swim()
    +quack()
  }
  class Fish{
    -int sizeInFeet
    -canEat()
  }
  class Zebra{
    +bool is_wild
    +run()
  }
```

```mermaid
gantt
  dateFormat  YYYY-MM-DD
  title       Adding GANTT diagram functionality to mermaid
  excludes    weekends
  %% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".)

  section A section
  Completed task            :done,    des1, 2014-01-06,2014-01-08
  Active task               :active,  des2, 2014-01-09, 3d
  Future task               :         des3, after des2, 5d
  Future task2              :         des4, after des3, 5d

  section Critical tasks
  Completed task in the critical line :crit, done, 2014-01-06,24h
  Implement parser                    :crit, done, after des1, 2d
  Create tests for parser             :crit, active, 3d
  Future task in critical line        :crit, 5d
  Create tests for renderer           :2d
  Add to mermaid                      :1d

  section Documentation
  Describe gantt syntax               :active, a1, after des1, 3d
  Add gantt diagram to demo page      :after a1  , 20h
  Add another diagram to demo page    :doc1, after a1  , 48h

  section Last section
  Describe gantt syntax               :after doc1, 3d
  Add gantt diagram to demo page      :20h
  Add another diagram to demo page    :48h
```
plugins/npm-to.md
md
# Npm To

<NpmBadge name="vitepress-plugin-npm-to" />

将 npm 命令自动转换为其他包管理器(pnpm、yarn、bun、deno)的对应命令。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-npm-to
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import npmTo from 'vitepress-plugin-npm-to'

export default defineConfig({
  plugins: [
    npmTo(['npm', 'pnpm', 'yarn']),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { npmToPlugin } from 'vitepress-plugin-npm-to'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(npmToPlugin, ['npm', 'pnpm', 'yarn'])
    },
  },
})
```

## 语法

使用 `::: npm-to` 容器包裹 npm 命令,插件会自动转换为多标签代码组:

````md
::: npm-to
```sh
npm install vitepress-plugin-steps
```
:::
````

渲染后会显示为多个标签页,每个标签页展示对应包管理器的安装命令:

- **npm**: `npm install vitepress-plugin-steps`
- **pnpm**: `pnpm add vitepress-plugin-steps`
- **yarn**: `yarn add vitepress-plugin-steps`

### 支持的命令类型

插件支持以下 npm 命令的自动转换:

| 命令                    | 示例                  | 支持                           |
| ----------------------- | --------------------- | ------------------------------ |
| `npm install` / `npm i` | `npm install react`   | 转换为各包管理器的 add 命令    |
| `npm install` (无参数)  | `npm install`         | 转换为纯安装命令               |
| `npm uninstall`         | `npm uninstall react` | 转换为各包管理器的 remove 命令 |
| `npm run`               | `npm run build`       | 转换为各包管理器的 run 命令    |
| `npm create`            | `npm create vite`     | 转换为各包管理器的 create 命令 |
| `npm init`              | `npm init -y`         | 转换为各包管理器的 init 命令   |
| `npx`                   | `npx eslint .`        | 转换为各包管理器的等效命令     |
| `npm ci`                | `npm ci`              | 转换为各包管理器的 ci 命令     |

### 自定义标签

可以通过 `tabs` 属性指定要显示的标签:

````md
::: npm-to tabs="npm,pnpm"
```sh
npm install vitepress-plugin-steps
```
:::
````

## 配置

```ts
type NpmToPluginOptions =
  | NpmToPackageManager[]   // 如 ['npm', 'pnpm', 'yarn']
  | {
      tabs?: NpmToPackageManager[]
    }

type NpmToPackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno'
```

- 默认显示 `npm`、`pnpm`、`yarn` 三个标签
- 支持 `bun` 和 `deno` 的转换

### 示例

````md
::: npm-to
```sh
npm install vitepress-plugin-steps
```
:::
````

::: npm-to

```sh
npm install vitepress-plugin-steps
```

:::
plugins/obsidian.md
md
# Obsidian

<NpmBadge name="vitepress-plugin-obsidian" />

提供 Obsidian 风格 Markdown 语法支持,包括 Wiki 链接、Callout 标注、嵌入文件和注释语法。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-obsidian
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import obsidian from 'vitepress-plugin-obsidian'

export default defineConfig({
  plugins: [
    obsidian({
      // 可选项,全部默认为 true
      callout: true,
      comment: true,
      embedLink: true,
      wikiLink: true,
    }),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { obsidianMarkdownPlugin } from 'vitepress-plugin-obsidian'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(obsidianMarkdownPlugin, {
        callout: true,
        comment: true,
        embedLink: true,
        wikiLink: true,
      })
    },
  },
})
```

## Wiki 链接

Wiki 链接是 Obsidian 中用于链接到其他笔记的语法。使用双括号 `[[]]` 包裹内容来创建内部链接。

### 语法

```md
[[文件名]]
[[文件名#标题]]
[[文件名#标题#子标题]]
[[文件名|别名]]
[[文件名#标题|别名]]
[[https://example.com|外部链接]]
```

### 文件名搜索规则

当使用 Wiki 链接时,文件名会按照以下规则进行搜索匹配:

**匹配优先级:**

1. **完整路径** - 精确匹配文件路径
2. **模糊匹配** - 匹配路径结尾的文件名,优先匹配最短路径

**路径解析规则:**

- **相对路径**(以 `.` 开头):相对于当前文件所在目录解析
- **绝对路径**(不以 `.` 开头):在整个文档树中搜索,优先匹配最短路径
- **目录形式**(以 `/` 结尾):匹配该目录下的 `index.md`

**示例:**

假设文档结构如下:

```txt
docs/
├── index.md
├── guide/
│   ├── index.md
│   └── markdown/
│       └── obsidian.md
```

在 `docs/guide/markdown/obsidian.md` 中:

| 语法           | 匹配结果                                                 |
| -------------- | -------------------------------------------------------- |
| `[[obsidian]]` | 匹配 `docs/guide/markdown/obsidian.md`(通过文件名检索) |
| `[[./]]`       | 匹配 `docs/guide/markdown/index.md`(相对路径)          |
| `[[../]]`      | 匹配 `docs/guide/README.md`(上级目录)                  |
| `[[guide/]]`   | 匹配 `docs/guide/README.md`(目录形式)                  |

### 示例

**外部链接:**

**输入:**

```md
[[https://example.com|外部链接]]
```

**输出:**

[[https://example.com|外部链接]]

**内部锚点链接:**

**输入:**

```md
[[npm-to]]  <!-- 通过文件名检索 -->
[[#Wiki 链接]]  <!-- 当前页面使用 heading -->
[[file-tree#配置]]  <!-- 通过文件名检索,并链接到 heading -->
```

**输出:**

[[npm-to]]

[[#Wiki 链接]]

[[file-tree#配置]]

[Obsidian 官方 - **Wiki Links**](https://obsidian.md/zh/help/links){.readmore}

## 嵌入内容

嵌入语法允许你将其他文件资源插入到当前页面中。

### 语法

```md
![[文件名]]
![[文件名#标题]]
![[文件名#标题#子标题]]
```

文件名搜索规则与 [Wiki 链接](#文件名搜索规则) 相同。

::: info 以 `/` 开头或 无路径前缀如 `./` 形式的,从 `public` 目录中加载资源
:::

### 图片嵌入

**语法:**

```md
![[图片]]
![[图片|宽度]]
![[图片|宽度x高度]]
```

支持格式:`jpg`、`jpeg`、`png`、`gif`、`avif`、`webp`、`svg`、`bmp`、`ico`、`tiff`、`apng`、`jfif`、`pjpeg`、`pjp`、`xbm`

**示例:**

```md
![[tuck-logo.svg]]
```

![[tuck-logo.svg|125]]

### PDF 嵌入

> [!NOTE]
> PDF 嵌入需要添加 [vitepress-plugin-pdf](./pdf.md) 插件才能正常工作。

**语法:**

```md
![[文档.pdf]]
![[文档.pdf#page=1]]  <!-- #page=1 表示第一页 -->
![[文档.pdf#page=1#height=300]]  <!-- #page=页码 #height=高度 -->
```

支持格式:`pdf`

**示例:**

```md
![[https://plume.pengzhanbo.cn/files/sample-1.pdf]]
```

![[https://plume.pengzhanbo.cn/files/sample-1.pdf]]

---

### 音频嵌入

**语法:**

```md
![[音频文件]]
```

支持格式:`mp3`、`flac`、`wav`、`ogg`、`opus`、`webm`、`acc`

---

### 视频嵌入

> [!NOTE]
> 视频嵌入需要添加 [vitepress-plugin-video](./video.md) 插件才能正常工作。

**语法:**

```md
![[视频文件]]
![[视频文件#height=400]]  <!-- 设置视频高度 -->
```

支持格式:`mp4`、`webm`、`mov` 等

---

### 内容片段嵌入

通过 `#标题` 可以嵌入指定标题下的内容片段:

**输入:**

```md
![[我的笔记]]
![[我的笔记#标题一]]
![[我的笔记#标题一#子标题]]
```

[Obsidian 官方 - 插入文件](https://obsidian.md/zh/help/embeds){.readmore}
[Obsidian 官方 - 文件格式](https://obsidian.md/zh/help/file-formats){.readmore}

## Callout

Callout 是一种用于突出显示重要信息的语法,类似于 Vitepress 的 `::: note` 提示框语法。

### 语法

```md
> [!note]
> 内容
```

**可选标题:**

```md
> [!tip] 自定义标题
> 内容
```

### 类型

Callout 支持以下类型,别名会自动映射到对应的主要类型:

| 类型        | 别名                                                                | 说明             |
| ----------- | ------------------------------------------------------------------- | ---------------- |
| `note`      | `quote`, `cite`                                                     | 笔记、引用       |
| `tip`       | `hint` ,`check`, `done`,`success`                                   | 技巧、提示       |
| `info`      | `todo`                                                              | 信息、待办       |
| `warning`   | `question`, `help`, `faq`                                           | 警告、问题、帮助 |
| `caution`   | `attention`, `failure`, `fail`, `missing`, `danger`, `error`, `bug` | 注意、失败、危险 |
| `important` | `example`                                                           | 重要、示例       |
| `details`   | `abstract`, `summary`, `tldr`                                       | 详情、摘要       |

### 示例

**基础用法:**

**输入:**

```md
> [!NOTE]
> 这是一个笔记提示框。
```

**输出:**

> [!NOTE]
> 这是一个笔记提示框。

---

**带标题:**

**输入:**

```md
> [!TIP] 实用技巧
> 使用 `pnpm` 可以显著加快依赖安装速度。
```

**输出:**

> [!TIP] 实用技巧
> 使用 `pnpm` 可以显著加快依赖安装速度。

---

**多种类型:**

**输入:**

```md
> [!success]
> 操作成功完成!
>
> [!warning]
> 这是一个警告信息。
>
> [!caution]
> 请谨慎操作,此操作不可撤销。
```

**输出:**

> [!success]
> 操作成功完成!

> [!warning]
> 这是一个警告信息。

> [!caution]
> 请谨慎操作,此操作不可撤销。

---

**Details 类型:**

`details` 类型会渲染为 HTML `<details>` 元素,支持折叠展开:

**输入:**

```md
> [!details]
> 点我展开更多内容
>
> 这是一段隐藏的内容。
```

**输出:**

> [!details]
> 点我展开更多内容
>
> 这是一段隐藏的内容。

[Obsidian 官方 - Callout](https://obsidian.md/zh/help/callouts){.readmore}

## 注释

使用 `%%` 包裹的内容会被当作注释,不会渲染到页面中。

### 语法

**行内注释:**

```md
这是一个 %%行内注释%% 示例。
```

**块级注释:**

```md
%%
这是一个块级注释。
可以跨越多行。
%%
```

### 示例

**行内注释:**

**输入:**

```md
这是一个 %%行内注释%% 示例。
```

**输出:**

这是一个 %%行内注释%% 示例。

---

**块级注释:**

**输入:**

```md
注释之前的内容

%%
这是一个块级注释。

可以跨越多行。
%%

注释之后的内容
```

**输出:**

注释之前的内容

%%
这是一个块级注释。
%%

可以跨越多行。

[Obsidian 官方 - 注释](https://obsidian.md/zh/help/syntax#%E6%B3%A8%E9%87%8A){.readmore}

## 注意事项

- 这些插件提供的是 **兼容性支持**,并非完全实现 Obsidian 的全部功能
- 部分 Obsidian 特有的功能(如内部链接的图谱视图、双向链接等)不在支持范围内
- 嵌入内容时,被嵌入的页面也会参与主题的构建过程
- PDF 嵌入需要添加 [vitepress-plugin-pdf](./pdf.md) 插件才能正常工作
- 视频嵌入需要添加 [vitepress-plugin-video](./video.md) 插件才能正常工作
- 以 `/` 开头或使用 `./` 形式的嵌入资源会从 `public` 目录加载
plugins/pdf.md
md
# PDF

<NpmBadge name="vitepress-plugin-pdf" />

PDF 文件嵌入插件,在页面中嵌入 PDF 查看器。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-pdf
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import pdf from 'vitepress-plugin-pdf'

export default defineConfig({
  plugins: [pdf()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { pdfMarkdownPlugin } from 'vitepress-plugin-pdf'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(pdfMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithPDF } from 'vitepress-plugin-pdf/client' // [!code ++]
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithPDF(ctx) // [!code ++]
  },
} satisfies Theme
```

## 语法

使用 `@[pdf]()` 嵌入 PDF:

```md
@[pdf](https://example.com/sample.pdf)
@[pdf](./sample.pdf)
```

### 指定页码

```md
@[pdf page="3"](https://example.com/sample.pdf)
@[pdf p="3"](https://example.com/sample.pdf)
```

### 配置选项

```md
@[pdf no-toolbar width="100%" height="600px" zoom="100"](https://example.com/sample.pdf)
```

### 属性说明

| 属性         | 类型               | 默认值   | 说明       |
| ------------ | ------------------ | -------- | ---------- |
| `width`      | `string`           | `'100%'` | 宽度       |
| `height`     | `string`           | -        | 高度       |
| `ratio`      | `number \| string` | -        | 宽高比     |
| `zoom`       | `number`           | `50`     | 缩放比例   |
| `no-toolbar` | `boolean`          | `false`  | 隐藏工具栏 |

## 示例

```md
@[pdf](https://plume.pengzhanbo.cn/files/sample-1.pdf)
```

@[pdf](https://plume.pengzhanbo.cn/files/sample-1.pdf)
plugins/plantuml.md
md
# PlantUML

<NpmBadge name="vitepress-plugin-plantuml" />

PlantUML 图表插件,支持在 Markdown 中渲染 PlantUML 图表。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-plantuml
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import plantuml from 'vitepress-plugin-plantuml'

export default defineConfig({
  plugins: [
    plantuml(),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { plantumlMarkdownPlugin, plantumlVitePlugin } from 'vitepress-plugin-plantuml'

export default defineConfig({
  vite: {
    plugins: [plantumlVitePlugin()],
  },
  markdown: {
    config: (md) => {
      md.use(plantumlMarkdownPlugin)
    },
    languageAlias: { plantuml: 'txt' },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithPlantuml } from 'vitepress-plugin-plantuml/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithPlantuml(ctx)
  },
} satisfies Theme
```

## 语法

使用 `plantuml` 语言标记的代码块:

````md
```plantuml
@startuml
Alice -> Bob: 认证请求
Bob --> Alice: 认证响应
@enduml
```
````

### 输出格式

插件支持 `svg`(默认)和 `png` 两种输出格式。可在代码块中指定:

````md
```plantuml png
@startuml
class Example {
  +attribute: string
  +method(): void
}
@enduml
```
````

或全局配置默认格式:

```ts
plantuml({ format: 'png' }) // 默认为 'svg'
```

## 配置

### PlantumlPluginOptions

```ts
interface PlantumlPluginOptions {
  /**
   * 输出格式,可选 'svg' | 'png'
   * @default 'svg'
   */
  format?: PlantumlFormat

  /**
   * PlantUML 服务器 URL
   * @default 'https://www.plantuml.com/plantuml'
   */
  serverURL?: string
}
```

## 功能特性

- **明暗主题适配** — 自动生成明暗两套图表,跟随 VitePress 主题切换
- **图表 / 源码切换** — 在渲染结果和 PlantUML 源码之间切换查看
- **全屏查看** — 点击全屏按钮以浮层方式查看图表
- **图片下载** — 一键下载当前图表为图片文件
- **多语言支持** — 内置中、英、日、韩、西、法、俄、德、葡九种语言
- **SVG 优化** — 输出 SVG 时自动通过 SVGO 优化,移除冗余样式和背景层
- **构建缓存** — 渲染结果缓存到磁盘,加速增量构建

## 内置语言

插件内置了以下语言的支持:

- English (en, en-US)
- 简体中文 (zh, zh-CN)
- 日本語 (ja)
- 한국어 (ko)
- Español (es)
- Français (fr)
- Русский (ru)
- Deutsch (de)
- Português (pt)

## 示例

### 时序图

```plantuml
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response

Alice -> Bob: Another authentication Request
Alice <-- Bob: Another authentication Response
@enduml
```

### 用例图

```plantuml
@startuml
left to right direction
actor "顾客" as customer
actor "收银员" as cashier

rectangle 收银系统 {
  customer -- (结账)
  (结账) -- cashier
  (结账) .> (打印小票) : include
  (结账) .> (支付) : include
  (支付) .> (现金支付)
  (支付) .> (扫码支付)
}
@enduml
```

### 类图

```plantuml
@startuml
class Vehicle
class Car
class Bike

Vehicle <|-- Car
Vehicle <|-- Bike

class Engine
class Wheel

Car *-- Engine
Car *-- Wheel
@enduml
```

### 活动图

```plantuml
@startuml
start
:用户登录;
if (验证通过?) then (是)
  :进入首页;
  if (有新消息?) then (是)
    :显示消息提醒;
  else (否)
    :继续浏览;
  endif
else (否)
  :显示错误提示;
  :返回登录页;
endif
stop
@enduml
```
plugins/plot.md
md
# Plot

<NpmBadge name="vitepress-plugin-plot" />

隐文遮罩插件,通过点击或悬停来显示隐藏的文本内容。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-plot
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import plot from 'vitepress-plugin-plot'

export default defineConfig({
  plugins: [
    plot({
      trigger: 'hover',  // 'hover' | 'click',默认 'hover'
      effect: 'mask',    // 'mask' | 'blur',默认 'mask'
    }),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { plotMarkdownPlugin } from 'vitepress-plugin-plot'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(plotMarkdownPlugin, {
        trigger: 'hover',
        effect: 'mask',
      })
    },
  },
})
```

```ts
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'
import { enhanceAppWithPlot } from 'vitepress-plugin-plot/client' // [!code ++]
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithPlot(ctx) // [!code ++]
  },
} satisfies Theme
```

## 语法

使用 `!!` 包裹隐藏文本:

```md
答案是 !!plot!!
```

使用 `classname` 设置交互行为:

```md
!!plot!!{.click .blur}
```

支持以下 `classname`

- `.click / .hover`:点击显示 / 鼠标悬停显示
- `.blur / .mask`:模糊效果 / 遮罩效果

## 配置

```ts
interface PlotOptions {
  /**
   * 触发方式
   * - 'hover': 鼠标悬停显示
   * - 'click': 点击显示
   * @default 'hover'
   */
  trigger?: 'hover' | 'click'

  /**
   * 隐藏效果
   * - 'mask': 遮罩效果(默认)
   * - 'blur': 模糊效果
   * @default 'mask'
   */
  effect?: 'mask' | 'blur'
}
```

## 示例

```md
鲁迅说过:“!!我没说这段话!!”
```

鲁迅说过:“!!我没说这段话!!”

```md
书山有路勤为径,!!学海无涯苦作舟!!{.click .blur}。
```

书山有路勤为径,!!学海无涯苦作舟!!{.click .blur}。
plugins/qrcode.md
md
# QRCode

<NpmBadge name="vitepress-plugin-qrcode" />

二维码生成插件,将文本或链接生成二维码。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-qrcode
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import qrcode from 'vitepress-plugin-qrcode'

export default defineConfig({
  plugins: [qrcode()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { qrcodeMarkdownPlugin } from 'vitepress-plugin-qrcode'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(qrcodeMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithQrcode } from 'vitepress-plugin-qrcode/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithQrcode(ctx)
  },
} satisfies Theme
```

## 语法

### 嵌入块语法

```md
@[qrcode](https://www.baidu.com)
@[qrcode](任意文本)
@[qrcode](./caniuse.md)
```

### 带样式的卡片模式

```md
@[qrcode card title="扫码访问"](https://www.baidu.com)
```

### 容器语法

适合使用于长文本的情况

```md
::: qrcode title="扫码访问"
https://www.baidu.com
:::
```

### 属性说明

| 属性       | 类型      | 说明                          |
| ---------- | --------- | ----------------------------- |
| `card`     | `boolean` | 以卡片模式展示                |
| `title`    | `string`  | 卡片标题                      |
| `logo`     | `string`  | 二维码 logo, link 格式, 可选  |
| `logoSize` | `number`  | logo 大小占比, 可选, 默认 0.2 |
| `width`    | `number`  | 二维码宽度, 可选              |

## 示例

```md
@[qrcode](https://www.baidu.com)
```

@[qrcode](https://www.baidu.com)

**站内链接,自动添加 logo:**

```md
@[qrcode](./file-tree.md)
```

@[qrcode](./file-tree.md)

**使用卡片模式:**

```md
@[qrcode card title="扫码访问 文件树插件"](./file-tree.md)
```

@[qrcode card title="扫码访问 文件树插件"](./file-tree.md)
plugins/repo-card.md
md
# Repo Card

<NpmBadge name="vitepress-plugin-repo-card" />

仓库信息卡片插件,在你的 Markdown 中展示 GitHub/Gitee 仓库的详细信息卡片,支持 embed 内嵌语法和容器多卡片网格布局。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-repo-card
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import repoCard from 'vitepress-plugin-repo-card'

export default defineConfig({
  plugins: [repoCard()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { repoCardMarkdownPlugin } from 'vitepress-plugin-repo-card'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(repoCardMarkdownPlugin)
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithRepoCard } from 'vitepress-plugin-repo-card/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithRepoCard(ctx)
  },
} satisfies Theme
```

## 语法

### 嵌入语法

使用 `@[repo]()` 语法嵌入单个仓库信息卡片。不指定 `register` 时默认使用 GitHub。

```md
@[repo](owner/name)
@[repo github](owner/name)
@[repo gitee](owner/name)
```

### 显示完整仓库名

使用 `fullname` 参数显示 `owner/name` 完整路径。对于组织仓库,会自动显示完整名称。

```md
@[repo fullname github](pengzhanbo/vitepress-tuck)
```

### 容器语法

使用 `::: repo` 容器将多个仓库卡片以响应式网格布局展示。

```md
::: repo
@[repo github](vuejs/vitepress)
@[repo github](vuejs/core)
:::
```

## 示例

### 单个仓库卡片

**GitHub 仓库:**

```md
@[repo github](vuejs/vitepress)
```

@[repo github](vuejs/vitepress)

**Gitee 仓库:**

```md
@[repo gitee](openharmony/kernel_liteos_a)
```

@[repo gitee](openharmony/kernel_liteos_a)

**显示完整名称:**

```md
@[repo fullname](pengzhanbo/vitepress-tuck)
```

@[repo fullname](pengzhanbo/vitepress-tuck)

### 多卡片网格布局

```md
::: repo
@[repo fullname](pengzhanbo/vitepress-tuck)
@[repo](pengzhanbo/vite-plugin-mock-dev-server)
@[repo](pengzhanbo/utils)
@[repo](pengzhanbo/vuepress-theme-plume)
:::
```

::: repo
@[repo fullname](pengzhanbo/vitepress-tuck)
@[repo](pengzhanbo/vite-plugin-mock-dev-server)
@[repo](pengzhanbo/utils)
@[repo](pengzhanbo/vuepress-theme-plume)
:::

## 卡片信息

每个仓库卡片会展示以下信息:

| 字段     | 说明                         |
| -------- | ---------------------------- |
| 仓库名   | 仓库名称或 `owner/name` 全名 |
| 可见性   | Public / Private 标签        |
| 归档状态 | 已归档时显示警告标记         |
| 描述     | 仓库描述文本                 |
| 主要语言 | 主要编程语言及对应颜色标记   |
| Star 数  | 格式化展示(如 `1.2k`)      |
| Fork 数  | 格式化展示(如 `1.2k`)      |
| 许可证   | 许可证名称(如有)           |

## 组件用法

你也可以直接使用 `VPRepoCard` 组件:

```vue
<script setup lang="ts">
import { VPRepoCard } from 'vitepress-plugin-repo-card/client'
</script>

<template>
  <VPRepoCard repo="vuejs/vitepress" register="github" />
  <VPRepoCard repo="owner/name" register="gitee" fullname />
</template>
```

### Props

| 属性       | 类型                  | 默认值     | 说明                        |
| ---------- | --------------------- | ---------- | --------------------------- |
| `repo`     | `string`              | (必填)     | `owner/name` 格式的仓库标识 |
| `register` | `'github' \| 'gitee'` | `'github'` | 仓库平台                    |
| `fullname` | `boolean`             | -          | 显示 `owner/name` 完整名称  |
plugins/steps.md
md
# Steps

<NpmBadge name="vitepress-plugin-steps" />

步骤容器插件,用于在 Markdown 中创建步骤引导内容。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-steps
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import steps from 'vitepress-plugin-steps'

export default defineConfig({
  plugins: [steps()],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { stepsMarkdownPlugin } from 'vitepress-plugin-steps'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(stepsMarkdownPlugin)
    },
  },
})
```

同时在主题中引入样式:

```ts [.vitepress/theme/index.ts]
import 'vitepress-plugin-steps/style.css'
```

## 语法

使用 `::: steps` 容器包裹步骤内容,每个步骤以无序/有序列表项开头:

```md
::: steps

- 第一步

  第一步的描述内容

- 第二步

  第二步的描述内容

- 第三步

  第三步的描述内容,支持标题语法

:::
```

**渲染结果:**

::: steps

- 第一步

  第一步的描述内容

- 第二步

  第二步的描述内容

- 第三步

  第三步的描述内容,支持标题语法

:::
plugins/video.md
md
# Video

<NpmBadge name="vitepress-plugin-video" />

视频嵌入插件,支持 Bilibili、YouTube、AcFun 和 ArtPlayer。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-video
```

:::

## 使用

### vitepress-tuck 模式 <Badge type="tip">推荐</Badge>

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress-tuck'
import video from 'vitepress-plugin-video'

export default defineConfig({
  plugins: [
    video({
      // 可选项,全部默认为 true
      artplayer: true,
      youtube: true,
      bilibili: true,
      acfun: true,
    }),
  ],
})
```

[查看 **vitepress-tuck** 了解更多](../guide/quick-start.md){.readmore}

### 传统模式

```ts [.vitepress/config.ts]
import { defineConfig } from 'vitepress'
import { videoMarkdownPlugin } from 'vitepress-plugin-video'

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(videoMarkdownPlugin, {
        artplayer: true,
        youtube: true,
        bilibili: true,
        acfun: true,
      })
    },
  },
})
```

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import { enhanceAppWithVideo } from 'vitepress-plugin-video/client'
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp(ctx) {
    enhanceAppWithVideo(ctx)
  },
} satisfies Theme
```

## 语法

### Bilibili

嵌入 Bilibili 视频

```md
@[bilibili](bvid)
@[bilibili](aid cid)
@[bilibili p2 autoplay time=30](bvid)
```

| 配置项      | 类型               | 描述                           |
| ----------- | ------------------ | ------------------------------ |
| `p{number}` | -                  | 视频分 P 号                    |
| `autoplay`  | `boolean`          | 自动播放                       |
| `time`      | `number \| string` | 开始时间,秒数或 HH:MM:SS 格式 |

### YouTube

嵌入 YouTube 视频

```md
@[youtube](video_id)
@[youtube autoplay loop start=10 end=120](video_id)
```

| 配置项     | 类型      | 描述           |
| ---------- | --------- | -------------- |
| `autoplay` | `boolean` | 自动播放       |
| `loop`     | `boolean` | 循环播放       |
| `start`    | `number`  | 开始时间,秒数 |
| `end`      | `number`  | 结束时间,秒数 |

### AcFun

嵌入 AcFun 视频

```md
@[acfun](ac_id)
```

### ArtPlayer

使用 ArtPlayer 播放器嵌入本地或远程视频:

```md
@[artPlayer](/videos/demo.mp4)
@[artPlayer muted autoplay poster="/cover.jpg" width="800px"](/videos/demo.mp4)
```

| 配置项     | 类型      | 默认值   | 描述              |
| ---------- | --------- | -------- | ----------------- |
| `autoplay` | `boolean` | `false`  | 自动播放          |
| `muted`    | `boolean` | `false`  | 静音              |
| `loop`     | `boolean` | `false`  | 循环播放          |
| `volume`   | `number`  | `0.75`   | 音量等级          |
| `poster`   | `string`  | -        | 封面图片 URL      |
| `autoMini` | `boolean` | `false`  | 自 mini模式       |
| `width`    | `string`  | `"100%"` | 播放器宽度        |
| `height`   | `string`  | -        | 播放器高度        |
| `ratio`    | `string`  | -        | 比例,例如 "16:9" |

支持 `mp4`, `mp3`, `webm`, `ogg`, `mkv`, `mov` 格式。

如果您的视频格式为 `'mpd'`, `'dash'`, 还需要再手动安装 `dashjs` :

:::npm-to

```sh
npm i dashjs
```

:::

如果您的视频格式为 `'m3u8'`, `'hls'`, 还需要再手动安装 `hls.js` :

:::npm-to

```sh
npm i hls.js
```

:::

如果您的视频格式为 `'ts'`, `'flv'`, 还需要再手动安装 `mpegts.js` :

:::npm-to

```sh
npm i mpegts.js
```

:::

## 配置

```ts
interface VideoPluginOptions {
  /**
   * 启用 ArtPlayer 播放器
   * @default true
   */
  artplayer?: boolean

  /**
   * 启用 YouTube 视频嵌入
   * @default true
   */
  youtube?: boolean

  /**
   * 启用 Bilibili 视频嵌入
   * @default true
   */
  bilibili?: boolean

  /**
   * 启用 AcFun 视频嵌入
   * @default true
   */
  acfun?: boolean
}
```

## 示例

### Bilibili

```md
@[bilibili](BV1EZ42187Hg)
```

@[bilibili](BV1EZ42187Hg)

### YouTube

```md
@[youtube](0JJPfz5dg20)
```

@[youtube](0JJPfz5dg20)

### AcFun

```md
@[acfun](ac47431669)
```

@[acfun](ac47431669)

### ArtPlayer

```md
@[artPlayer](https://artplayer.org/assets/sample/video.mp4)
```

@[artPlayer](https://artplayer.org/assets/sample/video.mp4)
plugins/watermark.md
md
---
watermark: true
---

# 水印

<NpmBadge name="vitepress-plugin-watermark" />

给站点添加水印的插件,纯客户端实现,无需 Node 侧配置。

## 安装

::: npm-to

```sh
npm install vitepress-plugin-watermark
```

:::

## 使用

此插件仅提供客户端 `setupWatermark` 函数,需要在 Layout 包装组件的 `<script setup>` 中调用。

创建一个自定义的 Layout 包装组件:

``` vue [.vitepress/theme/Layout.vue]
<script setup lang="ts">
import { setupWatermark } from 'vitepress-plugin-watermark'
import Theme from 'vitepress/theme'
import { h, useAttrs, useSlots } from 'vue'

const slots = useSlots()
const attrs = useAttrs()

const Layout = () => h(Theme.Layout, attrs, slots)

setupWatermark()
</script>

<template>
  <Layout />
</template>
```

然后在主题中注册此 Layout:

```ts [.vitepress/theme/index.ts]
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'

export default {
  extends: DefaultTheme,
  Layout,
} satisfies Theme
```

## 配置

`setupWatermark` 接受一个可选的配置对象:

```ts
setupWatermark({
  enabled: true,
  content: '我的水印',
  fontColor: '#76747f',
  globalAlpha: 0.165,
  width: 200,
  height: 200,
  rotate: -22,
  fontSize: '16px',
  fontFamily: 'sans-serif',
})
```

| 选项                | 类型                                           | 默认值                                     | 说明                           |
| ------------------- | ---------------------------------------------- | ------------------------------------------ | ------------------------------ |
| `enabled`           | `boolean \| ((pageData: PageData) => boolean)` | `true`                                     | 是否启用水印,支持函数动态判断 |
| `content`           | `string`                                       | 站点标题                                   | 水印文本内容                   |
| `fontColor`         | `string`                                       | `'#76747f'`                                | 字体颜色                       |
| `globalAlpha`       | `number`                                       | `0.165`(普通模式)/ `0.005`(盲水印模式) | 透明度                         |
| `mode`              | `'default' \| 'blind'`                         | `'default'`                                | 水印模式,`'blind'` 为盲水印   |
| `width`             | `number`                                       | —                                          | 单个水印区域宽度               |
| `height`            | `number`                                       | —                                          | 单个水印区域高度               |
| `rotate`            | `number`                                       | —                                          | 旋转角度                       |
| `fontSize`          | `string`                                       | —                                          | 字体大小                       |
| `fontFamily`        | `string`                                       | —                                          | 字体                           |
| `fontStyle`         | `string`                                       | —                                          | 字体样式                       |
| `fontWeight`        | `string`                                       | —                                          | 字体粗细                       |
| `image`             | `string`                                       | —                                          | 图片水印 URL                   |
| `layout`            | `'default' \| 'grid'`                          | —                                          | 布局模式                       |
| `zIndex`            | `number`                                       | —                                          | CSS 层级                       |
| `mutationObserve`   | `boolean`                                      | —                                          | 启用 DOM 变化监听保护          |
| `monitorProtection` | `boolean`                                      | —                                          | 启用监控保护                   |
| `movable`           | `boolean`                                      | —                                          | 允许拖拽移动水印               |
| `parent`            | `Element \| string`                            | —                                          | 挂载的父元素                   |

更多配置项请参阅 [watermark-js-plus](https://github.com/zhensherlock/watermark-js-plus) 文档。

## 单页水印

通过页面的 frontmatter 控制单个页面的水印行为:

### 启用水印

```yaml
---
watermark: true
---
```

### 自定义文本

```yaml
---
watermark: 机密文件
---
```

### 完全自定义

```yaml
---
watermark:
  content: 草稿
  fontColor: '#ff0000'
  globalAlpha: 0.3
  rotate: 30
---
```

### 禁用特定页面

```yaml
---
watermark: false
---
```

### 动态启用水印

通过 `enabled` 函数基于页面路径动态控制:

```ts
setupWatermark({
  enabled: (pageData) => {
    // 仅在 guide/ 目录下的页面启用水印
    return pageData.relativePath.startsWith('guide/')
  },
})
```
config.ts
ts
import { defineAdditionalConfig } from 'vitepress'

export default defineAdditionalConfig({
  lang: 'zh-CN',
  themeConfig: {
    nav: [
      { text: '指南', link: '/zh/guide/quick-start', activeMatch: '/zh/guide/' },
      { text: '插件', link: '/zh/plugins/intro', activeMatch: '/zh/plugins/' },
    ],
    sidebar: {
      '/zh/guide/': { base: '/zh/guide/', items: [
        { text: '快速开始', link: 'quick-start' },
        { text: '插件开发', link: 'plugin-dev' },
        { text: '包装现有插件', link: 'wrap-plugin' },
        { text: 'Core API', link: 'api' },
        { text: 'Toolkit API', link: 'toolkit' },
      ] },
      '/zh/plugins/': { base: '/zh/plugins/', items: [
        { text: '总览', link: 'intro' },
        { text: '步骤', link: 'steps' },
        { text: '文件树', link: 'file-tree' },
        { text: '代码树', link: 'code-tree' },
        { text: '字段', link: 'field' },
        { text: '折叠面板', link: 'collapse' },
        { text: '隐秘文本', link: 'plot' },
        { text: 'Npm To', link: 'npm-to' },
        { text: 'Mermaid', link: 'mermaid' },
        { text: 'PlantUML', link: 'plantuml' },
        { text: 'Repo Card', link: 'repo-card' },
        { text: '水印', link: 'watermark' },
        { text: '二维码', link: 'qrcode' },
        { text: '缩写词', link: 'abbr' },
        { text: '视频嵌入', link: 'video' },
        { text: 'Obsidian', link: 'obsidian' },
        { text: 'PDF', link: 'pdf' },
        { text: 'Can I Use', link: 'caniuse' },
        { text: '代码块行折叠', link: 'code-collapse' },
        { text: 'CodePen', link: 'codepen' },
        { text: 'JSFiddle', link: 'jsfiddle' },
      ] },
    },

    search: {
      options: {
        translations: {
          button: {
            buttonText: '搜索',
            buttonAriaLabel: '搜索',
          },
          modal: {
            displayDetails: '显示详细列表',
            resetButtonTitle: '清除查询条件',
            backButtonTitle: '返回',
            noResultsText: '没有找到相关结果',
            footer: {
              selectText: '选择',
              selectKeyAriaLabel: '键入',
              navigateText: '导航',
              navigateUpKeyAriaLabel: '向上',
              navigateDownKeyAriaLabel: '向下',
              closeText: '关闭',
              closeKeyAriaLabel: '退出',
            },
          },
        },
      },
    },
    editLink: {
      pattern: 'https://github.com/pengzhanbo/vitepress-tuck/edit/main/docs/:path',
      text: '在 Github 编辑此页',
    },

    footer: {
      message: '基于 MIT 许可发布',
      copyright: `版权所有 © 2022-${new Date().getFullYear()} pengzhanbo`,
    },

    docFooter: {
      prev: '上一页',
      next: '下一页',
    },

    outline: {
      label: '页面导航',
      level: [2, 3],
    },

    lastUpdated: {
      text: '最后更新于',
    },

    notFound: {
      title: '页面未找到',
      quote:
        '但如果你不改变方向,并且继续寻找,你可能最终会到达你所前往的地方。',
      linkLabel: '前往首页',
      linkText: '带我回首页',
    },

    langMenuLabel: '多语言',
    returnToTopLabel: '回到顶部',
    sidebarMenuLabel: '菜单',
    darkModeSwitchLabel: '主题',
    lightModeSwitchTitle: '切换到浅色模式',
    darkModeSwitchTitle: '切换到深色模式',
    skipToContentLabel: '跳转到内容',
  },
})
index.md
md
---
layout: home
title: VitePress Tuck
hero:
  name: VitePress Tuck
  text: 增强 vitepress 配置,提供插件能力。
  actions:
    - text: 开始使用
      link: /zh/guide/quick-start
    - text: Github
      link: https://github.com/pengzhanbo/vitepress-tuck
      theme: alt
  image:
    src: /tuck-logo.svg
    alt: VitePress Tuck
---

基于 MIT 许可发布