Skip to content

Code Tree

npm versionnpm monthly downloadsnpm sizenpm license

Code tree plugin for rendering code structures with a file tree sidebar in Markdown, supporting file switching.

This plugin depends on the File Tree plugin for the file tree sidebar components.

Installation

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

Usage

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

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

Learn more about vitepress-tuck

Native Mode

.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

Syntax

The plugin provides two syntaxes to render a code tree: a container syntax for inline file content, and an embed syntax to load files from a directory.

Container Syntax

Use the ::: code-tree container with fenced code blocks inside. Each fence must declare a filename via the [filename] syntax in its info string.

md
::: code-tree title="Project Structure"

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

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

:::

Container Attributes

AttributeDescriptionDefault
titleCode tree title-
heightCode tree container height420px
entryEntry file, opened by default-
show-sidebarShow sidebar by defaultfalse

Active File

Add :active to a fence's info string to mark it as the default active file:

md
::: code-tree

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

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

:::

Embed Syntax

Use @[code-tree](dir) to embed a directory as a code tree. Files in the directory are loaded and rendered automatically.

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

The dir supports the following prefixes:

PrefixDescription
@Relative to VitePress srcDir
/Relative to VitePress project root
-Relative to the current markdown file's directory

Embed Attributes

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

Configuration

CodeTreePluginOptions

ts
interface CodeTreePluginOptions {
  /**
   * Default code tree container height
   * @default '420px'
   */
  height?: string | number

  /**
   * Glob patterns to ignore files
   * Applied when loading files from a directory via the embed syntax
   * node_modules and .DS_Store are always ignored
   * @default []
   */
  ignores?: string[]

  /**
   * File loaders
   * Used to load resource files when embedding a directory with `@[code-tree](dir)`
   * Custom loaders are merged before the built-in ones, so they take precedence
   * @default []
   */
  loaders?: CodeTreeFileLoader[]
}

File Loaders

Loaders are used by the embed syntax to load file content. The plugin ships with built-in loaders for common file types, and custom loaders are merged before the built-in ones, so they take precedence.

Built-in loaders cover the following file types:

  • Dot files (.git*, .env*, .*ignore, .npmrc): Rendered as plain text
  • .XXXrc config files (e.g. .eslintrc): Rendered as JSON
  • Image files: Rendered as <img> tags with proper src resolution
  • Source files supported by Shiki: Rendered as fenced code blocks with syntax highlighting
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'),
        },
      ],
    }),
  ],
})

The filter field accepts a glob pattern string, an array of glob patterns, or a predicate function that receives a CodeTreeFile and returns a boolean.

Example

Container Syntax

md
:::code-tree title="Code Tree" show-sidebar
```ts [index.ts]
const a = 1
```

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

Code Tree

index.ts

main.rs

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

Embed Syntax

md
@[code-tree title="Code Tree" show-sidebar](@/en)

Code Tree

guide

api.md

plugin-dev.md

quick-start.md

toolkit.md

wrap-plugin.md

plugins

abbr.md

annotation.md

caniuse.md

code-collapse.md

code-tree.md

codepen.md

collapse.md

field.md

file-tree.md

icons.md

intro.md

jsfiddle.md

mark.md

mermaid.md

npm-to.md

obsidian.md

pdf.md

plantuml.md

plot.md

qrcode.md

repo-card.md

stackblitz.md

steps.md

video.md

watermark.md

index.md

guide/api.md
md
# API Reference

<NpmBadge name="vitepress-tuck" />

## vitepress-tuck

### defineConfig

`defineConfig` is the core function of `vitepress-tuck`, used as a replacement for VitePress's `defineConfig`.

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

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

#### Parameters

| Parameter | Type                      | Description                                  |
| --------- | ------------------------- | -------------------------------------------- |
| `config`  | `UserConfig & TuckConfig` | VitePress original config + `plugins` option |

`TuckConfig` definition:

```ts
interface TuckConfig {
  /**
   * vitepress plugin list
   */
  plugins?: VitepressPlugin[]

  /**
   * Options for the unplugin-vue-components plugin.
   *
   * @see - https://github.com/unplugin/unplugin-vue-components
   */
  components?: ComponentsOptions
}
```

#### Return Value

Returns the merged and complete VitePress `UserConfig` object.

#### How It Works

1. Iterates through the `plugins` array, merging each plugin's `markdown`, `vite`, `vue` and other configurations.
2. Collects all hooks (`buildEnd`, `transformHead`, `transformHtml`, `transformPageData`, `postRender`)
   and merges them according to their respective strategies:
   - `markdown.config`, `buildEnd` → concurrent execution
   - `transformHead` → concurrent execution with result merging
   - `transformHtml`, `transformPageData`, `postRender` → sequential execution, chained
3. Injects the `client` configuration from plugins into the built-in `virtual-enhance-app` virtual module.
4. Collects `componentResolver` declarations from plugins into the built-in `unplugin-vue-components` resolver list.
5. Finally merges the user's own configuration.

---

### definePlugin

Used to define VitePress plugins. It is a type helper function that helps developers create plugins in a standardized way.

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

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

#### Parameters

| Parameter | Type                              | Description                                                                 |
| --------- | --------------------------------- | --------------------------------------------------------------------------- |
| `plugin`  | `(option?: T) => VitepressPlugin` | Plugin factory function, receives optional options, returns a plugin object |

---

### VitepressPlugin

Type definition for plugin objects:

```ts
interface VitepressPlugin extends Pick<
  UserConfig,
  'markdown' | 'vite' | 'vue' | 'buildEnd' | 'transformHead' | 'transformHtml' | 'transformPageData' | 'postRender'
> {
  /**
   * Plugin name (required, for identification and debugging)
   */
  name: string

  /**
   * Client configuration, automatically injected into virtual:enhance-app
   */
  client?: {
    /**
     * Custom import statement list
     *
     * @example
     * ```ts
     * imports: ['import "my-plugin/style.css"']
     * ```
     */
    imports?: string[]

    /**
     * Client-side enhance function name.
     * - Not set: no enhanceApp function is injected
     * - true: default function name is `enhanceApp`
     * - string: the specified function name
     *
     * @example
     * ```ts
     * enhance: 'enhanceAppWithMyPlugin'
     * ```
     */
    enhance?: string | boolean
  }

  /**
   * Component resolvers for unplugin-vue-components auto-import.
   *
   * - String array: declared component names are resolved from `<plugin-name>/client`
   * - ComponentResolver object: custom resolve logic
   *
   * @example
   * ```ts
   * // Simple form
   * componentResolver: ['MyComponent', 'OtherComponent']
   * ```
   */
  componentResolver?: string[] | ComponentResolver
}
```

#### Supported VitePress Configuration Options

| Property            | Type                              | Description                                                                                          |
| ------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `markdown`          | `UserConfig['markdown']`          | Markdown-related configuration, commonly used with `markdown.config` to register markdown-it plugins |
| `vite`              | `UserConfig['vite']`              | Vite configuration, for registering Vite plugins and optimization options                            |
| `vue`               | `UserConfig['vue']`               | Vue application-level configuration                                                                  |
| `buildEnd`          | `UserConfig['buildEnd']`          | Build completion hook (concurrent)                                                                   |
| `transformHead`     | `UserConfig['transformHead']`     | HTML head transform hook (concurrent, results merged)                                                |
| `transformHtml`     | `UserConfig['transformHtml']`     | HTML content transform hook (sequential, chained)                                                    |
| `transformPageData` | `UserConfig['transformPageData']` | Page data transform hook (sequential, chained)                                                       |
| `postRender`        | `UserConfig['postRender']`        | Post-render hook (sequential, chained)                                                               |

---

### virtual:enhance-app

`virtual:enhance-app` is a virtual module provided by `vitepress-tuck`.
It automatically collects all plugins' `client` configurations and generates the corresponding code.

Import it in the theme entry:

```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 support requires adding the type reference:

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

---

### Built-in Plugin: auto-components

`vitepress-tuck` integrates [`unplugin-vue-components`](https://github.com/unplugin/unplugin-vue-components) as
a built-in plugin, providing automatic on-demand component importing. This plugin is enabled by default — no manual registration required.

#### Default Behavior

- Scans `.vue` and `.md` files for component usage
- Generates type declarations at `node_modules/.vite/components.d.ts`
- Automatically collects `componentResolver` declarations from all plugins

#### Custom Configuration

Customize `unplugin-vue-components` behavior via the `components` option in `defineConfig`:

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

export default defineConfig({
  components: {
    // Custom scan directories
    dirs: ['src/components'],
    // Use directory as namespace
    directoryAsNamespace: true,
    // Other unplugin-vue-components options...
  },
  plugins: [],
})
```

::: tip
Plugin `componentResolver` declarations are merged with the user's `components` configuration.
Plugin developers only need to declare `componentResolver` in their plugin,
and users can use the corresponding components directly in Markdown or Vue files without manual imports.
:::
guide/plugin-dev.md
md
# Plugin Development

<NpmBadge name="vitepress-tuck" />

`vitepress-tuck` provides the `definePlugin` function to help developers easily create VitePress plugins.
With `definePlugin`, you can centralize options like `markdown`, `vite`, and `vue` — which would
otherwise require scattered configuration by users — into a single plugin function.

## Basic Structure

A plugin based on `vitepress-tuck` consists of the following parts:

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

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

  // Client configuration: auto-injected into virtual:enhance-app
  client: {
    imports: [],
    enhance: 'enhanceAppWithMyPlugin',
  },

  // Component resolver: declare plugin components for auto on-demand import
  componentResolver: ['MyComponent', 'OtherComponent'],

  // Markdown configuration: register markdown-it plugins
  markdown: {
    config: (md) => {
      md.use(myMarkdownPlugin, options?.markdownOptions)
    },
  },

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

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

## Core Concepts

### name

Each plugin must have a unique `name` for identification and debugging:

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

### client

The `client` configuration is used to inject code into the client side. `vitepress-tuck` automatically
injects this code into the `virtual:enhance-app` virtual module.

#### client.imports

Add custom import statements, commonly used for importing style files:

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

#### client.enhance

Specifies the client-side enhance function name. This function will be called within the VitePress theme's `enhanceApp(ctx)`.

- When set to `true`, the default function name is `enhanceApp`
- When set to a string, that string is the function name

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

Correspondingly, export a function with the same name in the plugin's client entry file:

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

export function enhanceAppWithMyPlugin({ app }: EnhanceAppContext) {
  // Register components, directives, etc.
  app.component('MyComponent', MyComponent)
}
```

### componentResolver

The `componentResolver` declares the Vue components a plugin provides, enabling automatic on-demand import via
the built-in `unplugin-vue-components`. Users can use component names directly in Markdown or Vue files without manual imports.

#### String Array Form

The simplest form is an array of component names — they are resolved from `<plugin-name>/client`:

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

Correspondingly, export these components in the plugin's client entry:

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

export { MyComponent, OtherComponent }
```

#### Custom ComponentResolver

For more complex resolution logic, pass a `ComponentResolver` object from `unplugin-vue-components`:

```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
When a plugin uses both `client.enhance` for component registration and `componentResolver`,
prefer `componentResolver` for on-demand importing to reduce unnecessary component bundling.
:::

### markdown

Configure markdown-it plugins to extend Markdown syntax:

```ts
{
  markdown: {
    config: (md) => {
      // Register custom containers
      md.use(containerPlugin)
      // Register inline rules
      md.inline.ruler.before('emphasis', 'my_rule', myRule)
    },
  }
}
```

### vite

Configure Vite plugins and optimization options:

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

### Hooks

`definePlugin` supports various VitePress lifecycle hooks:

| Hook                | Description                    | Execution                  |
| ------------------- | ------------------------------ | -------------------------- |
| `buildEnd`          | Triggered on build completion  | Concurrent                 |
| `transformHead`     | Transforms HTML head           | Concurrent, results merged |
| `transformHtml`     | Transforms HTML content        | Sequential, chained        |
| `transformPageData` | Transforms page data           | Sequential, chained        |
| `postRender`        | Triggered after page rendering | Sequential, chained        |

## Client Code Organization

When a plugin needs to provide client-side code (e.g., Vue components), the recommended directory structure is:

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

- my-plugin
  - src
    - client
      - components
        - MyComponent.vue
      - index.ts # Export enhanceApp function
    - node
      - myPlugin.ts # Plugin core logic
      - index.ts # Plugin entry, using definePlugin
  - package.json
  - tsconfig.json

:::

Configure `exports` in `package.json` to export client code separately:

```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"
    }
  }
}
```

## Recommended: vitepress-plugin-toolkit

`vitepress-plugin-toolkit` provides a rich set of utility functions to assist plugin development, including:

- `createContainerPlugin` — Create markdown-it custom containers
- `createContainerSyntaxPlugin` — Create custom syntax containers
- `createEmbedRuleBlock` — Create embed syntax blocks
- `resolveAttrs` / `stringifyAttrs` — Attribute parsing and serialization
- `createLogger` — Logging utility
- `createLocales` — Internationalization support
- `useSize` — Responsive size calculation

See the [Toolkit API documentation](./toolkit) for details.

## Complete Example

Here is a simple step plugin built with `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)
    },
  },
}))
```

Users simply need to:

```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
# Quick Start

## What is vitepress-tuck?

`vitepress-tuck` provides simple, flexible, and low-barrier plugin development and integration capabilities
for VitePress. It wraps VitePress's `defineConfig` to provide an additional `plugins` option, shifting the
complexity of plugin integration into the plugins themselves, so users only need to add plugins to the `plugins` array.

## Installation

::: npm-to

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

:::

## Using in a VitePress Site

::: steps

- ### Replace the Configuration File

  Replace `defineConfig` in `.vitepress/config.ts` with `vitepress-tuck`'s `defineConfig`:

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

  export default defineConfig({
    plugins: [
      // Add plugins here
    ],
    // Other VitePress config options ...
  })
  ```

- ### Configure the Client Entry

  Import `virtual:enhance-app` in `.vitepress/theme/index.ts`:

  ```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` is a virtual module. `vitepress-tuck` automatically injects plugin client code into it — no manual configuration required.

- ### TypeScript Support

  If your project uses TypeScript, add the type reference in `tsconfig.json`:

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

:::

## Using Plugins

Using plugins is simple — just import and invoke them in the `plugins` array:

```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({
      // Plugin options can be passed here
    }),
  ],
})
```

Refer to each plugin's documentation for its configuration options.
guide/toolkit.md
md
# Toolkit API

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

`vitepress-plugin-toolkit` is a VitePress plugin development toolkit that provides a rich set of utility functions to assist plugin development.

Installation:

::: npm-to

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

:::

## Node-side API

Import from `vitepress-plugin-toolkit`:

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

---

### createContainerPlugin

Create a markdown-it custom container plugin. Used for processing `::: type` syntax, where content is parsed normally by markdown-it.

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

**ContainerOptions:**

```ts
interface ContainerOptions {
  /**
   * Callback for rendering container opening tag
   */
  before?: (info: string, tokens: Token[], index: number, options: Options, env: any) => string
  /**
   * Callback for rendering container closing tag
   */
  after?: (info: string, tokens: Token[], index: number, options: Options, env: any) => string
}
```

**Usage Example:**

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

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

---

### createContainerSyntaxPlugin

Create a custom syntax container plugin. Unlike `createContainerPlugin`, the content is not parsed by markdown-it and must be handled manually.

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

Raw content inside the container is accessible via `token.content`, and metadata via `token.meta`.

**Usage Example:**

```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

Create an embed rule block for handling `@[type ...](args)` syntax.

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

**EmbedRuleBlockOptions:**

```ts
interface EmbedRuleBlockOptions<Meta extends Record<string, any>> {
  /** Embed type, e.g. 'pdf', 'qrcode' */
  type: string
  /** Token name, defaults to type */
  name?: string
  /** Name of the rule to insert before, default 'code' */
  beforeName?: string
  /** Rule options */
  ruleOptions?: RuleOptions
  /** Parse the `info` and `source` in `@[type info](source)` and convert them into a metadata object. */
  meta: (info: string, source: string) => Meta
  /** Generate content from metadata */
  content?: (meta: Meta, env: MarkdownEnv) => string
  /** Render function */
  render?: (tokens: Token[], index: number, env: MarkdownEnv) => string
}
```

**Usage Example:**

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

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

---

### resolveAttrs

Parse an attribute string into an object.

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

**Example:**

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

---

### resolveAttr

Parse a single attribute value from an info string.

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

---

### stringifyAttrs

Serialize an attributes object into an HTML attribute string.

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

**Example:**

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

---

### createLogger

Create a logger instance.

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

**LogLevel:**

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

**Example:**

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

logger.info('Plugin loaded')       // [my-plugin] Plugin loaded
logger.warn('Potential issue')     // [my-plugin] Potential issue
logger.error('An error occurred')  // [my-plugin] An error occurred
logger.debug('Debug info', true)   // [my-plugin] Debug info
```

---

### createLocales

Create multi-language configuration, automatically matching VitePress's language settings.

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

**Example:**

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

---

### getVitepressConfig

Get the current VitePress site configuration.

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

---

### getLocaleWithPath

Get language information based on a file path.

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

---

### resolveRouteLink

Convert a relative path to a VitePress route link.

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

---

### parseRect

Parse a size string, automatically appending a unit if a number is passed.

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

**Example:**

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

---

### slugify

Convert a string to a URL-friendly slug format.

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

---

### treatAsHtml

Determine whether a filename should be treated as HTML (non-known-resource extensions are treated as HTML).

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

---

## Client-side API

Import from `vitepress-plugin-toolkit/client`:

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

### VPCopyButton

Copy button component.

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

### VPLoading

Loading state component.

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

### useSize

Responsive size calculation 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
}
```

### Device Detection Utilities

| Function | Description |
| -------- | ----------- |
| `isIPhone()` | Check if iPhone |
| `isIPad()` | Check if iPad |
| `isIOS()` | Check if iOS device |
| `isMacOS()` | Check if macOS |
| `isWindows()` | Check if Windows |
| `isMobile()` | Check if mobile device |
| `isSafari()` | Check if Safari browser |

---

## Shared Utilities

Can be imported from either `vitepress-plugin-toolkit` or `vitepress-plugin-toolkit/client`:

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

### isExternal

Check if a link is an external link.

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

### isLinkWithProtocol

Check if a link contains a protocol prefix.

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

---

## CSS Transition Animations

`vitepress-plugin-toolkit` provides predefined CSS transition animations that can be imported in plugins:

```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
# Wrapping Existing Plugins

If your VitePress site is already using plugins, or you have a plugin that hasn't been packaged
for `vitepress-tuck` mode, you can easily wrap it into a compatible form.

## Why Wrap?

Traditional VitePress plugins often require users to configure multiple locations:

```ts
// Traditional approach — configuration scattered across multiple places
import { defineConfig } from 'vitepress'
import { someMarkdownPlugin } from 'some-plugin'

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

By wrapping into a `vitepress-tuck` plugin, users only need to add it to the `plugins` array — all
configuration is handled internally by the plugin.

## Wrapping Methods

### Wrapping with definePlugin

For any existing plugin logic, use `definePlugin` to wrap it:

```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)],
  },
}))
```

### Wrapping a Markdown-Only Plugin

If the plugin only involves a markdown-it extension, wrapping is very simple:

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

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

### Wrapping a Vite-Only Plugin

If the plugin only needs Vite configuration:

```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)],
  },
}))
```

### Wrapping a Plugin That Requires Client Injection

If the plugin needs to inject components or styles on the client side, configure the `client` option:

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

export default definePlugin(() => ({
  name: 'vitepress-plugin-my-plugin',
  client: {
    imports: [
      // Inject styles
      'import "my-plugin/style.css"',
    ],
    enhance: 'enhanceAppWithMyPlugin',
  },
  markdown: {
    config: (md) => {
      md.use(myMarkdownPlugin)
    },
  },
  vite: {
    ssr: {
      // Ensure correct bundling during SSR
      noExternal: ['my-plugin'],
    },
  },
}))
```

And export the enhance function in the client entry file:

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

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

## Real-World Example

Here's how to wrap `vitepress-plugin-group-icons` into a `vitepress-tuck` plugin:

```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',
      ],
    },
  },
}))
```

## Maintaining Compatibility

Wrapped plugins can still be used independently (without `vitepress-tuck`). Simply provide both usage modes in the plugin's README:

```ts
// Method 1: vitepress-tuck mode (recommended)
import { defineConfig } from 'vitepress-tuck'
import myPlugin from 'my-plugin'

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

// Method 2: Native VitePress mode
import { defineConfig } from 'vitepress'
import { myPlugin } from 'my-plugin/raw'

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

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

Abbreviation plugin that adds an interactive tooltip to abbreviations in Markdown, displaying the full description on hover or focus.

Forked and modified from [`markdown-it-abbr`](https://github.com/markdown-it/markdown-it-abbr).

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

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

Register the component in the theme:

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

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

## Syntax

Define an abbreviation using the `*[ABBR]: Full description` syntax. Once defined,
every occurrence of the abbreviation throughout the document is automatically recognized
and rendered with an interactive tooltip.

```md
The HTML specification is maintained by the W3C.

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

**Rendered Result:**

The HTML specification is maintained by the W3C.

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

### Inline Markdown in Descriptions

The full description of an abbreviation supports inline Markdown syntax, such as bold, italic, links, and code.

```md
The **HTML** specification is maintained by the W3C.

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

**Rendered Result:**

The **HTML** specification is maintained by the W3C.

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

## Global Abbreviations

Instead of defining abbreviations inline in each Markdown file, you can provide a global preset of abbreviations
through the plugin options. When both a global definition and an inline definition exist for the same abbreviation,
the inline definition takes precedence.

```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',
    }),
  ],
})
```

With this configuration, every occurrence of `HTML` and `W3C` across all pages will be rendered as
abbreviations automatically, without needing to define them in each file.

## Examples

Hover over or focus the abbreviations below to see their full descriptions:

```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/annotation.md
md
# Annotation

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

Annotation plugin that adds interactive annotation markers to Markdown content.
Clicking a marker reveals a popover displaying the annotation content.

Annotation content supports full block-level Markdown syntax.
A single label can be defined multiple times — all definitions are collected and displayed as separate items in the popover.

## Installation

::: npm-to

```sh
npm install vitepress-plugin-annotation
```

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

export default defineConfig({
  markdown: {
    config: (md) => {
      md.use(annotationMarkdownPlugin, {
        HTML: 'HyperText Markup Language',
      })
    },
  },
})
```

Register the component in the theme:

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

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

## Syntax

Define an annotation using the `[+label]: annotation content` syntax, then reference it anywhere in your document
with `[+label]`. The reference is rendered as a small interactive marker that, on click,
reveals a popover displaying the annotation content.

```md
The **four great classical novels** [+novels] of Chinese literature are widely known.

[+novels]: **Romance of the Three Kingdoms** — a historical novel set in the Three Kingdoms period.
```

**Rendered Result:**

The **four great classical novels** [+novels1] of Chinese literature are widely known.

[+novels1]: **Romance of the Three Kingdoms** — a historical novel set in the Three Kingdoms period.

### Block-level Markdown in Annotations

Annotation content supports full block-level Markdown syntax, including headings, paragraphs, lists, code blocks, and more.

```md
The framework is built on [+vue].

[+vue]:
  ## Vue

  A progressive framework for building user interfaces.

  - Reactive data binding
  - Component-based architecture
  - [Learn more](https://vuejs.org/)
```

### Multi-line Definitions

Annotation definitions can span multiple lines. Continuation lines must be indented by at least two spaces.
Empty lines within the indented block are preserved as part of the annotation content.
A non-indented content line (such as the next definition or a new paragraph) terminates the multi-line definition.

```md
This concept is explained in [+detail].

[+detail]:
  This is the first paragraph of the annotation.

  This is the second paragraph, separated by an empty line
  but still part of the same annotation.

This line is not indented, so the annotation definition ends here.
```

### Multiple Definitions per Label

A single label can be defined multiple times. All definitions are collected and displayed as separate items
in the popover, allowing you to provide multiple pieces of related information for the same reference.

```md
JavaScript has multiple versions [+js].

[+js]: **ES2015 (ES6)** — Introduced classes, modules, arrow functions, and more.

[+js]: **ES2020** — Added optional chaining, nullish coalescing, and BigInt.
```

**Rendered Result:**

JavaScript has multiple versions [+js].

[+js]: **ES2015 (ES6)** — Introduced classes, modules, arrow functions, and more.

[+js]: **ES2020** — Added optional chaining, nullish coalescing, and BigInt.

## Global Annotations

Instead of defining annotations inline in each Markdown file, you can provide a global preset of annotations through the plugin options. This is useful for terms that should be annotated consistently across all pages.

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

export default defineConfig({
  plugins: [
    annotation({
      HTML: 'HyperText Markup Language',
      CSS: ['Cascading Style Sheets', 'A style sheet language used for describing the presentation of a document.'],
    }),
  ],
})
```

Each global annotation value can be a string or an array of strings. When an array is provided, each element becomes a separate item in the popover, just like multiple inline definitions for the same label.

## Examples

Click the annotation markers below to see the full content:

```md
The **four great classical novels** [+novels] of Chinese literature are widely known.

[+novels]:
  **Romance of the Three Kingdoms**

  Set against the backdrop of the Three Kingdoms period, it depicts the political and military struggles among Wei, Shu, and Wu.

[+novels]:
  **Journey to the West**

  Tells the story of the Tang Monk and his three disciples on their journey to obtain Buddhist scriptures, filled with mythology and fantastical adventures.

[+novels]:
  **Dream of the Red Chamber**

  Set against the rise and fall of the four great families — Jia, Shi, Wang, and Xue — it depicts the love tragedy of Jia Baoyu and Lin Daiyu.

[+novels]:
  **Water Margin**

  Depicts the story of 108 outlaws led by Song Jiang who gathered at Mount Liang during the late Northern Song dynasty.
```

The **four great classical novels** [+novels] of Chinese literature are widely known.

[+novels]:
  **Romance of the Three Kingdoms**

  Set against the backdrop of the Three Kingdoms period, it depicts the political and military struggles among Wei, Shu, and Wu.

[+novels]:
  **Journey to the West**

  Tells the story of the Tang Monk and his three disciples on their journey to obtain Buddhist scriptures, filled with mythology and fantastical adventures.

[+novels]:
  **Dream of the Red Chamber**

  Set against the rise and fall of the four great families — Jia, Shi, Wang, and Xue — it depicts the love tragedy of Jia Baoyu and Lin Daiyu.

[+novels]:
  **Water Margin**

  Depicts the story of 108 outlaws led by Song Jiang who gathered at Mount Liang during the late Northern Song dynasty.
plugins/caniuse.md
md
# Can I Use

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

Embed browser compatibility data from [caniuse.com](https://caniuse.com/), displaying browser support for CSS/JS features on the page.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

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

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

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

## Syntax

Use `@[caniuse]()` to embed browser compatibility data:

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

### Baseline Mode

Use baseline mode to display a feature support overview:

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

### Custom Version Range

Use `{}` to specify a version range:

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

- `{past, future}`: past is the number of versions to look back, future is the number to look ahead
- Default is `{5, 2}` — 5 versions back,  2 versions forward

### Getting Feature Names

Search for the feature you want to display on [caniuse.com](https://caniuse.com/),
click the `#` on the left side of the card, and you'll get the feature name in the browser's address bar.

::: details Not sure which part is the feature name?
**Take CSS `grid` as an example**

Search for `grid` on caniuse.com, then click the `#` on the first card. The address bar will change to `https://caniuse.com/css-grid`.

The feature name is `css-grid`.

Verify here: <https://caniuse.com/?search=grid>
:::

## Examples

### Display browser support for the `fetch` feature

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

@[caniuse](fetch)

### Display baseline for the `fetch` feature

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

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

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

Code block folding plugin that automatically collapses code blocks exceeding a specified number of lines, improving page readability.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Global Folding

When `options` is set to a number, or `true` (defaults to 15 lines), all code blocks exceeding that line count will be automatically folded.

### Per-Block Folding

Use the `:collapsed-lines={N}` syntax in the code block's info string to control individual blocks:

````md
```ts :collapsed-lines
// Folds using the default value (15 lines)
```

```ts :collapsed-lines=10
// Folds starting from line 10
```
````

## Configuration

```ts
/**
 * Collapsed lines configuration
 * - `true`: Fold when exceeding 15 lines
 * - `number`: Fold when exceeding the specified line count
 * - `false`: No global folding, allows per-block control (default)
 * - `'disable'`: Fully disable the plugin
 * @default 'false'
 */
type options = boolean | number | 'disable'
```

## Example

````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" />

Code tree plugin for rendering code structures with a file tree sidebar in Markdown, supporting file switching.

This plugin depends on the [File Tree](./file-tree.md) plugin for the file tree sidebar components.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

The plugin provides two syntaxes to render a code tree: a container syntax for inline file content,
and an embed syntax to load files from a directory.

### Container Syntax

Use the `::: code-tree` container with fenced code blocks inside.
Each fence must declare a filename via the `[filename]` syntax in its info string.

````md
::: code-tree title="Project Structure"

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

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

:::
````

#### Container Attributes

| Attribute      | Description                   | Default |
| -------------- | ----------------------------- | ------- |
| `title`        | Code tree title               | -       |
| `height`       | Code tree container height    | `420px` |
| `entry`        | Entry file, opened by default | -       |
| `show-sidebar` | Show sidebar by default       | `false` |

#### Active File

Add `:active` to a fence's info string to mark it as the default active file:

````md
::: code-tree

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

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

:::
````

### Embed Syntax

Use `@[code-tree](dir)` to embed a directory as a code tree. Files in the directory are loaded and rendered automatically.

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

The `dir` supports the following prefixes:

| Prefix | Description                                       |
| ------ | ------------------------------------------------- |
| `@`    | Relative to VitePress `srcDir`                    |
| `/`    | Relative to VitePress project root                |
| -      | Relative to the current markdown file's directory |

#### Embed Attributes

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

## Configuration

### CodeTreePluginOptions

```ts
interface CodeTreePluginOptions {
  /**
   * Default code tree container height
   * @default '420px'
   */
  height?: string | number

  /**
   * Glob patterns to ignore files
   * Applied when loading files from a directory via the embed syntax
   * node_modules and .DS_Store are always ignored
   * @default []
   */
  ignores?: string[]

  /**
   * File loaders
   * Used to load resource files when embedding a directory with `@[code-tree](dir)`
   * Custom loaders are merged before the built-in ones, so they take precedence
   * @default []
   */
  loaders?: CodeTreeFileLoader[]
}
```

### File Loaders

Loaders are used by the embed syntax to load file content. The plugin ships with built-in loaders for common file types,
and custom loaders are merged before the built-in ones, so they take precedence.

Built-in loaders cover the following file types:

- Dot files (`.git*`, `.env*`, `.*ignore`, `.npmrc`): Rendered as plain text
- `.XXXrc` config files (e.g. `.eslintrc`): Rendered as JSON
- Image files: Rendered as `<img>` tags with proper `src` resolution
- Source files supported by Shiki: Rendered as fenced code blocks with syntax highlighting

```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'),
        },
      ],
    }),
  ],
})
```

The `filter` field accepts a glob pattern string, an array of glob patterns,
or a predicate function that receives a `CodeTreeFile` and returns a boolean.

## Example

### Container Syntax

````md
:::code-tree title="Code Tree" show-sidebar
```ts [index.ts]
const a = 1
```

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

:::code-tree title="Code Tree" show-sidebar

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

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

:::

### Embed Syntax

```md
@[code-tree title="Code Tree" show-sidebar](@/en)
```

@[code-tree title="Code Tree" show-sidebar](@/en)
plugins/codepen.md
md
# CodePen

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

Embed [CodePen](https://codepen.io/) projects into VitePress pages.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Basic Usage

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

### With Options

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

### Attribute Reference

| Attribute  | Type      | Default    | Description                         |
| ---------- | --------- | ---------- | ----------------------------------- |
| `title`    | `string`  | -          | Title                               |
| `user`     | `string`  | -          | CodePen username (parsed from link) |
| `slash`    | `string`  | -          | Pen identifier (parsed from link)   |
| `tab`      | `string`  | `'result'` | Default tab to display              |
| `theme`    | `string`  | -          | Theme                               |
| `preview`  | `boolean` | `false`    | Preview mode                        |
| `editable` | `boolean` | `false`    | Editable mode                       |
| `width`    | `string`  | `'100%'`   | Width                               |
| `height`   | `string`  | -          | Height                              |

## Example

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

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

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

Collapse container plugin for creating collapsible content sections in Markdown, with support for accordion mode,
card style, and per-item expand control.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

Use the `::: collapse` container with a list to create collapsible sections. Each
list item becomes a collapse panel: the first line is the title, and the
following indented content is the panel body.

### Basic

```md
::: collapse

- Title 1

  Content 1

- Title 2

  Content 2

:::
```

**Rendered Result:**

::: collapse

- Title 1

  Content 1

- Title 2

  Content 2

:::

### Accordion

Add `accordion` to enable accordion mode — only one item can be expanded at a
time; expanding a new item collapses all others. Use `expand` to expand the
first item by default when no explicit `:+` flag is set.

```md
::: collapse accordion expand

- Question 1

  Answer 1

- Question 2

  Answer 2

:::
```

**Rendered Result:**

::: collapse accordion expand

- Question 1

  Answer 1

- Question 2

  Answer 2

:::

### Card Style

Add `card` to render the container with a bordered, rounded card style. It can
be combined with `accordion`.

```md
::: collapse accordion card

- Question 1

  Answer 1

- Question 2

  Answer 2

:::
```

**Rendered Result:**

::: collapse accordion card

- Question 1

  Answer 1

- Question 2

  Answer 2

- Question 3

  Answer 3

:::

### Per-item Expand Flag

Prefix a title with `:+` to expand an item, or `:-` to collapse it. In accordion
mode, only the first `:+` flag takes effect. Items without a flag follow the
container's `expand` attribute.

```md
::: collapse

- :+ Expanded by default

  Content

- :- Collapsed by default

  Content

- No flag, follows container expand attribute

  Content

:::
```

**Rendered Result:**

::: collapse

- :+ Expanded by default

  Content

- :- Collapsed by default

  Content

- No flag, follows container expand attribute

  Content

:::

## Container Attributes

| Attribute   | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `accordion` | `boolean` | Enable accordion mode (only one item expanded at a time) |
| `card`      | `boolean` | Render with card style (bordered and rounded)            |
| `expand`    | `boolean` | Default expanded state                                   |

## Item Flags

| Flag | Description                                                  |
| ---- | ------------------------------------------------------------ |
| `:+` | Expand this item (only the first one wins in accordion mode) |
| `:-` | Collapse this item                                           |
plugins/field.md
md
# Field

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

Field container plugin for rendering structured API fields and properties documentation in Markdown,
with support for field grouping and JSDoc-style tag annotations.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

Use the `::: field` container to document API fields and properties, with JSDoc-style tags for metadata.

### Basic Field

```md
::: field count
@type Number
@default 0
Total number of users.
:::
```

### Field Tags

Supported tags: `@name`, `@type`, `@default`, `@required`, `@deprecated`, `@optional`, `@description`.

```md
::: field userName
@type String
@required
Unique identifier for the user.
:::

::: field email
@type String
@optional
@default example@mail.com
Contact email address.
:::

::: field oldField
@type String
@deprecated
This field is deprecated, please use the new field instead.
:::
```

**Rendered Result:**

::: field userName
@type String
@required
Unique identifier for the user.
:::

::: field email
@type String
@optional
@default example@mail.com
Contact email address.
:::

::: field oldField
@type String
@deprecated
This field is deprecated, please use the new field instead.
:::

### Tag Reference

| Tag            | Description                                                          |
| -------------- | -------------------------------------------------------------------- |
| `@name`        | Override the field name (defaults to the name from `info`)           |
| `@type`        | Field type annotation                                                |
| `@default`     | Default value for the field                                          |
| `@required`    | Mark the field as required                                           |
| `@optional`    | Mark the field as optional                                           |
| `@deprecated`  | Mark the field as deprecated                                         |
| `@description` | Explicit description text; any non-tag line also becomes description |

### Field Group

Use the `::: field-group` container to group related fields together.

```md
:::: field-group

::: field id
@type Number
@required
Unique identifier.
:::

::: field name
@type String
@optional
Display name.
:::

::: field createdAt
@type Date
@default Date.now()
Creation timestamp.
:::

::::
```

**Rendered Result:**

:::: field-group

::: field id
@type Number
@required
Unique identifier.
:::

::: field name
@type String
@optional
Display name.
:::

::: field createdAt
@type Date
@default Date.now()
Creation timestamp.
:::

::::

### Explicit Description

Use the `@description` tag to specify description text explicitly. Any non-tag lines are also automatically treated as description content.

```md
::: field count
@description Total number of users. This field represents the count of active users in the system.
@type Number
@default 0
:::
```
plugins/file-tree.md
md
# File Tree

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

File tree display plugin for rendering file directory structures in Markdown.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Container Syntax

Use the `::: file-tree` container to represent file hierarchy via indentation:

```md
::: file-tree title="Project Structure"

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

:::
```

### Fenced Code Syntax

Alternatively, use a fenced code block with the `tree` or `file-tree` language identifier.
The content follows the output format of the `tree` command-line tool (using Unicode
box-drawing characters):

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

This is especially useful when you already have a `tree` command output and want to
paste it directly into your Markdown without reformatting.

### Node Annotations

Both syntaxes support the following node annotations:

| Syntax               | Description                          |
| -------------------- | ------------------------------------ |
| `**filename**`       | Highlight/focus the file             |
| `-- filename`        | Mark as removed                      |
| `++ filename`        | Mark as added                        |
| `filename # comment` | Add a comment                        |
| `folder/`            | Mark as folder, collapsed by default |
| `…`                  | Ellipsis marker                      |

Container example with annotations:

```md
::: file-tree title="Changed Files"

- src/
  - -- old-file.ts
  - ++ new-file.ts
  - **main.ts** # Core entry
  -

:::
```

Fenced code example with annotations:

````md
```tree
.
├── src/
│   ├── -- old-file.ts
│   ├── ++ new-file.ts
│   └── **main.ts** # Core entry
└── …
```
````

## Example

```md
::: file-tree

- docs
  - .vitepress
    - ++ config.ts
  - -- page1.md
  - index.md
- theme  # A **theme** directory
  - 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  # A **theme** directory
  - client
    - components
      - **Navbar.vue**
    - composables
      - useNavbar.ts
    - styles
      - navbar.css
    - config.ts
  - node/
- package.json
- pnpm-lock.yaml
- .gitignore
- README.md
-
:::
plugins/icons.md
md
# Icons

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

Icons plugin that uses a simple `::name::` syntax to inline icons from [Iconify](https://iconify.design/), [iconfont](https://www.iconfont.cn/), or [FontAwesome](https://fontawesome.com/) in Markdown.

## Installation

::: npm-to

```sh
npm install vitepress-plugin-icons
```

:::

> When using Iconify, you also need to install [`@iconify/json`](https://www.npmjs.com/package/@iconify/json) or the specific icon collections you need.

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

> [!WARNING]
> Due to the high complexity of plugin configuration, it is only recommended for use in `vitepress-tuck` mode.

## Syntax

Use `::` delimiters to insert an icon inline. The full syntax is:

```md
::name =size /color::
```

- `name` — Icon name. The format depends on the provider (see below).
- `=size` — Optional. Icon size. Supports `=widthxheight` (e.g. `=1.2emx1.5em`) or a single `=width` (e.g. `=1.2em`) which applies to both width and height.
- `/color` — Optional. Icon color, e.g. `/red` or `/#ff0000`.

You can also prefix the content with a provider name (`iconify`/`iconfont`/`fontawesome`) to override the default provider for a single icon, and append extra attributes for FontAwesome.

### Iconify

Icon names use the `collection:icon` format, e.g. `tdesign:logo-github-filled`.

```md
::tdesign:logo-github-filled::
::tdesign:logo-github-filled =36px::
::tdesign:logo-github-filled /#f00::
::tdesign:logo-github-filled =36px /#f00::
::skill-icons:vscode-dark =36px::
```

**Rendered Result:**

::tdesign:logo-github-filled::

::tdesign:logo-github-filled =36px::

::tdesign:logo-github-filled /#f00::

::tdesign:logo-github-filled =36px /#f00::

::skill-icons:vscode-dark =36px::

### iconfont

Icon names use the plain name (without the `icon-` prefix), e.g. `hot` resolves to `icon-hot`.

```md
::hot::
::hot =24px::
::hot =24px /#f00::
::iconfont hot =24px /#f00::
```

**Rendered Result:**

::iconfont hot::

::iconfont hot =24px::

::iconfont hot =24px /#f00::

::iconfont hot =24px /#f00::

### FontAwesome

Icon names use the plain name, e.g. `circle-user`. Extra attributes like `border`, `beat`, `rotate-90`, `2xl` are appended after the name and converted to `fa-*` classes.

```md
::circle-user::
::circle-user =1.2em /#fff::
::fontawesome circle-user::
::fontawesome circle-user border::
::fontawesome circle-user rotate-90::
::fontawesome circle-user beat::
::fontawesome circle-user 2xl beat::
```

**Rendered Result:**

::fontawesome circle-user 2xl::

::fontawesome circle-user rotate-90::

::fontawesome circle-user beat::

::fontawesome circle-user border::

::fontawesome circle-user 2xl beat::

## Configuration

The plugin accepts a single provider option, an array of provider options, or no options (defaults to Iconify). When multiple providers are configured, mark one with `default: true` to set the default provider used by the `::name::` syntax.

### Common Options

| Option    | Type                                       | Default     | Description                       |
| --------- | ------------------------------------------ | ----------- | --------------------------------- |
| `provider`| `'iconify' \| 'iconfont' \| 'fontawesome'` | `'iconify'` | Icon provider                     |
| `default` | `boolean`                                  | `false`     | Use as the default provider       |
| `color`   | `string`                                   | `''`        | Default icon color                |
| `size`    | `string \| number`                         | `'1.2em'`   | Default icon size                 |

### Iconify

Extends [`unplugin-icons`](https://github.com/unplugin/unplugin-icons) options and [`unplugin-icons/resolver`](https://github.com/unplugin/unplugin-icons#resolver) options.

```ts [.vitepress/config.ts]
icons({
  provider: 'iconify',
  default: true,
  size: '1.2em',
  // unplugin-icons options
  scale: 1,
  customCollections: { /* ... */ },
})
```

### iconfont

| Option   | Type                               | Default   | Description                       |
| -------- | ---------------------------------- | --------- | --------------------------------- |
| `prefix` | `string`                           | `'icon-'` | Class name prefix for icons       |
| `assets` | `IconAssetLink \| IconAssetLink[]` | -         | iconfont CSS/JS resource URLs     |

```ts [.vitepress/config.ts]
icons({
  provider: 'iconfont',
  default: true,
  prefix: 'icon-',
  assets: '//at.alicdn.com/w/font_xxx.css',
})
```

### FontAwesome

| Option   | Type                                                  | Default         | Description                                          |
| -------- | ----------------------------------------------------- | --------------- | ---------------------------------------------------- |
| `prefix` | `LiteralUnion<FontAwesomePrefix>`                     | `'fas'`         | Default icon prefix                                  |
| `assets` | `Arrayable<FontAwesomeAssetBuiltIn \| IconAssetLink>` | `'fontawesome'` | Built-in asset name or custom URL                    |

The `assets` option accepts:

- `'fontawesome'` — Injects the solid, regular, and base kits from the jsDelivr CDN.
- `'fontawesome-with-brands'` — Injects only the brands kit from the jsDelivr CDN.
- A custom `.css` or `.js` URL.

```ts [.vitepress/config.ts]
icons({
  provider: 'fontawesome',
  default: true,
  prefix: 'fas',
  assets: 'fontawesome',
})
```

The `prefix` option supports the following aliases:

| Prefix   | Alias   | Resulting Classes                       |
| -------- | ------- | --------------------------------------- |
| `fas`    | `s`     | `fa-solid fa-name`                      |
| `far`    | `r`     | `fa-regular fa-name`                    |
| `fal`    | `l`     | `fa-light fa-name`                      |
| `fat`    | `t`     | `fa-thin fa-name`                       |
| `fads`   | `ds`    | `fa-duotone fa-solid fa-name`           |
| `fass`   | `ss`    | `fa-sharp fa-solid fa-name`             |
| `fasr`   | `sr`    | `fa-sharp fa-regular fa-name`           |
| `fasl`   | `sl`    | `fa-sharp fa-light fa-name`             |
| `fast`   | `st`    | `fa-sharp fa-thin fa-name`              |
| `fasds`  | `sds`   | `fa-sharp-duotone fa-solid fa-name`     |
| `fab`    | `b`     | `fa-brands fa-name`                     |

You can also use the `prefix:name` format in the icon name to specify a prefix for a single icon, e.g. `fab:github`.

### Multiple Providers

```ts [.vitepress/config.ts]
icons([
  { provider: 'iconify' },
  { provider: 'iconfont', default: true, assets: '//at.alicdn.com/w/font_xxx.css' },
  { provider: 'fontawesome', assets: 'fontawesome' },
])
```

With the configuration above, `::name::` uses iconfont by default, while `::iconify name::` and `::fontawesome name::` explicitly select the other providers.
plugins/intro.md
md
# Plugin Overview

The `vitepress-tuck` ecosystem provides a rich set of plugins covering Markdown syntax extensions,
content embedding, code enhancement, and more.

All plugins support two usage modes:

1. **vitepress-tuck mode (recommended)**: One-click integration via the `plugins` option
2. **Native mode**: Manual configuration in VitePress
plugins/jsfiddle.md
md
# JSFiddle

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

Embed [JSFiddle](https://jsfiddle.net/) projects into VitePress pages.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Basic Usage

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

### With Options

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

### Attribute Reference

| Attribute | Type               | Default                | Description                                       |
| --------- | ------------------ | ---------------------- | ------------------------------------------------- |
| `title`   | `string`           | `'JS Fiddle'`          | Title                                             |
| `theme`   | `string`           | -                      | Theme (e.g. `dark`)                               |
| `tab`     | `string`           | `'js,css,html,result'` | Tabs to display (comma-separated, spaces removed) |
| `width`   | `string`           | `'100%'`               | Width                                             |
| `height`  | `string`           | -                      | Height                                            |
| `ratio`   | `number \| string` | -                      | Aspect ratio                                      |

## Example

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

@[jsfiddle](pengzhanbo/1xbwz2p9)
plugins/mark.md
md
# Mark

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

Mark plugin that adds `==highlight==` syntax support to Markdown,
rendering highlighted text with a scroll-triggered animation and multiple color variants.

## Installation

::: npm-to

```sh
npm install vitepress-plugin-mark
```

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

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

Register the component in the theme and import the styles:

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

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

## Syntax

Wrap inline content with `==` to render it as a highlighted `<mark>` element. When the element
scrolls into the viewport, a fill-from-left highlight animation plays once, giving the marked text
a vivid emphasis effect.

```md
==Marked== text
```

**Rendered Result:**

==Marked== text

Inline content inside the markers fully supports Markdown syntax (bold, italic, links, code, etc.):

```md
==**bold**==, ==*italic*==, ==[link](https://example.com)==, ==`code`==
```

**Rendered Result:**

==**bold**==, ==*italic*==, ==[link to](https://example.com)==, ==`code`==

## Color Variants

Specify different highlight colors by appending an [attrs syntax](https://vitepress.dev/guide/markdown#attribute)
to the marker. Variant names correspond to VitePress container types and automatically adapt to dark mode.

```md
==Default==
==note=={.note}
==info=={.info}
==tip=={.tip}
==warning=={.warning}
==caution=={.caution}
==important=={.important}
```

**Rendered Result:**

==Default==

==note=={.note}

==info=={.info}

==tip=={.tip}

==warning=={.warning}

==caution=={.caution}

==important=={.important}

### Variant Reference

| Variant      | Description                       |
| ------------ | --------------------------------- |
| _(default)_  | Yellow highlight                  |
| `.note`      | Cyan highlight                    |
| `.info`      | Neutral gray highlight            |
| `.tip`       | Green highlight                   |
| `.warning`   | Gold/amber highlight              |
| `.caution`   | Pink highlight (alias: `.danger`) |
| `.important` | Lavender/purple highlight         |

## Customization

Customize the highlight appearance by overriding the following CSS variables in your theme styles:

```css
:root {
  --vp-mark-linear-color: #f0a;
}
```

| Variable                 | Description                                   | Default                              |
| ------------------------ | --------------------------------------------- | ------------------------------------ |
| `--vp-mark-text`         | Text color                                    | `currentcolor`                       |
| `--vp-mark-bg`           | Background color                              | `transparent`                        |
| `--vp-mark-linear-color` | Highlight fill color                          | `#ff0`                               |
| `--vp-mark-bg-image`     | Background image used for the fill (gradient) | `linear-gradient(to right, ...)`     |
| `--vp-mark-bg-shift`     | Vertical position shift of the background     | `0.55lh`                             |
| `--vp-mark-animation`    | Animation shorthand                           | `mark-highlight 1.25s 0.5s forwards` |

You can also extend highlight color variants by customizing CSS class names:

```css
mark.custom {
  --vp-mark-linear-color: #f0a;
}

```

```md
==Custom variant=={.custom}
```

Rendered result:

==Custom variant=={.custom}

<style>

mark.custom {
  --vp-mark-linear-color: #f0a;
}

</style>
plugins/mermaid.md
md
# Mermaid

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

Mermaid diagram plugin, supporting Mermaid chart rendering in Markdown.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

Use code blocks with the `mermaid` language tag:

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

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

## Configuration

### MermaidPluginOptions

```ts
interface MermaidPluginOptions {
  /**
   * Mermaid configuration (excluding startOnLoad and themeVariables)
   */
  options?: Omit<MermaidConfig, 'startOnLoad' | 'themeVariables'> & {
    themeVariables?: MermaidThemeVariables
  }

  /**
   * Locale configuration
   */
  locales?: Record<string, MermaidLocaleData>
}
```

### MermaidThemeVariables

Supports custom theme variables for various Mermaid diagram types, covering:

- Basic variables (background, text color, line color, etc.)
- C4, Class, ER diagram variables
- Flowchart variables
- Gantt chart variables
- Git graph variables
- Journey diagram variables
- Pie chart variables
- Requirement diagram variables
- State diagram variables
- Sequence diagram variables

### MermaidLocaleData

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

## Built-in Languages

The plugin includes built-in support for the following languages:

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

## Examples

```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" />

Automatically converts npm commands to equivalent commands for other package managers (pnpm, yarn, bun, deno).

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</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']),
  ],
})
```

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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'])
    },
  },
})
```

## Syntax

Use the `::: npm-to` container to wrap npm commands. The plugin automatically converts them into multi-tab code groups:

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

After rendering, it displays as multiple tabs, each showing the corresponding package manager's install command:

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

### Supported Command Types

The plugin supports automatic conversion of the following npm commands:

| Command                 | Example               | Support                                   |
| ----------------------- | --------------------- | ----------------------------------------- |
| `npm install` / `npm i` | `npm install react`   | Converts to each manager's add command    |
| `npm install` (no args) | `npm install`         | Converts to pure install command          |
| `npm uninstall`         | `npm uninstall react` | Converts to each manager's remove command |
| `npm run`               | `npm run build`       | Converts to each manager's run command    |
| `npm create`            | `npm create vite`     | Converts to each manager's create command |
| `npm init`              | `npm init -y`         | Converts to each manager's init command   |
| `npx`                   | `npx eslint .`        | Converts to each manager's equivalent     |
| `npm ci`                | `npm ci`              | Converts to each manager's ci command     |

### Custom Tabs

You can specify which tabs to display using the `tabs` attribute:

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

## Configuration

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

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

- Displays `npm`, `pnpm`, `yarn` tabs by default
- Supports `bun` and `deno` conversion

### Example

````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" />

Provides Obsidian-style Markdown syntax support, including Wiki links, Callout annotations, embedded files, and comment syntax.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

export default defineConfig({
  plugins: [
    obsidian({
      // All optional, default to true
      callout: true,
      comment: true,
      embedLink: true,
      wikiLink: true,
    }),
  ],
})
```

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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 Links

Wiki links are Obsidian's syntax for linking to other notes. Use double brackets `[[ ]]` to create internal links.

### Syntax

```md
[[filename]]
[[filename#heading]]
[[filename#heading#subheading]]
[[filename|alias]]
[[filename#heading|alias]]
[[https://example.com|external link]]
```

### File Name Search Rules

When using Wiki links, file names are matched according to the following search rules:

**Matching Priority:**

1. **Full Path** — exact match of the file path
2. **Fuzzy Match** — match by filename at the end of the path, preferring the shortest path

**Path Resolution Rules:**

- **Relative paths** (starting with `.`): resolved relative to the current file's directory
- **Absolute paths** (not starting with `.`): searched across the entire document tree, preferring the shortest match
- **Directory form** (ending with `/`): matches the `index.md` in that directory

**Example:**

Given the following document structure:

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

In `docs/guide/markdown/obsidian.md`:

| Syntax         | Match Result                                                    |
| -------------- | --------------------------------------------------------------- |
| `[[obsidian]]` | matches `docs/guide/markdown/obsidian.md` (via filename search) |
| `[[./]]`       | matches `docs/guide/markdown/index.md` (relative path)          |
| `[[../]]`      | matches `docs/guide/README.md` (parent directory)               |
| `[[guide/]]`   | matches `docs/guide/README.md` (directory form)                 |

### Examples

**External Link:**

**Input:**

```md
[[https://example.com|external link]]
```

**Output:**

[[https://example.com|external link]]

**Internal Anchor Links:**

**Input:**

```md
[[npm-to]]  <!-- via filename search -->
[[#Wiki Links]]  <!-- current page heading -->
[[file-tree#Configuration]]  <!-- via filename search, link to heading -->
```

**Output:**

[[npm-to]]

[[#Wiki Links]]

[[file-tree#Configuration]]

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

## Embedded Content

Embed syntax allows you to insert content from other files into the current page.

### Syntax

```md
![[filename]]
![[filename#heading]]
![[filename#heading#subheading]]
```

File name search rules are the same as [Wiki Links](#file-name-search-rules).

::: info Paths starting with `/` or without a `./` prefix load resources from the `public` directory
:::

### Image Embedding

**Syntax:**

```md
![[image]]
![[image|width]]
![[image|widthxheight]]
```

Supported formats: `jpg`, `jpeg`, `png`, `gif`, `avif`, `webp`, `svg`, `bmp`, `ico`, `tiff`, `apng`, `jfif`, `pjpeg`, `pjp`, `xbm`

**Example:**

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

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

### PDF Embedding

> [!NOTE]
> PDF embedding requires the [vitepress-plugin-pdf](./pdf.md) plugin to work properly.

**Syntax:**

```md
![[document.pdf]]
![[document.pdf#page=1]]  <!-- #page=1 for first page -->
![[document.pdf#page=1#height=300]]  <!-- #page=page #height=height -->
```

Supported formats: `pdf`

**Example:**

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

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

---

### Audio Embedding

**Syntax:**

```md
![[audio file]]
```

Supported formats: `mp3`, `flac`, `wav`, `ogg`, `opus`, `webm`, `acc`

---

### Video Embedding

> [!NOTE]
> Video embedding requires the [vitepress-plugin-video](./video.md) plugin to work properly.

**Syntax:**

```md
![[video file]]
![[video file#height=400]]  <!-- Set video height -->
```

Supported formats: `mp4`, `webm`, `mov`, etc.

---

### Content Fragment Embedding

Use `#heading` to embed content fragments under specific headings:

**Input:**

```md
![[my-note]]
![[my-note#heading-one]]
![[my-note#heading-one#subheading]]
```

[Obsidian Official - Embed Files](https://help.obsidian.md/embeds){.readmore}
[Obsidian Official - File Formats](https://help.obsidian.md/file-formats){.readmore}

## Callout

Callout is a syntax for highlighting important information, similar to VitePress's `::: note` alert syntax.

### Syntax

```md
> [!note]
> Content
```

**Optional Title:**

```md
> [!tip] Custom Title
> Content
```

### Types

Callout supports the following types, with aliases automatically mapping to the corresponding main type:

| Type        | Aliases                                                             | Description                |
| ----------- | ------------------------------------------------------------------- | -------------------------- |
| `note`      | `quote`, `cite`                                                     | Notes, quotes              |
| `tip`       | `hint`, `check`, `done`, `success`                                  | Tips, hints                |
| `info`      | `todo`                                                              | Info, todos                |
| `warning`   | `question`, `help`, `faq`                                           | Warnings, questions, help  |
| `caution`   | `attention`, `failure`, `fail`, `missing`, `danger`, `error`, `bug` | Cautions, failures, danger |
| `important` | `example`                                                           | Important, examples        |
| `details`   | `abstract`, `summary`, `tldr`                                       | Details, summaries         |

### Examples

**Basic Usage:**

**Input:**

```md
> [!NOTE]
> This is a note callout.
```

**Output:**

> [!NOTE]
> This is a note callout.

---

**With Title:**

**Input:**

```md
> [!TIP] Useful Tip
> Using `pnpm` can significantly speed up dependency installation.
```

**Output:**

> [!TIP] Useful Tip
> Using `pnpm` can significantly speed up dependency installation.

---

**Multiple Types:**

**Input:**

```md
> [!success]
> Operation completed successfully!
>
> [!warning]
> This is a warning message.
>
> [!caution]
> Proceed with caution, this operation is irreversible.
```

**Output:**

> [!success]
> Operation completed successfully!

> [!warning]
> This is a warning message.

> [!caution]
> Proceed with caution, this operation is irreversible.

---

**Details Type:**

The `details` type renders as an HTML `<details>` element, supporting expand/collapse:

**Input:**

```md
> [!details]
> Click to expand more content
>
> This is hidden content.
```

**Output:**

> [!details]
> Click to expand more content
>
> This is hidden content.

[Obsidian Official - Callouts](https://help.obsidian.md/callouts){.readmore}

## Comments

Content wrapped with `%%` is treated as a comment and will not be rendered on the page.

### Syntax

**Inline Comments:**

```md
This is an %%inline comment%% example.
```

**Block Comments:**

```md
%%
This is a block comment.
It can span multiple lines.
%%
```

### Examples

**Inline Comment:**

**Input:**

```md
This is an %%inline comment%% example.
```

**Output:**

This is an %%inline comment%% example.

---

**Block Comment:**

**Input:**

```md
Content before the comment

%%
This is a block comment.

It can span multiple lines.
%%

Content after the comment
```

**Output:**

Content before the comment

%%
This is a block comment.
%%

It can span multiple lines.

[Obsidian Official - Comments](https://help.obsidian.md/syntax#comments){.readmore}

## Notes

- These plugins provide **compatibility support**, not a full implementation of all Obsidian features
- Some Obsidian-specific features (such as graph view for internal links, backlinks, etc.) are not supported
- When embedding content, the embedded page also participates in the theme's build process
- PDF embedding requires the [vitepress-plugin-pdf](./pdf.md) plugin to work properly
- Video embedding requires the [vitepress-plugin-video](./video.md) plugin to work properly
- Embedded resources starting with `/` or using `./` form will be loaded from the `public` directory
plugins/pdf.md
md
# PDF

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

PDF file embedding plugin for displaying a PDF viewer in the page.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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'
import DefaultTheme from 'vitepress/theme'

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

## Syntax

Embed a PDF using `@[pdf]()`:

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

### Specifying a Page

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

### Configuration Options

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

### Attribute Reference

| Attribute    | Type               | Default  | Description  |
| ------------ | ------------------ | -------- | ------------ |
| `width`      | `string`           | `'100%'` | Width        |
| `height`     | `string`           | -        | Height       |
| `ratio`      | `number \| string` | -        | Aspect ratio |
| `zoom`       | `number`           | `50`     | Zoom level   |
| `no-toolbar` | `boolean`          | `false`  | Hide toolbar |

## Example

```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 diagram plugin, supporting PlantUML chart rendering in Markdown.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

Use code blocks with the `plantuml` language tag:

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

### Output Format

The plugin supports `svg` (default) and `png` output formats. You can specify the format per diagram:

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

Or set a global default:

```ts
plantuml({ format: 'png' }) // default is 'svg'
```

## Configuration

### PlantumlPluginOptions

```ts
interface PlantumlPluginOptions {
  /**
   * Output format, 'svg' | 'png'
   * @default 'svg'
   */
  format?: PlantumlFormat

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

## Features

- **Dark / Light mode** — Automatically generates both dark and light diagram variants, following the VitePress theme
- **Chart / Source tabs** — Toggle between the rendered diagram and its PlantUML source code
- **Fullscreen mode** — Click the fullscreen button to view the diagram in an overlay
- **Download** — Download the current diagram as an image file
- **Multi-language** — Built-in support for English, Chinese, Japanese, Korean, Spanish, French, Russian, German, and Portuguese
- **SVG optimization** — SVGs are automatically optimized via SVGO, removing redundant styles and background layers
- **Build caching** — Rendered diagrams are cached to disk for faster incremental builds

## Built-in Languages

The plugin includes built-in support for the following languages:

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

## Examples

### Sequence Diagram

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

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

### Use Case Diagram

```plantuml
@startuml
left to right direction
actor "Customer" as customer
actor "Cashier" as cashier

rectangle "POS System" {
  customer -- (Checkout)
  (Checkout) -- cashier
  (Checkout) .> (Print Receipt) : include
  (Checkout) .> (Payment) : include
  (Payment) .> (Cash Payment)
  (Payment) .> (Scan to Pay)
}
@enduml
```

### Class Diagram

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

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

class Engine
class Wheel

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

### Activity Diagram

```plantuml
@startuml
start
:User Login;
if (Authenticated?) then (yes)
  :Enter Dashboard;
  if (New Messages?) then (yes)
    :Show Notification;
  else (no)
    :Continue Browsing;
  endif
else (no)
  :Show Error;
  :Return to Login;
endif
stop
@enduml
```
plugins/plot.md
md
# Plot

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

Hidden text plugin that reveals concealed text content on click or hover.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</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', default 'hover'
      effect: 'mask',    // 'mask' | 'blur', default 'mask'
    }),
  ],
})
```

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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'
import DefaultTheme from 'vitepress/theme'

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

## Syntax

Wrap hidden text with `!!`:

```md
The answer is !!plot!!
```

Use `classname` to set interaction behavior:

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

Supported `classname` values:

- `.click` / `.hover`: Reveal on click / Reveal on hover
- `.blur` / `.mask`: Blur effect / Mask effect

## Configuration

```ts
interface PlotOptions {
  /**
   * Trigger method
   * - 'hover': Reveal on hover
   * - 'click': Reveal on click
   * @default 'hover'
   */
  trigger?: 'hover' | 'click'

  /**
   * Hide effect
   * - 'mask': Mask effect (default)
   * - 'blur': Blur effect
   * @default 'mask'
   */
  effect?: 'mask' | 'blur'
}
```

## Examples

```md
Lu Xun once said: "!!I never said that!!"
```

Lu Xun once said: "!!I never said that!!"

```md
There is no royal road to learning, !!but hard work pays off!!{.click .blur}.
```

There is no royal road to learning, !!but hard work pays off!!{.click .blur}.
plugins/qrcode.md
md
# QRCode

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

QR code generation plugin, generating QR codes from text or links.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Embed Block Syntax

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

### Styled Card Mode

```md
@[qrcode card title="Scan to visit"](https://www.baidu.com)
```

### Container Syntax

Suitable for long text:

```md
::: qrcode title="Scan to visit"
https://www.baidu.com
:::
```

### Attribute Reference

| Attribute  | Type      | Description                            |
| ---------- | --------- | -------------------------------------- |
| `card`     | `boolean` | Display in card mode                   |
| `title`    | `string`  | Card title                             |
| `logo`     | `string`  | QR code logo, link format, optional    |
| `logoSize` | `number`  | Logo size ratio, optional, default 0.2 |
| `width`    | `number`  | QR code width, optional                |

## Examples

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

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

**Internal links auto-add logo:**

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

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

**Using card mode:**

```md
@[qrcode card title="Scan to visit File Tree Plugin"](./file-tree.md)
```

@[qrcode card title="Scan to visit File Tree Plugin"](./file-tree.md)
plugins/repo-card.md
md
# Repo Card

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

A repository information card plugin that displays detailed GitHub/Gitee repository
cards in your Markdown. Supports embed syntax for individual cards and container
syntax for multi-card grid layouts.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Embed Syntax

Use the `@[repo]()` syntax to embed individual repository cards. Defaults to GitHub
if `register` is omitted.

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

### Display Full Name

Use the `fullname` parameter to show the `owner/name` full path. For
organization-owned repositories, the full name is displayed automatically.

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

### Container Syntax

Use the `::: repo` container to display multiple repository cards in a responsive
grid layout.

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

## Examples

### Single Repository Card

**GitHub repository:**

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

@[repo github](vuejs/vitepress)

**Gitee repository:**

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

@[repo gitee](openharmony/kernel_liteos_a)

**Display full name:**

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

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

### Multi-Card Grid Layout

```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)
:::

## Card Information

Each repository card displays the following:

| Field       | Description                          |
| ----------- | ------------------------------------ |
| Name        | Repository name or `owner/name` full |
| Visibility  | Public / Private badge               |
| Archived    | Warning badge if archived            |
| Description | Repository description text          |
| Language    | Primary language with color dot      |
| Stars       | Formatted count (e.g. `1.2k`)        |
| Forks       | Formatted count (e.g. `1.2k`)        |
| License     | License name (if available)          |

## Component Usage

You can also use the `VPRepoCard` component directly:

```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

| Prop       | Type                  | Default    | Description                       |
| ---------- | --------------------- | ---------- | --------------------------------- |
| `repo`     | `string`              | (required) | Repository in `owner/name` format |
| `register` | `'github' \| 'gitee'` | `'github'` | Repository platform               |
| `fullname` | `boolean`             | -          | Display full name (`owner/name`)  |
plugins/stackblitz.md
md
# StackBlitz

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

Embed [StackBlitz](https://stackblitz.com/) projects into VitePress pages.
Supports embedding online editors via StackBlitz ID, GitHub repository, or local project files.
Can also display as a button to open in a new tab.

## Installation

::: npm-to

```sh
npm install vitepress-plugin-stackblitz
```

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

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

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

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

## Syntax

The plugin provides two syntaxes for embedding StackBlitz projects:
embed syntax for referencing external projects, and container syntax for writing project
files directly inline within Markdown.

### Embed by StackBlitz ID

```md
@[stackblitz](sdk-github-project)
```

### Embed by GitHub Repository

```md
@[stackblitz github](user/repo)
```

### Embed Local Project

Place a `stackblitz.config.json` (or `.yaml`, `.yml`) configuration file in your project directory, then use the `local` attribute:

```md
@[stackblitz local](path/to/your/project)
```

`path` supports the following prefixes:

| Prefix | Description                                       |
| ------ | ------------------------------------------------- |
| `@`    | Relative to VitePress `srcDir`                    |
| `/`    | Relative to VitePress project root                |
| -      | Relative to the current markdown file's directory |

See [Local Project Configuration](#local-project-configuration) for more details.

### Button Mode

Display as a button that opens the project in a new tab:

```md
@[stackblitz button](stackblitz-id)
```

It can also be combined with GitHub:

```md
@[stackblitz github button](user/repo)
```

### Container Syntax

Write project files directly inline within Markdown, ideal for quickly demonstrating simple projects:

````md
::: stackblitz title="Project" description="Project description" template="javascript"

```yml [config]
title: Project
template: javascript
description: Project description
```

```json [package.json]
{
  "name": "project",
  "version": "1.0.0"
}
```

```js [index.js]
console.log('Hello StackBlitz!');
```

```html [index.html]
<!DOCTYPE html>
<html lang="en">
<body>
  <h1>Hello StackBlitz!</h1>
  <script src="./index.js"></script>
</body>
</html>
```

:::
````

### Attribute Reference

#### Embed / Open Options

Applicable to both embed syntax and button mode:

| Attribute          | Type                                 | Default     | Description                                   |
| ------------------ | ------------------------------------ | ----------- | --------------------------------------------- |
| `theme`            | `'dark' \| 'light' \| 'default'`     | auto        | Color theme, auto follows VitePress dark mode |
| `view`             | `'default' \| 'preview' \| 'editor'` | `'default'` | Initial UI view                               |
| `height`           | `number`                             | `400`       | Embed height (embed mode only)                |
| `width`            | `number`                             | -           | Embed width (embed mode only)                 |
| `clickToLoad`      | `boolean`                            | `false`     | Show "click to run" dialog                    |
| `openFile`         | `string \| string[]`                 | -           | File(s) to open on load                       |
| `hideExplorer`     | `boolean`                            | `false`     | Hide the file explorer                        |
| `hideDevTools`     | `boolean`                            | `false`     | Hide the dev tools console                    |
| `showSidebar`      | `boolean`                            | `false`     | Show sidebar as open on load                  |
| `terminalHeight`   | `number` (0-100)                     | -           | Terminal height percentage                    |
| `devToolsHeight`   | `number` (0-100)                     | -           | Dev tools height percentage                   |
| `newWindow`        | `boolean`                            | `false`     | Open project in a new tab                     |
| `forceEmbedLayout` | `boolean`                            | `false`     | Force embed layout (deprecated)               |
| `origin`           | `string`                             | -           | StackBlitz EE instance URL                    |

#### Project Options

Only applicable to local project embed mode and container syntax:

| Attribute      | Type                                                                                                            | Default | Description                                |
| -------------- | --------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------ |
| `title`        | `string`                                                                                                        | -       | Project title (required)                   |
| `description`  | `string`                                                                                                        | -       | Project description (required)             |
| `template`     | `'angular-cli' \| 'create-react-app' \| 'html' \| 'javascript' \| 'typescript' \| 'polymer' \| 'vue' \| 'node'` | -       | Project template (required)                |
| `dependencies` | `Record<string, string>`                                                                                        | -       | npm dependencies                           |
| `files`        | `Record<string, string>`                                                                                        | -       | Project files (auto-loaded for local mode) |

#### Build Settings

Set via `settings`-prefixed attributes, only applicable to local project embed mode and container syntax:

| Attribute              | Type                              | Default  | Description                     |
| ---------------------- | --------------------------------- | -------- | ------------------------------- |
| `settingsTrigger`      | `'auto' \| 'save' \| 'keystroke'` | `'auto'` | Compilation trigger timing      |
| `settingsAction`       | `'hmr' \| 'refresh'`              | `'hmr'`  | How to inject compiled changes  |
| `settingsClearConsole` | `boolean`                         | `true`   | Clear console after compilation |

```md
@[stackblitz local title="Demo" description="A demo" template="html" settingsTrigger="save" settingsAction="refresh"](path/to/project)
```

## Local Project Configuration

When using `@[stackblitz local](path)` to embed a local project, the plugin automatically:

1. Loads all files from the specified directory (excluding `node_modules`)
2. Searches for `stackblitz.config.json`, `stackblitz.config.yaml`, `stackblitz.config.yml` in order
3. Merges the configuration file options with the loaded files

Example `stackblitz.config.json`:

```json
{
  "$schema": "../../node_modules/vitepress-plugin-stackblitz/schema.json",
  "title": "Project",
  "description": "Project description",
  "template": "javascript",
  "dependencies": {
    "lodash": "^4.17.0"
  },
  "settings": {
    "trigger": "save"
  }
}
```

> [!TIP]
> Including the `$schema` field provides intelligent autocompletion and validation for configuration files in your editor.
>
> ```json
> {
>   "$schema": "https://unpkg.com/vitepress-plugin-stackblitz/schema.json"
> }
> ```

## Example

### StackBlitz ID Embed

```md
@[stackblitz](sdk-github-project)
```

@[stackblitz](sdk-github-project)

### Button Mode

```md
@[stackblitz button](sdk-github-project)
```

@[stackblitz button](sdk-github-project)

### Container Syntax

````md
::: stackblitz button

```yml [config]
title: Project
template: javascript
description: Project description
```

```json [package.json]
{
  "name": "project",
  "version": "1.0.0",
  "description": "Project description",
  "main": "index.js",
  "keywords": ["project"],
  "author": "Project Author",
  "license": "MIT"
}
```

```js [index.js]
console.log('Hello StackBlitz!');
```

```html [index.html]
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Project</title>
</head>
<body>
  <h1>Hello StackBlitz!</h1>
  <script src="./index.js"></script>
</body>
</html>
```

:::
````

::: stackblitz button

```yml [config]
title: Project
template: javascript
description: Project description
```

```json [package.json]
{
  "name": "project",
  "version": "1.0.0",
  "description": "Project description",
  "main": "index.js",
  "keywords": ["project"],
  "author": "Project Author",
  "license": "MIT"
}
```

```js [index.js]
console.log('Hello StackBlitz!');
```

```html [index.html]
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Project</title>
</head>
<body>
  <h1>Hello StackBlitz!</h1>
  <script src="./index.js"></script>
</body>
</html>
```

:::

### Local Project Embed

@[code-tree title="Local Project Directory" entry="stackblitz.config.json"](@/snippets/stack)

```md
@[stackblitz local](@/snippets/stack)
```

@[stackblitz local](@/snippets/stack)
plugins/steps.md
md
# Steps

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

Step container plugin for creating step-by-step guided content in Markdown.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

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

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

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

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

Also import the styles in the theme:

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

## Syntax

Use the `::: steps` container to wrap step content, with each step starting from an unordered/ordered list item:

```md
::: steps

- Step One

  Description for step one

- Step Two

  Description for step two

- Step Three

  Description for step three, supports heading syntax

:::
```

**Rendered Result:**

::: steps

- Step One

  Description for step one

- Step Two

  Description for step two

- Step Three

  Description for step three, supports heading syntax

:::
plugins/video.md
md
# Video

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

Multi-platform video embedding plugin, supporting Bilibili, YouTube, AcFun, and ArtPlayer.

## Installation

::: npm-to

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

:::

## Usage

### vitepress-tuck Mode <Badge type="tip">Recommended</Badge>

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

export default defineConfig({
  plugins: [
    video({
      // All optional, default to true
      artplayer: true,
      youtube: true,
      bilibili: true,
      acfun: true,
    }),
  ],
})
```

[Learn more about **vitepress-tuck**](../guide/quick-start.md){.readmore}

### Native Mode

```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
```

## Syntax

### Bilibili

Embed Bilibili videos:

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

| Option      | Type               | Description                            |
| ----------- | ------------------ | -------------------------------------- |
| `p{number}` | -                  | Video part number                      |
| `autoplay`  | `boolean`          | Auto play                              |
| `time`      | `number \| string` | Start time, seconds or HH:MM:SS format |

### YouTube

Embed YouTube videos:

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

| Option     | Type      | Description           |
| ---------- | --------- | --------------------- |
| `autoplay` | `boolean` | Auto play             |
| `loop`     | `boolean` | Loop playback         |
| `start`    | `number`  | Start time in seconds |
| `end`      | `number`  | End time in seconds   |

### AcFun

Embed AcFun videos:

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

### ArtPlayer

Use ArtPlayer to embed local or remote videos:

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

| Option     | Type      | Default  | Description               |
| ---------- | --------- | -------- | ------------------------- |
| `autoplay` | `boolean` | `false`  | Auto play                 |
| `muted`    | `boolean` | `false`  | Muted                     |
| `loop`     | `boolean` | `false`  | Loop playback             |
| `volume`   | `number`  | `0.75`   | Volume level              |
| `poster`   | `string`  | -        | Cover image URL           |
| `autoMini` | `boolean` | `false`  | Auto mini mode            |
| `width`    | `string`  | `"100%"` | Player width              |
| `height`   | `string`  | -        | Player height             |
| `ratio`    | `string`  | -        | Aspect ratio, e.g. "16:9" |

Supports `mp4`, `mp3`, `webm`, `ogg`, `mkv`, `mov` formats.

If your video is in `mpd` or `dash` format, you'll also need to install `dashjs`:

:::npm-to

```sh
npm i dashjs
```

:::

If your video is in `m3u8` or `hls` format, you'll also need to install `hls.js`:

:::npm-to

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

:::

If your video is in `ts` or `flv` format, you'll also need to install `mpegts.js`:

:::npm-to

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

:::

## Configuration

```ts
interface VideoPluginOptions {
  /**
   * Enable ArtPlayer
   * @default true
   */
  artplayer?: boolean

  /**
   * Enable YouTube video embedding
   * @default true
   */
  youtube?: boolean

  /**
   * Enable Bilibili video embedding
   * @default true
   */
  bilibili?: boolean

  /**
   * Enable AcFun video embedding
   * @default true
   */
  acfun?: boolean
}
```

## Examples

### 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
---

# Watermark

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

Add watermark to your site. Pure client-side implementation, no node-side configuration required.

## Installation

::: npm-to

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

:::

## Usage

This plugin only provides a client-side `setupWatermark` function, which must be called inside the
`<script setup>` block of a Layout wrapper component.

Create a custom Layout wrapper component:

```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>
```

Then register this Layout in your theme:

```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
```

## Configuration

`setupWatermark` accepts an optional configuration object:

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

| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `enabled` | `boolean \| ((pageData: PageData) => boolean)` | `true` | Enable watermark; supports a function for dynamic control |
| `content` | `string` | Site title | Watermark text content |
| `fontColor` | `string` | `'#76747f'` | Font color |
| `globalAlpha` | `number` | `0.165` (normal) / `0.005` (blind mode) | Opacity |
| `mode` | `'default' \| 'blind'` | `'default'` | Watermark mode: `'blind'` for blind watermark |
| `width` | `number` | — | Width of each watermark cell |
| `height` | `number` | — | Height of each watermark cell |
| `rotate` | `number` | — | Rotation angle in degrees |
| `fontSize` | `string` | — | Font size |
| `fontFamily` | `string` | — | Font family |
| `fontStyle` | `string` | — | Font style |
| `fontWeight` | `string` | — | Font weight |
| `image` | `string` | — | Image watermark URL |
| `layout` | `'default' \| 'grid'` | — | Layout mode |
| `zIndex` | `number` | — | CSS z-index |
| `mutationObserve` | `boolean` | — | Enable DOM mutation observer protection |
| `monitorProtection` | `boolean` | — | Enable monitoring protection |
| `movable` | `boolean` | — | Allow dragging the watermark |
| `parent` | `Element \| string` | — | Mount target element |

For more options, see the [watermark-js-plus](https://github.com/zhensherlock/watermark-js-plus) documentation.

## Per-Page Watermark

Control watermark on individual pages via frontmatter:

### Enable Watermark

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

### Custom Text

```yaml
---
watermark: CONFIDENTIAL
---
```

### Full Customization

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

### Disable on Specific Pages

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

### Dynamic Enable via Function

Use an `enabled` function to dynamically control watermark based on the page path:

```ts
setupWatermark({
  enabled: (pageData) => {
    // Only enable on pages under guide/
    return pageData.relativePath.startsWith('guide/')
  },
})
```
index.md
md
---
layout: home
title: VitePress Tuck
hero:
  name: VitePress Tuck
  text: Enhance vitepress configuration, provide plugins capability.
  actions:
    - text: Get Started
      link: /guide/quick-start
    - text: Github
      link: https://github.com/pengzhanbo/vitepress-tuck
      theme: alt
  image:
    src: /tuck-logo.svg
    alt: VitePress Tuck
---

Released under the MIT License