docs: update en docs (#708)

* docs: update en docs

* chore: tweak

* chore: tweak

* chore: tweak
This commit is contained in:
pengzhanbo 2025-10-09 15:46:05 +08:00 committed by GitHub
parent c6347676cd
commit 385059f214
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
147 changed files with 20826 additions and 2386 deletions

View File

@ -1,8 +1,10 @@
import { defineCollections, type ThemeCollections } from 'vuepress-theme-plume' import { defineCollections, type ThemeCollections } from 'vuepress-theme-plume'
import { themeConfig } from './theme-config.js' import { themeConfig } from './theme-config.js'
import { themeGuide } from './theme-guide.js' import { themeGuide } from './theme-guide.js'
import { tools } from './tools.js'
export const enCollections: ThemeCollections = defineCollections([ export const enCollections: ThemeCollections = defineCollections([
themeGuide, themeGuide,
themeConfig, themeConfig,
tools,
]) ])

View File

@ -3,27 +3,49 @@ import { defineCollection } from 'vuepress-theme-plume'
export const themeConfig: ThemeCollectionItem = defineCollection({ export const themeConfig: ThemeCollectionItem = defineCollection({
type: 'doc', type: 'doc',
dir: 'config',
title: 'Config', title: 'Config',
dir: 'config',
linkPrefix: '/config/', linkPrefix: '/config/',
sidebar: [ sidebar: [
{ {
text: 'Config', text: 'Configuration',
collapsed: false, collapsed: false,
items: [ items: [
'intro', 'intro',
'basic', 'theme',
'locales', 'locales',
'notes', 'navbar',
'sidebar',
'collections',
'markdown',
], ],
}, },
{ {
text: 'frontmatter', text: 'Page Configuration',
prefix: 'frontmatter', prefix: 'frontmatter',
collapsed: false, collapsed: false,
items: [ items: [
'basic', 'basic',
'article', 'home',
'post',
'friend',
],
},
{
text: 'Built-in Plugins',
prefix: 'plugins',
collapsed: false,
items: [
'',
'shiki',
'search',
'reading-time',
'markdown-enhance',
'markdown-power',
'markdown-image',
'markdown-math',
'markdown-include',
'watermark',
], ],
}, },
], ],

View File

@ -16,10 +16,14 @@ export const themeGuide: ThemeCollectionItem = defineCollection({
'intro', 'intro',
'usage', 'usage',
'project-structure', 'project-structure',
{
text: 'Collection',
link: 'collection',
items: ['collection-post', 'collection-doc'],
},
'sidebar',
'write', 'write',
// 'blog', 'locales',
// 'document',
'international',
'deployment', 'deployment',
'optimize-build', 'optimize-build',
], ],
@ -45,15 +49,120 @@ export const themeGuide: ThemeCollectionItem = defineCollection({
'card', 'card',
'steps', 'steps',
'file-tree', 'file-tree',
'code-tree',
'field',
'tabs', 'tabs',
'timeline', 'timeline',
'demo-wrapper', 'demo-wrapper',
'flex',
'collapse', 'collapse',
'npm-to', 'npm-to',
'caniuse', 'caniuse',
'chat',
'include', 'include',
], ],
}, },
{
text: 'code block',
prefix: 'code',
icon: 'ph:code-bold',
collapsed: true,
items: [
'intro',
'features',
'copy-code',
'code-tabs',
'import',
'twoslash',
],
},
{
text: 'code repl',
prefix: 'repl',
icon: 'carbon:demo',
collapsed: true,
items: [
'frontend',
'rust',
'golang',
'kotlin',
'python',
'codepen',
'jsFiddle',
'codeSandbox',
'replit',
],
},
{
text: 'charts',
icon: 'mdi:chart-line',
prefix: 'chart',
collapsed: true,
items: [
'chart',
'echarts',
'mermaid',
'flowchart',
'markmap',
'plantuml',
],
},
{
text: 'resource embedded',
icon: 'dashicons:embed-video',
prefix: 'embed',
collapsed: true,
items: [
'pdf',
'bilibili',
'acfun',
'youtube',
'artplayer',
'audioReader',
],
},
],
},
{
text: 'Features',
icon: 'lucide:box',
collapsed: false,
prefix: 'features',
items: [
'icon',
'search',
'image-preview',
'comments',
'bulletin',
'encryption',
'contributors',
'changelog',
'copyright',
'watermark',
'friend-links',
'replace-assets',
'seo',
'sitemap',
],
},
{
text: 'Component',
prefix: 'components',
icon: 'uiw:component',
collapsed: false,
items: [
'badge',
'icon',
'plot',
'card',
'link-card',
'image-card',
'card-grid',
'card-masonry',
'home-box',
'repo-card',
'npm-badge',
'swiper',
], ],
}, },
{ {
@ -64,6 +173,18 @@ export const themeGuide: ThemeCollectionItem = defineCollection({
items: [ items: [
'home', 'home',
'style', 'style',
'slots',
'component-overrides',
],
},
{
text: 'API',
icon: 'mdi:api',
prefix: 'api',
collapsed: false,
items: [
'client',
'node',
], ],
}, },
], ],

View File

@ -0,0 +1,20 @@
import type { ThemeCollectionItem } from 'vuepress-theme-plume'
import { defineCollection } from 'vuepress-theme-plume'
export const tools: ThemeCollectionItem = defineCollection({
type: 'doc',
dir: 'tools',
title: 'Theme Tools',
linkPrefix: '/tools/',
sidebar: [
{
text: 'Tools',
icon: 'tabler:tools',
items: [
'custom-theme',
'home-hero-tint-plate',
'caniuse',
],
},
],
})

View File

@ -78,11 +78,15 @@ export const enNavbar: ThemeNavItem[] = defineNavbarConfig([
text: 'More', text: 'More',
icon: 'icon-park-outline:more-three', icon: 'icon-park-outline:more-three',
items: [ items: [
{ text: 'FAQ', link: '/en/faq/', icon: 'wpf:faq' },
{ text: 'Theme Tools', link: '/en/tools/', icon: 'jam:tools' },
{ text: 'Friend Links', link: '/en/friends/', icon: 'carbon:friendship' },
{ {
text: 'Vuepress', text: 'Vuepress',
icon: 'logos:vue',
items: [ items: [
{ text: 'Official Docs', link: 'https://v2.vuepress.vuejs.org' }, { text: 'Official Docs', link: 'https://v2.vuepress.vuejs.org', icon: 'logos:vue' },
{ text: 'Ecosystem', link: 'https://ecosystem.vuejs.press/' }, { text: 'Ecosystem', link: 'https://ecosystem.vuejs.press/', icon: 'logos:vue' },
], ],
}, },
], ],
@ -90,9 +94,10 @@ export const enNavbar: ThemeNavItem[] = defineNavbarConfig([
{ {
text: `${version}`, text: `${version}`,
icon: 'codicon:versions', icon: 'codicon:versions',
badge: '新',
items: [ items: [
{ text: 'Changelog', link: '/changelog/' }, { text: 'Changelog', link: '/en/changelog/' },
{ text: 'Contributing', link: '/contributing/' }, { text: 'Contributing', link: '/en/contributing/' },
], ],
}, },
]) ])

View File

@ -1,11 +1,37 @@
<script setup lang="ts"> <script setup lang="ts">
import { shallowRef, useId } from 'vue' import type { LocaleConfig } from 'vuepress'
import { computed, shallowRef, useId } from 'vue'
import { useRouteLocale } from 'vuepress/client'
import { useCaniuse, useCaniuseFeaturesSearch, useCaniuseVersionSelect } from '../composables/caniuse.js' import { useCaniuse, useCaniuseFeaturesSearch, useCaniuseVersionSelect } from '../composables/caniuse.js'
import CodeViewer from './CodeViewer.vue' import CodeViewer from './CodeViewer.vue'
const LOCALES: LocaleConfig<
Record<'select-feature' | 'placeholder' | 'embed-type' | 'output' | 'browser-version' | 'no-recommend', string>
> = {
'/': {
'select-feature': '选择特性:',
'placeholder': '输入特性',
'embed-type': '嵌入方式:',
'output': '输出:',
'browser-version': '浏览器版本:',
'no-recommend': '不推荐',
},
'/en/': {
'select-feature': 'Select feature:',
'placeholder': 'Input feature',
'embed-type': 'Embed type: ',
'output': 'Output:',
'browser-version': 'Browser version: ',
'no-recommend': 'Not recommended',
},
}
const listEl = shallowRef<HTMLUListElement | null>(null) const listEl = shallowRef<HTMLUListElement | null>(null)
const inputEl = shallowRef<HTMLInputElement | null>(null) const inputEl = shallowRef<HTMLInputElement | null>(null)
const id = useId() const id = useId()
const routeLocale = useRouteLocale()
const locale = computed(() => LOCALES[routeLocale.value])
const { feature, featureList, onSelect, isFocus } = useCaniuseFeaturesSearch(inputEl, listEl) const { feature, featureList, onSelect, isFocus } = useCaniuseFeaturesSearch(inputEl, listEl)
const { past, pastList, future, futureList, embedType, embedTypeList } = useCaniuseVersionSelect() const { past, pastList, future, futureList, embedType, embedTypeList } = useCaniuseVersionSelect()
@ -16,7 +42,7 @@ const { output, rendered } = useCaniuse({ feature, embedType, past, future })
<div class="caniuse-config-wrapper"> <div class="caniuse-config-wrapper">
<form> <form>
<label class="caniuse-form-item" :for="`caniuse-feature-input-${id}`"> <label class="caniuse-form-item" :for="`caniuse-feature-input-${id}`">
<span>选择特性</span> <span>{{ locale['select-feature'] }}</span>
<div class="feature-input"> <div class="feature-input">
<input <input
:id="`caniuse-feature-input-${id}`" :id="`caniuse-feature-input-${id}`"
@ -24,7 +50,7 @@ const { output, rendered } = useCaniuse({ feature, embedType, past, future })
class="feature-input__input" class="feature-input__input"
type="text" type="text"
name="feature" name="feature"
placeholder="输入特性" :placeholder="locale.placeholder"
> >
<span class="vpi-chevron-down" /> <span class="vpi-chevron-down" />
<ul v-show="isFocus" ref="listEl" class="feature-list"> <ul v-show="isFocus" ref="listEl" class="feature-list">
@ -45,7 +71,7 @@ const { output, rendered } = useCaniuse({ feature, embedType, past, future })
</div> </div>
</label> </label>
<div class="caniuse-form-item"> <div class="caniuse-form-item">
<span>嵌入方式</span> <span>{{ locale['embed-type'] }}</span>
<div class="caniuse-embed-type"> <div class="caniuse-embed-type">
<label <label
v-for="(item, index) in embedTypeList" v-for="(item, index) in embedTypeList"
@ -54,12 +80,12 @@ const { output, rendered } = useCaniuse({ feature, embedType, past, future })
> >
<input :id="`caniuse-embed-${id}-${index}`" v-model="embedType" type="radio" name="embedType" :value="item.value"> <input :id="`caniuse-embed-${id}-${index}`" v-model="embedType" type="radio" name="embedType" :value="item.value">
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
<Badge v-if="item.value === 'image'" type="warning" text="不推荐" /> <Badge v-if="item.value === 'image'" type="warning" :text="locale['no-recommend']" />
</label> </label>
</div> </div>
</div> </div>
<div v-if="!embedType" class="caniuse-form-item"> <div v-if="!embedType" class="caniuse-form-item">
<span>浏览器版本</span> <span>{{ locale['browser-version'] }}</span>
<div class="caniuse-browser-version"> <div class="caniuse-browser-version">
<label :for="`caniuse-past-${id}`"> <label :for="`caniuse-past-${id}`">
<select :id="`caniuse-past-${id}`" v-model="past" name="past"> <select :id="`caniuse-past-${id}`" v-model="past" name="past">
@ -80,7 +106,7 @@ const { output, rendered } = useCaniuse({ feature, embedType, past, future })
</div> </div>
</form> </form>
<div class="caniuse-output"> <div class="caniuse-output">
<h4>输出</h4> <h4>{{ locale.output }}</h4>
<CodeViewer lang="md" :content="output" /> <CodeViewer lang="md" :content="output" />
</div> </div>
<div v-if="embedType === 'image'" v-html="rendered" /> <div v-if="embedType === 'image'" v-html="rendered" />

View File

@ -1,16 +1,40 @@
<script setup lang="ts"> <script setup lang="ts">
import VPButton from '@theme/VPButton.vue' import { computed } from 'vue'
import VPButton from 'vuepress-theme-plume/components/VPButton.vue'
import { useRouteLocale } from 'vuepress/client'
import { useThemeColors } from '../composables/theme-colors.js' import { useThemeColors } from '../composables/theme-colors.js'
import CodeViewer from './CodeViewer.vue' import CodeViewer from './CodeViewer.vue'
import ColorPick from './ColorPick.vue' import ColorPick from './ColorPick.vue'
const locales: LocaleConfig<
Record<'reset' | 'light' | 'dark' | 'desc' | 'tip', string>
> = {
'/': {
reset: '重置',
light: '浅色主题',
dark: '深色主题',
desc: '复制下方的代码到您的项目中,请参考',
tip: '主题定制',
},
'/en/': {
reset: 'Reset',
light: 'Light theme',
dark: 'Dark theme',
desc: 'Copy the code below and paste it into your project, please refer to',
tip: 'Theme customization',
},
}
const routeLocale = useRouteLocale()
const locale = computed(() => locales[routeLocale.value])
const { lightColors, darkColors, css, reset } = useThemeColors() const { lightColors, darkColors, css, reset } = useThemeColors()
</script> </script>
<template> <template>
<VPButton theme="alt" text="重置" @click="reset" /> <VPButton theme="alt" :text="locale.reset" @click="reset" />
<h2>浅色主题</h2> <h2>{{ locale.light }}</h2>
<div class="theme-colors-wrapper"> <div class="theme-colors-wrapper">
<div v-for="({ name, group }, index) in lightColors" :key="index" class="group"> <div v-for="({ name, group }, index) in lightColors" :key="index" class="group">
<h4>{{ name }}</h4> <h4>{{ name }}</h4>
@ -23,7 +47,7 @@ const { lightColors, darkColors, css, reset } = useThemeColors()
</section> </section>
</div> </div>
</div> </div>
<h2>深色主题</h2> <h2>{{ locale.dark }}</h2>
<div class="theme-colors-wrapper"> <div class="theme-colors-wrapper">
<div v-for="({ name, group }, index) in darkColors" :key="index" class="group"> <div v-for="({ name, group }, index) in darkColors" :key="index" class="group">
<h4>{{ name }}</h4> <h4>{{ name }}</h4>
@ -36,7 +60,7 @@ const { lightColors, darkColors, css, reset } = useThemeColors()
</section> </section>
</div> </div>
</div> </div>
<p>复制下方的代码到您的项目中请参考 <a href="/guide/custom-style/">主题定制</a> </p> <p>{{ locale.desc }} <a href="/guide/custom-style/">{{ locale.tip }}</a> </p>
<CodeViewer :content="css" lang="css" /> <CodeViewer :content="css" lang="css" />
</template> </template>

View File

@ -1,6 +1,8 @@
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import type { LocaleConfig } from 'vuepress'
import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core' import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core'
import { computed, onMounted, readonly, ref, watch } from 'vue' import { computed, onMounted, readonly, ref, watch } from 'vue'
import { useRouteLocale } from 'vuepress/client'
interface Feature { interface Feature {
label: string label: string
@ -14,21 +16,22 @@ interface SelectItem {
const api = 'https://caniuse.pengzhanbo.cn/features.json' const api = 'https://caniuse.pengzhanbo.cn/features.json'
const pastVersions: SelectItem[] = [ const locales: LocaleConfig<
{ label: '不显示旧版本', value: '0' }, Record<'past0' | 'pastIndex' | 'future0' | 'futureIndex', string>
...Array.from({ length: 5 }).fill(0).map((_, i) => ({ > = {
label: `旧版本(当前版本 - ${i + 1}`, '/': {
value: `${i + 1}`, past0: '不显示旧版本',
})), pastIndex: '旧版本(当前版本 - {index}',
] future0: '不显示未来版本',
futureIndex: '未来版本(当前版本 + {index}',
const futureVersions: SelectItem[] = [ },
{ label: '不显示未来版本', value: '0' }, '/en/': {
...Array.from({ length: 3 }).fill(0).map((_, i) => ({ past0: 'Do not show old versions',
label: `未来版本(当前版本 + ${i + 1}`, pastIndex: 'Old version (current version - {index})',
value: `${i + 1}`, future0: 'Do not show future versions',
})), futureIndex: 'Future version (current version + {index})',
] },
}
const embedTypes: SelectItem[] = [ const embedTypes: SelectItem[] = [
{ label: 'iframe', value: '' }, { label: 'iframe', value: '' },
@ -39,16 +42,34 @@ export function useCaniuseVersionSelect(): {
past: Ref<string> past: Ref<string>
future: Ref<string> future: Ref<string>
embedType: Ref<string> embedType: Ref<string>
pastList: Readonly<SelectItem[]> pastList: ComputedRef<SelectItem[]>
futureList: Readonly<SelectItem[]> futureList: ComputedRef<SelectItem[]>
embedTypeList: Readonly<SelectItem[]> embedTypeList: Readonly<SelectItem[]>
} { } {
const routeLocale = useRouteLocale()
const past = ref('2') const past = ref('2')
const future = ref('1') const future = ref('1')
const embedType = ref('') const embedType = ref('')
const pastList = readonly(pastVersions) const pastList = computed(() => {
const futureList = readonly(futureVersions) return [
{ label: locales[routeLocale.value].past0 || '', value: '0' },
...Array.from({ length: 5 }).fill(0).map((_, i) => ({
label: locales[routeLocale.value]?.pastIndex?.replace('{index}', `${i + 1}`) ?? '',
value: `${i + 1}`,
})),
]
})
const futureList = computed(() => {
return [
{ label: locales[routeLocale.value].future0 || '', value: '0' },
...Array.from({ length: 3 }).fill(0).map((_, i) => ({
label: locales[routeLocale.value]?.futureIndex?.replace('{index}', `${i + 1}`) ?? '',
value: `${i + 1}`,
})),
]
})
const embedTypeList = readonly(embedTypes) const embedTypeList = readonly(embedTypes)
return { return {

View File

@ -1,12 +1,12 @@
--- ---
title: 博客文章 title: posts文章
createTime: 2024/03/03 11:01:03 createTime: 2024/03/03 11:01:03
permalink: /config/frontmatter/article/ permalink: /config/frontmatter/article/
--- ---
## 概述 ## 概述
适用于 博客类型的文章。 适用于 集合类型为 post 的文章。
示例: 示例:
@ -68,7 +68,7 @@ tags:
封面图配置。 封面图配置。
```ts ```ts
interface BlogPostCoverStyle { interface PostCoverStyle {
/** /**
* 博客文章封面图的位置 * 博客文章封面图的位置
*/ */

View File

@ -0,0 +1,195 @@
---
title: '⚠️ 1.0.0-rc.165 Breaking Change Notice: Blog & Notes Features Migrated to "Collections"'
sticky: true
createTime: 2025/10/09 22:18:52
permalink: /en/blog/dk58a4t2/
tags:
- Breaking Change
- Important Update
---
:::important
‼️ This update is a BREAKING CHANGE! The blog and notes features have been entirely migrated to a new "Collections" architecture. ‼️
:::
<!-- more -->
## Design Motivation: Why Introduce "Collections"?
[Jump to Migration Guide 👇👇👇](#migration-guide){.read-more}
### Background & Problem Analysis
The theme initially only supported the **blog** feature, treating all Markdown files under the `docs`
source directory as blog posts. As the version iterated, we added the **notes/knowledge base** feature,
defaulting the `notes` directory as the root for notes and excluding its content from the blog list.
This phased implementation led to an **architectural imbalance**: the blog became a "first-class citizen,"
while the notes feature appeared marginalized. This caused the following issues for users:
- **Path Redundancy**: Note files had to be stored under the `notes/` directory, adding unnecessary directory levels.
- **Complex Links**: When `autoFrontmatter` was not enabled, URLs were forced to include the `/notes/` prefix.
- **Conceptual Confusion**: Users were often confused about the functional difference between "notes" and "docs."
- **Cumbersome Configuration**: Extra adjustments to the `notes.dir` configuration were needed to achieve a standard documentation site structure.
These design flaws were legacy issues from historical iterations, and we sincerely apologize for the inconvenience.
### Solution: Unified Content Abstraction
After researching mainstream static site generators (like Hugo, VitePress) and full-stack frameworks
(like Nuxt), we drew inspiration from the `collection` concept in `@nuxt/content`.
We decided to introduce **Collections** as a unified unit for content organization. Whether it's a blog,
notes, documentation, or a knowledge base, they are essentially specific collections of Markdown files, differing only in their presentation.
:::important
Core Insight: Use the "Collection" abstraction to unify the organization of various content types while preserving their respective display characteristics.
:::
Based on content characteristics, we defined two collection types:
- **`post` type**: Suitable for fragmented, loosely related content (e.g., blogs, columns), providing an article list as a navigation entry.
- **`doc` type**: Suitable for structured, strongly related content (e.g., documentation, manuals), providing a sidebar for quick navigation.
This design solves the historical architectural problems and lays the foundation for extending more content types in the future.
## Migration Guide
### Core Concepts
- **Collection**: Specified via `collection.dir`; all Markdown files under this directory belong to the collection.
- **Collection Type**:
- `post`: Fragmented content, supports article list navigation.
- `doc`: Structured content, supports sidebar navigation.
### Configuration Migration
Replace the original `blog` and `notes` configurations:
```ts twoslash
// @noErrors
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// [!code --:9]
// Remove old blog and notes configuration
blog: { /* Blog configuration */ },
notes: {
link: '/',
dir: '/notes/',
notes: [
{ dir: 'typescript', link: '/typescript/', sidebar: 'auto' }
]
},
// [!code ++:16]
// Use collections configuration
collections: [
{
type: 'post', // Replaces original blog functionality
dir: 'blog', // Points to docs/blog directory
title: 'Blog' // Collection display name
// Original blog configuration continues here
// ...
},
{
type: 'doc', // Replaces original notes functionality
dir: 'typescript', // Points to docs/typescript directory
title: 'TypeScript Notes',
sidebar: 'auto', // Auto-generate sidebar
},
]
})
})
```
### Directory Structure Adjustment
Migrate files according to the following steps:
**Procedure:**
1. Move subdirectories under the `notes` directory directly to the `docs` root directory.
2. Create a `blog` directory and move original blog posts into it.
3. Remove the now-empty `notes` directory.
:::: flex
<div style="flex:1">
::: file-tree title="Pre-migration Structure"
- docs
- -- notes
- typescript
- basic.md
- advanced.md
- blog-cate-1
- post-1.md
- blog-cate-2
- post-2.md
- blog-post.md
- README.md
:::
</div>
<div style="align-self: center"><Icon name="mingcute:arrow-right-fill" /></div>
<div style="flex:1">
:::file-tree title="Post-migration Structure"
- docs
- typescript
- basic.md
- advanced.md
- ++ blog
- blog-cate-1
- post-1.md
- blog-cate-2
- post-2.md
- blog-post.md
- README.md
:::
</div>
::::
### Helper Functions
- `defineCollection`: Helper function for defining a single collection configuration.
- `defineCollections`: Helper function for defining multiple collection configurations.
```ts twoslash
import { defineCollection, defineCollections } from 'vuepress-theme-plume'
export const blog = defineCollection({
type: 'post',
dir: 'blog',
title: 'Blog'
})
export const typescript = defineCollection({
type: 'doc',
dir: 'typescript',
title: 'TypeScript Notes',
sidebar: 'auto'
})
export const collections = defineCollections([
blog,
typescript
])
```
## Detailed Documentation
[Collections Documentation](../../guide/quick-start/collection.md){.read-more}
[Post Collection](../../guide/quick-start/collection-post.md){.read-more}
[Doc Collection](../../guide/quick-start/collection-doc.md){.read-more}

View File

@ -1,7 +1,334 @@
--- ---
title: How to user frontmatter title: How to Use Frontmatter
createTime: 2025/03/03 14:12:59 createTime: 2025/10/09 09:19:36
permalink: /en/article/gh9w1jj1/ permalink: /en/article/ecxnxxd0/
--- ---
todo ... ::: info Note
This article translates parts of [Introduction to YAML](https://dev.to/paulasantamaria/introduction-to-yaml-125f).
It provides a basic explanation of how to use frontmatter in markdown files.
If you have a good foundation in English reading, to avoid potential content distortion from translation,
it is recommended that you read the original article.
Original article: <https://dev.to/paulasantamaria/introduction-to-yaml-125f>.
:::
## Introduction
YAML is a data serialization language, commonly used for configuration files, such as the
[Open API Specification](https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v2.0/yaml/api-with-examples.yaml) or [CI/CD pipelines](https://docs.gitlab.com/ee/ci/yaml/).
::: note Fun Fact! 🤓
According to the [YAML 1.0 Specification Document (2001-05-26)](https://yaml.org/spec/history/2001-05-26.html),
the acronym "YAML" stood for "Yet Another Markup Language".However,
it was later changed to the recursive acronym "YAML Ain't Markup Language" in the [2002-04-07 specification](https://yaml.org/spec/history/2002-04-07.html).
:::
As stated in the latest specification, __YAML__ is designed to be __human-friendly for handling data__
and achieves "unique cleanliness" by __minimizing the use of structural characters__,
allowing data to be displayed in a natural and meaningful way.
The latest specification also states that YAML _1.2 is officially a superset of JSON_, meaning most JSON documents can be parsed as YAML.
YAML makes it easy to inspect data structures by using indentation-based scoping (similar to Python).
::: note Another Fun Fact! 🤓
DEV.to articles use YAML to define custom variables like title, description, tags, etc.
:::
## Basic Syntax
A YAML document is essentially a __collection of key-value pairs__, where values can be as simple as a string or as complex as a tree.
Here are some notes on YAML syntax:
- __Indentation is used to denote structure__. Tabs are not allowed. The number of spaces doesn't matter
as long as child nodes are indented more than their parent nodes.
- UTF-8, UTF-16, and UTF-32 encodings are permitted.
### Strings
```md
---
# Strings do not require quotes:
title: Introduction to YAML
# But you can still use them:
title-w-quotes: 'Introduction to YAML'
# Multi-line strings start with |
execute: |
npm ci
npm build
npm test
---
```
The above code converts to JSON as:
```json
{
"title": "Introduction to YAML",
"title-w-quotes": "Introduction to YAML",
"execute": "npm ci\nnpm build\nnpm test\n"
}
```
### Numbers
```md
---
# Integer:
age: 29
# Float:
price: 15.99
# Scientific notation:
population: 2.89e+6
---
```
The above code converts to JSON as:
```json
{
"age": 29,
"price": 15.99,
"population": 2890000
}
```
### Booleans
```md
---
# Booleans can be represented in different ways:
published: false
published: False
published: FALSE
---
```
All of the above will convert to JSON as follows:
```json
{
"published": false
}
```
### Null Values
```md
---
# Null values can be represented by not setting a value:
null-value:
# Or more explicitly:
null-value: null
null-value: NULL
null-value: Null
---
```
All of the above will convert to JSON as follows:
```json
{
"null-value": null
}
```
### Dates and Timestamps
Dates can be used in ISO format as shown below:
```md
---
date: 2002-12-14
canonical: 2001-12-15T02:59:43.1Z
iso8601: 2001-12-14t21:59:43.10-05:00
spaced: 2001-12-14 21:59:43.10 -5
---
```
### Sequences
Sequences allow us to define lists in YAML:
```md
---
# Numbered list using hyphens:
numbers:
- one
- two
- three
# Inline version:
numbers: [ one, two, three ]
---
```
Both sequences above will parse to JSON as follows:
```json
{
"numbers": [
"one",
"two",
"three"
]
}
```
### Nested Values
We can use all the types mentioned above to create objects with nested values, as shown below:
```md
---
# Data for the novel Nineteen Eighty-Four.
nineteen-eighty-four:
author: George Orwell
published-at: 1949-06-08
page-count: 328
description: |
A Novel, often published as 1984, is a dystopian novel by English novelist George Orwell.
It was published in June 1949 by Secker & Warburg as Orwell's ninth and final book.
---
```
This converts to JSON as:
```json
{
"nineteen-eighty-four": {
"author": "George Orwell",
"published-at": "1949-06-08T00:00:00.000Z",
"page-count": 328,
"description": "A Novel, often published as 1984, is a dystopian novel by English novelist George Orwell.\nIt was published in June 1949 by Secker & Warburg as Orwell's ninth and final book.\n"
}
}
```
### Lists of Objects
Combining sequences and nested values, we can create a list of objects.
```md
---
# Let's list books:
- nineteen-eighty-four:
author: George Orwell
published-at: 1949-06-08
page-count: 328
description: |
A Novel, often published as 1984, is a dystopian novel by English novelist George Orwell.
- the-hobbit:
author: J. R. R. Tolkien
published-at: 1937-09-21
page-count: 310
description: |
The Hobbit, or There and Back Again is a children's fantasy novel by English author J. R. R. Tolkien.
---
```
## Unique Features
Here are some __more complex features__ that caught my attention, which also distinguish YAML from JSON.
### Comments
As you may have noticed in my previous examples, YAML allows comments starting with `#`.
```md
---
# This is a very useful comment.
---
```
### Reusability with Anchors
Node anchors are used to __mark a node__ for future reference, allowing us to reuse that node.
To mark a node, we use the `&` character; to reference it, we use `*`:
In the following example, we define a list of books and reuse the author data, so we only need to define it once:
```md
---
# Author data:
author: &gOrwell
name: George
last-name: Orwell
# Some books:
books:
- 1984:
author: *gOrwell
- animal-farm:
author: *gOrwell
---
```
When parsed to JSON, the above code will look like this:
```json
{
"author": {
"name": "George",
"last-name": "Orwell"
},
"books": [
{
"1984": {
"author": {
"name": "George",
"last-name": "Orwell"
}
}
},
{
"animal-farm": {
"author": {
"name": "George",
"last-name": "Orwell"
}
}
}
]
}
```
### Explicit Data Types with Tags
As we saw in previous examples, YAML automatically detects the types of our values, but we can also __specify the desired type__.
We specify it by prefixing the value with `!!` followed by the type.
Here are some examples:
```md
---
# The following value should be an integer, regardless:
should-be-int: !!int 3.2
# Parse any value as a string:
should-be-string: !!str 30.25
# I need the next value to be a boolean:
should-be-boolean: !!bool yes
---
```
This converts to JSON as:
```json
{
"should-be-int": 3,
"should-be-string": "30.25",
"should-be-boolean": true
}
```

16
docs/en/changelog.md Normal file
View File

@ -0,0 +1,16 @@
---
title: Changelog
createTime: 2024/03/13 21:15:50
permalink: /en/changelog/
article: false
aside: false
externalLinkIcon: false
readingTime: false
comment: false
editLink: false
contributors: false
changelog: false
search: false
---
<!-- @include: ../../CHANGELOG.md -->

View File

@ -1,17 +0,0 @@
---
title: Theme Configuration
createTime: 2025/03/03 14:27:13
permalink: /en/config/basic/
---
todo ...
## Basic Configuration
### autoFrontmatter
### article
### blog
## Locales Configuration

View File

@ -0,0 +1,531 @@
---
title: Collections Configuration
createTime: 2025/10/09 14:00:06
permalink: /en/config/collections/
---
## Overview
**Collections** are the core concept in the theme for organizing and managing documents.
Each collection points to a specific folder under the source directory, managing all Markdown files within it as a logical unit.
Through flexible collection configuration, you can easily build various content systems:
- **Blog** - Personal essays and technical shares
- **Column** - Thematic series articles
- **User Manual** - Product usage documentation
- **Notes** - Study notes and knowledge organization
- **Product Documentation** - Complete project documentation
- **Knowledge Base** - Team knowledge management system
Collections are primarily divided into two types to accommodate different content organization needs:
- **`post` type**: Suitable for fragmented content with weak inter-article relationships, such as blogs, columns, etc.
- **`doc` type**: Suitable for structured documentation with closely related content,
such as user manuals, product documentation, knowledge bases, etc.
::: tip Configuration Location
Collection configuration can be done in either `.vuepress/config.ts` or the standalone `plume.config.ts` file.
:::
## Basic Configuration
Assume your project uses the following directory structure:
::: file-tree title="Project Structure"
- docs
- **blog**
- post-1.md
- post-2.md
- **typescript**
- basic
- intro.md
- variable.md
- types.md
:::
The corresponding collection configuration example is as follows:
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Collection Configuration // [!code focus:7]
collections: [
// Register post-type collection for blog functionality
{ type: 'post', dir: 'blog', title: 'Blog' },
// Register doc-type collection for TypeScript documentation functionality
{ type: 'doc', dir: 'typescript', title: 'TypeScript Notes', sidebar: 'auto' }
],
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
// Collection configuration in standalone config file // [!code focus:7]
collections: [
{ type: 'post', dir: 'blog', title: 'Blog' },
{ type: 'doc', dir: 'typescript', title: 'TypeScript Notes', sidebar: 'auto' }
],
})
```
:::
## Multi-language Configuration
For multi-language projects, you can configure collections separately for each language within the `locales` field:
::: file-tree title="Multi-language Project Structure"
- docs
- **blog/**
- post-1.md
- post-2.md
- **typescript/**
- basic
- intro.md
- variable.md
- types.md
- en
- **blog/**
- post-1.md
- post-2.md
- **typescript/**
- basic
- intro.md
- variable.md
- types.md
:::
::: code-tabs#configs
@tab .vuepress/config.ts
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
locales: {
'/': {
// Chinese collection configuration // [!code focus:4]
collections: [
{ type: 'post', dir: 'blog', title: '博客' },
{ type: 'doc', dir: 'typescript', title: 'TypeScript笔记', sidebar: 'auto' }
],
},
'/en/': {
// English collection configuration // [!code focus:4]
collections: [
{ type: 'post', dir: 'blog', title: 'Blog' },
{ type: 'doc', dir: 'typescript', title: 'TypeScript Note', sidebar: 'auto' }
],
}
}
})
})
```
@tab .vuepress/plume.config.ts
```ts
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
locales: {
'/': {
// Chinese collection configuration // [!code focus:4]
collections: [
{ type: 'post', dir: 'blog', title: '博客' },
{ type: 'doc', dir: 'typescript', title: 'TypeScript笔记', sidebar: 'auto' }
],
},
'/en/': {
// English collection configuration // [!code focus:4]
collections: [
{ type: 'post', dir: 'blog', title: 'Blog' },
{ type: 'doc', dir: 'typescript', title: 'TypeScript Note', sidebar: 'auto' }
],
}
}
})
```
:::
## Post Collection Details
Post collections are designed specifically for fragmented content like blogs and columns, providing a complete article management system:
### Core Features
- **Article List Page** - Supports article pinning, cover images, excerpt display, personal information, etc.
- **Article Categories Page** - Automatically generates categories based on directory structure
- **Article Tags Page** - Flexible tag management
- **Article Archives Page** - Organizes content by time dimension
### Configuration Example
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
collections: [
// Blog collection configuration
{
type: 'post',
dir: 'blog',
title: 'Blog',
link: '/blog/', // List page link
linkPrefix: '/article/', // Article link prefix
postCover: 'top', // Cover image position
autoFrontmatter: { permalink: true }, // Auto frontmatter
},
// Interview column configuration
{
type: 'post',
dir: 'interview',
title: 'Interview Column',
link: '/interview/', // List page link
}
]
})
```
## Doc Collection Details
Doc collections are suitable for structured documentation, emphasizing logical relationships between content:
### Core Features
- **Sidebar Navigation** - Provides clear document structure navigation
- **Auto-generated Table of Contents** - Intelligently generates sidebar based on file structure
- **Multi-level Nesting Support** - Supports complex document hierarchy structures
### Configuration Example
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
collections: [
// TypeScript Notes - Auto-generated sidebar
{
type: 'doc',
dir: 'typescript',
title: 'TypeScript Notes',
sidebar: 'auto'
},
// Python Notes - Manually configured sidebar
{
type: 'doc',
dir: 'python',
title: 'Python Notes',
sidebar: [
{ text: 'Basic Syntax', link: 'basic' },
{
text: 'API Documentation',
items: [
{ text: 'asyncio', link: 'asyncio' }
]
},
'advanced' // Shorthand form
]
}
]
})
```
## Configuration Type Declarations
### Base Collection Configuration
```ts
/* Collection configuration array */
type ThemeCollections = ThemeCollectionItem[]
/* Single collection item */
type ThemeCollectionItem = ThemePostCollection | ThemeDocCollection
/* Collection common configuration */
interface ThemeBaseCollection {
/**
* Collection type
* - `post`: Article list (blog, column)
* - `doc`: Structured documentation (notes, knowledge base)
*/
type: 'post' | 'doc'
/**
* Document directory (relative to source directory)
*/
dir: string
/**
* Article link prefix
*/
linkPrefix?: string
/**
* Collection title (used for breadcrumb navigation)
*/
title: string
/**
* Tag color theme
* @default 'colored'
*/
tagsTheme?: 'colored' | 'gray' | 'brand'
/**
* Auto-generate frontmatter
*/
autoFrontmatter?: AutoFrontmatterOptions | false
}
```
### Post Collection Specific Configuration
```ts title="Post Collection Configuration"
interface ThemePostCollection extends ThemeBaseCollection {
type: 'post'
/**
* Include file patterns (glob pattern)
* @default ['**\/*.md']
*/
include?: string[]
/**
* Exclude file patterns (glob pattern)
* @default []
*/
exclude?: string[]
/**
* Pagination configuration
*/
pagination?: false | number | {
/**
* Number of articles per page
* @default 15
*/
perPage?: number
}
/**
* Article list page link
* @default '/{dir}/'
*/
link?: string
/**
* Whether to enable article list page
* @default true
*/
postList?: boolean
/**
* Whether to enable tags page
* @default true
*/
tags?: boolean
/**
* Tags page link
* @default '/{link}/tags/'
*/
tagsLink?: string
/**
* Tags page text
*/
tagsText?: string
/**
* Whether to enable archives page
* @default true
*/
archives?: boolean
/**
* Archives page link
* @default '/{link}/archives/'
*/
archivesLink?: string
/**
* Archives page text
*/
archivesText?: string
/**
* Whether to enable categories feature
* @default true
*/
categories?: boolean
/**
* Categories page link
* @default '/{link}/categories/'
*/
categoriesLink?: string
/**
* Categories page text
*/
categoriesText?: string
/**
* Categories expand depth
* @default 'deep'
*/
categoriesExpand?: number | 'deep'
/**
* Categories list transformation function
*/
categoriesTransform?: (categories: PostsCategoryItem[]) => PostsCategoryItem[]
/**
* Article cover image configuration
* @default 'right'
*/
postCover?: PostsCoverLayout | PostsCoverStyle
/**
* Profile configuration
*/
profile?: ProfileOptions | false
/**
* Social account configuration
*/
social?: SocialLink[] | false
}
/* Article category item */
interface PostsCategoryItem {
id: string
sort: number
name: string
}
/* Cover image layout */
type PostsCoverLayout = 'left' | 'right' | 'odd-left' | 'odd-right' | 'top'
/* Cover image style */
interface PostsCoverStyle {
layout?: PostsCoverLayout
ratio?: number | `${number}:${number}` | `${number}/${number}`
width?: number
compact?: boolean
}
/* Social link icon */
type SocialLinkIcon = SocialLinkIconUnion | { svg: string, name?: string }
/* Social link */
interface SocialLink {
icon: SocialLinkIcon
link: string
ariaLabel?: string
}
/**
* Profile
*/
export interface ProfileOptions {
/**
* Avatar image URL
*/
avatar?: string
/**
* Name
*/
name?: string
/**
* Description / Bio / Motto / Signature
*/
description?: string
/**
* Whether to display as circular avatar
*/
circle?: boolean
/**
* Location
*/
location?: string
/**
* Organization, Company
*/
organization?: string
/**
* Layout position, left or right
* @default 'right'
*/
layout?: 'left' | 'right'
}
```
### Doc Collection Specific Configuration
```ts title="Doc Collection Configuration"
interface ThemeDocCollection extends ThemeBaseCollection {
type: 'doc'
/**
* Sidebar configuration
*/
sidebar?: 'auto' | (string | ThemeSidebarItem)[]
/**
* Whether to show sidebar scrollbar
* @default true
*/
sidebarScrollbar?: boolean
/**
* Sidebar default collapsed state
* @default false
*/
sidebarCollapsed?: boolean
}
/* Sidebar item configuration */
interface ThemeSidebarItem {
text?: string
link?: string
icon?: ThemeIcon
badge?: string | ThemeBadge
items?: 'auto' | (string | ThemeSidebarItem)[]
collapsed?: boolean
prefix?: string
rel?: string
target?: string
}
/* Icon type */
type ThemeIcon = string | { svg: string }
/* Badge configuration */
export interface ThemeBadge {
text?: string
type?: string
color?: string
bgColor?: string
borderColor?: string
}
```
### Auto Sidebar Generation
When the Doc collection's `sidebar` is set to `'auto'`, the system automatically generates sidebar
navigation based on the directory structure. The sorting rules follow the [Folder Naming Conventions](../guide/quick-start/write.md#folder-naming-conventions).
### Sidebar Icon Configuration
The theme supports two methods for configuring sidebar icons:
- **Define directly in sidebar configuration**:
```ts
sidebar: [
{ text: 'Introduction', link: 'intro', icon: 'mdi:tooltip-text-outline' }
]
```
- **Define in document frontmatter**:
```md
---
title: Theme Introduction
icon: mdi:tooltip-text-outline
---
```
Both methods have the same effect, and you can choose which to use based on specific scenarios.
For complete sidebar configuration options and usage tips, please refer to the [Sidebar Configuration Guide](../guide/quick-start/sidebar.md).

View File

@ -1,11 +0,0 @@
---
title: Blog Article
createTime: 2025/03/03 19:11:37
permalink: /en/config/frontmatter/article/
---
## Configuration
### sticky
todo ...

View File

@ -1,8 +1,230 @@
--- ---
title: General configuration title: General Configuration
createTime: 2025/03/04 12:30:38 createTime: 2025/10/09 20:01:09
permalink: /en/config/frontmatter/basic/ permalink: /en/config/frontmatter/basic/
badge: badge
--- ---
todo ... ## Overview
General Frontmatter configuration applicable to all articles.
Example:
```md
---
title: Title
createTime: 2024/03/02 20:01:09
permalink: /config/frontmatter/basic/
---
```
## Configuration
### pageLayout
- Type: `false | 'home' | 'doc' | 'custom' | 'page' | 'friends' | string`
- Default: `doc`
- Details:
Page layout mode.
- `'home'`: Homepage layout
- `'doc'`: Documentation page layout, including left sidebar, article content, right sidebar, navbar, comments, etc.
- `'page'`: Includes only the navbar and MD file content. Use this layout for custom page content.
- `'friends'`: Friends link page layout
- `'custom'`: Includes only MD file content. Use this layout for fully custom page content.
- `false`: Equivalent to `custom`
- `string`: A global component name can be passed, which will be applied as the layout component.
### pageClass
- Type: `string`
- Default: `''`
- Details:
Additional custom CSS class name for the page.
### title
- Type: `string`
- Default: `''`
- Details:
Article title.
The theme automatically populates the current filename as the article title upon file creation.
### badge
- Type: `string | { text: string, type?: 'info' | 'tip' | 'warning' | 'danger' }`
Display a badge on the right side of the article title.
### createTime
- Type: `string`
- Default: `''`
- Details:
Article creation time.
The theme automatically populates the current time as the article creation time upon file creation.
### permalink
- Type: `string`
- Default: `''`
- Details:
Article permanent link.
Upon file creation, the theme automatically populates:
- For blog-type articles: `/article/` + `8-character random string generated by nanoid` as the permanent link.
- For articles under the notes directory: The permanent link is automatically populated based on the notes configuration.
### externalLinkIcon
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the external link icon for external links within the current article.
### backToTop
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the back-to-top button for the current article.
### comments
- Type: `boolean`
- Default: `true`
- Details:
Whether the current article is commentable. Only takes effect when the comment feature is enabled.
### aside
- Type: `boolean | 'left'`
- Default: `true`
- Details:
Whether to display the right sidebar for the current article.
When set to `'left'`, the right sidebar will be displayed on the left side.
### navbar
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the navigation bar for the current article.
### outline
- Type: `false | number | [number, number] | 'deep'`
- Default: `[2, 3]`
- Details:
The heading levels to display.
A single number indicates displaying only headings of that level.
If a tuple is passed, the first number is the minimum level and the second number is the maximum level.
`'deep'` is the same as `[2, 6]`, which displays all headings from `<h2>` to `<h6>`.
### prev
- Type: `string | { text: string, link: string, icon?: string }`
- Default: `''`
- Details:
Customize the previous article for the current article.
- For blog-type articles: The theme automatically populates the title and link of the previous article based on chronological order.
- For notes-type articles: The theme automatically populates the title and link of the previous article based on the note sidebar configuration.
### next
- Type: `string | { text: string, link: string, icon?: string }`
- Default: `''`
- Details:
Customize the next article for the current article.
- For blog-type articles: The theme automatically populates the title and link of the next article based on chronological order.
- For notes-type articles: The theme automatically populates the title and link of the next article based on the note sidebar configuration.
### readingTime
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the reading time for the current article.
### lastUpdated
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the last updated time for the current article.
The last updated time is automatically populated based on the git commit time.
### contributors
- Type: `boolean | string | string[]`
- Default: `true`
- Details:
Whether to display contributors for the current article. Contributors are automatically populated based on git committers.
If your article originates from a third party and git commits cannot fully list all authors, you can supplement contributors here.
For additional information, please refer to [contributors](../../guide/features/contributors.md#info).
### changelog
- Type: `boolean`
- Default: `false`
- Details:
Whether to display the page change history for the current page.
### copyright
- Type: `boolean | CopyrightLicense | CopyrightFrontmatter`
- Default: `false`
- Details:
Whether to display copyright information for the current article.
For complete information, please refer to [copyright](../../guide/features/copyright.md).
### editLink
- Type: `boolean`
- Default: `true`
- Details:
Whether to display the article edit button for the current article.
### watermark
- Type: `boolean | WatermarkOptions`
- Default: `undefined` (When the theme does not enable watermarks,
or when global watermarks are not enabled, the default value is `false`. If global watermarks are enabled, the default is `true`.)
- Details:
Configure the watermark for the current article.
When the type is boolean, it indicates whether to enable the watermark.
When the type is WatermarkOptions, it represents the watermark configuration for the current page.
You can refer to [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/zh/config/).

View File

@ -0,0 +1,150 @@
---
title: Friends Links Page
createTime: 2025/10/09 18:00:52
permalink: /en/config/frontmatter/friends/
---
## Overview
Applicable to friends links pages.
Example:
```md
---
pageLayout: friends
title: Friends Links
list:
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: Guangzhou, China
organization: VuePress
desc: Even if slow, never stop. Even if falling behind, even if failing, one must be able to reach the goal they aspire to.
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
---
```
## Configuration
### friends <Badge type="warning" text="Deprecated" />
- Type: `boolean`
- Default: `false`
- Details:
Whether the current page is a friends links page.
### pageLayout
- Type: `'friends'`
- Details:
Declares the current page as a friends links page.
### title
- Type: `string`
- Default: `Friends Links`
The title of the friends links page.
### description
- Type: `string`
The description of the friends links page.
### contentPosition <Badge text="New" />
- Type: `'before' | 'after'`
- Default: `'after'`
Whether the markdown content is placed before or after the friends links list. By default, it is inserted after the list.
### list
- Type: `FriendsItem[]`
- Default: `[]`
The friends links list.
```ts
interface FriendsItem {
/**
* Friends link name
*/
name: string
/**
* Friends link URL
*/
link: string
/**
* Friends link avatar
*/
avatar?: string
/**
* Friends link description
*/
desc?: string
/**
* Geographic location
*/
location?: string
/**
* Organization/Company
*/
organization?: string
/**
* Social links
*/
socials?: SocialLink[]
/**
* Background color
*/
backgroundColor?: string | { light: string, dark: string }
/**
* Font color
*/
color?: string | { light: string, dark: string }
/**
* Name color
*/
nameColor?: string | { light: string, dark: string }
}
```
For social link configuration, please refer to [Configuration/Theme Configuration/Social Links](../../config/theme.md#social).
### groups
- Type: `FriendsGroup[]`
- Default: `[]`
Friends links grouping.
```ts
interface FriendsGroup {
/**
* Group title
*/
title?: string
/**
* Group description
*/
desc?: string
/**
* Friends links list
*/
list?: FriendsItem[]
}
```

View File

@ -0,0 +1,76 @@
---
title: Homepage
createTime: 2025/10/09 15:00:43
permalink: /en/config/frontmatter/home/
---
## Overview
Applicable only to the homepage. Configure in `{sourceDir}/README.md`.
Example:
```md
---
pageLayout: home
config:
- type: banner
- type: custom
---
```
## Configuration
### home <Badge type="warning" text="Deprecated" />
- Type: `boolean`
- Details:
Declares whether the page is the homepage.
Deprecated. Please use `pageLayout: 'home'` instead.
### pageLayout
- Type: `'home'`
- Details:
Declares the current page as the homepage.
### config
- Type: `PlumeHomeConfig[]`
Defines homepage sections.
```ts
interface PlumeHomeConfigBase {
/**
* The type of this section, which determines the component to be applied
*/
type: 'banner' | 'hero' | 'text-image' | 'image-text' | 'features' | 'profile' | 'custom' | string
/**
* Whether this section should be full-screen
*/
full?: boolean
/**
* Background image for this section
* You can define background images for light/dark modes
*/
backgroundImage?: string | { light: string, dark: string }
/**
* Background attachment style for this section
*/
backgroundAttachment?: 'fixed' | 'local'
}
```
For more detailed configuration, please refer to [Custom Homepage](../../guide/custom/home.md).
### signDown
- Type: `boolean`
- Default: `false`
- Details:
Whether to display the downward arrow indicator.

View File

@ -0,0 +1,95 @@
---
title: Posts Article
createTime: 2025/10/09 11:01:03
permalink: /en/config/frontmatter/article/
---
## Overview
Applicable to articles with collection type set to 'post'.
Example:
```md
---
title: Article Title
tags:
- tag1
- tag2
---
```
## Configuration
### sticky
- Type: `boolean | number`
- Default: `false`
Whether to pin the current article to the top in article lists.
If a `number` is provided, a higher value will position the article closer to the top when pinned.
### article
- Type: `boolean`
- Default: `true`
Whether to display the current article in article lists.
### draft
- Type: `boolean`
- Default: `false`
Marks the article as a draft. Articles marked as drafts **only appear in article lists during development** and are hidden in production environments.
### tags
- Type: `string[]`
- Default: `[]`
Article tags.
### cover
- Type: `string`
- Default: `''`
Article cover image. The cover image is only displayed on the article list page.
Only absolute paths and remote image URLs are supported.
### coverStyle
- Type: `BlogPostCoverStyle`
- Default: `null`
Cover image configuration.
```ts
interface PostCoverStyle {
/**
* Layout position of the blog post cover image
*/
layout?: 'left' | 'right' | 'odd-left' | 'odd-right' | 'top'
/**
* Aspect ratio of the blog post cover image
*
* @default '4:3'
*/
ratio?: number | `${number}:${number}`
/**
* Width of the cover image, only effective when layout is 'left' or 'right'
*
* @default 240
*/
width?: number
/**
* Whether to use compact mode. In compact mode, the cover image fits snugly against the container edge.
* @default false
*/
compact?: boolean
}
```

View File

@ -1,7 +1,180 @@
--- ---
title: Configuration title: Configuration Guide
createTime: 2024/03/02 10:48:14 createTime: 2025/10/09 10:48:14
permalink: /en/config/intro/ permalink: /en/config/intro/
--- ---
Todo... ## Overview
==vuepress-theme-plume== is a theme developed based on [VuePress](https://v2.vuepress.vuejs.org/),
and its configuration fully adheres to VuePress's configuration specifications.
**VuePress provides three configuration types:**
- **Site Configuration**: The object directly exported from the configuration file (e.g., `.vuepress/config.ts`)
- **Theme Configuration**: The parameter object passed to the `plumeTheme()` function
- **Page Configuration**: Defined in the page Frontmatter using YAML syntax
## VuePress Configuration File
The base configuration file for VuePress is typically `.vuepress/config.js`,
while TypeScript configuration files are also supported.
Using `.vuepress/config.ts` provides more comprehensive type hints.
VuePress resolves configuration files in the following priority order:
**In the current working directory (cwd):**
- `vuepress.config.ts`
- `vuepress.config.js`
- `vuepress.config.mjs`
**In the source directory (sourceDir):**
- `.vuepress/config.ts` <Badge type="tip" text="Recommended" />
- `.vuepress/config.js`
- `.vuepress/config.mjs`
**Basic Configuration Example:**
```ts title=".vuepress/config.ts" twoslash
import { viteBundler } from '@vuepress/bundler-vite'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
// [!code hl:5]
// VuePress Base Configuration
lang: 'zh-CN',
title: 'Hello, VuePress!',
description: 'This is my first VuePress site',
// ...
// Use Vite as the bundler
bundler: viteBundler(),
// Enable the Plume theme
theme: plumeTheme({ // [!code ++:4]
// Theme configuration options
// ...
}),
})
```
## Theme Configuration File
Typically, we configure the theme in `.vuepress/config.ts`:
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Theme configuration
}),
// ...
})
```
However, modifying this file causes the VuePress service to restart and perform a full refresh.
For small sites, this process is quick; but for sites with substantial content, each restart requires considerable time.
Frequent configuration file modifications can also easily lead to VuePress ==service crashes=={.caution}
::twemoji:angry-face::, requiring manual service restarts and severely impacting content writing efficiency.
**Solution: Theme Hot-Reload Configuration:**
The theme provides a `plume.config.ts` configuration file. ==Modifications to this file support hot-reload
and do not require restarting the service=={.tip} ::twemoji:confetti-ball::.
You can configure hot-reload supported fields within it, such as `navbar`, `profile`, etc.
::: tip
These fields can still be configured within the `theme` section of the VuePress configuration file,
but the settings from the theme configuration file will ultimately be merged into the main configuration.
To avoid data duplication, please do not configure the same field in both locations.
:::
::: details What is Hot-Reload?
**Hot-Reload** is a development technique that, in the context of VuePress, manifests as:
- Configuration changes take effect immediately without restarting the service; the browser does not refresh the page
- Page modifications take effect immediately; the browser updates content without a full refresh
:::
### Configuration Method
Create the `plume.config.ts` file in the same directory as the VuePress configuration file:
::: file-tree
- docs
- .vuepress
- config.ts
- **plume.config.ts**
:::
```ts title="plume.config.ts" twoslash
// @filename: ./navbar.ts
export default []
// ---cut---
import { defineThemeConfig } from 'vuepress-theme-plume'
import navbar from './navbar'
export default defineThemeConfig({
// Theme configuration
profile: {
name: 'Your name',
},
navbar,
})
```
The `defineThemeConfig(config)` function provides full type hints. Except for `plugins`, most configurations can be defined in this file.
::: warning Notes
- The theme configuration file only supports hot-reload for specific fields.
- Avoid reconfiguring fields in the VuePress configuration file that have already been set in the theme configuration file.
:::
### Custom Configuration File Path
If a non-default path is required, it can be specified in the VuePress configuration:
```ts title=".vuepress/config.ts" twoslash
import path from 'node:path'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Custom configuration file path
configFile: path.join(__dirname, 'custom/config.ts'), // [!code ++]
}),
})
```
::: warning Not recommended for beginners; custom paths might cause unexpected issues.
:::
## Page Configuration
Using the YAML Frontmatter at the top of the page, the theme can be configured individually for each page:
```md {1,5} title="article.md"
---
title: Article Title
createTime: 2024/09/08 22:53:34
permalink: /article/xxx/
---
```
The section wrapped by the `---` delimiters at the top of a Markdown file is the Frontmatter, which uses YAML syntax for configuration.
:::tip For an introduction to basic YAML syntax, please refer to [this blog post](/article/ecxnxxd0/)
:::

View File

@ -1,7 +1,283 @@
--- ---
title: Multilingual Configuration title: Locales Configuration
createTime: 2025/03/04 13:23:51 createTime: 2025/10/09 10:07:15
permalink: /en/config/locales/ permalink: /en/config/locales/
--- ---
todo ... These options are used to configure language-related text.
If your site is served in languages other than the built-in supported languages, you should set these options for each language to provide translations.
## Built-in Language Support
The theme has built-in support for the following languages:
- Simplified Chinese (`zh-CN`) - `/zh/`
- Traditional Chinese (`zh-TW`) - `/zh-tw/`
- English (`en-US`) - `/en/`
- French (`fr-FR`) - `/fr/`
- German (`de-DE`) - `/de/`
- Russian (`ru-RU`) - `/ru/`
- Japanese (`ja-JP`) - `/ja/`
- Korean (`ko-KR`) - `/ko/`
## Configuration
You should write the configuration in `theme.locales`.
You can configure it in `.vuepress/config.ts`, or in `.vuepress/plume.config.ts`:
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
locales: {
// Language code for non-built-in languages
'/xxx/': {
// Language configuration
}
}
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
locales: {
// Language code for non-built-in languages
'/xxx/': {
// Language configuration
}
}
})
```
:::
For detailed configuration methods, please refer to: [Internationalization](../guide/quick-start/locales.md)
### appearanceText
- Type: `string`
- Default: `'Appearance'`
- Details: The text for the theme toggle button in the navigation bar.
### selectLanguageName
- Type: `string`
- Default: `''`
- Details:
The language name for the locale.
This configuration item **only takes effect inside the [locales](./theme.md#locales) of the theme configuration**.
It will be used as the language name for the locale, displayed in the _Select Language Menu_.
### selectLanguageText
- Type: `string`
- Default: `''`
- Details:
The text for the _Select Language Menu_.
If you set multiple [locales](./theme.md#locales) in the site configuration,
the _Select Language Menu_ will be displayed next to the repository button in the navigation bar.
### selectLanguageAriaLabel
- Type: `string`
- Default: `''`
- Details:
The `aria-label` attribute for the _Select language menu_.
It is primarily for site accessibility (a11y).
### homeText
- Type: `string`
- Default: `'Home'`
- Details: The text for the home link.
- The text for the home link in the theme's default navigation bar.
- The text for the home link in breadcrumb navigation.
### postsText
- Type: `string`
- Default: `'Posts'`
- Details: The text for the posts list page link.
- The text for the posts list page link in the theme's default navigation bar.
- The text for the posts list page link in breadcrumb navigation.
### tagText
- Type: `string`
- Default: `'Tags'`
- Details: The text for the tags link.
- The text for the tags link in the theme's default navigation bar.
- The text for the tags link on blog pages.
- The title on the blog tags page.
### categoryText
- Type: `string`
- Default: `'Categories'`
- Details: The text for the categories link.
- The text for the categories link in the theme's default navigation bar.
- The text for the categories link on blog pages.
- The title on the blog categories page.
### archiveText
- Type: `string`
- Default: `'Archives'`
- Details: The text for the archives link.
- The text for the archives link in the theme's default navigation bar.
- The text for the archives link on blog pages.
- The title on the blog archives page.
### archiveTotalText
- Type: `string`
- Default: `'{count} articles'`
- Details: The text for the total article count on the archives page.
### sidebarMenuLabel
- Type: `string`
- Default: `'Menu'`
- Details:
The text for the menu option in the navigation bar on mobile devices.
### returnToTopLabel
- Type: `string`
- Default: `'return to top'`
- Details:
The text for returning to the top in the navigation bar on mobile devices.
### outlineLabel
- Type: `string`
- Default: `'On this page'`
- Details:
The text for the outline title in the navigation bar on mobile devices.
### editLinkText
- Type: `string`
- Default: `'Edit this page'`
- Details: The edit link text.
### latestUpdatedText
- Type: `string`
- Default: `'Latest Updated'`
- Details: The text for "Latest Updated".
### contributorsText
- Type: `string`
- Default: `'Contributors'`
- Details: The text for contributors.
### changelogText
- Type: `string`
- Default: `'Changelog'`
- Details: The text for changelog.
### changelogOnText
- Type: `string`
- Default: `'On'`
- Details: The time text for a single changelog entry.
### changelogButtonText
- Type: `string`
- Default: `'View All Changelog'`
- Details: The button text for changelog.
### copyrightText
- Type: `string`
- Default: `'Copyright'`
- Details: The text for copyright.
### copyrightAuthorText
- Type: `string`
- Default: `'Copyright Ownership:'`
- Details: The text for copyright owner.
### copyrightCreationOriginalText
- Type: `string`
- Default: `'This article link:'`
- Details: The text for "This article link".
### copyrightCreationTranslateText
- Type: `string`
- Default: `'This article translated from:'`
- Details: The text for "This article translated from".
### copyrightCreationReprintText
- Type: `string`
- Default: `'This article reprint from:'`
- Details: The text for "This article reprint from".
### copyrightLicenseText
- Type: `string`
- Default: `'License under:'`
- Details: The text for copyright license.
### prevPageLabel
- Type: `string`
- Default: `'Previous Page'`
- Details: The text for previous page.
### nextPageLabel
- Type: `string`
- Default: `'Next Page'`
- Details: The text for next page.
### notFound
- Type: `NotFound | undefined`
- Default: `undefined`
- Details: 404 page configuration.
```ts
interface NotFound {
code?: string
title?: string
quote?: string
linkLabel?: string
linkText?: string
}
```

313
docs/en/config/markdown.md Normal file
View File

@ -0,0 +1,313 @@
---
title: Markdown Configuration
createTime: 2025/10/09 14:54:19
permalink: /en/config/markdown/
---
## Overview
Markdown configuration is used to control the behavior of Markdown.
This configuration aggregates various feature configurations provided by the theme for Markdown enhancements.
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// [!code ++:3]
markdown: {
// Configure markdown here
},
}),
})
```
::: warning
Do not confuse this with VuePress's [markdown](https://v2.vuepress.vuejs.org/reference/config.html#markdown) configuration.
These two are independent. Do not configure the theme's markdown settings
within [VuePress > markdown](https://v2.vuepress.vuejs.org/reference/config.html#markdown), and vice versa.
:::
**Default Configuration:**
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// [!code focus:11]
markdown: {
hint: true,
alert: true,
fileTree: true,
plot: true,
icons: true,
math: { type: 'katex' },
include: {
// ...option
},
},
}),
})
```
## Configuration Options
::: tip
The `hint` and `alert` features are implemented by
the [@vuepress/plugin-markdown-hint](https://ecosystem.vuejs.press/plugins/markdown/markdown-hint.html) plugin.
:::
### hint
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to enable hint containers.
### alert
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to enable GitHub-style alert syntax.
---
::: tip
The `image` configuration is implemented by the
[@vuepress/plugin-markdown-image](https://ecosystem.vuejs.press/plugins/markdown/markdown-image.html) plugin.
:::
### image
- **Type:** `boolean | MarkdownImagePluginOptions`
- **Default:** `false`
- **Details:** Whether to enable image enhancement syntax.
---
::: tip
The `math` configuration is implemented by the
[@vuepress/plugin-markdown-math](https://ecosystem.vuejs.press/plugins/markdown/markdown-math.html) plugin.
:::
### math
- **Type:** `boolean | MarkdownMathPluginOptions`
- **Default:** `{ type: 'katex' }`
- **Details:** Whether to enable math support.
---
### include
::: tip
The `include` configuration is implemented by the
[@vuepress/plugin-markdown-include](https://ecosystem.vuejs.press/plugins/markdown/markdown-include.html) plugin.
:::
- **Type:** `boolean | MarkdownIncludePluginOptions`
- **Default:** `true`
- **Details:** Whether to enable Markdown file inclusion.
---
::: tip The following configuration fields are implemented by the [vuepress-plugin-md-power](./plugins/markdown-power.md) plugin.
:::
### annotation
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable content annotations.
### abbr
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable abbreviation syntax.
### codeTabs
- **Type:** `boolean | CodeTabsOptions`
- **Default:** `true`
- **Details:** Whether to enable code block grouping.
### tabs
- **Type:** `boolean | TabsOptions`
- **Default:** `true`
- **Details:** Whether to enable grouping.
### npmTo
- **Type:** `boolean | NpmToOptions`
- **Default:** `false`
- **Details:** Whether to enable the `npm-to` container.
### icon
- **Type:** `IconOptions`
- **Default:** `{ provider: 'iconify' }`
- **Details:** Icon configuration.
[View **icon** usage instructions](../../theme/guide/features/icon.md){.read-more}
### plot
- **Type:** `boolean | PlotOptions`
- **Default:** `true`
- **Details:** Whether to enable hidden text syntax.
### fileTree
- **Type:** `boolean | FileTreeOptions`
- **Default:** `true`
- **Details:** Whether to enable file tree container syntax.
### field
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable field containers.
### table
- **Type:** `boolean | TableContainerOptions`
- **Default:** `false`
- **Details:** Whether to enable enhanced table containers.
### timeline
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable timeline container syntax.
### collapse
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable collapsible panel container syntax.
### chat
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable chat record containers.
### demo
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Demo containers.
### pdf
- **Type:** `boolean | PdfOptions`
- **Default:** `false`
- **Details:** Whether to enable PDF embedding syntax.
### bilibili
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Bilibili video embedding syntax.
### youtube
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable YouTube video embedding syntax.
### artPlayer
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable ArtPlayer video embedding syntax.
### audioReader
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Audio Reader audio embedding syntax.
### codepen
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable CodePen embedding syntax.
### codeSandbox
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable CodeSandbox embedding syntax.
### jsfiddle
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable JS Fiddle embedding syntax.
### repl
- **Type:** `boolean | ReplOptions`
- **Default:** `false`
- **Details:** Whether to enable Repl container syntax.
### caniuse
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Can I Use embedding syntax.
### imageSize
- **Type:** `boolean | 'local' | 'all'`
- **Default:** `false`
- **Details:** Whether to enable automatic filling of image width and height attributes.
---
::: tip
The following configuration fields are implemented by the
[@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/) plugin.
:::
### chartjs
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Chart.js chart embedding syntax.
### echarts
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable ECharts chart embedding syntax.
### mermaid
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Mermaid diagram embedding syntax.
### markmap
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Markmap diagram embedding syntax.
### plantuml
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable PlantUML diagram embedding syntax.
### flowchart
- **Type:** `boolean`
- **Default:** `false`
- **Details:** Whether to enable Flowchart diagram embedding syntax.

251
docs/en/config/navbar.md Normal file
View File

@ -0,0 +1,251 @@
---
title: Navbar Configuration
createTime: 2025/10/09 15:07:33
permalink: /en/config/navigation/
---
## Overview
::: tip Navbar configuration can be set in either `.vuepress/config.ts` or `plume.config.ts`.
:::
The theme automatically generates the simplest navbar configuration by default, including only the **Home** and **Blog List** pages.
You can also configure the navbar yourself to override the default configuration.
The default configuration is as follows:
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
navbar: [
{ text: 'Home', link: '/' },
{ text: 'Blog', link: '/blog/' },
{ text: 'Tags', link: '/blog/tags/' },
{ text: 'Archives', link: '/blog/archives/' }
]
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
navbar: [
{ text: 'Home', link: '/' },
{ text: 'Blog', link: '/blog/' },
{ text: 'Tags', link: '/blog/tags/' },
{ text: 'Archives', link: '/blog/archives/' }
]
})
```
:::
When multi-language configuration is enabled, the default navbar configuration for the corresponding languages will be generated:
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
lang: 'zh-CN',
locales: {
'/': { lang: 'zh-CN', title: '博客' }, // Simplified Chinese
'/en/': { lang: 'en-US', title: 'Blog' }, // English
},
theme: plumeTheme({
locales: {
'/': {
navbar: [
{ text: '首页', link: '/' },
{ text: '博客', link: '/blog/' },
{ text: '标签', link: '/blog/tags/' },
{ text: '归档', link: '/blog/archives/' }
]
},
'/en/': {
navbar: [
{ text: 'Home', link: '/en/' },
{ text: 'Blog', link: '/en/blog/' },
{ text: 'Tags', link: '/en/blog/tags/' },
{ text: 'Archives', link: '/en/blog/archives/' }
]
}
}
})
})
```
As your site content becomes richer, for example, when configuring collections for blog, notes, documentation,
friend links, external links, etc., the default generated navbar configuration may no longer meet your needs.
At this point, you can fully customize the navbar using the `navbar` field, which will directly override the default navbar configuration.
## Configuration
Type: `NavItem[]`
```ts
interface ThemeBadge {
/* Badge text */
text?: string
/* Badge type, built-in: 'info' | 'tip' | 'danger' | 'warning' */
type?: string
/* Text color */
color?: string
/* Background color */
bgColor?: string
/* Border color */
borderColor?: string
}
type NavItem = string | {
/**
* Navbar text
*/
text: string
/**
* Navbar link
* - Can be an external link
* - Can be a permalink from frontmatter
* - Can be a relative path to a markdown file (relative to `sourceDir`), must start with `/`
*/
link: string
/**
* Icon name, or SVG icon
*/
icon?: string | { svg: string }
/**
* Badge, supports custom badge styles
*/
badge?: string | ThemeBadge
/**
* Controls when the element is activated
*/
activeMatch?: string
/**
* Prefix for the current group of page links
*/
prefix?: string
/**
* Nested navbar items, maximum depth is 2
*/
items?: NavItem[]
}
```
### Nested Configuration Example
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
navbar: [
{ text: 'Home', link: '/', icon: 'material-symbols:home-outline' },
{ text: 'Blog', link: '/blog/', icon: 'material-symbols:article-outline' },
{
text: 'Technical Docs',
icon: 'mdi:idea',
items: [
{
text: 'Vuepress Theme',
icon: 'icon-park-solid:theme',
items: [
{
text: 'vuepress-theme-plume',
link: '/vuepress-theme-plume/',
icon: 'mdi:paper-airplane',
badge: 'Badge'
},
],
},
{
text: 'Vuepress Plugin',
icon: 'mingcute:plugin-2-fill',
badge: { text: 'Badge', type: 'warning' },
items: [
{
text: 'caniuse',
link: '/vuepress-plugin/caniuse/',
icon: 'pajamas:feature-flag',
},
{
text: 'auto-frontmatter',
link: '/vuepress-plugin/auto-frontmatter/',
icon: 'material-symbols:move-selection-down-rounded',
},
{
text: 'blog-data',
link: '/vuepress-plugin/blog-data/',
icon: 'ic:baseline-post-add',
},
{
text: 'notes-data',
link: '/vuepress-plugin/notes-data/',
icon: 'material-symbols:note-alt-rounded',
},
{
text: 'shiki',
link: '/vuepress-plugin/shiki/',
icon: 'material-symbols-light:code-blocks-outline-rounded',
},
],
},
],
},
]
})
})
```
### `activeMatch`
`activeMatch` is used to control when the current link element is activated. Its value is a regex-like string.
**Example:** `activeMatch: '^/(blog|article)/'`
This means that for page links starting with `/blog/` or `/article/`, this link element will be activated.
### Icons
Supports icons from sources configured via [markdown -> icon](../guide/features/icon.md).
- When `markdown.icon.provide` is `iconify`, [iconify](https://iconify.design/) icons are supported
- When `markdown.icon.provide` is `iconfont`, [iconfont](https://www.iconfont.cn/) icons are supported
- When `markdown.icon.provide` is `fontawesome`, [fontawesome](https://fontawesome.com/) icons are supported
When `markdown.icon.provide` is not `iconify`, you can prefix the icon name with `iconify` to force the
use of [iconify](https://iconify.design/) icons.
```ts
const item = { text: 'Home', link: '/', icon: 'iconify carbon:home' }
```
## Configuration Helper Function
The theme provides the `defineNavbarConfig(config)` function to offer type assistance for the navbar
configuration to theme users. This facilitates separating the `navbar` configuration into an independent configuration file.
```ts title="navbar.ts" twoslash
import { defineNavbarConfig } from 'vuepress-theme-plume'
export default defineNavbarConfig([
{ text: 'Home', link: '/' },
{ text: 'Blog', link: '/blog/' },
// ... more
])
```

View File

@ -1,11 +0,0 @@
---
title: Notes Configuration
createTime: 2025/03/03 14:44:18
permalink: /en/config/notes/
---
## Configuration
### Auto Generated Sidebar
todo ...

View File

@ -0,0 +1,54 @@
---
title: Introduction
createTime: 2025/10/09 8:26:44
permalink: /en/config/plugins/
---
The theme comes with built-in plugins that extend its numerous functionalities.
You can configure these internal plugins through the `plugins` option.
## Configuration
All plugins used internally by the theme are configured within the `plugins` field.
``` js title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
plugins: {
// more options... // [!code ++]
}
}),
})
```
## Plugin List
- [@vuepress/plugin-nprogress](https://ecosystem.vuejs.press/zh/plugins/features/nprogress.html) - Page loading progress bar
- [@vuepress/plugin-photo-swipe](https://ecosystem.vuejs.press/zh/plugins/features/photo-swipe.html) - Image preview
- [@vuepress/plugin-reading-time](https://ecosystem.vuejs.press/zh/plugins/development/reading-time.html) - Article reading time
- [@vuepress/plugin-watermark](https://ecosystem.vuejs.press/zh/plugins/features/watermark.html) - Article watermark
- [@vuepress-plume/plugin-search](./search.md) - Local search
- [@vuepress/plugin-docsearch](https://ecosystem.vuejs.press/zh/plugins/search/docsearch.html) - Algolia documentation search
- [@vuepress/plugin-copy-code](https://ecosystem.vuejs.press/zh/plugins/features/copy-code.html) - Code copy
- [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/markdown/shiki.html) - Syntax highlighting
- [@vuepress/plugin-comment](https://ecosystem.vuejs.press/zh/plugins/blog/comment/) - Article comments
- [@vuepress/plugin-markdown-hint](https://ecosystem.vuejs.press/zh/plugins/markdown/hint.html) - Markdown hints
- [@vuepress/plugin-markdown-image](https://ecosystem.vuejs.press/zh/plugins/markdown/image.html) - Markdown images
- [@vuepress/plugin-markdown-math](https://ecosystem.vuejs.press/zh/plugins/markdown/math.html) - Markdown mathematical formulas
- [@vuepress/plugin-markdown-include](https://ecosystem.vuejs.press/zh/plugins/markdown/include.html) - Markdown file inclusion
- [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/zh/plugins/markdown/markdown-chart/) - Markdown charts
chartjs / echarts / mermaid / flowchart / markmap / plantUML
- [@vuepress/plugin-replace-assets](https://ecosystem.vuejs.press/zh/plugins/tools/replace-assets.html) - Asset link replacement
- [vuepress-plugin-md-power](./markdown-power.md) - Markdown Power
- [@vuepress/plugin-git](https://ecosystem.vuejs.press/zh/plugins/development/git.html) - Git commit information
- [@vuepress/plugin-cache](https://ecosystem.vuejs.press/zh/plugins/tools/cache.html) - Page compilation cache
- [@vuepress/plugin-seo](https://ecosystem.vuejs.press/zh/plugins/seo/seo/) - SEO optimization
- [@vuepress/plugin-sitemap](https://ecosystem.vuejs.press/zh/plugins/seo/sitemap/) - Sitemap
:::tip
You don't need to install these built-in plugins separately, nor should you add them to [vuepress config > plugins](https://v2.vuepress.vuejs.org/zh/reference/config.html#plugins).
The theme has already handled their integration internally.
:::

View File

@ -0,0 +1,45 @@
---
title: Markdown Enhance
createTime: 2025/10/09 20:25:36
permalink: /en/config/plugins/markdown-enhance/
badge:
type: danger
text: Deprecated
---
::: danger Removed from theme starting from `1.0.0-rc.154`
The functionality provided by [vuepress-plugin-md-enhance](https://plugin-md-enhance.vuejs.press/) that
was explicitly supported in the theme has been migrated to other plugins in the
`vuepress/ecosystem` repository. Therefore, the theme will safely remove the `vuepress-plugin-md-enhance` plugin starting from `1.0.0-rc.154`.
:::
## Overview
Provides Markdown enhancement features.
Related plugin: [vuepress-plugin-md-enhance](https://plugin-md-enhance.vuejs.press/)
Default configuration:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: {
// The following options are not enabled by default in the theme,
// Please configure them explicitly in the theme if needed
// stylize: true, // Style inline syntax to create code snippets
// playground: true, // Interactive playgrounds
// kotlinPlayground: true, // Kotlin interactive playgrounds
// vuePlayground: true, // Vue interactive playgrounds
// sandpack: true, // Sandpack interactive playgrounds
},
}),
})
```
## Configuration
For detailed configuration, please refer to [vuepress-plugin-md-enhance](https://plugin-md-enhance.vuejs.press/config.html).

View File

@ -0,0 +1,44 @@
---
title: Markdown Image
createTime: 2025/10/09 12:06:28
permalink: /en/config/plugins/markdown-image/
---
## Overview
Adds additional features for Markdown images.
Related plugin: [@vuepress/plugin-markdown-image](https://ecosystem.vuejs.press/plugins/markdown/markdown-image.html)
## Configuration
The plugin does not enable any features by default; you need to enable them manually.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: {
image: {
// Enable figure
// figure: true,
// Enable image lazy loading
// lazyload: true,
// Enable image marking
// mark: true,
// Enable image dimensions
// size: true,
}
},
// Can also be configured in `plugins.markdownImage`, but not recommended
plugins: {
markdownImage: {}
}
}),
})
```

View File

@ -0,0 +1,60 @@
---
title: Markdown Include
createTime: 2025/10/09 12:01:58
permalink: /en/config/plugins/markdown-include/
---
## Overview
Import content from other markdown files within Markdown files.
Related plugin: [@vuepress/plugin-markdown-include](https://ecosystem.vuejs.press/plugins/markdown/markdown-include.html)
## Configuration
The theme enables `markdownInclude` by default. You can further customize its behavior through configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
include: {
// ... options
}
},
// Can also be configured in `plugins.markdownInclude`, but not recommended
plugins: {
markdownInclude: {},
}
})
})
```
### resolvePath
- Type: `(path: string, cwd: string | null) => string`
- Default: `(path) => path`
- Details: Processes the include file path.
### deep
- Type: `boolean`
- Details: Whether to enable deep include support.
### useComment
- Type: `boolean`
- Default: `true`
- Details: Whether to use `<!-- @include: xxx -->` instead of `@include: xxx` for file inclusion.
### resolveImagePath
- Type: `boolean`
- Default: `true`
- Details: Whether to resolve relative image paths within the included Markdown files.
### resolveLinkPath
- Type: `boolean`
- Default: `true`
- Details: Whether to resolve relative file paths within the included Markdown files.

View File

@ -0,0 +1,36 @@
---
title: Markdown Math
createTime: 2025/10/09 13:20:41
permalink: /en/config/plugins/markdown-math/
---
## Overview
Adds mathematical formula support for Markdown.
Related plugin: [@vuepress/plugin-markdown-math](https://ecosystem.vuejs.press/plugins/markdown/markdown-math.html)
This plugin allows you to render $\TeX$ content in Markdown using MathJax or KaTeX.
## Configuration
The plugin enables `katex` by default.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: {
math: {
type: 'katex',
}
},
// Can also be configured in `plugins.markdownMath`, but not recommended
plugins: {
markdownMath: {}
}
}),
})
```

View File

@ -0,0 +1,272 @@
---
title: Markdown Power
createTime: 2025/10/09 06:56:33
permalink: /en/config/plugins/markdown-power/
---
## Overview
Provides Markdown enhancement features for the theme.
Related plugin: [vuepress-plugin-md-power](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-md-power)
## Configuration
Default configuration:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: {
fileTree: true, // :::file-tree File tree container
plot: true, // !!plot!! Hidden text
icons: true, // ::collect:name:: Inline iconify icons
// The following features are not enabled by default; you need to enable them manually
// npmTo: true, // :::npm-to
// demo: true, // :::demo
// pdf: true, // @[pdf](url) Embed PDF files
// bilibili: true, // @[bilibili](bvid) Embed bilibili videos
// acfun: true, // @[acfun](id) Embed AcFun videos
// youtube: true, // @[youtube](id) Embed YouTube videos
// codepen: true, // @[codepen](user/slash) Embed CodePen
// replit: true, // @[replit](user/repl-name) Embed Replit
// codeSandbox: true, // @[codesandbox](id) Embed CodeSandbox
// jsfiddle: true, // @[jsfiddle](id) Embed JSFiddle
// caniuse: true, // @[caniuse](feature) Embed Can I Use
// repl: true, // :::go-repl :::kotlin-repl :::rust-repl
// imageSize: true, // Add width/height attributes to images during build
},
// Can also be configured in `plugins.markdownPower`, but not recommended
plugins: {
markdownPower: {}
}
}),
})
```
## Features
### Embed PDF
This feature is not enabled by default; you need to manually set `pdf` to `true`.
__Syntax:__
```md
@[pdf](url)
```
Please refer to the [Complete Usage Documentation](../../guide/embed/pdf.md)
### Iconify Icons
This feature is not enabled by default; you need to manually set `icons` to `true`.
Thanks to [iconify](https://iconify.design/), you can use __200k+__ icons from Iconify in Markdown.
__Syntax:__
```md
::collect:name::
```
Please refer to the [Complete Usage Documentation](../../guide/markdown/icons.md)
### Bilibili Video
This feature is not enabled by default; you need to manually set `bilibili` to `true`.
__Syntax:__
```md
@[bilibili](bvid)
```
Please refer to the [Complete Usage Documentation](../../guide/embed/bilibili.md)
### AcFun Video
This feature is not enabled by default; you need to manually set `acfun` to `true`.
__Syntax:__
```md
@[acfun](id)
```
Please refer to the [Complete Usage Documentation](../../guide/embed/video/acfun.md)
### YouTube Video
This feature is not enabled by default; you need to manually set `youtube` to `true`.
__Syntax:__
```md
@[youtube](id)
```
Please refer to the [Complete Usage Documentation](../../guide/embed/youtube.md)
### CodePen Demo
This feature is not enabled by default; you need to manually set `codepen` to `true`.
__Syntax:__
```md
@[codepen](user/slash)
```
Please refer to the [Complete Usage Documentation](../../guide/repl/codepen.md)
### CodeSandbox Demo
This feature is not enabled by default; you need to manually set `codeSandbox` to `true`.
__Syntax:__
```md
@[codesandbox](id)
```
Please refer to the [Complete Usage Documentation](../../guide/repl/codeSandbox.md)
### JSFiddle Demo
This feature is not enabled by default; you need to manually set `jsfiddle` to `true`.
__Syntax:__
```md
@[jsfiddle](id)
```
Please refer to the [Complete Usage Documentation](../../guide/repl/jsFiddle.md)
### Can I Use Browser Support
This feature is not enabled by default; you need to manually set `caniuse` to `true`.
__Syntax:__
```md
@[caniuse](feature)
```
Please refer to the [Complete Usage Documentation](../../guide/markdown/caniuse.md)
### Repl Code Demo Container
This feature is not enabled by default; you need to manually set `repl` to `true`.
Supports online execution of Rust, Golang, and Kotlin code, as well as online editing.
Alternatively, you can enable specific features, as shown below:
``` ts
export default defineUserConfig({
theme: plumeTheme({
plugins: {
markdownPower: {
repl: {
rust: true,
go: true,
kotlin: true,
},
},
}
})
})
```
__Syntax:__
````md
::: rust-repl
```rust
// rust code
```
:::
::: go-repl
```go
// go code
```
:::
::: kotlin-repl
```kotlin
// kotlin code
```
:::
::: python-repl
```python
// python code
```
:::
````
Please refer to the complete usage documentation:
- [Code Demo > Rust](../../guide/repl/rust.md)
- [Code Demo > Golang](../../guide/repl/golang.md)
- [Code Demo > Kotlin](../../guide/repl/kotlin.md)
- [Code Demo > Python](../../guide/repl/python.md)
### Plot Hidden Text
This feature is not enabled by default; you need to manually set `plot` to `true`.
__Syntax:__
```md
!!content!!
```
Please refer to the [Complete Usage Documentation](../../guide/markdown/plot.md)
### File Tree
This feature is not enabled by default; you need to manually set `fileTree` to `true`.
__Syntax:__
```md
::: file-tree
- folder1
- file1.md
- file2.ts
- folder2
- file3.md
- folder3
:::
```
Please refer to the [Complete Usage Documentation](../../guide/markdown/file-tree.md)
### Image Dimensions
This feature adds `width` and `height` attributes to image references in markdown files.
It reads the original dimensions of the images to set default image sizes and aspect ratios.
This resolves layout flickering issues that occur between when an image starts loading and when it completes.
This feature is not enabled by default; you need to manually configure `imageSize`:
- If `imageSize` is `true`, the plugin only processes local images, equivalent to the `local` option;
- If `imageSize` is `'local'`, the plugin only processes local images;
- If `imageSize` is `'all'`, the plugin processes both local and remote images.
::: important
__This feature only takes effect during production builds.__
Use the `'all'` option with caution, as it will request remote image resources during production builds,
which can significantly increase build time.
Although the theme optimizes this by loading only __a few KB__ of data from each image to analyze dimensions, it will still impact build performance.
:::

View File

@ -0,0 +1,85 @@
---
title: Reading Statistics
createTime: 2025/10/09 15:23:39
permalink: /en/config/plugins/reading-time/
---
## Overview
Generates word count and estimated reading time for each page.
Related plugin: [@vuepress/plugin-reading-time](https://ecosystem.vuejs.press/plugins/search/docsearch.html)
Default configuration:
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
readingTime: {
wordPerMinute: 300
},
// Can also be configured via plugins.readingTime, but not recommended
plugins: {
readingTime: {}
}
}),
})
```
## Configuration
### wordPerMinute
- Type: `number`
- Default: `300`
Words read per minute.
### locales
- Type: `ReadingTimePluginLocaleConfig`
Internationalization configuration for the reading time plugin.
```ts
interface ReadingTimePluginLocaleData {
/**
* Word count template, where `$word` will be automatically replaced with the actual word count
*/
word: string
/**
* Text for less than one minute
*/
less1Minute: string
/**
* Time template
*/
time: string
}
interface ReadingTimePluginLocaleConfig {
[localePath: string]: ReadingTimePluginLocaleData
}
```
## Disabling
You can disable this feature by setting `readingTime` to `false`.
When disabled, article pages will not display word count and estimated reading time.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
readingTime: false
}),
})
```

View File

@ -0,0 +1,103 @@
---
title: Content Search
createTime: 2025/10/09 09:19:26
permalink: /en/config/plugins/search/
---
## Local Search
### Overview
Adds local search functionality to the site.
Related plugin: [@vuepress-plume/plugin-search](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-search)
This plugin uses [minisearch](https://github.com/lucaong/minisearch) for content search.
Default configuration:
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
search: {
provider: 'local', // [!code hl]
// more options...
},
// Can also be configured via plugins.search, but not recommended
plugins: {
search: {},
}
})
})
```
### Configuration
```ts
interface SearchOptions {
/**
* Local search internationalization
*/
locales?: {
[locale: string]: SearchBoxLocale
}
/**
* Whether articles are searchable, defaults to `() => true`
*/
isSearchable?: (page: Page) => boolean
}
interface SearchBoxLocale {
placeholder: string
buttonText: string
resetButtonTitle: string
backButtonTitle: string
noResultsText: string
footer: {
selectText: string
selectKeyAriaLabel: string
navigateText: string
navigateUpKeyAriaLabel: string
navigateDownKeyAriaLabel: string
closeText: string
closeKeyAriaLabel: string
}
}
```
## Algolia DocSearch
### Overview
A site content search plugin powered by [Algolia DocSearch](https://docsearch.algolia.com/)
Related plugin: [@vuepress/plugin-docsearch](https://ecosystem.vuejs.press/zh/plugins/search/docsearch.html)
Refer to [Algolia DocSearch Reference](/guide/features/content-search/#algolia-docsearch) for more information.
### Enable
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
search: {
provider: 'algolia', // [!code hl]
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_API_KEY',
indexName: 'YOUR_INDEX_NAME',
// more options
},
// Can also be configured via plugins.docsearch, but not recommended
plugins: {
docsearch: {},
}
})
})
```

View File

@ -0,0 +1,125 @@
---
title: Code Highlighting
createTime: 2025/10/09 10:21:47
permalink: /en/config/plugins/code-highlight/
---
## Overview
The theme's built-in code highlighting plugin provides syntax highlighting for code blocks.
Related plugin: [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/markdown/shiki.html)
The theme uses [Shiki](https://github.com/shikijs/shiki) to implement syntax highlighting with colored
text in Markdown code blocks. Shiki supports multiple programming languages.
You can find the [list of supported languages](https://shiki.style/languages) in Shiki's repository.
## Features
- [Code Block Titles](../../guide/code/features.md#code-block-titles)
- [Line Highlighting](../../guide/code/features.md#line-highlighting-in-code-blocks)
- [Code Focus](../../guide/code/features.md#focus-in-code-blocks)
- [Code Diff](../../guide/code/features.md#color-differences-in-code-blocks)
- [Error and Warning Highlighting](../../guide/code/features.md#highlighting-errors-and-warnings)
- [Word Highlighting](../../guide/code/features.md#word-highlighting-in-code-blocks)
- [Code Block Folding](../../guide/code/features.md#collapsing-code-blocks)
- [twoslash](../../guide/code/twoslash.md#twoslash): Provides inline type hints within code blocks.
## Configuration
Default configuration:
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
themes: { light: 'vitesse-light', dark: 'vitesse-dark' },
notationDiff: true,
notationErrorLevel: true,
notationFocus: true,
notationHighlight: true,
notationWordHighlight: true,
highlightLines: true,
collapsedLines: false,
lineNumbers: true,
},
// Can also be configured via plugins.shiki, but not recommended
plugins: {
shiki: {}
}
}),
})
```
### themes
- Type: `{ light: string, dark: string }`
- Default: `{ light: 'vitesse-light', dark: 'vitesse-dark' }`
Code highlighting themes supporting light/dark dual themes.
You can choose your preferred themes from the [list of supported themes](https://shiki.style/themes).
### langs
- Type: `string[]`
- Default: `[]`
Programming languages to be highlighted, such as `javascript`, `typescript`, `python`, `java`, `c++`, `c#`, etc.
By default, languages are automatically detected based on code block language identifiers.
You can find the [list of supported languages](https://shiki.style/languages) in Shiki's repository.
### defaultLang
- Type: `string`
- Default: `text`
Fallback language to use when the specified language is not available.
### lineNumbers
- Type: `boolean | number`
- Default: `true`
Whether to display line numbers.
`true`: Display line numbers\
`false`: Hide line numbers\
`number`: Minimum number of lines required to display line numbers.
### twoslash
- Type: `boolean | ShikiTwoslashOptions`
- Default: `false`
Whether to enable type hint support for `typescript` and `vue` languages.
### whitespace
- Type: `boolean | 'all' | 'boundary' | 'trailing'`
- Default: `false`
Render whitespace characters (tabs and spaces) as separate spans (with `tab` or `space` class names).
Effect:
<!-- @include: ../../snippet/whitespace.snippet.md{17-23} -->
### collapsedLines
- Type: `boolean | number`
- Default: `false`
Collapse code blocks to the specified number of lines.
### transformers
- Type: `ShikiTransformer[]`
- Default: `[]`
Code transformers. Refer to [shiki transformers](https://shiki.style/guide/transformers) for more information.

View File

@ -0,0 +1,100 @@
---
title: Watermark
createTime: 2025/10/09 15:37:18
permalink: /en/config/watermark/
---
## Overview
The theme's built-in watermark plugin adds watermarks to the entire site or individual pages.
Related plugin: [@vuepress/plugin-watermark](https://ecosystem.vuejs.press/zh/plugins/features/watermark.html)
## Usage
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// watermark: true,
watermark: {
// enabled: false, // boolean type controls global enablement
enabled: page => true, // function type filters which pages enable watermark
/**
* Whether to enable full-page watermark, defaults to `true`,
* When set to `false`, watermark is only displayed in the content area.
*/
fullPage: true,
/** @see https://zhensherlock.github.io/watermark-js-plus/zh/config/ */
watermarkOptions: {
content: 'your watermark',
// ...
}
},
// Can also be configured via plugins.watermark, but not recommended
plugins: {
watermark: {}
}
})
})
```
## Configuration Options
### enabled
- Type: `boolean | ((page: Page) => boolean)`
- Default: `false`
- Details:
Specifies which pages should have watermarks added.
Pages with a `true` value will have watermarks applied.
### watermarkOptions
- Type: `WatermarkOptions`
- Default: `undefined`
- Details: Configuration options refer to [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/zh/config/).
#### watermarkOptions.parent
- Type: `string`
- Default: `body`
- Details: Parent element selector for adding watermarks.
By default inserted into the body, can be specified to insert into a specific element on the page.
## Frontmatter
### watermark
- Type: `boolean | WatermarkOptions`
- Details:
When type is `boolean`, indicates whether to enable watermark.
When type is `WatermarkOptions`, indicates current page watermark configuration.
Refer to [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/zh/config/) for configuration options.
```md
---
watermark:
width: 200
height: 200
content: Your content
opacity: 0.5
---
```

123
docs/en/config/sidebar.md Normal file
View File

@ -0,0 +1,123 @@
---
title: Sidebar Configuration
createTime: 2025/10/09 21:05:36
permalink: /en/config/sidebar/
---
## Overview
The sidebar is a core navigation area located on the left side of the page in the theme,
serving the important function of guiding users to jump between different pages.
In the VuePress ecosystem, the default theme `@vuepress/theme-default` manages the sidebar through
the `sidebar` configuration option. While retaining this classic configuration method,
this theme also provides a more flexible collection-level sidebar configuration solution.
## Collection-Level Sidebar Configuration
Collections are the core concept for organizing series of documents in the theme.
When a collection type is set to `doc`, you can define a dedicated sidebar navigation within `collection.sidebar`.
The following example demonstrates how to create a collection of type `doc` under the `docs` directory and configure its sidebar:
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { defineCollection, plumeTheme } from 'vuepress-theme-plume'
// Define document collection configuration // [!code hl:10]
const demo = defineCollection({
type: 'doc',
dir: 'demo', // Document directory
title: 'Demo', // Collection name
sidebar: [ // Sidebar configuration // [!code ++:4]
{ text: 'one item', link: 'one' },
{ text: 'two item', link: 'two' },
]
})
export default defineUserConfig({
theme: plumeTheme({
collections: [demo], // Register collection // [!code hl]
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineCollection, defineThemeConfig } from 'vuepress-theme-plume'
// Define collection using independent config file // [!code hl:10]
const demo = defineCollection({
type: 'doc',
dir: 'demo',
title: 'Demo',
sidebar: [
{ text: 'one item', link: 'one' },
{ text: 'two item', link: 'two' },
]
})
export default defineThemeConfig({
collections: [demo], // [!code hl]
})
```
:::
The `defineCollection` utility function provided by the theme simplifies the collection configuration
process. For complete collection configuration options, please refer to the [Collections Configuration Documentation](./collections.md).
## Global Sidebar Configuration
If you prefer to manage the sidebar using the traditional global configuration approach,
you can directly use the `sidebar` option in the theme configuration.
This method is suitable for scenarios that do not require navigation grouping by collections.
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Global sidebar configuration // [!code hl:7]
sidebar: {
'/config/': [ // Matches /config/ path
{ text: 'Sidebar Configuration', link: 'sidebar-1' },
{ text: 'Sidebar Configuration', link: 'sidebar-2' },
]
}
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
// Define global sidebar in independent config file // [!code hl:7]
sidebar: {
'/config/': [
{ text: 'Sidebar Configuration', link: 'sidebar-1' },
{ text: 'Sidebar Configuration', link: 'sidebar-2' },
]
}
})
```
:::
Both configuration methods have their advantages: collection-level configuration is suitable for
modular document structures, while global configuration is convenient for unified navigation management in simple projects.
For complete sidebar configuration options and usage tips, please refer to the [Sidebar Configuration Guide](../guide/quick-start/sidebar.md).

748
docs/en/config/theme.md Normal file
View File

@ -0,0 +1,748 @@
---
title: Theme Configuration
createTime: 2025/10/09 18:07:03
permalink: /en/config/theme/
---
## Overview
Theme configuration is used to customize various features of the theme and control its behavior.
You can configure it in either `.vuepress/config.ts` or `.vuepress/plume.config.ts`.
When a field description includes the following statement, it indicates that the field is not supported for configuration in `.vuepress/plume.config.ts`:
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
For fields without the above declaration, you can configure them in either `.vuepress/config.ts`
or `.vuepress/plume.config.ts`. It is generally recommended to configure them in `.vuepress/plume.config.ts`.
::: warning Avoid reconfiguring a field that has already been configured in one configuration file within another configuration file.
:::
::: code-tabs#configs
@tab .vuepress/config.ts
```ts twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Theme configuration
})
})
```
@tab .vuepress/plume.config.ts
```ts twoslash
import { defineThemeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
// Theme configuration
})
```
:::
## Basic Configuration
### configFile
- **Type:** `string`
- **Default:** `''`
- **Details:**
Custom path to the theme configuration file.
Refer to [Theme Config File `plume.config.js`](./intro.md#theme-config-file) for more information.
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### plugins
- **Type:** `PlumeThemePluginOptions`
- **Default:** `{}`
- **Details:**
Custom configuration for plugins used internally by the theme.
The plugins used by the theme are configured by default. In most cases, modification is not required.
For detailed customization, please refer to [this documentation](./plugins/README.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### markdown <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `MarkdownOptions`
- **Default:** `{}`
- **Details:**
Markdown feature configuration. Refer to [this documentation](./markdown.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### codeHighlighter <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | ShikiPluginOptions`
- **Default:** `{}`
- **Details:**
Code highlighting configuration. Refer to [this documentation](../guide/code/intro.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### search <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | SearchOptions`
- **Default:** `{ provider: 'local' }`
- **Details:**
Search configuration. Refer to [this documentation](../guide/features/search.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### comment <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | CommentPluginOptions`
- **Default:** `false`
- **Details:**
Comment configuration. Refer to [this documentation](../guide/features/comments.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### watermark <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | WatermarkPluginOptions`
- **Default:** `false`
- **Details:**
Watermark configuration. Refer to [this documentation](../guide/features/watermark.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### readingTime <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | ReadingTimePluginOptions`
- **Default:** `false`
- **Details:**
Reading time configuration. Refer to [this documentation](./plugins/reading-time.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### copyCode <Badge type="tip" text="1.0.0-rc.136 +" />
- **Type:** `false | CopyCodePluginOptions`
- **Default:** `{}`
- **Details:**
Copy code configuration. Refer to [this documentation](../guide/code/copy-code.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### replaceAssets <Badge type="tip" text="1.0.0-rc.139 +" />
- **Type:** `false | ReplaceAssetsPluginOptions`
- **Default:** `false`
- **Details:**
Replace assets configuration. Refer to [this documentation](../guide/features/replace-assets.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### hostname
- **Type:** `string`
- **Default:** `''`
- **Details:**
Deployment site domain name.
When `hostname` is configured to a valid domain, the theme will generate `sitemap` and SEO-related content.
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### autoFrontmatter
- **Type:** `false | AutoFrontmatterOptions`
- **Details:**
Whether to automatically add frontmatter configuration to markdown files.
```ts
interface AutoFrontmatterOptions {
/**
* Whether to automatically generate permalink
*
* @default true
*/
permalink?: boolean
/**
* Whether to automatically generate createTime
*
* Reads file creation time by default. `createTime` is more precise (to the second) than the default VuePress `date` field.
*/
createTime?: boolean
/**
* Whether to automatically generate title
*
* Uses the filename as the title by default.
*/
title?: boolean
}
```
### cache
- **Type:** `false | 'memory' | 'filesystem'`
- **Default:** `filesystem`
- **Details:**
Whether to enable compilation cache, or configure the caching method.
This configuration item addresses the slow startup speed of VuePress.
It caches the compilation results during the first service start.
On subsequent starts, it reads the cache directly, skipping compilation to speed up startup.
- `false`: Disable cache.
- `'memory'`: Use memory cache. This method provides faster startup speed but increases memory usage
as the number of project files grows. Suitable for projects with fewer articles.
- `'filesystem'`: Use filesystem cache. This method provides a relatively fast and stable startup speed,
more suitable for projects with substantial content.
::: warning
This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
For the cache to take effect, you should **remove** the `--clean-cache` parameter from the
`vuepress dev` development server startup script in your `package.json`.
:::
### docsRepo
- **Type:** `string`
- **Default:** `''`
- **Details:** Documentation repository configuration, used to generate the `Edit this page` link.
### docsBranch
- **Type:** `string`
- **Default:** `''`
- **Details:** Documentation repository branch configuration, used to generate the `Edit this page` link.
### docsDir
- **Type:** `string`
- **Default:** `''`
- **Details:** Documentation repository directory configuration, used to generate the `Edit this page` link.
### editLink
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to enable the edit link.
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### lastUpdated
- **Type:** `false | LastUpdatedOptions`
- **Default:** `{ formatOptions: { dateStyle: 'short', timeStyle: 'short' } }`
- **Details:** Last updated time.
```ts
interface LastUpdatedOptions {
/**
* Options to set the format of the last updated time.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat#using_options
*
* @default
* { dateStyle: 'short', timeStyle: 'short' }
*/
formatOptions?: Intl.DateTimeFormatOptions & { forceLocale?: boolean }
}
```
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### contributors
- **Type:** `boolean | ContributorsOptions`
- **Default:** `true`
- **Details:** Whether to display contributors.
For more configuration, please refer to [this documentation](../guide/features/contributors.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### changelog
- **Type:** `boolean | ChangelogOptions`
- **Default:** `false`
- **Details:** Whether to display page change history.
For more configuration, please refer to [this documentation](../guide/features/changelog.md).
::: warning This field is not supported for configuration in the [Theme Config File `plume.config.js`](./intro.md#theme-config-file).
:::
### locales
- **Type:** `Record<string, ThemeLocaleData>`
- **Default:** `{}`
- **Details:** Multi-language configuration.
Text configuration for different languages. Refer to [this documentation](./locales.md).
The multi-language configuration supports all configuration options under the following
[Locale](#locale-configuration) to control theme behavior in different languages.
## Locale Configuration
::: tip All the following fields are also basic configuration fields and can be configured at the same level as fields like `locales`.
:::
### home
- **Type:** `false | string`
- **Default:** `/`
- **Details:**
The path to the home page. It will be used for:
- The logo link in the navbar;
- The *Back to Home* link on the 404 page;
### logo
- **Type:** `false | string`
- **Default:** `false`
- **Details:** Logo in the navbar.
### logoDark
- **Type:** `false | string`
- **Default:** `false`
- **Details:** Logo in the navbar for Dark mode.
### appearance
- **Type:** `boolean | 'dark' | 'force-dark'`
- **Default:** `true`
Whether to enable dark mode.
- If this option is set to `true`, the default theme will be determined by the user's preferred color scheme.
- If this option is set to `dark`, the theme will be dark by default unless the user manually toggles it.
- If this option is set to `false`, the user will not be able to switch themes.
- If this option is set to `force-dark`, the user will not be able to switch themes, and the theme will be forced to dark.
This option injects an inline script that restores user settings from local storage.
This ensures `[data-theme="dark"]` is applied before the page is rendered to avoid flashing.
### profile
- **Type:** `ProfileOptions`
- **Default:** `{}`
- **Details:** Configure the site owner's personal information.
- `profile.avatar`: Avatar URL, used for displaying blogger information on the right.
- `profile.name`: Name, used for displaying blogger information on the right.
- `profile.description`: Personal description, used for displaying blogger information on the right.
- `profile.circle`: Whether the avatar is circular.
- `profile.location`: User's geographical location.
- `profile.organization`: User's organization/company.
- `profile.layout`: Whether to display personal information on the left or right, `'left' | 'right'`.
Example:
``` ts
export default defineUserConfig({
theme: plumeTheme({
profile: {
avatar: '/avatar.jpg',
name: 'Zhang San',
description: 'Here is no silver three hundred two, next door Wang Er never stole',
circle: true,
location: 'Hangzhou, China',
organization: 'XXX Company',
layout: 'right',
}
})
})
```
### social
- **Type:** `false | SocialLink[]`
- **Default:** `false`
- **Details:** Personal social information configuration.
Displayed as icon links on the far right of the navbar.
Available icon options:
- `'github'`
- `'gitlab'`
- `'npm'`
- `'docker'`
- `'discord'`
- `'telegram'`
- `'facebook'`
- `'instagram'`
- `'linkedin'`
- `'mastodon'`
- `'slack'`
- `'twitter'`
- `'x'`
- `'youtube'`
- `'juejin'`
- `'stackoverflow'`
- `'qq'`
- `'weibo'`
- `'bilibili'`
- `'zhihu'`
- `'douban'`
- `'steam'`
- `'xbox'`
- `{ svg: string, name?: string }`: Custom icon, pass the SVG source string. The optional `name` field is used to configure [`navbarSocialInclude`](#navbarsocialinclude).
Example:
``` ts
export default defineUserConfig({
theme: plumeTheme({
social: [
{ icon: 'github', link: 'https://github.com/zhangsan' },
{
icon: { svg: '<svg>xxxxx</svg>', name: 'xxx' },
link: 'https://xxx.com'
},
]
})
})
```
### navbarSocialInclude
- **Type:** `string[]`
- **Default:** `['github', 'twitter', 'discord', 'facebook']`
- **Details:**
Social links allowed to be displayed in the navbar.
This configuration is only effective on PC.
If [`social`](#social) is configured as `{ svg: string, name: string}`, then `name` can be used as a value for `navbarSocialInclude`.
### navbar
- **Type:** `NavItem[]`
- **Default:** `[]`
- **Details:** Navbar configuration.
To configure navbar elements, you can set it to a navbar array, where each element is a `string` or a `NavItem` object.
- A `NavItem` object should have a `text` field and a `link` field, with an optional `activeMatch` field.
- A `string` represents a page file path or a page access path.
``` ts
type NavItem = string | {
text: string
link: string
/**
* The page prefix for the current group.
*/
prefix?: string
/**
* Navigation items under this group.
*/
items?: NavItem[]
/**
* Supports iconify icons. Use the iconify name directly and it will be loaded automatically.
*
* @see https://icon-sets.iconify.design/
*/
icon: string
/**
* Controls when the element is active.
*/
activeMatch?: string
}
```
- Example 1:
``` js
export default defineUserConfig({
theme: plumeTheme({
navbar: [
// NavbarItem
{ text: 'Foo', link: '/foo/' },
// NavbarGroup
{
text: 'Group',
prefix: '/group/',
items: ['foo/', 'bar/'],
},
// String - page file path
'/bar', // The `.md` extension can be omitted directly.
],
}),
})
```
- Example 2:
``` js
export default defineUserConfig({
theme: plumeTheme({
navbar: [
// Nested Group - maximum depth is 2
{
text: 'Group',
items: [
{
text: 'SubGroup',
items: ['/group/sub/', '/group/sub/bar/'],
},
],
},
// Control when the element is active
{
text: 'Group 2',
items: [
{
text: 'Always active',
link: '/',
// This element will always be active
activeMatch: '/',
},
{
text: 'Active on /foo/',
link: '/not-foo/',
// This element is active when the current route path starts with /foo/
// Supports regular expressions
activeMatch: '^/foo/',
},
],
},
],
}),
})
```
### collections
- **Type:** `ThemeCollectionItem[]`
- **Default:** `[]`
- **Details:** Documentation collections configuration.
[Check **Collections Guide**](../guide/quick-start/collection.md){.read-more}
[Check **Collections Configuration** to learn more](./collections.md){.read-more}
### sidebar
- **Type:** `false | SidebarMulti`
- **Details:**
Sidebar configuration. **The theme recommends configuring the sidebar in [collections configuration](./collections.md).**
The `key` of the configuration object is the common access path prefix for the sidebar.
For `value`:
- `'auto'` means automatically generate the sidebar based on the directory structure.
- `string` represents the page file path corresponding to the sidebar.
- `SidebarItem` represents a single sidebar item configuration.
```ts
type ThemeIcon = string | { svg: string }
type SidebarMulti = Record<
string,
| 'auto'
| (string | SidebarItem)[]
| { items: 'auto' | (string | SidebarItem)[], prefix?: string }
>
interface SidebarItem {
/**
* Sidebar text.
*/
text?: string
/**
* Sidebar link.
*/
link?: string
/**
* Sidebar icon.
*/
icon?: ThemeIcon
/**
* Nested sidebar group.
*/
items?: 'auto' | (string | SidebarItem)[]
/**
* If not specified, the group is not collapsible.
* If `true`, the group is collapsible and collapsed by default.
* If `false`, the group is collapsible but expanded by default.
*/
collapsed?: boolean
/**
* Link prefix for the current group.
*/
prefix?: string
rel?: string
target?: string
}
```
### sidebarScrollbar
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to show the sidebar scrollbar.
When set to `false`, only the scrollbar is hidden, but the scrolling behavior remains unchanged.
### aside
- **Type:** `boolean | 'left'`
- **Default:** `true`
- **Details:**
Whether to display the aside (right sidebar).
- `false` disables the right aside.
- `true` enables the right aside.
- `'left'` moves the right aside to the left side of the article content, to the right of the main sidebar.
Each page can override this global configuration via [frontmatter aside](./frontmatter/basic.md#aside).
### outline
- **Type:** `false | number | [number, number] | 'deep'`
- **Default:** `[2, 3]`
- **Details:**
The heading levels to display.
A single number means only display headings of that level.
If a tuple is passed, the first number is the minimum level and the second is the maximum level.
`'deep'` is the same as `[2, 6]`, which displays all headings from `<h2>` to `<h6>`.
When [aside](#aside) is disabled, `outline` is also disabled.
Each page can override this global configuration via [frontmatter outline](./frontmatter/basic.md#outline).
### transition
- **Type:** `boolean | TransitionOptions`
- **Default:** `true`
- **Details:**
Whether to enable transition animations.
When passing a `boolean`, `true` enables and `false` disables.
An object can also be passed for specific configuration, as shown below.
```ts
interface TransitionOptions {
/**
* Whether to enable page transition animations.
* @default true
*/
page?: boolean
/**
* Whether to enable blog post list transition animations.
* @default true
*/
postList?: boolean
/**
* Whether to enable dark/light mode switch transition animations,
* or configure the transition animation type.
* @default 'fade'
*/
appearance?: boolean | 'fade' | 'circle-clip' | 'horizontal-clip' | 'vertical-clip' | 'skew-clip'
}
```
### footer
- **Type:** `false | { message: string; copyright: string }`
- **Default:** `false`
- **Details:** Footer configuration.
### bulletin
- **Type:** `boolean | BulletinOptions`
- **Default:** `false`
- **Details:** Bulletin board configuration.
For details, please refer to [Bulletin Board](../guide/features/bulletin.md).
### editLinkPattern
- **Type:** `string`
- **Default:** `''`
- **Details:** Regular expression for the edit link.
Example: `':repo/edit/:branch/:path'`
### copyright
- **Type:** `boolean | CopyrightLicense | CopyrightOptions`
- **Default:** `false`
- **Details:** Copyright configuration.
For details, please refer to [Copyright](../guide/features/copyright.md).
### prevPage
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to show the previous page link.
### nextPage
- **Type:** `boolean`
- **Default:** `true`
- **Details:** Whether to show the next page link.
### createTime
- **Type:** `boolean | 'only-posts'`
- **Default:** `true`
- **Details:** Whether to display the creation time.
- `false` - Do not display.
- `'only-posts'` - Only display on blog post list pages.
- `true` - Display on all article pages.

14
docs/en/contributing.md Normal file
View File

@ -0,0 +1,14 @@
---
title: Contributing
createTime: 2024/03/13 21:27:45
permalink: /en/contributing/
article: false
externalLinkIcon: false
readingTime: false
editLink: false
contributors: false
changelog: false
search: false
---
<!-- @include: ../../CONTRIBUTING.en-US.md{2-} -->

164
docs/en/friends.md Normal file
View File

@ -0,0 +1,164 @@
---
pageLayout: friends
title: Friends Links
description: Here is the description text for the friend links. This page is for demonstration purposes only.
permalink: /en/friends/
article: true
list:
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
location: GuangZhou
organization: PengZhanBo
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
groups:
-
title: 分组 1
desc: 自定义颜色
list:
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
backgroundColor: rgb(255,153,0)
color: rgb(255,255,153)
nameColor: rgb(255,255,170)
socials:
-
icon: github
link: https://github.com/pengzhanbo
-
icon: twitter
link: https://twitter.com/pengzhanbo
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
backgroundColor: rgb(255,102,102)
color: rgb(255,204,204)
nameColor: rgb(255,238,238)
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
backgroundColor: rgb(0,153,204)
color: rgb(153,238,255)
nameColor: rgb(153,255,255)
-
title: 分组 2
desc: 这里是分组 2 的描述文字
list:
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: 即使慢,驰而不息,纵会落后,纵会失败,但必须能够到达他所向的目标。
---

126
docs/en/guide/api/client.md Normal file
View File

@ -0,0 +1,126 @@
---
title: Client
icon: nimbus:browser
createTime: 2025/10/08 21:58:48
permalink: /en/guide/api/client/
---
## Usage
```ts
import { Layout } from 'vuepress-theme-plume/client'
```
## Layout Components
- `<Layout />`: Page layout component
- `<NotFound />`: 404 page layout component
```ts
import { Layout, NotFound } from 'vuepress-theme-plume/client'
```
## Common Components
- `<VPLink />`: Link component
- `<VPButton />`: Button component
- `<VPIcon />`: Icon component
- `<VPBadge />`: Badge component
- `<VPImage />`: Image component
- `<VPHomeBox />`: Home page layout component
For more components, please check the [source code](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/theme/src/client/components).
```ts
import VPButton from 'vuepress-theme-plume/components/VPButton.vue'
import VPLink from 'vuepress-theme-plume/components/VPLink.vue'
```
## Composable APIs
### `useDarkMode()`
- Type: `() => Ref<boolean>`
- Details:
Returns a reactive reference indicating whether dark mode is enabled.
```ts
import { useDarkMode } from 'vuepress-theme-plume/composables'
const isDark = useDarkMode()
// Switch to dark mode
isDark.value = true
// Switch to light mode
isDark.value = false
```
### `useData()`
- Type: `() => Data`
- Details:
Returns reactive data for various theme properties.
```ts
interface Data {
// Theme configuration
theme: ThemeLocaleDataRef<PlumeThemeLocaleData>
// Current page data
page: PageDataRef<PlumeThemePageData>
// Current page frontmatter
frontmatter: PageFrontmatterRef<Frontmatter<T>>
// Current language
lang: Ref<string>
// Site data
site: SiteLocaleDataRef
// Whether dark mode is enabled
isDark: Ref<boolean>
}
```
```ts
import { useData } from 'vuepress-theme-plume/composables'
const { site, page, frontmatter, isDark, lang } = useData()
// Current page title
console.log(frontmatter.value.title)
```
### `useLocalePostList()`
- Type: `() => Ref<PostItem[]>`
- Details:
Returns a reactive reference to the post list data.
```ts
interface PostItem {
path: string
title: string
excerpt: string
tags: string[]
sticky: boolean
categoryList: CategoryItem[]
createTime: string
lang: string
encrypt?: boolean
}
interface CategoryItem {
type: string | number
name: string
}
```
```ts
import { useLocalePostList } from 'vuepress-theme-plume/composables'
const postList = useLocalePostList()
```
### More
For other composable APIs, please check the [source code](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/theme/src/client/composables).

48
docs/en/guide/api/node.md Normal file
View File

@ -0,0 +1,48 @@
---
title: Node
icon: fa6-brands:node
createTime: 2025/10/08 21:59:13
permalink: /en/guide/api/node/
---
## Usage
```ts
import { plumeTheme } from 'vuepress-theme-plume'
```
## `plumeTheme(options)`
__options__ : `PlumeThemeOptions`
Theme configuration function.
See [Theme Configuration](../config/theme.md) for more information.
## `defineThemeConfig(options)`
Theme configuration helper function for use in separate `plume.config.ts` files.
See [Theme Configuration File](../config/intro.md#theme-configuration-file) for more information.
## `defineNavbarConfig(options)`
Theme navbar configuration type helper function.
See [Theme Configuration](../config/navbar.md) for more information.
## `defineCollections(options)`
__options:__ `(ThemePostCollection | ThemeDocCollection)[]`
Theme collections configuration type helper function.
See [Theme Configuration](../config/collection.md) for more information.
## `defineCollection(options)`
__options:__ `ThemePostCollection | ThemeDocCollection`
Theme single collection configuration type helper function.
See [Theme Configuration](../config/collection.md) for more information.

View File

@ -0,0 +1,121 @@
---
title: chart.js
createTime: 2025/10/08 19:33:49
icon: solar:chart-bold
permalink: /en/guide/chart/chartjs/
---
[chart.js]: https://www.chartjs.org/docs/latest/
## Overview
The theme supports embedding [chart.js] charts within articles.
This feature is powered by [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/).
## Configuration
This feature is disabled by default in the theme.
You need to install the [chart.js] library in your project.
::: npm-to
```sh
npm install chart.js
```
:::
Then, enable the feature in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
chartjs: true, // [!code ++]
},
})
})
```
## Syntax
````md
::: chartjs Title
```json
{
// Chart configuration goes here
}
```
:::
````
Refer to the [chart.js] documentation for chart configuration details.
## Examples
::: note
Examples are forked from [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/chartjs.html),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
### Bar Chart
**Input:**
<!-- @include: ../../../snippet/chart-1.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-1.snippet.md{2-41} -->
### Bubble Chart
**Input:**
<!-- @include: ../../../snippet/chart-2.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-2.snippet.md{2-20} -->
### Line Chart
**Input:**
<!-- @include: ../../../snippet/chart-3.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-3.snippet.md{2-20} -->
### Polar Area Chart
**Input:**
<!-- @include: ../../../snippet/chart-4.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-4.snippet.md{2-24} -->
### Radar Chart
**Input:**
<!-- @include: ../../../snippet/chart-5.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-5.snippet.md{2-42} -->
### Scatter Chart
**Input:**
<!-- @include: ../../../snippet/chart-6.snippet.md -->
**Output:**
<!-- @include: ../../../snippet/chart-6.snippet.md{2-30} -->

View File

@ -0,0 +1,230 @@
---
title: ECharts
createTime: 2025/10/08 19:34:03
icon: raphael:piechart
permalink: /en/guide/chart/echarts/
---
## Overview
The theme supports embedding [ECharts](https://echarts.apache.org/zh/index.html) charts within articles.
This feature is powered by [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/).
## Configuration
This feature is disabled by default in the theme.
You need to install the [ECharts](https://echarts.apache.org/zh/index.html) library in your project.
::: npm-to
```sh
npm install echarts
```
:::
Then, enable the feature in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
echarts: true, // [!code ++]
},
})
})
```
::: note
The following documentation is forked from [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/echarts.html),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
## Syntax
### JSON Configuration
If you can easily generate data, you can provide ECharts configuration directly through a JSON code block:
````md
::: echarts Title
```json
{
// ECharts chart configuration goes here
}
```
:::
````
### JavaScript Configuration
If you need to fetch data through scripts, you can use js and javascript code blocks.
The Echarts instance is exposed through the `echarts` variable, and you should assign the Echarts configuration to the `option` variable.
You can also assign `width` and `height` to set the chart dimensions.
````md
::: echarts Title
```js
const option = {
// ECharts chart configuration goes here
}
```
:::
````
:::tip
You can use top-level `await` and `fetch` to retrieve data from network requests.
:::
Refer to the [ECharts documentation](https://echarts.apache.org/handbook/zh/get-started/) for configuration details.
## Advanced
You can import and use `defineEchartsConfig` in the
[client configuration file](https://vuejs.press/zh/guide/configuration.html##使用脚本) to customize ECharts:
```ts
import { defineEchartsConfig } from '@vuepress/plugin-markdown-chart/client'
import { defineClientConfig } from 'vuepress/client'
defineEchartsConfig({
options: {
// Global ECharts configuration
},
setup: async () => {
// ECharts setup
// Example: await import("echarts-wordcloud")
},
})
export default defineClientConfig({
// ...
})
```
## Examples
### Line Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-1.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-1.snippet.md{2-100} -->
### Bar Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-2.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-2.snippet.md{2-75} -->
### Pie Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-3.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-3.snippet.md{2-74} -->
### Scatter Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-4.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-4.snippet.md{2-39} -->
### Polar Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-5.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-5.snippet.md{2-40} -->
### Candlestick Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-6.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-6.snippet.md{2-308} -->
### Radar Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-7.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-7.snippet.md{2-36} -->
### Heatmap
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-8.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-8.snippet.md{2-179} -->
### Tree Chart
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-9.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-9.snippet.md{2-33} -->
### Multiple Charts
**Input:**
:::: details View Code
<!-- @include: ../../../snippet/echarts-10.snippet.md -->
::::
**Output:**
<!-- @include: ../../../snippet/echarts-10.snippet.md{2-69} -->

View File

@ -0,0 +1,351 @@
---
title: flowchart
createTime: 2025/10/08 19:34:31
icon: f7:flowchart
permalink: /en/guide/chart/flowchart/
---
## Overview
The theme supports embedding [flowchart](http://flowchart.js.org/) diagrams within articles.
This feature is powered by [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/).
## Configuration
This feature is disabled by default in the theme.
You need to install the [flowchart.ts](http://flowchart.js.org/) library in your project.
::: npm-to
```sh
npm install flowchart.ts
```
:::
Then, enable the feature in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
flowchart: true, // [!code ++]
},
})
})
```
::: note
The following documentation is forked from [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/flowchart.html),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
## Syntax
````md
<!------- ↓ :preset is optional -->
```flow:preset
<!-- Place your flowchart code here -->
```
````
Currently available presets:
- vue (default)
- ant
- pie
## Demo
::: demo markdown title="Vue Theme"
````md
```flow
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End|future:>http://www.google.com
op1=>operation: Operation 1|past
op2=>operation: Operation 2|current
sub1=>subroutine: Subroutine|invalid
cond=>condition: Yes/No?|approved:>http://www.google.com
c2=>condition: Condition 2|rejected
io=>inputoutput: Process input...|future
st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e
```
````
:::
::: demo markdown title="Ant Theme"
````md
```flow:ant
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End|future:>http://www.google.com
op1=>operation: Operation 1|past
op2=>operation: Operation 2|current
sub1=>subroutine: Subroutine|invalid
cond=>condition: Yes/No?|approved:>http://www.google.com
c2=>condition: Condition 2|rejected
io=>inputoutput: Process input...|future
st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e
```
````
:::
::: demo markdown title="Pie Theme"
````md
```flow:pie
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End|future:>http://www.google.com
op1=>operation: Operation 1|past
op2=>operation: Operation 2|current
sub1=>subroutine: Subroutine|invalid
cond=>condition: Yes/No?|approved:>http://www.google.com
c2=>condition: Condition 2|rejected
io=>inputoutput: Process input...|future
st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e
```
````
:::
## Flowchart Introduction
### Node Types
Define node shapes.
#### Start & End
- `[Variable]->start: [Text]`
Used for the first node in the flowchart.
Default text is `Start`.
- `[Variable]->end: [Text]`
Used for the last node in the flowchart.
Default text is `End`.
::: demo markdown title="Start & End"
````md
```flow
st=>start: Start
e=>end: End
st->e
```
````
:::
#### Operation
`[Variable]->operation: [Text]`
::: demo markdown title="Operation"
````md
```flow
process=>operation: Operation
e=>end: End
process->e
```
````
:::
#### Input/Output
`[Variable]->inputoutput: [Text]`
:::demo markdown title="Input/Output"
````md
```flow
process=>inputoutput: Input/Output
e=>end: End
process->e
```
````
:::
#### Subroutine
`[Variable]->subroutine: [Text]`
::: demo markdown title="Subroutine"
````md
```flow
process=>subroutine: Subroutine
e=>end: End
process->e
```
````
:::
#### Condition
- `[Variable]->condition: [Text]`
- `[Variable]([yesText])->[Position]`
- `[Variable]([noText])->[Position]`
::: demo markdown title="Condition"
````md
```flow
cond=>condition: Execute operation?
process=>operation: Operation
e=>end: End
cond(yes)->process->e
cond(no)->e
```
````
:::
#### Parallel
Define multiple processes that start simultaneously.
- `[Variable]->parallel: [Text]`
- `[Variable](path1, direction)->[Position]`
- `[Variable](path1, direction)->[Position]`
::: demo markdown title="Parallel"
````md
```flow
para=>parallel: Parallel tasks
process=>operation: Operation
e=>end: End
para(path1, bottom)->process->e
para(path2)->e
```
````
:::
### Connections
Connections are described after node definitions in the flowchart, using `->` to specify links between nodes, e.g., `nodeVar1->nodeVar2->nodeVar3`
Flows can be split:
```md
nodeVar1->nodeVar2
nodeVar2->nodeVar3
```
Connection format is defined as:
`<node variable name>[(<specification1>[, <specification2])]-><node variable name>[[(<specification1>[, <specification2])]-><node variable name>]`
Items in `[]` are optional.
### Directions
The following directions are available and define which direction the connection will leave the node.
If more than one specifier is present, the last one takes precedence.
All nodes have default directions, making this an optional specification. Available values for `<direction>` are:
- `left`
- `right`
- `top`
- `bottom`
### Node-specific Specifiers
Each node variable has optional specifiers, such as direction, and some variables have special specifiers
depending on the node type defined below. Add specifiers after the variable name in `()` separated by `,`,
e.g., `nodeVar (spec1, spec2)`.
- **start**
**operation**
**inputoutput**
**subroutine**
Optional direction
`startVar(<direction>)->nextNode`
`operationVar(<direction>)->nextNode`
`inputoutputVar(<direction>)->nextNode`
`subroutineVar(<direction>)->nextNode`
- **condition**
Must specify `yes` or `no`
Optional direction
```md
conditionalVar(yes, <direction>)->nextNode1
conditionalVar(no, <direction>)->nextNode2
```
- **parallel**
Must specify path direction `path1`, `path2`, or `path3`
Optional direction
```md
parallelVar(path1, <direction>)->nextNode1
parallelVar(path2, <direction>)->nextNode2
parallelVar(path3, <direction>)->nextNode3
```
### URLs
External links can be added to nodes using the `:>` operator.
`[blank]` specifies opening in a new page
```md
st=>start: Start:>http://www.google.com[blank]
e=>end: End:>http://www.yahoo.com
```
### Recommendations
Symbols that should probably not be used in text: `=>`, `->`, `:>`, `|`, `@>`, and `:$`
To emphasize a specific path in the flowchart, you can additionally define it as follows:
```
st@>op1({"stroke":"Red"})@>cond({"stroke":"Red","stroke-width":6,"arrow-end":"classic-wide-long"})@>c2({"stroke":"Red"})@>op2({"stroke":"Red"})@>e({"stroke":"Red"})
```

View File

@ -0,0 +1,90 @@
---
title: markmap
icon: ri:mind-map
createTime: 2025/10/08 14:35:59
permalink: /en/guide/chart/markmap/
---
## Overview
The theme supports embedding [markmap](https://markmap.js.org/) mind maps within articles.
This feature is powered by [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/).
## Configuration
This feature is disabled by default in the theme.
You need to install `markmap-lib`, `markmap-toolbar` and `markmap-view` in your project:
::: npm-to
```sh
npm i markmap-lib markmap-toolbar markmap-view
```
:::
Then, enable the feature in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
markmap: true, // [!code ++]
},
})
})
```
::: note
The following documentation is forked from [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/markmap.html),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
## Syntax
````md
```markmap
<!-- Place content here -->
```
````
Configuration via Frontmatter syntax is supported.
## Example
::: demo markdown title="markmap"
`````md
````markmap
---
markmap:
colorFreezeLevel: 2
---
# markmap
## Links
- <https://markmap.js.org/>
- [GitHub](https://github.com/markmap/markmap)
## Features
- Links
- **Strong** ~~Strikethrough~~ *Italic* ==Highlight==
- Multi-line
text
- `Inline code`
-
```js
console.log('code block');
```
- Katex
- $x = {-b \pm \sqrt{b^2-4ac} \over 2a}$
- Now we can wrap very very very very very very very very very very long text automatically with `maxWidth` option
````
`````
:::

View File

@ -0,0 +1,306 @@
---
title: mermaid
createTime: 2025/10/08 19:34:16
icon: file-icons:mermaid
permalink: /en/guide/chart/mermaid/
---
## 概述
主题支持在 文章中 嵌入由 [Mermaid](https://mermaid.js.org/) 。
该功能由 [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/) 提供支持。
## 配置
主题默认不启用该功能。
你需要在你的项目中安装 [mermaid](https://mermaid.js.org/) 库。
::: npm-to
```sh
npm install mermaid
```
:::
然后在 `.vuepress/config.ts` 配置文件中,启用该功能:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
mermaid: true, // [!code ++]
},
})
})
```
::: note
以下文档 Fork 自 [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/mermaid.html),
遵循 [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE) 许可证。
:::
## 语法
````md
```mermaid
<!-- 在此处放置 mermaid 代码 -->
```
````
除了使用 mermaid 代码块,你也可以直接使用以下代码块:
- class: `classDiagram`
- c4c: `C4Context`
- er: `erDiagram`
- gantt: `gantt`
- git-graph: `gitGraph`
- journey: `journey`
- mindmap: `mindmap`
- pie: `pie`
- quadrant: `quadrantChart`
- requirement: `requirementDiagram`
- sankey: `sankey-beta`
- sequence: `sequenceDiagram`
- state: `stateDiagram-v2`
- timeline: `timeline`
- xy: `xychart-beta`
你不需要再声明图表类型,也不需要缩进图表代码。
当图表支持设置标题时,你可以直接在代码块信息后添加标题:
````md
```sequence 代码标题
<!-- 顺序图代码内容
... -->
```
````
配置文档详见 [Mermaid 文档](https://mermaid.js.org/)
## 高级
你可以在 [客户端配置文件](https://vuejs.press/zh/guide/configuration.html#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)
中导入并使用 `defineMermaidConfig` 来自定义 Mermaid 配置:
```ts
import { defineMermaidConfig } from '@vuepress/plugin-markdown-chart/client'
import { defineClientConfig } from 'vuepress/client'
defineMermaidConfig({
// 在此设置 mermaid 选项
})
export default defineClientConfig({
// ...
})
```
## 示例
### 流程图
**输入:**
<!-- @include: ../../snippet/mermaid-1.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-1.snippet.md{2-20} -->
### 循序图
**输入:**
<!-- @include: ../../snippet/mermaid-2.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-2.snippet.md{2-11} -->
### 类图
**输入:**
<!-- @include: ../../snippet/mermaid-3.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-3.snippet.md{2-25} -->
### 状态图
**输入:**
<!-- @include: ../../snippet/mermaid-4.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-4.snippet.md{2-9} -->
### 关系图
**输入:**
<!-- @include: ../../snippet/mermaid-5.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-5.snippet.md{2-15} -->
### 用户日记图
**输入:**
<!-- @include: ../../snippet/mermaid-6.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-6.snippet.md{2-11} -->
### 甘特图
**输入:**
:::: details 查看代码
<!-- @include: ../../snippet/mermaid-7.snippet.md -->
::::
**输出:**
<!-- @include: ../../snippet/mermaid-7.snippet.md{2-31} -->
### 饼图
**输入:**
````md
```pie
title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
```
````
**输出:**
```pie
title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
```
### Git 图表
**输入:**
:::: details 查看代码
<!-- @include: ../../snippet/mermaid-8.snippet.md-->
::::
**输出:**
<!-- @include: ../../snippet/mermaid-8.snippet.md{2-44} -->
### C4C 图表
**输入:**
:::: details 查看代码
<!-- @include: ../../snippet/mermaid-9.snippet.md -->
::::
**输出:**
<!-- @include: ../../snippet/mermaid-9.snippet.md{2-34} -->
### 思维导图
**输入:**
<!-- @include: ../../snippet/mermaid-10.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-10.snippet.md{2-19} -->
### 时序图
**输入:**
<!-- @include: ../../snippet/mermaid-11.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-11.snippet.md{2-11} -->
### 桑基图
**输入:**
:::: details 查看代码
<!-- @include: ../../snippet/mermaid-12.snippet.md -->
::::
**输出:**
<!-- @include: ../../snippet/mermaid-12.snippet.md{2-71} -->
### 依赖图
**输入:**
<!-- @include: ../../snippet/mermaid-13.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-13.snippet.md{2-15} -->
### 象限图
**输入:**
<!-- @include: ../../snippet/mermaid-14.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-14.snippet.md{2-16} -->
### XY图
**输入:**
<!-- @include: ../../snippet/mermaid-15.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-15.snippet.md{2-8} -->
### 块图
**输入:**
<!-- @include: ../../snippet/mermaid-16.snippet.md -->
**输出:**
<!-- @include: ../../snippet/mermaid-16.snippet.md{2-12} -->
### 复杂例子
**输入:**
:::: details 查看代码
<!-- @include: ../../snippet/mermaid-17.snippet.md -->
::::
**输出:**
<!-- @include: ../../snippet/mermaid-17.snippet.md{2-24} -->

View File

@ -0,0 +1,690 @@
---
title: PlantUML
icon: arcticons:uml-class-editor
createTime: 2025/10/08 15:02:08
permalink: /en/guide/chart/plantuml/
---
## Overview
The theme supports embedding [PlantUML](https://plantuml.com/) diagrams within articles.
This feature is powered by [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/).
## Configuration
This feature is disabled by default in the theme.
Enable the feature in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
plantuml: true, // [!code ++]
},
})
})
```
::: note
The following documentation is forked from [@vuepress/plugin-markdown-chart](https://ecosystem.vuejs.press/plugins/markdown/markdown-chart/plantuml.html),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
## Format
You can insert the same content supported by [plantuml](https://plantuml.com/), for example:
```md
@startuml
content
@enduml
```
## Examples
::: demo markdown title="Sequence Diagram"
```md
@startuml
Alice -> Bob: Authentication Request
alt successful case
Bob -> Alice: Authentication Accepted
else some failure case
Bob -> Alice: Authentication Failed
group My own label
Alice -> Log : Log attack start
loop 1000 times
Alice -> Bob: DNS Attack
end
Alice -> Log : Log attack end
end
else Another failure
Bob -> Alice: Please repeat
end
@enduml
```
:::
::: demo markdown title="Use Case Diagram"
```md
@startuml
:Main Admin: as Admin
(Use the application) as (Use)
User -> (Start)
User --> (Use)
Admin ---> (Use)
note right of Admin : This is an example.
note right of (Use)
A note can also
be on several lines
end note
note "This note is connected\nto several objects." as N2
(Start) .. N2
N2 .. (Use)
@enduml
```
:::
::: demo markdown title="Class Diagram"
```md
@startuml
abstract class AbstractList
abstract AbstractCollection
interface List
interface Collection
List <|-- AbstractList
Collection <|-- AbstractCollection
Collection <|- List
AbstractCollection <|- AbstractList
AbstractList <|-- ArrayList
class ArrayList {
Object[] elementData
size()
}
enum TimeUnit {
DAYS
HOURS
MINUTES
}
annotation SuppressWarnings
annotation Annotation {
annotation with members
String foo()
String bar()
}
@enduml
```
:::
::: demo markdown title="Activity Diagram"
```md
@startuml
start
:ClickServlet.handleRequest();
:new page;
if (Page.onSecurityCheck) then (true)
:Page.onInit();
if (isForward?) then (no)
:Process controls;
if (continue processing?) then (no)
stop
endif
if (isPost?) then (yes)
:Page.onPost();
else (no)
:Page.onGet();
endif
:Page.onRender();
endif
else (false)
endif
if (do redirect?) then (yes)
:redirect process;
else
if (do forward?) then (yes)
:Forward request;
else (no)
:Render page template;
endif
endif
stop
@enduml
```
:::
::: demo markdown title="Component Diagram"
```md
@startuml
package "Some Group" {
HTTP - [First Component]
[Another Component]
}
node "Other Groups" {
FTP - [Second Component]
[First Component] --> FTP
}
cloud {
[Example 1]
}
database "MySql" {
folder "This is my folder" {
[Folder 3]
}
frame "Foo" {
[Frame 4]
}
}
[Another Component] --> [Example 1]
[Example 1] --> [Folder 3]
[Folder 3] --> [Frame 4]
@enduml
```
:::
::: demo markdown title="State Diagram"
``` md
@startuml
state start1 <<start>>
state choice1 <<choice>>
state fork1 <<fork>>
state join2 <<join>>
state end3 <<end>>
[*] --> choice1 : from start\nto choice
start1 --> choice1 : from start stereo\nto choice
choice1 --> fork1 : from choice\nto fork
choice1 --> join2 : from choice\nto join
choice1 --> end3 : from choice\nto end stereo
fork1 ---> State1 : from fork\nto state
fork1 --> State2 : from fork\nto state
State2 --> join2 : from state\nto join
State1 --> [*] : from state\nto end
join2 --> [*] : from join\nto end
@enduml
```
:::
::: demo markdown title="Object Diagram"
```md
@startuml
object London
object Washington
object Berlin
object NewYork
map CapitalCity {
UK *-> London
USA *--> Washington
Germany *---> Berlin
}
NewYork --> CapitalCity::USA
@enduml
```
:::
::: demo markdown title="Deployment Diagram"
```md
@startuml
node node1
node node2
node node3
node node4
node node5
node1 -- node2 : label1
node1 .. node3 : label2
node1 ~~ node4 : label3
node1 == node5
@enduml
```
:::
::: demo markdown title="Timing Diagram"
```md
@startuml
scale 5 as 150 pixels
clock clk with period 1
binary "Enable" as en
binary "Read/Write" as rw
binary "Data Valid" as dv
concise "Data Bus" as db
concise "Address Bus" as addr
@6 as :write_beg
@10 as :write_end
@15 as :read_beg
@19 as :read_end
@0
en is low
db is "0x0"
addr is "0x03f"
rw is low
dv is 0
@:write_beg-3
en is high
@:write_beg-2
db is "0xDEADBEEF"
@:write_beg-1
dv is 1
@:write_beg
rw is high
@:write_end
rw is low
dv is low
@:write_end+1
rw is low
db is "0x0"
addr is "0x23"
@12
dv is high
@13
db is "0xFFFF"
@20
en is low
dv is low
@21
db is "0x0"
highlight :write_beg to :write_end #Gold:Write
highlight :read_beg to :read_end #lightBlue:Read
db@:write_beg-1 <-> @:write_end : Setup Time
db@:write_beg-1 -> addr@:write_end+1 : Hold
@enduml
```
:::
::: demo markdown title="Regex Diagram"
```md
@startregex
/<style(\s*lang=(['"])(.*?)\2)?\s*(?:scoped)?>([\s\S]+)<\/style>
@endregex
```
:::
::: demo markdown title="Network Diagram"
```md
@startuml
nwdiag {
network dmz {
address = "210.x.x.x/24"
web01 [address = "210.x.x.1"];
web02 [address = "210.x.x.2"];
}
network internal {
address = "172.x.x.x/24";
web01 [address = "172.x.x.1"];
web02 [address = "172.x.x.2"];
db01;
db02;
}
}
@enduml
```
:::
::: demo markdown title="Salt (GUI) Diagram"
```md
@startsalt
{+
{/ <b>General | Full Screen | Behavior | Saving }
{
{ Image opening mode: | ^Smart Mode^ }
[X] Smooth images when zooming
[X] Confirm image deletion
[ ] Show hidden images
}
[Close]
}
@endsalt
```
:::
::: demo markdown title="Archimate Diagram"
```md
@startuml
skinparam rectangle<<behavior>> {
roundCorner 25
}
sprite $bProcess jar:archimate/business-process
sprite $aService jar:archimate/application-service
sprite $aComponent jar:archimate/application-component
rectangle "Handle claim" as HC <<$bProcess>><<behavior>> #Business
rectangle "Capture Information" as CI <<$bProcess>><<behavior>> #Business
rectangle "Notify\nAdditional Stakeholders" as NAS <<$bProcess>><<behavior>> #Business
rectangle "Validate" as V <<$bProcess>><<behavior>> #Business
rectangle "Investigate" as I <<$bProcess>><<behavior>> #Business
rectangle "Pay" as P <<$bProcess>><<behavior>> #Business
HC *-down- CI
HC *-down- NAS
HC *-down- V
HC *-down- I
HC *-down- P
CI -right->> NAS
NAS -right->> V
V -right->> I
I -right->> P
rectangle "Scanning" as scanning <<$aService>><<behavior>> #Application
rectangle "Customer admnistration" as customerAdministration <<$aService>><<behavior>> #Application
rectangle "Claims admnistration" as claimsAdministration <<$aService>><<behavior>> #Application
rectangle Printing <<$aService>><<behavior>> #Application
rectangle Payment <<$aService>><<behavior>> #Application
scanning -up-> CI
customerAdministration -up-> CI
claimsAdministration -up-> NAS
claimsAdministration -up-> V
claimsAdministration -up-> I
Payment -up-> P
Printing -up-> V
Printing -up-> P
rectangle "Document\nManagement\nSystem" as DMS <<$aComponent>> #Application
rectangle "General\nCRM\nSystem" as CRM <<$aComponent>> #Application
rectangle "Home & Away\nPolicy\nAdministration" as HAPA <<$aComponent>> #Application
rectangle "Home & Away\nFinancial\nAdministration" as HFPA <<$aComponent>> #Application
DMS .up.|> scanning
DMS .up.|> Printing
CRM .up.|> customerAdministration
HAPA .up.|> claimsAdministration
HFPA .up.|> Payment
legend left
Example from the "Archisurance case study" (OpenGroup).
See
====
<$bProcess> :business process
====
<$aService> : application service
====
<$aComponent> : application component
endlegend
@enduml
```
:::
::: demo markdown title="Gantt Chart"
```md
@startgantt
<style>
ganttDiagram {
task {
FontName Helvetica
FontColor red
FontSize 18
FontStyle bold
BackGroundColor GreenYellow
LineColor blue
}
milestone {
FontColor blue
FontSize 25
FontStyle italic
BackGroundColor yellow
LineColor red
}
note {
FontColor DarkGreen
FontSize 10
LineColor OrangeRed
}
arrow {
FontName Helvetica
FontColor red
FontSize 18
FontStyle bold
BackGroundColor GreenYellow
LineColor blue
LineStyle 8.0;13.0
LineThickness 3.0
}
separator {
BackgroundColor lightGreen
LineStyle 8.0;3.0
LineColor red
LineThickness 1.0
FontSize 16
FontStyle bold
FontColor purple
Margin 5
Padding 20
}
timeline {
BackgroundColor Bisque
}
closed {
BackgroundColor pink
FontColor red
}
}
</style>
Project starts the 2020-12-01
[Task1] requires 10 days
sunday are closed
note bottom
memo1 ...
memo2 ...
explanations1 ...
explanations2 ...
end note
[Task2] requires 20 days
[Task2] starts 10 days after [Task1]'s end
-- Separator title --
[M1] happens on 5 days after [Task1]'s end
<style>
separator {
LineColor black
Margin 0
Padding 0
}
</style>
-- end --
@endgantt
```
:::
::: demo markdown title="Mind Map"
```md
@startmindmap
caption figure 1
title My super title
* <&flag>Debian
** <&globe>Ubuntu
*** Linux Mint
*** Kubuntu
*** Lubuntu
*** KDE Neon
** <&graph>LMDE
** <&pulse>SolydXK
** <&people>SteamOS
** <&star>Raspbian with a very long name
*** <s>Raspmbc</s> => OSMC
*** <s>Raspyfi</s> => Volumio
header
My super header
endheader
center footer My super footer
legend right
Short
legend
endlegend
@endmindmap
```
:::
::: demo markdown title="Work Breakdown Structure"
```md
@startwbs
+ New Job
++ Decide on Job Requirements
+++ Identity gaps
+++ Review JDs
++++ Sign-Up for courses
++++ Volunteer
++++ Reading
++- Checklist
+++- Responsibilities
+++- Location
++ CV Upload Done
+++ CV Updated
++++ Spelling & Grammar
++++ Check dates
---- Skills
+++ Recruitment sites chosen
@endwbs
```
:::
::: demo markdown title="JSON"
```md
@startjson
#highlight "lastName"
#highlight "address" / "city"
#highlight "phoneNumbers" / "0" / "number"
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 28,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null
}
@endjson
```
:::
::: demo markdown title="YAML"
```md
@startyaml
doe: "a deer, a female deer"
ray: "a drop of golden sun"
pi: 3.14159
xmas: true
french-hens: 3
calling-birds:
- huey
- dewey
- louie
- fred
xmas-fifth-day:
calling-birds: four
french-hens: 3
golden-rings: 5
partridges:
count: 1
location: "a pear tree"
turtle-doves: two
@endyaml
```
:::

View File

@ -0,0 +1,295 @@
---
title: Code Grouping
icon: fluent:group-list-20-filled
createTime: 2025/10/08 10:36:59
permalink: /en/guide/code/group/
---
## Overview
Code Tabs are a powerful feature in the theme for displaying multiple related code snippets side by side.
By organizing code in tabbed interfaces, you can clearly compare implementation differences across
various tech stacks, configuration approaches, or language versions.
## Basic Syntax
### Multiple Code Block Grouping
Use code tab syntax to organize multiple code blocks within the same tab container:
**Input:**
````md
::: code-tabs
@tab config.js
```js
/**
* @type {import('vuepress').UserConfig}
*/
const config = {
// ..
}
export default config
```
@tab config.ts
```ts
import type { UserConfig } from 'vuepress'
const config: UserConfig = {
// ..
}
export default config
```
:::
````
**Output:**
::: code-tabs
@tab config.js
```js
/**
* @type {import('vuepress').UserConfig}
*/
const config = {
// ..
}
export default config
```
@tab config.ts
```ts
import type { UserConfig } from 'vuepress'
const config: UserConfig = {
// ..
}
export default config
```
:::
### Setting Default Active Tab
Specify the default displayed code tab using the `@tab:active` syntax:
**Input:**
````md
::: code-tabs
@tab config.js
```js
/**
* @type {import('vuepress').UserConfig}
*/
const config = {
// ..
}
export default config
```
@tab:active config.ts <!-- [!code hl] -->
```ts
import type { UserConfig } from 'vuepress'
const config: UserConfig = {
// ..
}
export default config
```
:::
````
**Output:**
::: code-tabs
@tab config.js
```js
/**
* @type {import('vuepress').UserConfig}
*/
const config = {
// ..
}
export default config
```
@tab:active config.ts
```ts
import type { UserConfig } from 'vuepress'
const config: UserConfig = {
// ..
}
export default config
```
:::
## Tab Icon Support <Badge type="tip" text="v1.0.0-rc.103 +" />
The theme provides intelligent icon display functionality for code tab labels,
automatically matching relevant technology icons based on tab titles.
### Automatic Icon Recognition
The theme includes built-in icon mappings for mainstream technologies and languages:
**Input:**
````md
::: code-tabs
@tab pnpm
```sh
pnpm i
```
@tab yarn
```sh
yarn
```
@tab npm
```sh
npm install
```
:::
````
**Output:**
::: code-tabs
@tab pnpm
```sh
pnpm i
```
@tab yarn
```sh
yarn
```
@tab npm
```sh
npm install
```
:::
### Supported Icon Categories
The theme automatically adapts icons for the following tech stacks:
- **Runtime Environments**: Node.js, Deno, Bun
- **Package Managers**: pnpm, yarn, npm
- **Frontend Frameworks**: Vue, React, Angular, Svelte, Solid, Next.js, Nuxt
- **Programming Languages**: TypeScript, JavaScript, C, C++, Java, Python, Rust, Kotlin, Swift, Go
::: info Icon Support Feedback
If the technology stack you're using doesn't display icons correctly, please submit an
[issue](https://github.com/pengzhanbo/vuepress-theme-plume/issues/new) to let us know, and we'll add the relevant icon support as soon as possible.
:::
## Icon Configuration Options
Precisely control icon display behavior through the `markdown.codeTabs` configuration option:
```ts
export default defineUserConfig({
theme: plumeTheme({
markdown: {
codeTabs: {
icon: true, // Enable icon functionality
}
},
})
})
```
Configuration interface definition:
```ts
export interface CodeTabsOptions {
icon?: boolean | {
named?: false | string[]
extensions?: false | string[]
}
}
```
### Configuration Examples
**Disable All Icons**:
```ts
export default defineUserConfig({
theme: plumeTheme({
markdown: {
codeTabs: {
icon: false
}
}
})
})
```
**Display Only Specified Technology Stack Icons**:
```ts
export default defineUserConfig({
theme: plumeTheme({
markdown: {
codeTabs: {
icon: {
named: ['pnpm', 'yarn', 'npm'], // Only match these technology names
extensions: false // Disable file extension matching
}
}
}
})
})
```
**Configuration Notes**:
- `named`: Exact match for technology names (e.g., `pnpm`, `vue`, `react`)
- `extensions`: Match file extensions (e.g., `.ts`, `.js`, `.py`)
- Set to `false` to disable the corresponding matching method
- Empty arrays use default matching rules
- String matching is case-sensitive
## Performance Optimization Notes
::: tip Icon Volume Optimization
You don't need to worry about the impact of icon resources on build size.
Code tab icons are implemented based on the Iconify system, and with the locally installed `@iconify/json` package, the theme automatically:
- Parses and extracts actually used icon data
- Generates optimized local icon resources
- Ensures the same icon is bundled only once
The average size of each colored icon is only 1-2KB. Even with extensive use of different icons, the impact on the final build size is minimal.
:::
Through reasonable configuration and usage, the code tabs feature can significantly improve the readability
of technical documentation and user experience, helping readers more efficiently understand differences between various technical solutions.

View File

@ -0,0 +1,102 @@
---
title: Copy Code
icon: ph:code
createTime: 2025/10/08 09:59:29
permalink: /en/guide/code/copy-code/
---
## Overview
This feature is powered by [@vuepress/plugin-copy-code](https://ecosystem.vuejs.press/zh/plugins/features/copy-code.html).
The code copying feature is enabled by default in the theme. It supports one-click copying of code displayed in articles.
By default, the theme adds a copy button to every code block. This button is only displayed on desktop.
When hovering over a code block, a copy button appears in the top-right corner.
## Configuration
Modify the behavior of code copying in the `.vuepress/config.ts` configuration file:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
// copyCode: false // Disable code copying
copyCode: {
// ...More configurations
}
})
})
```
### showInMobile
- **Type:** `boolean`
- **Default:** `false`
Whether to show the copy button on mobile devices.
### ignoreSelector
- **Type:** `string | string[]`
- **Default:** `[]`
CSS selector for elements within code blocks to be ignored during copying.
Example: `['.token.comment']` will ignore nodes with the class `.token.comment` in code blocks (this would ignore comments in prismjs).
### inlineSelector
- **Type:** `string | string[] | boolean`
- **Default:** `false`
Whether to copy inline code content on double-click.
`boolean`: Whether to copy inline code content on double-click.
`string[] | string`: Selector indicating which inline code content should be copyable.
### transform <Badge type="tip" text="Composition API Only" />
- **Type:** `(preElement: HTMLPreElement) => void`
- **Default:** `null`
A transformer function to modify the code block content within `<pre>` before copying. This option is only effective when using `useCopyCode()`.
## Composition API
The Composition API for this feature can be configured in `.vuepress/client.ts`:
```ts title=".vuepress/client.ts"
import { defineClientConfig } from '@vuepress/client'
import { useCopyCode } from '@vuepress/plugin-copy-code/client'
export default defineClientConfig({
setup() {
useCopyCode({
// ...
})
},
})
```
### Example
Add copyright information when copying code:
```ts title=".vuepress/client.ts"
import { defineClientConfig } from '@vuepress/client'
import { useCopyCode } from '@vuepress/plugin-copy-code/client'
export default defineClientConfig({
setup() {
useCopyCode({
transform: (preElement) => {
// Insert copyright information
pre.innerHTML += `\n Copied by vuepress-theme-plume`
},
})
},
})
```

View File

@ -0,0 +1,567 @@
---
title: Feature Support
icon: majesticons:code-block-line
createTime: 2025/10/08 10:41:28
permalink: /en/guide/code/features/
---
The theme provides additional features beyond basic code highlighting, enhancing the expressiveness of your code blocks.
## Code Block Title <Badge type="tip" text="1.0.0-rc.136 +" />
Add `title="xxxx"` after <code>\`\`\` [lang]</code> to add a title to the current code block.
**Input:**
````md {1}
```json title="package.json"
{
"name": "vuepress-theme-plume"
}
```
````
**Output:**
```json title="package.json"
{
"name": "vuepress-theme-plume"
}
```
## Line Numbers
Line numbers are displayed by default in the theme, controlled by `codeHighlighter.line-numbers`.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
lineNumbers: true, // [!code ++]
}
})
})
```
You can also control whether to display line numbers for the current code block using `:line-numbers` / `:no-line-numbers`.
Additionally, you can customize the starting line number by adding `=` after `:line-numbers`,
for example `:line-numbers=2` indicates that line numbers in the code block start from `2`.
**Input:**
````
```ts:line-numbers
// Line numbers enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:no-line-numbers
// Line numbers disabled
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
```ts:line-numbers=2
// Line numbers enabled, starting from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
````
**Output:**
```ts:line-numbers
// Line numbers enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:no-line-numbers
// Line numbers disabled
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
```ts:line-numbers=2
// Line numbers enabled, starting from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
## Line Highlighting in Code Blocks
Add `{xxxx}` immediately after `[lang]` to enable line highlighting, where `xxx` represents the line numbers to be highlighted.
**Input:**
````
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
````
**Output:**
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
In addition to single lines, you can specify multiple single lines, line ranges, or both:
- Line ranges: e.g., `{5-8}`, `{3-10}`, `{10-17}`
- Multiple single lines: e.g., `{4,7,9}`
- Mixed single lines and ranges: e.g., `{4,7-13,16,23-27,40}`
**Input:**
````
```js{1,4,6-8}
export default { // Highlighted
data () {
return {
msg: `Highlighted!
This line isn't highlighted,
but this and the next 2 are.`,
motd: 'VitePress is awesome',
lorem: 'ipsum'
}
}
}
```
````
**Output:**
```js{1,4,6-8}
export default { // Highlighted
data () {
return {
msg: `Highlighted!
This line isn't highlighted,
but this and the next 2 are.`,
motd: 'VitePress is awesome',
lorem: 'ipsum'
}
}
}
```
You can also use the `// [!code highlight]` comment to enable line highlighting.
**Input:**
````
```js
export default {
data () {
return {
msg: 'Highlighted!' // [\!code highlight]
}
}
}
```
````
**Output:**
```js
export default {
data() {
return {
msg: 'Highlighted!' // [!code highlight]
}
}
}
```
## Focus in Code Blocks
Adding the `// [!code focus]` comment on a specific line will focus it and blur the rest of the code.
Additionally, you can use `// [!code focus:<lines>]` to define the number of lines to focus.
**Input:**
````
```js
export default {
data () {
return {
msg: 'Focused!' // [\!code focus]
}
}
}
```
````
**Output:**
```js
export default {
data() {
return {
msg: 'Focused!' // [!code focus]
}
}
}
```
::: tip Use the valid line comment syntax for the language in different code blocks
For example, in bash code blocks, use `# [!code focus]`
````md
```bash
mkdir hello && cd hello # [\!code focus]
pnpm install
```
````
```bash
mkdir hello && cd hello # [!code focus]
pnpm install
```
:::
## Diff in Code Blocks
Adding `// [!code --]` or `// [!code ++]` comments to a line will create a diff for that line while preserving the code block's syntax highlighting.
**Input:**
````
```js
export default {
data () {
return {
error: 'Removed', // [\!code --]
warning: 'Added' // [\!code ++]
}
}
}
```
````
**Output:**
```js
export default {
data() {
return {
error: 'Removed', // [!code --]
warning: 'Added' // [!code ++]
}
}
}
```
::: tip Use the valid line comment syntax for the language in different code blocks
For example, in bash code blocks, use `# [!code ++]`
````md
```bash
mkdir hello && cd hello # [\!code ++]
```
````
```bash
mkdir hello && cd hello # [!code ++]
```
:::
## Highlight "Errors" and "Warnings"
Adding `// [!code warning]` or `// [!code error]` comments to a line will apply corresponding coloring to that line.
**Input:**
````
```js
export default {
data () {
return {
error: 'Error', // [\!code error]
warning: 'Warning' // [\!code warning]
}
}
}
```
````
**Output:**
```js
export default {
data() {
return {
error: 'Error', // [!code error]
warning: 'Warning' // [!code warning]
}
}
}
```
::: tip Use the valid line comment syntax for the language in different code blocks
For example, in bash code blocks, use `# [!code warning]`
````md
```bash
mkdir hello && cd hello # [\!code warning]
```
````
```bash
mkdir hello && cd hello # [!code warning]
```
:::
## Word Highlighting in Code Blocks
**Input:**
````
```ts
export function foo() { // [\!code word:Hello]
const msg = 'Hello World'
console.log(msg) // prints Hello World
}
```
````
**Output:**
```ts
export function foo() { // [!code word:Hello]
const msg = 'Hello World'
console.log(msg) // prints Hello World
}
```
You can also specify the number of occurrences to highlight, for example `[!code word:options:2]`
will highlight only the first two occurrences of `options`.
**Input:**
````
```ts
// [\!code word:options:2]
const options = { foo: 'bar' }
options.foo = 'baz'
console.log(options.foo) // This won't be highlighted
```
````
**Output:**
```ts
// [!code word:options:2]
const options = { foo: 'bar' }
options.foo = 'baz'
console.log(options.foo) // This won't be highlighted
```
:::tip Use the valid line comment syntax for the language in different code blocks
For example, in bash code blocks, use `# [!code word:hello]`
````md
```bash
mkdir hello && cd hello # [\!code word:hello]
```
````
```bash
mkdir hello && cd hello # [!code word:hello]
```
:::
## Whitespace in Code Blocks
Render whitespace characters (tabs and spaces) as visible.
Add `:whitespace` after the code block.
<!-- @include: ../../../snippet/whitespace.snippet.md -->
You can also globally enable the `whitespace` feature in `codeHighlighter`:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
whitespace: true, // [!code ++]
}
})
})
```
When globally enabled, you can use `:no-whitespace` to disable the `whitespace` feature for a specific code block.
## Collapsed Code Blocks
Sometimes code blocks can be very long, which can be cumbersome when reading other content and
affect the reading experience. In such cases, you can collapse code blocks.
Add `:collapsed-lines` after the code block to collapse it, starting from line 15 by default.
**Input:**
````txt
```css :collapsed-lines
html {
margin: 0;
background: black;
height: 100%;
}
... more code
```
````
**Output:**
```css :collapsed-lines
html {
margin: 0;
background: black;
height: 100%;
}
body {
margin: 0;
width: 100%;
height: inherit;
}
/* the three main rows going down the page */
body > div {
height: 25%;
}
.thumb {
float: left;
width: 25%;
height: 100%;
object-fit: cover;
}
.main {
display: none;
}
.blowup {
display: block;
position: absolute;
object-fit: contain;
object-position: center;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2000;
}
.darken {
opacity: 0.4;
}
```
You can also specify the starting line for collapsing. `:collapsed-lines=10` indicates collapsing starts from the tenth line.
**Input:**
````txt
```css :collapsed-lines=10
html {
margin: 0;
background: black;
height: 100%;
}
... more code
```
````
**Output:**
```css :collapsed-lines=10
html {
margin: 0;
background: black;
height: 100%;
}
body {
margin: 0;
width: 100%;
height: inherit;
}
/* the three main rows going down the page */
body > div {
height: 25%;
}
.thumb {
float: left;
width: 25%;
height: 100%;
object-fit: cover;
}
.main {
display: none;
}
.blowup {
display: block;
position: absolute;
object-fit: contain;
object-position: center;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2000;
}
.darken {
opacity: 0.4;
}
```
You can also globally enable the `collapsed-lines` feature in `codeHighlighter`:
::: code-tabs
@tab .vuepress/config.ts
```ts
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
collapsedLines: true // [!code ++]
}
})
})
```
:::
When globally enabled, you can use `:no-collapsed-lines` to disable the `collapsed-lines` feature for a specific code block.

View File

@ -0,0 +1,43 @@
---
title: Import Code
icon: mdi:import
createTime: 2025/10/08 10:39:22
permalink: /en/guide/code/import/
---
## Overview
Importing code allows you to include code from another file in your markdown file and have it highlighted.
It helps you reference code from other files in your articles, avoiding duplicate code writing.
## Syntax
You can use the following syntax to import code blocks from files:
**Input:**
```md
@[code](../snippet/snippet-1.js)
```
**Output:**
@[code](../../../snippet/snippet-1.js)
If you only want to import a specific portion of the file:
```md
<!-- Import only lines 1 to 10 -->
@[code{1-10}](../snippet/snippet-1.js)
```
The code language is inferred from the file extension, but we recommend explicitly specifying it:
```md
<!-- Specify code language -->
@[code js](../snippet/snippet-1.js)
<!-- Line highlighting -->
@[code js{2,4-5}](../foo.js)
```

View File

@ -0,0 +1,94 @@
---
title: Introduction
icon: ic:outline-code
createTime: 2025/10/08 10:35:45
permalink: /en/guide/code/intro/
---
## Overview
The theme uses [Shiki](https://shiki.style/) to implement syntax highlighting in Markdown code blocks.
::: important Important Changes <Badge type="danger" text="1.0.0-rc.136" />
Starting from version ==1.0.0-rc.136==, the theme has migrated the code highlighting plugin from the
internally implemented `@vuepress-plume/plugin-shikiji` to
[@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/markdown/shiki.html)
provided by the [vuepress ecosystem](https://github.com/vuepress/ecosystem).
_(No need to worry about significant changes - I am also one of the main developers of_
_`@vuepress/plugin-shiki`, which implements functionality consistent with the theme's original plugin.)_
Some configuration items require adjustments:
- The `languages` configuration has been changed to the `langs` option. You no longer need to manually
add the languages you use; the plugin will automatically recognize and load language packages as needed.
- The `themes` configuration has been changed to:
- When using a single theme configuration, use the `theme` option to configure the code block theme
- When using dual theme configuration, use the `themes` option to configure the code block themes.
:::
## Languages
[Shiki](https://shiki.style/) supports over 190+ languages.
You can view the complete list of supported languages at [languages](https://shiki.style/languages).
You can use the following syntax to enable highlighting for code written in your chosen language:
````md
``` [lang]
<!-- Your code content -->
```
````
Where `[lang]` represents the programming language you are using.
Example:
````md
// [!code word:js]
``` js
const a = 1
console.log(a)
```
````
```js
const a = 1
console.log(a)
```
## Highlighting Themes
[Shiki](https://shiki.style/) supports over 40+ highlighting themes.
You can find the complete list of supported themes at [Themes](https://shiki.style/themes) and
customize the highlighting theme according to your preference.
Theme Plume's default configuration for code block themes:
```ts
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
themes: { light: 'vitesse-light', dark: 'vitesse-dark' }, // [!code highlight]
}
})
})
```
The default configuration supports using the `vitesse-light`/`vitesse-dark` themes for light/dark modes respectively.
## Additional Features
Thanks to the powerful capabilities of [Shiki](https://shiki.style/), Theme Plume provides additional
[feature support](./features.md) for code blocks, enhancing their expressive power.
Additionally, to facilitate better code demonstrations, Theme Plume provides syntax support for embedding
[CodePen](../repl/codepen.md), [Js Fiddle](../repl/jsFiddle.md), [Code Sandbox](../repl/codeSandbox.md),
and [Replit](../repl/replit.md), allowing you to easily embed code demonstrations.
## Examples
<!-- @include: ../../../snippet/code-block.snippet.md -->

View File

@ -0,0 +1,767 @@
---
title: Two Slash
icon: material-symbols:experiment-outline
createTime: 2025/10/08 11:46:49
permalink: /en/guide/markdown/twoslash/
outline: [2, 4]
---
## Overview
Adds support for [TypeScript TwoSlash](https://www.typescriptlang.org/dev/twoslash/) in code blocks. Provides inline type hints within code blocks.
This feature is powered by [shiki](https://shiki.style/) and [@shikijs/twoslash](https://shiki.style/packages/twoslash), and integrated in [@vuepress-plume/plugin-shikiji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-shikiji).
::: important __twoslash__ is an advanced feature that requires proficiency in
[TypeScript](https://www.typescriptlang.org/) and understanding of [twoslash syntax](https://twoslash.netlify.app/).
:::
::: warning
`twoslash` is a relatively time-consuming feature. Since it requires type compilation of code,
if the code imports large packages, it can take considerable time.
Specifically, since VuePress pre-compiles all markdown files on startup, this directly affects VuePress startup time.
If you include many `twoslash` code blocks, this may significantly increase VuePress startup time.
For example, without `twoslash`, VuePress startup time typically ranges from `300ms ~ 1000ms`.
With `twoslash`, compiling a single `twoslash` code block may require an additional `500ms` or more.
However, don't worry about compilation time during markdown file hot updates.
The theme optimizes code highlighting compilation time - even if a single markdown file contains
multiple code blocks, the theme only compiles __modified code blocks__, so hot updates remain fast.
:::
[twoslash](https://twoslash.netlify.app/) is a `JavaScript` and `TypeScript` markup language.
You can write code examples that describe entire `JavaScript` projects.
`twoslash` treats __double-slash comments__ (`//`) as preprocessor directives for code examples.
`twoslash` uses the same compiler APIs as text editors to provide type-driven hover information, accurate errors, and type annotations.
## Feature Preview
Hover over __variables__ or __functions__ to see the effect:
```ts twoslash
import { createHighlighter } from 'shiki'
const highlighter = await createHighlighter({ themes: ['nord'], langs: ['javascript'] })
// ^?
//
// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```
## Configuration
### Enabling the Feature
Before enabling this feature, you need to install the `@vuepress/shiki-twoslash` package:
::: npm-to
```sh
npm i @vuepress/shiki-twoslash
```
:::
Enable the `twoslash` option in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
twoslash: true,
},
}),
})
```
::: important
For most users, `twoslash` is not a necessary feature, and `twoslash`-related dependencies have large
package sizes. Therefore, all `twoslash` implementations have been moved to `@vuepress/shiki-twoslash`,
which effectively reduces the initial installation size of the theme.
You only need to install `@vuepress/shiki-twoslash` when you require the `twoslash` feature.
:::
### Importing Type Files from `node_modules`
The main feature of __twoslash__ is type compilation of code blocks, which natively supports importing type files from your project's `node_modules`.
For example, if you need type hints for `express`, you need to install the `@types/express` dependency in your project:
::: npm-to
```sh
npm i -D @types/express
```
:::
Then, you can use `express` types in code blocks as follows:
```` md
```ts twoslash
import express from 'express'
const app = express()
```
````
### Importing Local Type Files
For importing local type files, since it's difficult to determine the real path of code in code blocks
during compilation, it's not intuitive to import type files via __relative paths__ in code blocks.
#### Reading Path Mappings from `tsconfig.json`
The theme supports reading path mappings from `compilerOptions.paths` in `tsconfig.json` at the project root to solve this issue.
Assume your project's `tsconfig.json` is configured as follows:
```json title="tsconfig.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
```
You can directly use paths starting with `@/` in code blocks to import type files from the `src` directory:
````md
```ts twoslash
import type { Foo } from '@/foo'
const foo: Foo = 1
```
````
#### Reading Path Mappings from `shiki.twoslash`
You can configure `compilerOptions` in `shiki.twoslash` to solve this issue:
```ts title=".vuepress/config.ts"
import path from 'node:path'
export default defineUserConfig({
theme: plumeTheme({
codeHighlighter: {
twoslash: {
compilerOptions: { // [!code hl:8]
paths: {
// Relative to working directory `process.cwd()`
'@/*': ['./src/*'],
// Using absolute paths
'@@/*': [path.resolve(process.cwd(), './src/*')],
}
}
}
},
}),
})
```
You can directly use paths starting with `@/` in code blocks to import type files from the `src` directory:
````md
```ts twoslash
import type { Foo } from '@/foo'
const foo: Foo = 1
```
````
::: important
Using `plugins.shiki.twoslash.compilerOptions` allows more flexible configuration of type compilation.
You can modify `baseUrl` and other configuration options here.
[Refer to CompilerOptions](https://www.typescriptlang.org/tsconfig/#compilerOptions)
Usually you only need to configure the `paths` option, keeping other options default unless you understand what you're configuring.
:::
## Usage
::: warning `twoslash` only supports `typescript` and `vue` code blocks.
:::
After enabling this feature, simply add the `twoslash` keyword after the code language declaration in your existing markdown code block syntax:
````md{1}
```ts twoslash
const a = 1
```
````
The theme only processes code blocks with the `twoslash` keyword.
## Syntax Reference
Complete syntax reference: [ts-twoslasher](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher) and [shikijs-twoslash](https://twoslash.netlify.app/)
`twoslash` treats __double slashes__ as preprocessor directives for code examples. Therefore, all annotations are added after `//`.
### Symbol Annotations
Common `twoslash` annotations:
#### `^?` {#extract-type}
Use `^?` to extract type information for specific identifiers on the code line above it.
__Input:__
````md
```ts twoslash
const hi = 'Hello'
const msg = `${hi}, world`
// ^?
```
````
__Output:__
```ts twoslash
const hi = 'Hello'
const msg = `${hi}, world`
// ^?
//
```
::: important The symbol `^` must correctly point to the variable whose type you want to highlight.
:::
#### `^|` {#completions}
Use `^|` to extract autocompletion information at a specific position.
__Input:__
````md
```ts twoslash
// @noErrors
console.e
// ^|
```
````
__Output:__
```ts twoslash
// @noErrors
console.e
// ^|
//
```
::: important The symbol `^` must correctly point to the position where you want content prediction.
:::
Twoslash requests TypeScript for autocompletion suggestions at the `^` position,
then filters possible outputs based on letters after the `.`. Up to 5 inline results are displayed,
and if a completion item is marked as deprecated, the output reflects this.
In this case, Twoslash requests completion suggestions for `console` from TypeScript,
then filters for items starting with `e`. Note the `// @noErrors` compiler flag is set because `console.e`
is a failing TypeScript code example, but we don't care about that.
#### `^^^` {#highlighting}
Use `^^^` to highlight specific ranges on the line above it.
__Input:__
````md
```ts twoslash
function add(a: number, b: number) {
// ^^^
return a + b
}
```
````
__Output:__
```ts twoslash
function add(a: number, b: number) {
// ^^^
return a + b
}
```
::: important Use consecutive `^` symbols to correctly point to the range you want to highlight.
:::
### `@filename` {#import-files}
`@filename: <filename>` declares which file subsequent code will come from. You can import this file via `import` in other parts of the code.
__Input:__
````md
```ts twoslash
// @filename: sum.ts
export function sum(a: number, b: number): number {
return a + b
}
// @filename: ok.ts
import { sum } from './sum'
sum(1, 2)
// @filename: error.ts
// @errors: 2345
import { sum } from './sum'
sum(4, 'woops')
```
````
__Output:__
```ts twoslash
// @filename: sum.ts
export function sum(a: number, b: number): number {
return a + b
}
// @filename: ok.ts
import { sum } from './sum'
sum(1, 2)
// @filename: error.ts
// @errors: 2345
import { sum } from './sum'
sum(4, 'woops')
```
### Code Cutting
#### `---cut-before---` {#cut-before}
After TypeScript generates the project and extracts all editor information (like identifiers,
queries, highlights, etc.), cutting operations adjust all offsets and line numbers to fit the smaller output.
Users see content below `// ---cut-before---`. The shorthand `// ---cut---` is also supported.
__Input:__
````md
```ts twoslash
const level: string = 'Danger'
// ---cut---
console.log(level)
````
__Output:__
```ts twoslash
const level: string = 'Danger'
// ---cut---
console.log(level)
```
Show only a single file:
__Input:__
````md
```ts twoslash
// @filename: a.ts
export const helloWorld: string = 'Hi'
// ---cut---
// @filename: b.ts
import { helloWorld } from './a'
console.log(helloWorld)
```
````
__Output:__
```ts twoslash
// @filename: a.ts
export const helloWorld: string = 'Hi'
// ---cut---
// @filename: b.ts
import { helloWorld } from './a'
console.log(helloWorld)
```
Only the last two lines are displayed, but to TypeScript, this is a program with two files,
and all IDE information is correctly connected between files. This is why `// @filename: [file]`
is the only Twoslash command not removed, as it can be `---cut---` if irrelevant.
#### `---cut-after---` {#cut-after}
The sibling of `---cut-before---`, used to trim everything after the symbol:
__Input:__
````md
```ts twoslash
const level: string = 'Danger'
// ---cut-before---
console.log(level)
// ---cut-after---
console.log('This is not shown')
```
````
__Output:__
```ts twoslash
const level: string = 'Danger'
// ---cut-before---
console.log(level)
// ---cut-after---
console.log('This is not shown')
```
#### `---cut-start---` and `---cut-end---` {#cut-start-end}
You can also use the `---cut-start---` and `---cut-end---` pair to cut code segments between two symbols.
__Input:__
````md
```ts twoslash
const level: string = 'Danger'
// ---cut-start---
console.log(level) // This part is cut
// ---cut-end---
console.log('This is shown')
```
````
__Output:__
```ts twoslash
const level: string = 'Danger'
// ---cut-start---
console.log(level) // This part is cut
// ---cut-end---
console.log('This is shown')
```
Multiple instances are supported to cut multiple sections, but symbols must appear in pairs.
### Custom Output Messages {id=twoslash-custom-message}
`@log`, `@error`, `@warn`, and `@annotate` output custom messages at different levels to users.
````md
```ts twoslash
// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```
````
```ts twoslash
// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```
### Output Compiled Files
Running Twoslash code examples triggers full TypeScript compilation runs that create files in a virtual
file system. You can replace code example content with results from running TypeScript on the project.
#### `@showEmit`
`// @showEmit` is the primary command to tell Twoslash you want to replace the code example output with the equivalent `.js` file.
__Input:__
````md
```ts twoslash
// @showEmit
const level: string = 'Danger'
```
````
__Output:__
```ts twoslash
// @showEmit
const level: string = 'Danger'
```
The result shows the `.js` file corresponding to this `.ts` file. You can see TypeScript output removes `: string` and adds `export {}`.
#### `@showEmittedFile: [file]`
While `.js` files may be the most useful out of the box, TypeScript does emit other files (`.d.ts` and `.map`)
when proper flags are enabled, and with multi-file code examples, you may need to tell Twoslash which file to
display. For all these cases, you can also add `@showEmittedFile: [file]` to tell Twoslash which file you want to display.
__Display `.d.ts` file for TypeScript code example:__
__Input:__
````md
```ts twoslash
// @declaration
// @showEmit
// @showEmittedFile: index.d.ts
export const hello = 'world'
```
````
__Output:__
```ts twoslash
// @declaration
// @showEmit
// @showEmittedFile: index.d.ts
export const hello = 'world'
```
__Display `.map` file from JavaScript to TypeScript:__
__Input:__
````md
```ts twoslash
// @sourceMap
// @showEmit
// @showEmittedFile: index.js.map
export const hello = 'world'
```
````
__Output:__
```ts twoslash
// @sourceMap
// @showEmit
// @showEmittedFile: index.js.map
export const hello = 'world'
```
__Display `.map` for `.d.ts` files (mainly for project references):__
__Input:__
````md
```ts twoslash
// @declaration
// @declarationMap
// @showEmit
// @showEmittedFile: index.d.ts.map
export const hello: string = 'world'
```
````
__Output:__
```ts twoslash
// @declaration
// @declarationMap
// @showEmit
// @showEmittedFile: index.d.ts.map
export const hello: string = 'world'
```
__Generate `.js` file for `b.ts`:__
__Input:__
````md
```ts twoslash
// @showEmit
// @showEmittedFile: b.js
// @filename: a.ts
export const helloWorld: string = 'Hi'
// @filename: b.ts
import { helloWorld } from './a'
console.log(helloWorld)
```
````
__Output:__
```ts twoslash
// @showEmit
// @showEmittedFile: b.js
// @filename: a.ts
export const helloWorld: string = 'Hi'
// @filename: b.ts
import { helloWorld } from './a'
console.log(helloWorld)
```
### `@errors`
`@errors: <error code>` shows how code produces errors:
__Input:__
````md
```ts twoslash
// @errors: 2322 2588
const str: string = 1
str = 'Hello'
````
__Output:__
```ts twoslash
// @errors: 2322 2588
const str: string = 1
str = 'Hello'
```
You need to declare corresponding TypeScript error codes after `@errors`. Separate multiple error codes with spaces.
::: note
If you don't know which error code to add, try writing the code first and wait for compilation to fail.
You should see relevant error information in the console, then find the corresponding error code in the
error message's `description`. Then add the error code to `@errors`.
Don't worry about compilation failures terminating the process - the theme displays error information
when compilation fails while outputting uncompiled code in the code block.
:::
### `@noErrors`
Suppresses all errors in code. You can also provide error codes to suppress specific errors.
__Input:__
````md
```ts twoslash
// @noErrors
const str: string = 1
str = 'Hello'
```
````
__Output:__
```ts twoslash
// @noErrors
const str: string = 1
str = 'Hello'
```
### `@noErrorsCutted`
Ignores errors occurring in cut code.
__Input:__
````md
```ts twoslash
// @noErrorsCutted
const hello = 'world'
// ---cut-after---
hello = 'hi' // Should be an error but ignored because it's cut.
```
````
__Output:__
```ts twoslash
// @noErrorsCutted
const hello = 'world'
// ---cut-after---
hello = 'hi' // Should be an error but ignored because it's cut.
```
### `@noErrorValidation`
Disables error validation. Error messages will still render, but Twoslash won't throw errors from compile-time code.
__Input:__
````md
```ts twoslash
// @noErrorValidation
const str: string = 1
```
````
__Output:__
```ts twoslash
// @noErrorValidation
const str: string = 1
```
### `@keepNotations`
Tells Twoslash not to remove any comments and keep original code unchanged. Nodes will contain position information of original code.
__Input:__
````md
```ts twoslash
// @keepNotations
// @module: esnext
// @errors: 2322
const str: string = 1
```
````
__Output:__
```ts twoslash
// @keepNotations
// @module: esnext
// @errors: 2322
const str: string = 1
```
### Overriding Compiler Options
Use `// @name` and `// @name: value` comments to override TypeScript's
[compiler options](https://www.typescriptlang.org/tsconfig#compilerOptions). These comments will be removed from output.
__Input:__
````md
```ts twoslash
// @noImplicitAny: false
// @target: esnext
// @lib: esnext
// This should throw an error,
// but since we disabled noImplicitAny, it won't.
const fn = a => a + 1
```
````
__Output:__
```ts twoslash
// @noImplicitAny: false
// @target: esnext
// @lib: esnext
// This should throw an error,
// but since we disabled noImplicitAny, it won't.
const fn = a => a + 1
```

View File

@ -0,0 +1,110 @@
---
title: Badge
icon: iconamoon:badge-light
createTime: 2025/10/08 22:45:50
permalink: /en/guide/components/badge/
---
## Overview <Badge type="tip" text="badge" />
Use the `<Badge>` component to display inline information such as status or labels.
Pass the content you want to display to the `text` prop of the `<Badge>` component.
## Props
:::: field-group
::: field name="type" type="'info' | 'tip' | 'warning' | 'danger' | string" default="'tip'" optional
Badge type. Different types use different color schemes. Custom types are supported.
:::
::: field name="text" type="string" default="''" optional
Badge text content.
:::
::: field name="color" type="string" optional
Custom badge text color.
:::
::: field name="bgColor" type="string" optional
Custom badge background color.
:::
::: field name="borderColor" type="string" optional
Custom badge border color.
:::
::::
## Examples
**Input:**
```md :no-line-numbers
- VuePress - <Badge type="info" text="v2" />
- VuePress - <Badge type="tip" text="v2" />
- VuePress - <Badge type="warning" text="v2" />
- VuePress - <Badge type="danger" text="v2" />
- VuePress - <Badge text="v2" color="#8e5cd9" bg-color="rgba(159, 122, 234, 0.16)" />
```
**Output:**
- VuePress - <Badge type="info" text="v2" />
- VuePress - <Badge type="tip" text="v2" />
- VuePress - <Badge type="warning" text="v2" />
- VuePress - <Badge type="danger" text="v2" />
- VuePress - <Badge text="v2" color="#8e5cd9" bg-color="rgba(159, 122, 234, 0.16)" />
Using custom `type` enables richer visual presentations.
**Input:**
1. Add predefined styles in the theme's [custom style file](../custom/style.md.md):
```css
/* Light theme */
.vp-badge.important {
color: #8e5cd9;
background-color: rgba(159, 122, 234, 0.14);
border-color: transparent;
}
/* Dark theme */
[data-theme="dark"] .vp-badge.important {
color: #8e5cd9;
background-color: rgba(159, 122, 234, 0.16);
border-color: transparent;
}
/**
'important' is a custom type
*/
```
2. Use the custom `type`:
```md :no-line-numbers
VuePress - <Badge type="important" text="v2" />
```
**Output:**
VuePress - <Badge type="important" text="v2" />
<style>
/* Light theme */
.vp-badge.important {
color: #8e5cd9;
background-color: rgba(159, 122, 234, 0.14);
border-color: transparent;
}
/* Dark theme */
[data-theme="dark"] .vp-badge.important {
color: #8e5cd9;
background-color: rgba(159, 122, 234, 0.16);
border-color: transparent;
}
</style>

View File

@ -0,0 +1,69 @@
---
title: Card Grid
icon: vaadin:grid-h
createTime: 2025/10/08 23:38:33
permalink: /en/guide/components/card-grid/
---
## Overview
The `<CardGrid>` component is used when multiple cards need to be arranged. Cards are automatically arranged when sufficient space is available.
## Props
:::: field-group
::: field name="cols" type="number | { sm: number, md: number, lg: number }" default="2"
Number of columns for card arrangement.
The component automatically adjusts the number of columns based on screen width by default.
Two columns are displayed when space permits, while a single column is shown on smaller screens.
The `cols` prop configures the number of columns. When a `number` is provided,
all screen sizes display `number` columns. When `{ sm: number, md: number, lg: number }` is provided,
the number of columns adjusts automatically based on screen width.
- `sm` : `< 768px`
- `md` : `>= 768px < 960px`
- `lg` : `>= 960px`
It is recommended that the provided `number` does not exceed `3`.
:::
::::
## Examples
**Input:**
```md :no-line-numbers
<CardGrid>
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
</CardGrid>
<CardGrid>
<LinkCard title="Card Title" href="/" />
<LinkCard icon="twemoji:astonished-face" title="Card Title" href="/" />
</CardGrid>
```
**Output:**
<CardGrid>
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
</CardGrid>
<CardGrid>
<LinkCard title="Link Card" href="/" />
<LinkCard icon="twemoji:astonished-face" title="Link Card" href="/" />
</CardGrid>

View File

@ -0,0 +1,273 @@
---
title: Masonry Card
icon: ri:layout-masonry-line
createTime: 2025/10/08 17:17:06
permalink: /en/guide/components/card-masonry/
---
## Overview
The Masonry Card is a versatile container component that automatically calculates the height of
each **item** and arranges them in a masonry layout. Any content can be placed within `<CardMasonry>`.
::: details What is an item?
An item represents individual content such as an image, text, video, etc.
- In Markdown syntax, any content occupying its own line (with blank lines before and after) is considered an item.
- In HTML structure, each direct child element of the container is considered an item.
:::
```md
<CardMasonry>
<img src="..." alt="...">
<!-- More content -->
</CardMasonry>
```
## Props
:::: field-group
::: field name="cols" type="number | { sm: number, md: number, lg: number }" optional
Number of columns.
The component automatically adjusts the number of columns based on screen width by default.
Three columns are displayed when space permits, while two columns are shown on smaller screens.
The `cols` prop configures the number of columns. When a `number` is provided,
all screen sizes display `number` columns. When `{ sm: number, md: number, lg: number }` is provided,
the number of columns adjusts automatically based on screen width.
- `sm` : `< 640px`
- `md` : `>= 640px < 960px`
- `lg` : `>= 960px`
:::
::: field name="gap" type="number" optional default="16"
Gap between columns.
:::
::::
## Markdown Syntax Support
In Markdown, the `::: card-masonry` container can be used instead of `<CardMasonry>`.
``` md
::: card-masonry cols="3" gap="16" <!-- [!code hl]-->
![](/images/1.png)
<!-- More content -->
::: <!-- [!code hl]-->
```
## Examples
### Image Masonry
Masonry layout is particularly suitable for displaying images. You can directly place `![](image_url)` within `::: card-masonry`.
**Input:**
``` md
::: card-masonry
![](image_url)
![](image_url)
![](image_url)
![](image_url)
![](image_url)
![](image_url)
:::
```
**Output:**
::: card-masonry
![a](https://images.unsplash.com/photo-1719937051124-91c677bc58fc?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwxfHx8ZW58MHx8fHx8)
![b](https://plus.unsplash.com/premium_photo-1731329153355-1015daf2cb92?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwyfHx8ZW58MHx8fHx8)
![c](https://images.unsplash.com/photo-1731323036230-fb37b4d9ed71?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwzfHx8ZW58MHx8fHx8)
![a](https://images.unsplash.com/photo-1730630906214-1256b57d65b7?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw0fHx8ZW58MHx8fHx8)
![b](https://plus.unsplash.com/premium_photo-1733864822156-f3cf26187fd9?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw2fHx8ZW58MHx8fHx8)
![a](https://images.unsplash.com/photo-1731756748993-85e1513dfc76?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw3fHx8ZW58MHx8fHx8)
![b](https://images.unsplash.com/photo-1733705879328-a18f2a025c67?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw4fHx8ZW58MHx8fHx)
:::
### Card Masonry
Masonry layout is also suitable for displaying cards. You can place `::: card` containers within `::: card-masonry`.
**Input:**
``` md :collapsed-lines
:::: card-masonry
::: card title="Card 1"
Card content
:::
::: card title="Card 2"
Card content
Additional card content
:::
::: card title="Card 3"
Card content
:::
::: card title="Card 4"
Card content
:::
::: card title="Card 5"
Card content
Additional card content
:::
::: card title="Card 6"
Card content
:::
::::
```
**Output:**
:::: card-masonry
::: card title="Card 1"
Card content
:::
::: card title="Card 2"
Card content
Additional card content
:::
::: card title="Card 3"
Card content
:::
::: card title="Card 4"
Card content
:::
::: card title="Card 5"
Card content
Additional card content
:::
::: card title="Card 6"
Card content
:::
::::
### Code Block Masonry
**Input:**
````md :collapsed-lines
:::card-masonry
```ts
const a = 1
```
```json
{
"name": "John"
}
```
```css
p {
color: red;
}
```
```html
<html>
<body>
<h1>Hello world</h1>
</body>
</html>
```
```ts
const a = 12
const b = 1
```
```rust
fn main() {
println!("Hello, world!");
}
```
:::
````
**Output:**
:::card-masonry
```ts
const a = 1
```
```json
{
"name": "John"
}
```
```css
p {
color: red;
}
```
```html
<html>
<body>
<h1>Hello world</h1>
</body>
</html>
```
```ts
const a = 12
const b = 1
```
```rust
fn main() {
println!("Hello, world!");
}
```
:::

View File

@ -0,0 +1,68 @@
---
title: Card
icon: solar:card-broken
createTime: 2025/10/08 23:09:07
permalink: /en/guide/components/card/
---
## Overview
Use the `<Card>` component to display cards within a page.
Alternatively, the markdown [card container](../markdown/card.md) syntax can be used as a substitute for the `<Card>` component.
## Props
:::: field-group
::: field name="title" type="string" default="''" optional
Card title.
:::
::: field name="icon" type="string | { svg: string }" default="''" optional
Icon displayed to the left of the title. Supports all icons from iconify, or an image URL can be used.
:::
::::
## Slots
| Name | Description |
| ------- | -------------------- |
| default | Card content. |
| title | Custom title content.|
## Examples
**Input:**
```md :no-line-numbers
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
<Card>
<template #title>
<p style="color: red">Card Title</p>
</template>
This is the card content.
</Card>
```
**Output:**
<Card title="Card Title" icon="twemoji:astonished-face">
This is the card content.
</Card>
<Card>
<template #title>
<p style="color: red;margin:0">Card Title</p>
</template>
This is the card content.
</Card>
:::info
Markdown syntax can also be used inside slots. However,
please note that an empty line is required between the markdown syntax and the tags. Otherwise, it will be interpreted as plain text.
:::

View File

@ -0,0 +1,32 @@
---
title: Home Layout Container
icon: tabler:container
createTime: 2025/10/08 23:49:40
permalink: /en/guide/components/home-box/
---
## Home Layout Container
The `<HomeBox>` component provides a wrapper container for areas when customizing the homepage layout.
## Props
:::: field-group
::: field name="type" type="string" default="''" optional
Area type.
:::
::: field name="full" type="boolean" default="false" optional
Whether to enable fullscreen mode.
:::
::: field name="background-image" type="string" default="''" optional
Area background image.
:::
::: field name="background-attachment" type="'fixed' | 'local'" default="'local'" optional
Area background attachment method.
:::
::::

View File

@ -0,0 +1,83 @@
---
title: Icon
icon: grommet-icons:emoji
createTime: 2025/10/08 22:58:39
permalink: /en/guide/components/icon/
---
## Overview
The `<Icon />` component loads icons from different icon libraries based on the `markdown.icon` configuration.
[The theme also provides markdown syntax support. Click to learn more](../markdown/icons.md){.read-more}
## Configuration
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: { // [!code ++:3]
icon: { provider: 'iconify' } // Enabled by default
}
})
})
```
```ts
interface IconOptions {
/**
* Icon provider
*/
provider: 'iconify' | 'iconfont' | 'fontawesome'
/**
* Default icon prefix. Different providers have different default prefixes
* - iconify - Default: `''`
* - iconfont - Default: `iconfont icon-`
* - fontawesome - Default: `fas`
*/
prefix?: string
/**
* Icon asset links
*/
assets?: IconAssetLink | IconAssetLink[]
size?: string | number
color?: string
}
```
## Props
:::: field-group
::: field name="name" type="string" default="''" optional
Icon name. When `markdown.icon.prefix` has a value, the prefix in `name` can be omitted.
:::
::: field name="color" type="string" default="'currentcolor'" optional
Icon color.
:::
::: field name="size" type="string" default="'1em'" optional
Icon size.
:::
::::
## Examples
**Input:**
```md :no-line-numbers
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
```
**Output:**
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />

View File

@ -0,0 +1,135 @@
---
title: Image Card
icon: fa:photo
createTime: 2025/10/08 23:35:51
permalink: /en/guide/components/image-card/
---
## Overview
Use the `<ImageCard>` component to display image cards on a page.
The Image Card differs from markdown's standard image insertion method by presenting more information
related to the image, including title, description, author, link, etc.
It is suitable for scenarios such as photography works, design works, promotional posters, and more.
## Props
:::: field-group
::: field name="image" type="string" required
The image URL. Local images must use an absolute path, i.e., a path starting with `/`, pointing to the `/public` directory.
:::
::: field name="title" type="string" optional
The image title.
:::
::: field name="description" type="string" optional
The image description.
:::
::: field name="author" type="string" optional
The image author.
:::
::: field name="href" type="string" optional
The link to navigate to when the image title is clicked.
:::
::: field name="date" type="string | Date | number" optional
The image creation date.
:::
::: field name="width" type="string | number" optional
The image width.
:::
::: field name="center" type="boolean" optional
Whether to center the image when its width is less than the screen width.
:::
::::
## Examples
**Input:**
```md :no-line-numbers
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
description="The lighthouse in today's photo is located at Cabo de São Vicente in the Algarve region of southern Portugal. The Alfanzina Lighthouse, built in 1919, shines over the sea, helping vessels navigate the treacherous waters around the area. This lighthouse is a famous tourist attraction and also a symbol of the region's deep connection with the ocean. If you're fortunate enough to live near the lighthouse, this weekend is the perfect time to visit."
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
```
**Output:**
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
:description="`The lighthouse in today's photo is located at Cabo de São Vicente in the Algarve region of
southern Portugal.The Alfanzina Lighthouse, built in 1919, shines over the sea, helping vessels navigate the
treacherous waters around the area. This lighthouse is a famous tourist attraction and also a symbol of the
region's deep connection with the ocean. If you're fortunate enough to live near the lighthouse, this weekend is the perfect time to visit.`"
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
It can also be placed within a `<CardGrid>` component.
**Input:**
```md :no-line-numbers
<CardGrid>
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
description="..."
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
description="..."
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
</CardGrid>
```
**Output:**
<CardGrid>
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
:description="`The lighthouse in today's photo is located at Cabo de São Vicente in the Algarve region of
southern Portugal.The Alfanzina Lighthouse, built in 1919, shines over the sea, helping vessels navigate the
treacherous waters around the area. This lighthouse is a famous tourist attraction and also a symbol of the
region's deep connection with the ocean. If you're fortunate enough to live near the lighthouse, this weekend is the perfect time to visit.`"
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
<ImageCard
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
title="Alfanzina Lighthouse, Algarve, Portugal"
:description="`The lighthouse in today's photo is located at Cabo de São Vicente in the Algarve region of
southern Portugal.The Alfanzina Lighthouse, built in 1919, shines over the sea, helping vessels navigate the
treacherous waters around the area. This lighthouse is a famous tourist attraction and also a symbol of the
region's deep connection with the ocean. If you're fortunate enough to live near the lighthouse, this weekend is the perfect time to visit.`"
href="/"
author="Andreas Kunz"
date="2024/08/16"
/>
</CardGrid>
[View Photography Works Example](../../../../../blog/1.示例/照片类作品示例.md)

View File

@ -0,0 +1,105 @@
---
title: Link Card
icon: solar:card-send-linear
createTime: 2025/10/08 23:14:00
permalink: /en/guide/components/link-card/
---
## Overview
Use the `<LinkCard>` component to display link cards on pages.
## Props
:::: field-group
::: field name="title" type="string" default="''" optional
Link card title.
:::
::: field name="icon" type="string | { svg: string }" default="''" optional
Icon displayed to the left of the title. Supports icons from providers configured in
[markdown.icon](../features/icon.md), or image URLs can be used.
:::
::: field name="href" type="string" default="''" optional
Link card navigation URL.
:::
::: field name="description" type="string" default="''" optional
Link card description. It is recommended to use the component's default slot for description content.
:::
::: field name="target" type="string" default="''" optional
Link target behavior.
:::
::::
## Slots
| Name | Description |
|---------|----------------------|
| default | Card detail content |
| title | Custom title content |
## Examples
**Input:**
```md :no-line-numbers
<LinkCard title="Title" href="/" description="Card content" />
<LinkCard icon="twemoji:astonished-face" title="Title" href="/" />
```
**Output:**
<LinkCard title="Title" href="/" description="Card content" />
<LinkCard icon="twemoji:astonished-face" title="Title" href="/" />
Using component slots enables richer presentations.
**Input:**
```md :no-line-numbers
<LinkCard title="Title" href="/">
- Card content
- Card content
</LinkCard>
<LinkCard href="/">
<template #title>
<span style="color: red" >Title</span>
</template>
- Card content
- Card content
</LinkCard>
```
**Output:**
<LinkCard title="Title" href="/">
- Card content
- Card content
</LinkCard>
<LinkCard href="/">
<template #title>
<span style="color: red" >Title</span>
</template>
- Card content
- Card content
</LinkCard>
:::info
Markdown syntax can also be used within slots, but note that there must be a blank line between the
markdown syntax and the tags. Otherwise, it will be parsed as plain text.
:::

View File

@ -0,0 +1,204 @@
---
title: Npm Badge
icon: akar-icons:npm-fill
createTime: 2025/10/08 22:07:23
permalink: /en/guide/components/npm-badge/
---
<script setup>
import NpmBadge from 'vuepress-theme-plume/features/NpmBadge.vue'
import NpmBadgeGroup from 'vuepress-theme-plume/features/NpmBadgeGroup.vue'
</script>
## Overview
The Npm Badge component is used to display npm package information and provide relevant links.
The badges are powered by <https://shields.io>.
## Usage
To use this component, you need to manually import the `NpmBadge` or `NpmBadgeGroup` components:
```md :no-line-numbers
<!-- Import in markdown -->
<script setup>
import NpmBadge from 'vuepress-theme-plume/features/NpmBadge.vue'
import NpmBadgeGroup from 'vuepress-theme-plume/features/NpmBadgeGroup.vue'
</script>
<!-- After importing, you can use them in markdown -->
<NpmBadge name="vuepress-theme-plume" type="dm" />
<!-- Display multiple npm badges side by side -->
<NpmBadgeGroup name="vuepress-theme-plume" items="version,dm" />
```
<NpmBadge name="vuepress-theme-plume" type="dm" />
<NpmBadgeGroup name="vuepress-theme-plume" items="version,dm" />
## `<NpmBadge />`
Single npm badge
### Props
:::: field-group
::: field name="name" type="string" optional
npm package name. If empty, it will be obtained from `repo`
:::
::: field name="repo" type="string"
Package GitHub repository address in `owner/repo` format. Required when `name` is empty
:::
::: field name="type" type="NpmBadgeType"
Badge type
:::
::: field name="theme" type="NpmBadgeTheme" optional default="'flat'"
Badge theme
:::
::: field name="label" type="string" optional
Badge label
:::
::: field name="color" type="string" optional default="'#32A9C3'"
Badge color
:::
::: field name="labelColor" type="string" optional default="'#1B3C4A'"
Badge label color
:::
::: field name="branch" type="string" optional default="'main'"
Repository branch
:::
::: field name="dir" type="string" optional
Package directory in repository. Suitable for monorepo projects
:::
::::
### Types
```ts
type NpmBadgeType
// github
= | 'source' // github source
| 'stars' // github stars
| 'forks' // github forks
| 'license' // github license
// npm
| 'version' // npm version
| 'dt' // alias d18m
| 'd18m' // npm downloads last 18 months
| 'dw' // npm downloads weekly
| 'dm' // npm downloads monthly
| 'dy' // npm downloads yearly
type NpmBadgeTheme = 'flat' | 'flat-square' | 'plastic' | 'for-the-badge' | 'social'
```
### Examples
- `<NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="source" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="source" />
- `<NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="stars" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="stars" />
- `<NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="forks" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="forks" />
- `<NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="license" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="license" />
- `<NpmBadge name="vuepress-theme-plume" type="version" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="version" />
- `<NpmBadge name="vuepress-theme-plume" type="dt" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="dt" />
- `<NpmBadge name="vuepress-theme-plume" type="d18m" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="d18m" />
- `<NpmBadge name="vuepress-theme-plume" type="dy" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="dy" />
- `<NpmBadge name="vuepress-theme-plume" type="dm" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="dm" />
- `<NpmBadge name="vuepress-theme-plume" type="dw" />` - <NpmBadge repo="pengzhanbo/vuepress-theme-plume" type="dw" />
## `<NpmBadgeGroup />`
Combines multiple npm badges
### Props
:::: field-group
::: field name="name" type="string" optional
npm package name. If empty, it will be obtained from `repo`
:::
::: field name="repo" type="string"
Package GitHub repository address in `owner/repo` format. Required when `name` is empty
:::
::: field name="items" type="string | NpmBadgeType[]" optional default="[]"
List of badge types. When passing a `string`, separate with `','` and it will be automatically converted to `NpmBadgeType[]`
:::
::: field name="theme" type="NpmBadgeTheme" optional
Badge theme
:::
::: field name="color" type="string" optional
Badge color
:::
::: field name="labelColor" type="string" optional
Badge label color
:::
::: field name="branch" type="string" optional
Repository branch
:::
::: field name="dir" type="string" optional
Package directory in repository. Suitable for monorepo projects
:::
::::
### Slots
`<NpmBadgeGroup />` supports passing multiple `<NpmBadge />` components.
The `Props` declared in `<NpmBadgeGroup />` will be injected into the `<NpmBadge />` components.
This approach is used to implement and simplify badge combinations.
### Examples
**Input:**
```md :no-line-numbers
<NpmBadgeGroup
repo="pengzhanbo/vuepress-theme-plume"
items="stars,version,dm,source"
/>
```
**Output:**
<NpmBadgeGroup repo="pengzhanbo/vuepress-theme-plume" items="stars,version,dm,source" />
Use `<slot />` to flexibly define badge combinations:
**Input:**
```md :no-line-numbers
<NpmBadgeGroup repo="pengzhanbo/vuepress-theme-plume">
<NpmBadge type="stars" />
<NpmBadge type="version" label="npm" />
<NpmBadge type="dm" />
<NpmBadge type="source" />
</NpmBadgeGroup>
```
**Output:**
<NpmBadgeGroup repo="pengzhanbo/vuepress-theme-plume">
<NpmBadge type="stars" />
<NpmBadge type="version" label="npm" />
<NpmBadge type="dm" />
<NpmBadge type="source" />
</NpmBadgeGroup>

View File

@ -0,0 +1,50 @@
---
title: Plot Text
icon: lets-icons:hide-eye
createTime: 2025/10/08 23:02:39
permalink: /en/guide/components/plot/
---
## Overview
Use the `<Plot>` component to display [plot text](../markdown/plot.md) with more flexible control over behavior.
This component is disabled by default and needs to be enabled in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
plot: true,
},
})
})
```
## Props
:::: field-group
::: field name="trigger" type="'hover' | 'click'" default="'hover'" optional
Trigger on mouse hover or click
:::
::: field name="effect" type="'blur' | 'mask'" default="'mask'" optional
Mask layer effect or text blur effect
:::
::::
## Examples
**Input:**
```md :no-line-numbers
- Hover - <Plot>Visible on hover</Plot>
- Click - <Plot trigger="click">Visible on click</Plot>
```
**Output:**
- Hover - <Plot>Visible on hover</Plot>
- Click - <Plot trigger="click">Visible on click</Plot>

View File

@ -0,0 +1,120 @@
---
title: Repo Card
icon: octicon:repo-16
createTime: 2025/10/08 21:11:56
permalink: /en/guide/components/github-repo-card/
---
<script setup>
import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
</script>
## Overview
The Repo Card component is used to display GitHub/Gitee repository information.
## Usage
To use this component, you need to manually import the `RepoCard` component:
```md :no-line-numbers
<!-- Import in markdown -->
<script setup>
import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
</script>
<!-- After importing, you can use it in markdown -->
<RepoCard repo="pengzhanbo/vuepress-theme-plume" />
```
Register as a global component:
```ts title=".vuepress/client.ts"
import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
import { defineClientConfig } from 'vuepress/client'
export default defineClientConfig({
enhance({ app }) {
app.component('RepoCard', RepoCard)
},
})
```
Global components can be used in any other markdown file:
```md
<RepoCard repo="pengzhanbo/vuepress-theme-plume" />
```
### Props
:::: field-group
::: field name="repo" type="string" required
Repository address in `owner/repo` format
:::
::: field name="provider" type="github | gitee" optional default="'github'"
Repository platform. Currently only `github`/`gitee` are supported.
:::
::: field name="fullname" type="boolean" optional
Whether to display the full repository name.
The full repository name is `owner/repo`.
- If the owner is an individual, the full repository name is not displayed by default, only `repo` is shown.
- If the owner is an organization, the full repository name is displayed by default.
:::
::::
## Examples
### Single Card
**Input:**
```md
<RepoCard repo="pengzhanbo/vuepress-theme-plume" />
```
**Output:**
<RepoCard repo="pengzhanbo/vuepress-theme-plume" />
### Multiple Cards
If you want to display multiple cards side by side in a compact way, you can use the `CardGrid` component.
**Input:**
```md
<CardGrid>
<RepoCard repo="vuepress/core" />
<RepoCard repo="vuepress/ecosystem" />
</CardGrid>
```
**Output:**
<CardGrid>
<RepoCard repo="vuepress/core" />
<RepoCard repo="vuepress/ecosystem" />
</CardGrid>
**Input:**
```md
<CardGrid>
<RepoCard repo="pengzb/vuepress-theme-plume" provider="gitee" />
<RepoCard repo="pengzb/vite-plugin-mock-dev-server" provider="gitee" />
</CardGrid>
```
**Output:**
<CardGrid>
<RepoCard repo="pengzb/vuepress-theme-plume" provider="gitee" />
<RepoCard repo="pengzb/vite-plugin-mock-dev-server" provider="gitee" />
</CardGrid>

View File

@ -0,0 +1,290 @@
---
title: Swiper
icon: dashicons:images-alt2
createTime: 2025/10/08 22:00:22
permalink: /en/guide/components/swiper/
---
## Overview
Use the `<Swiper>` component to display image carousels on pages.
## Usage
To use this component, first manually install the `swiper` library:
::: npm-to
```sh
npm install swiper
```
:::
Then, manually import the `Swiper` component:
```md
<!-- Import in markdown -->
<script setup>
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
</script>
<!-- After importing, you can use it in markdown -->
<Swiper :items="['img_link1', 'img_link2']" />
```
Register as a global component:
```ts title=".vuepress/client.ts"
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
import { defineClientConfig } from 'vuepress/client'
export default defineClientConfig({
enhance({ app }) {
app.component('Swiper', Swiper)
},
})
```
Global components can be used in any other markdown file:
```md
<Swiper :items="['img_link1', 'img_link2']" />
```
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
const images = ref([])
async function fetchImage() {
const res = await fetch('https://api.pengzhanbo.cn/wallpaper/bing/zh/').then((res) => res.json())
images.value = res.map(item => ({
name: item.title,
link: item.url,
}))
}
if (!__VUEPRESS_SSR__) {
fetchImage()
}
</script>
**Example:**
<Swiper v-if="images.length" :items="images" />
## Props
| Name | Type | Default | Description |
| ----------------- | ---------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------- |
| items | `string \| { link: string; href?: string; alt?: string}[]` | `[]` | Image link array. When passing objects, `link` represents image URL, `href` represents navigation link, `alt` represents image description |
| width | `number \| string` | `100%` | Carousel area width |
| height | `number \| string` | `100%` | Carousel area height |
| mode | `'banner' \| 'carousel' \| 'broadcast'` | `'banner'` | Carousel mode: `banner`: banner; `carousel`: carousel; `broadcast`: information display |
| navigation | `boolean` | `true` | Whether to show navigation buttons |
| effect | `'slide' \| 'fade' \| 'cube' \| 'coverflow' \| 'flip' \| 'cards' \| 'creative'` | `'slide'` | Carousel effect |
| delay | `number` | `3000` | Carousel interval time. Only effective when `mode: 'banner'`. Unit: `ms` |
| speed | `number` | `300` | Animation duration. Unit: `ms` |
| loop | `boolean` | `true` | Whether to loop |
| pauseOnMouseEnter | `boolean` | `false` | Whether to pause carousel on mouse hover |
| swipe | `boolean` | `true` | Whether to enable gesture swiping |
For more props, refer to [Swiper Documentation](https://swiperjs.com/swiper-api#parameters)
## Reference Examples
### Preset Animation Effects
**cube:**
<Swiper v-if="images.length" :items="images" effect="cube" />
:::details View Code
```md
<Swiper :items="images" effect="cube" />
```
:::
**fade:**
<Swiper v-if="images.length" :items="images" effect="fade" />
:::details View Code
```md
<Swiper :items="images" effect="fade" />
```
:::
**coverflow:**
<Swiper v-if="images.length" :items="images" effect="coverflow" />
:::details View Code
```md
<Swiper :items="images" effect="coverflow" />
```
:::
**flip:**
<Swiper v-if="images.length" :items="images" effect="flip" />
:::details View Code
```md
<Swiper :items="images" effect="flip" />
```
:::
**cards:**
<Swiper v-if="images.length" :items="images" effect="cards" />
:::details View Code
```md
<Swiper :items="images" effect="cards" />
```
:::
### Custom Animation Effects
**Example 1:**
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: [0, 0, -400] },
next: { translate: ['100%', 0, 0] },
}" />
::: details View Code
```md
<Swiper :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: [0, 0, -400] },
next: { translate: ['100%', 0, 0] },
}"
/>
```
:::
**Example 2:**
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: [0, 0, -800], rotate: [180, 0, 0] },
next: { shadow: true, translate: [0, 0, -800], rotate: [-180, 0, 0] },
}" />
:::details View Code
```md
<Swiper :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: [0, 0, -800], rotate: [180, 0, 0] },
next: { shadow: true, translate: [0, 0, -800], rotate: [-180, 0, 0] },
}"
/>
```
:::
**Example 3:**
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: ['-125%', 0, -800], rotate: [0, 0, -90] },
next: { shadow: true, translate: ['125%', 0, -800], rotate: [0, 0, 90] },
}" />
:::details View Code
```md
<Swiper :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, translate: ['-125%', 0, -800], rotate: [0, 0, -90] },
next: { shadow: true, translate: ['125%', 0, -800], rotate: [0, 0, 90] },
}"
/>
```
:::
**Example 4:**
<Swiper v-if="images.length" :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, origin: 'left center', translate: ['-5%', 0, -200], rotate: [0, 100, 0] },
next: { origin: 'right center', translate: ['5%', 0, -200], rotate: [0, -100, 0] },
}" />
:::details View Code
```md
<Swiper :items="images" effect="creative" :creativeEffect="{
prev: { shadow: true, origin: 'left center', translate: ['-5%', 0, -200], rotate: [0, 100, 0] },
next: { origin: 'right center', translate: ['5%', 0, -200], rotate: [0, -100, 0] },
}"
/>
```
:::
### Carousel
<Swiper
v-if="images.length"
:items="images"
mode="carousel"
:height="200"
:slides-per-view="3"
:space-between="20"
:speed="5500"
/>
:::details View Code
```md
<Swiper
:items="images"
mode="carousel"
:height="200"
:slides-per-view="3"
:space-between="20"
:speed="5500"
/>
```
:::
### Information Display
<Swiper
v-if="images.length"
:items="images"
mode="broadcast"
:height="200"
:slides-per-view="3"
:space-between="20"
mousewheel
/>
:::details View Code
```md
<Swiper
:items="images"
mode="broadcast"
:height="200"
:slides-per-view="3"
:space-between="20"
mousewheel
/>
```
:::

View File

@ -0,0 +1,46 @@
---
title: Component Overrides
icon: carbon:cics-sit-overrides
createTime: 2025/10/08 16:20:15
permalink: /en/guide/component-overrides/
---
## Overview
Layout slots are quite practical, but sometimes you might find them not flexible enough.
The theme also provides the capability to override individual components.
::: warning
Before using this feature, you should first familiarize yourself with this theme's source code
and understand the various built-in components to safely override them.
The theme's component source code is hosted on
[GitHub](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/theme/src/client/components) under the MIT license.
:::
## Usage
The theme registers all non-global components with an
[alias](https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias) prefixed with `@theme`.
For example, the alias for `VPFooter.vue` is `@theme/VPFooter.vue`.
If you want to override the `VPFooter.vue` component, you simply need to override this alias in the configuration file `.vuepress/config.ts`:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
import { getDirname, path } from 'vuepress/utils'
const __dirname = getDirname(import.meta.url)
export default defineUserConfig({
theme: plumeTheme(),
alias: {
'@theme/VPFooter.vue': path.resolve(
__dirname,
'./components/MyFooter.vue',
),
},
})
```

View File

@ -1,8 +1,698 @@
--- ---
title: Customize Homepage title: Custom Homepage
icon: material-symbols:home-outline icon: material-symbols:home-outline
createTime: 2025/03/04 12:40:11 createTime: 2025/10/08 10:44:03
permalink: /en/guide/custom-home/ permalink: /en/guide/custom-home/
--- ---
todo ... ## Overview
The theme provides a highly flexible approach to customizing the homepage. You can tailor your homepage according to your specific requirements.
The theme defines your homepage through `frontmatter`. Write the `frontmatter` in the `README.md` file located in your `sourceDir`.
```md title="README.md"
---
home: true
config:
- type: custom
---
```
The theme follows a flow layout approach to render the homepage, dividing it vertically into distinct,
independent sections, with each section applying a different component.
Multiple sections can be defined via the `config` property using an array structure. The `type` field specifies the type of each section.
The theme includes built-in types such as `banner`, `hero`, `text-image`, `image-text`, `features`, `profile`, and `custom`.
You can freely combine these to assemble your custom homepage.
If none of these meet your needs, you can also write custom components to define your homepage.
## Configuration
### home
- Type: `boolean`
Declares whether this page is the homepage.
### config
- Type: `PlumeHomeConfig[]`
- Default: `[]`
Defines the section content of the page based on the order of the array.
```ts
interface PlumeHomeConfigBase {
/**
* The type of this section, which determines the component applied.
*/
type: 'banner' | 'hero' | 'text-image' | 'image-text' | 'features' | 'profile' | 'custom' | string
/**
* Whether this section should occupy the full viewport height.
*/
full?: boolean
/**
* The background image for this section.
* You can define different images for light and dark modes.
*/
backgroundImage?: string | { light: string, dark: string }
/**
* The background attachment style for this section.
*/
backgroundAttachment?: 'fixed' | 'local'
}
```
## Section Types
### banner
- Type: `PlumeThemeHomeBanner`
A large banner section, suitable for placement at the very top of the homepage.
```ts
interface PlumeThemeHomeBanner extends PlumeHomeConfigBase {
type: 'banner'
/**
* The large background banner image.
*/
banner?: string
/**
* Value range: 0 - 1. Configures the opacity of the mask overlay for the homepage banner image.
* Supports different values for light and dark modes. When set to 0, the mask is not displayed.
* This can be used to darken the image if the first-screen banner is too bright.
*/
bannerMask?: number | { light?: number, dark?: number }
hero?: {
name: string
tagline?: string
text?: string
actions?: {
theme?: 'brand' | 'alt'
text: string
link?: string
}
}
}
```
**Example:**
```md
---
home: true
config:
-
type: banner
banner: https://api.pengzhanbo.cn/wallpaper/bing
bannerMask:
light: 0.1
dark: 0.3
hero:
name: Peng Zhanbo
tagline: Front End Developer
text: Even if slow, stop not, even if failed, keep going, but must be able to reach the goal he aims for.
actions:
-
text: My Blog
link: /blog/
theme: brand
-
text: Github
link: https://github.com/pengzhanbo
theme: alt
---
```
**Result:**
:::demo-wrapper img no-padding
![banner](/images/custom-banner.jpg)
:::
### hero
- Type: `PlumeThemeHomeHero`
Suitable for documentation-type sites, placed at the top.
**Tool Support: [Homepage Hero Tint Plate Configuration Tool](../../tools/home-hero-tint-plate.md)**
```ts
interface PlumeThemeHomeHero extends PlumeHomeConfigBase {
type: 'hero'
hero: {
name: string
tagline?: string
text?: string
actions?: {
theme?: 'brand' | 'alt' | 'sponsor'
text: string
link?: string
icon?: string // Icon to the left of the text
suffixIcon?: string // Icon to the right of the text
target?: '_blank' | '_self' | string
rel?: string
}
}
/**
* Background image. "tint-plate" is a preset effect, or a custom image URL can be provided.
*/
background?: 'tint-plate' | string
/**
* When background is the preset, configure RGB values to adjust the background color.
* This configuration only takes effect when `background` is set to `tint-plate`.
*/
tintPlate?: TintPlate
/**
* If using a non-preset background, set the filter effect for the background image.
*/
filter?: string
}
interface TintPlateObj {
// value represents the base color value, range 0 ~ 255
// offset represents the offset from the base value, range 0 ~ (255 - value)
r: { value: number, offset: number }
g: { value: number, offset: number }
b: { value: number, offset: number }
}
type TintPlate
= | number // e.g., 210
| string // e.g., '210,210,210' => red,green,blue
// e.g., { r: { value: 220, offset: 36 }, g: { value: 220, offset: 36 }, b: { value: 220, offset: 36 } }
| TintPlateObj
// e.g., { light: 210, dark: 20 }
// e.g., { light: '210,210,210', dark: '20,20,20' }
| { light: number | string, dark: number | string }
| { light: TintPlateObj, dark: TintPlateObj }
```
**Example:**
```md
---
home: true
config:
-
type: hero
full: true
background: tint-plate
hero:
name: Theme Plume
tagline: Vuepress Next Theme
text: A minimalistic, feature-rich vuepress documentation & blog theme
actions:
-
theme: brand
text: Get Started →
link: /
-
theme: alt
text: Github
link: https://github.com/pengzhanbo/vuepress-theme-plume
---
```
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-hero.jpg" alt="Theme Plume" />
:::
When `background` is configured as `tint-plate`, you can additionally configure `tintPlate` to adjust
the background hue, with a range of `0 ~ 255`:
```md
---
home: true
config:
-
type: hero
full: true
background: tint-plate
tintPlate: 210
---
```
`tintPlate` is used to configure RGB values:
- When configured as a single number, it sets the red, green, and blue color channels to the same value (range: 0 - 255). Example: `210`.
- When configured as three comma-separated values, it sets the red, green, and blue channels to different values (range: 0 - 255). Example: `210,210,210`.
- When configured as a `TintPlateObj`, it allows more granular control over each color channel and its corresponding offset.
- It can also be configured as `{ light, dark }` to use different color values in dark and light modes.
::: info
To facilitate the configuration of aesthetically pleasing and personalized backgrounds,
the theme also provides a [Homepage Hero Tint Plate Configuration Tool](../../tools/custom-theme.md)
for visual configuration. You can generate configuration content and copy it directly for use in your own project.
:::
The theme also supports customizing the colors of `name`, `tagline`, and `text`.
Configure this via CSS Variables.
```css
/* Default settings, can be overridden in `index.css` */
:root {
/* home hero name background color. The text color is defined via background clipping,
allowing gradient backgrounds for more expressive text. */
--vp-bg-home-hero-name: linear-gradient(315deg, var(--vp-c-purple-1) 15%, var(--vp-c-brand-2) 65%, var(--vp-c-brand-2) 100%);
--vp-c-home-hero-tagline: var(--vp-c-text-2);
--vp-c-home-hero-text: var(--vp-c-text-3);
}
```
### doc-hero
- Type: `PlumeThemeHomeDocHero`
Suitable for documentation-type sites, placed at the top.
```ts
interface PlumeThemeHomeDocHero {
type: 'doc-hero'
hero: {
name: string
tagline?: string
text?: string
image?: string
| { src: string, alt?: string }
| { dark: string, light: string, alt?: string }
actions?: {
theme?: 'brand' | 'alt' | 'sponsor'
text: string
link?: string
icon?: string // Icon to the left of the text
suffixIcon?: string // Icon to the right of the text
target?: '_blank' | '_self' | string
rel?: string
}
}
}
```
**Example:**
```md
---
home: true
config:
-
type: doc-hero
hero:
name: Theme Plume
text: VuePress Next Theme
tagline: A minimalistic, easy-to-use, feature-rich vuepress documentation & blog theme
image: /plume.png
actions:
-
theme: brand
text: Get Started →
link: /guide/intro/
-
theme: alt
text: Github
link: https://github.com/pengzhanbo/vuepress-theme-plume
---
```
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-doc-hero.jpg" alt="Theme Plume" />
:::
The theme also supports customizing the colors of `name`, `tagline`, `text`, and the background color of the `image`.
Configure this via CSS Variables.
```css
/* Default settings, can be overridden in `index.css` */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(120deg, var(--vp-c-purple-1) 30%, var(--vp-c-brand-2));
--vp-home-hero-tagline: var(--vp-c-text-2);
--vp-home-hero-text: var(--vp-c-text-1);
--vp-home-hero-image-background-image: linear-gradient(-45deg, var(--vp-c-brand-soft) 50%, var(--vp-c-brand-2) 50%);
--vp-home-hero-image-filter: blur(44px);
}
```
### features
- Type: `PlumeThemeHomeFeatures`
Suitable for displaying features, functionalities, etc.
```ts
interface PlumeThemeHomeFeatures extends PlumeHomeConfigBase {
type: 'features'
title?: string
description?: string
features: PlumeThemeHomeFeature[]
}
interface PlumeThemeHomeFeature {
/**
* Icon, also supports passing an iconify icon name.
*/
icon?: FeatureIcon
title: string
details?: string
link?: string
linkText?: string
rel?: string
target?: string
}
type FeatureIcon = string | {
src: string
alt?: string
width?: string
height?: string
wrap?: boolean
} | {
light: string
dark: string
alt?: string
width?: string
height?: string
wrap?: boolean
}
```
**Example:**
```md
---
home: true
config:
-
type: features
features:
-
title: Responsive Layout
icon: 💻
details: Adapts to mobile devices, PCs, and tablets.
-
title: Blog & Documentation
icon: 📖
details: Whether you want to write a blog, product documentation, or both.
-
title: Out of the Box
icon: 🚀
details: Supports zero-configuration usage, along with rich customization options.
-
title: Multi-language
icon: ⚖
details: Built-in support for Chinese/English, with the ability to add more languages.
-
title: Dual Theme
icon: 👨‍💻
details: Supports light/dark themes, including code highlighting.
-
title: Plugins
icon: 📦
details: Rich built-in plugins to handle common website needs in one place.
-
title: Search, Comments
icon: 🔍
details: Supports multiple comment systems, local search, and Algolia search.
-
title: Encryption
icon: 🔒
details: Supports full-site encryption, partial encryption (encrypted directories, encrypted articles).
-
title: Markdown Enhancements
icon: 📝
details: Supports Markdown syntax, code block groups, hint containers, task lists, mathematical formulas, code demos, etc.
---
```
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-features.jpg" alt="custom-features" />
:::
### text-image | image-text
- Type: `PlumeThemeHomeTextImage`
Left-right layout for text and image.
```ts
interface PlumeThemeHomeTextImage extends PlumeHomeConfigBase {
type: 'text-image' | 'image-text'
image: PlumeThemeImage
width?: number | string
title?: string
description?: string
list: (string | { title?: string, description?: string })[]
}
type PlumeThemeImage
= | string
| { src: string, alt?: string }
| { dark: string, light: string, alt?: string }
```
**Example:**
```md
---
home: true
config:
-
type: image-text
title: Features
description: Rich built-in features to meet general website requirements.
image: /images/plume-1.svg
list:
-
title: Article Information
description: Add tags, categories, word count, reading time, writing date, and other information to articles.
-
title: Comments
description: Support for 4 comment systems, allowing you to freely choose the one that fits your needs.
-
title: Search
description: Supports minisearch-based local search and Algolia search.
-
title: Encryption
description: Supports full-site encryption and partial encryption (encrypted directories, encrypted articles).
-
title: Code Copy
description: One-click copying of code block content.
-
type: text-image
title: Blog
description: The theme natively supports blogging, generating your personal blog.
image: /images/plume-2.svg
list:
-
title: Article List
description: Automatically sorts and generates blog article list pages based on writing dates.
-
title: Author Information
description: Customize name, motto, avatar, and social media links.
-
title: Tags, Archive
description: Automatically generates tag pages and archives articles by year.
---
```
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-image-text.jpg" alt="image-text" />
:::
:::demo-wrapper img no-padding
<img src="/images/custom-text-image.jpg" alt="text-image" />
:::
### posts
Inserts a post collection article list page as a separate section into the homepage.
```ts
interface PlumeThemeHomePosts extends PlumeHomeConfigBase {
type: 'posts'
collection?: string
}
```
When multiple post collections exist, it reads the article list from the first collection by default.
You can specify which collection's article list to read using the `collection` configuration option.
The value of `collection` should match the `dir` value of the collection.
**Example:**
```md
---
home: true
config:
-
type: posts
collection: blog
---
```
### profile
- Type: `PlumeThemeHomeProfile`
Displays personal information.
```ts
interface PlumeThemeHomeProfile extends PlumeHomeConfigBase {
type: 'profile'
name?: string
description?: string
avatar?: PlumeThemeImage
circle?: boolean
}
type PlumeThemeImage
= | string
| { src: string, alt?: string }
| { dark: string, light: string, alt?: string }
```
**Example:**
```md
---
home: true
config:
-
type: profile
name: pengzhanbo
description: Even if slow, stop not, even if failed, keep going, but must be able to reach the goal he aims for.
avatar: /images/avatar.png
---
```
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-profile.jpg" alt="profile" />
:::
### custom
- Type: `PlumeThemeHomeCustom`
Custom content. The markdown content written in the `README.md` file will be inserted into the corresponding section.
```ts
interface PlumeThemeHomeCustom extends PlumeHomeConfigBase {
type: 'custom'
}
```
**Example:**
````md
---
home: true
config:
-
type: custom
---
### Installation
:::code-tabs
@tab pnpm
```sh
pnpm add vuepress@next vuepress-theme-plume vue
```
@tab npm
```sh
npm install vuepress@next vuepress-theme-plume
```
@tab yarn
```sh
yarn add vuepress@next vuepress-theme-plume
```
:::
````
**Result:**
:::demo-wrapper img no-padding
<img src="/images/custom-content.jpg" alt="content" />
:::
## Custom Section Types
When the built-in section types are insufficient for your needs, you can create custom section types.
Each custom section type is essentially a component.
A simple example is as follows:
::: code-tabs
@tab your-component.vue
```vue
<script setup lang="ts">
import type { ThemeHomeConfigBase } from 'vuepress-theme-plume'
import { VPHomeBox } from 'vuepress-theme-plume/client'
const props = defineProps<ThemeHomeConfigBase & {
// Component props, properties from frontmatter will be passed to the component
}>()
</script>
<template>
<VPHomeBox
:type="type"
:background-image="backgroundImage"
:background-attachment="backgroundAttachment"
:full="full"
>
<!-- Customize your content -->
<div>...</div>
</VPHomeBox>
</template>
```
:::
Add the component in the `enhance` hook within `.vuepress/client.ts`
::: code-tabs
@tab .vuepress/client.ts
```ts
import { defineClientConfig } from 'vuepress/client'
import YourComponent from 'your-component.vue'
export default defineClientConfig({
enhance({ app }) {
app.component('your-component', YourComponent)
},
})
```
:::
Then, you can use `your-component` in your `README.md`.
```md
---
home: true
config:
-
type: 'your-component'
# ...
---
```

View File

@ -0,0 +1,179 @@
---
title: Layout Slots
icon: ph:layout-duotone
createTime: 2025/10/08 16:19:43
permalink: /en/guide/layout-slots/
---
## Overview
The theme provides extensive layout slots through `<Layout />` and `<NotFound />` components,
allowing content injection at different positions of the page. This enables users to personalize the theme according to their needs.
## Usage
Taking `<Layout />` as an example, first create a client configuration file: `.vuepress/client.ts`:
```ts title=".vuepress/client.ts"
import { defineClientConfig } from 'vuepress/client'
import Layout from './layouts/Layout.vue'
export default defineClientConfig({
layouts: {
Layout,
},
})
```
::: info
The `Layout` name in `layouts` is fixed. This uses JavaScript shorthand syntax,
essentially equivalent to `Layout: Layout`, which is crucial for implementing layout slots.
The same rule applies to `NotFound`.
Other components passed that are not `Layout`/`NotFound` are considered custom layout components.
:::
Then, create `.vuepress/layouts/Layout.vue` as the default component for layout slots,
and import the current theme's `<Layout />` component in this file.
```vue {7-11} title=".vuepress/layouts/Layout.vue"
<script setup>
import { Layout } from 'vuepress-theme-plume/client' // [!code hl]
</script>
<template>
<Layout>
<template #page-bottom>
<div class="custom-content">
Custom Content
</div>
</template>
</Layout>
</template>
<style>
.custom-content {
width: 100%;
}
</style>
```
Content injection can also be implemented using render functions in `.vuepress/client.ts`:
::: code-tabs
@tab .vuepress/client.ts
```ts
import { h } from 'vue'
import { Layout } from 'vuepress-theme-plume/client'
import { defineClientConfig } from 'vuepress/client'
import CustomContent from './components/CustomContent.vue'
export default defineClientConfig({
layouts: {
Layout: () => h(Layout, null, {
'page-bottom': () => h(CustomContent),
}),
},
})
```
@tab .vuepress/components/CustomContent.vue
```vue
<template>
<div class="custom-content">
Custom Content
</div>
</template>
```
:::
## Slots
::: info
You can preview <https://plume-layout-slots.netlify.app> to see the positions of all available slots in the site.
:::
### `<Layout />` Slots
- When `pageLayout: doc`:
- `doc-top`
- `doc-bottom`
- `doc-content-before`
- `doc-footer-before`
- `doc-before`
- `doc-after`
- `doc-meta-top`
- `doc-meta-bottom`
- `doc-meta-before`
- `doc-meta-after`
- `sidebar-nav-before`
- `sidebar-nav-after`
- `aside-top`
- `aside-bottom`
- `aside-outline-before`
- `aside-outline-after`
- When `pageLayout: page`:
- `page-top`
- `page-bottom`
- In post collection related pages (applicable to post list pages, tags pages, and archives pages):
- `posts-top`
- `posts-bottom`
- `posts-aside-top`
- `posts-aside-bottom`
- `posts-extract-before`
- `posts-extract-after`
- In post list pages:
- `posts-post-list-before`
- `posts-post-list-after`
- `posts-post-list-pagination-after`
- In tags pages:
- `posts-tags-before`
- `posts-tags-title-after`
- `posts-tags-content-before`
- `posts-tags-after`
- In archives pages:
- `posts-archives-before`
- `posts-archives-after`
- In categories pages:
- `posts-categories-before`
- `posts-categories-content-before`
- `posts-categories-after`
### `<NotFound />` Slots
- `not-found`
### Common Slots
The following slots are supported in both `<Layout />` and `<NotFound />`:
- `layout-top`
- `layout-bottom`
- `nav-bar-title-before`
- `nav-bar-title-after`
- `nav-bar-content-before`
- `nav-bar-content-after`
- `nav-bar-menu-before`
- `nav-bar-menu-after`
- `nav-screen-content-before`
- `nav-screen-content-after`
- `nav-screen-menu-before`
- `nav-screen-menu-after`
- `footer-content`
- `bulletin-content`

View File

@ -1,10 +1,96 @@
--- ---
title: Customize Style title: Custom Styles
icon: icon-park-outline:theme icon: icon-park-outline:theme
createTime: 2025/03/23 14:53:21 createTime: 2025/10/08 20:18:52
permalink: /en/guide/custom-style/ permalink: /en/guide/custom-style/
--- ---
## Theme Customization
Custom styles are supported.
Although the theme uses [SASS](https://sass-lang.com/) as the CSS preprocessor,
all colors are defined using `CSS Variables`. Therefore, you can create a CSS file or SCSS file to override them.
First, create a `styles/index.css` file in the `.vuepress` directory. Then, import this file in the [client configuration file](https://v2.vuepress.vuejs.org/guide/configuration.html#client-config-file).
:::code-tabs
@tab .vuepress/client.ts
```ts
import { defineClientConfig } from 'vuepress/client'
import './styles/index.css' // [!code ++]
export default defineClientConfig({
// ...
})
```
@tab .vuepress/styles/index.css
```css
:root {
--vp-c-brand-1: #5086a1;
}
```
:::
## Style File ## Style File
The titles are used for external jumps.
todo ... Create a file such as `custom.css` in the `.vuepress` directory.
Add additional styles or override default styles here:
```scss
:root {
scroll-behavior: smooth;
}
```
You can also use it to override the predefined CSS variables of the default theme.
Below are some predefined variables. For the complete list, please refer to [vars.css](https://github.com/pengzhanbo/vuepress-theme-plume/blob/main/theme/src/client/styles/vars.css).
```scss
:root {
/** Theme Colors */
--vp-c-brand-1: #5086a1;
--vp-c-brand-2: #6aa1b7;
--vp-c-brand-3: #8cccd5;
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
/** Background Colors */
--vp-c-bg: #ffffff;
--vp-c-bg-alt: #f6f6f7;
--vp-c-bg-elv: #ffffff;
--vp-c-bg-soft: #f6f6f7;
/** Text Colors */
--vp-c-text-1: rgba(60, 60, 67);
--vp-c-text-2: rgba(60, 60, 67, 0.78);
--vp-c-text-3: rgba(60, 60, 67, 0.56);
}
[data-theme="dark"] {
--vp-c-brand-1: #8cccd5;
--vp-c-brand-2: #6aa1b7;
--vp-c-brand-3: #5086a1;
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
--vp-c-bg: #1b1b1f;
--vp-c-bg-alt: #161618;
--vp-c-bg-elv: #202127;
--vp-c-bg-soft: #202127;
--vp-c-text-1: rgba(255, 255, 245, 0.86);
--vp-c-text-2: rgba(235, 235, 245, 0.6);
--vp-c-text-3: rgba(235, 235, 245, 0.38);
}
```
::: tip
The theme provides a [Theme Color Tool](../../../tools/custom-theme.md) that you can use to create custom colors.
:::

View File

@ -0,0 +1,62 @@
---
title: AcFun Video
icon: lets-icons:video-fill
createTime: 2025/10/08 10:57:45
permalink: /en/guide/embed/video/acfun/
badge: New
---
## Overview
The theme provides the capability to embed AcFun videos.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdown-power.md).
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
acfun: true, // [!code ++]
},
})
})
```
## Syntax
Simple syntax:
```md
@[acfun](id)
```
More options:
```md
@[acfun width="100%" height="400px" ratio="16:9"](id)
```
**Parameter Description:**
- id: Video ID
- width: Video width
- height: Video height
- ratio: Video aspect ratio, defaults to `16:9`
## Examples
### Widescreen Video
Input:
```md
@[acfun](ac47431669)
```
Output:
@[acfun](ac47431669)

View File

@ -0,0 +1,164 @@
---
title: ArtPlayer Video
icon: icon-park-outline:video
createTime: 2025/10/08 16:13:54
permalink: /en/guide/embed/video/artplayer/
---
## Overview
The theme provides the capability to embed custom source videos.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdown-power.md).
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
artPlayer: true, // [!code ++]
},
})
})
```
## Installation
This feature relies on the `artplayer` player implementation. The theme does not include this dependency
by default. When enabling the `artPlayer` feature, manual installation is required.
::: npm-to
```sh
npm i artplayer
```
:::
The artPlayer player natively supports video formats including `'mp4'`, `'mp3'`, `'webm'`, and `'ogg'`.
It also supports extending compatibility for additional formats.
If your video format is `'mpd'` or `'dash'`, you need to manually install `dashjs`:
::: npm-to
```sh
npm i dashjs
```
:::
If your video format is `'m3u8'` or `'hls'`, you need to manually install `hls.js`:
::: npm-to
```sh
npm i hls.js
```
:::
If your video format is `'ts'` or `'flv'`, you need to manually install `mpegts.js`:
::: npm-to
```sh
npm i mpegts.js
```
:::
## Markdown Syntax
```md
@[artPlayer](src)
```
With configuration options:
```md
@[artPlayer muted autoplay loop width="100%" height="400px" ratio="16:9"](src)
```
- `src`: Video source URL
**Parameter Description:**
- `width`: Video width
- `height`: Video height
- `ratio`: Video aspect ratio, defaults to `16:9`
- `type`: Video format, automatically parsed from the video URL by default
- `autoplay`: Whether to enable autoplay
- `muted`: Whether to mute, defaults to `true` when autoplay is enabled
- `volume`: Volume level, range from `0 - 1`
- `poster`: Video poster image URL
- `auto-mini`: Automatically enters mini-player mode when the player scrolls out of the browser viewport
## Global Component
The theme provides a global component `<ArtPlayer />` to support more flexible and comprehensive usage.
### Props
| Field | Type | Description |
| -- | -- | -- |
| src | `string` | Required, video source URL |
| type | `string` | Optional, video format, parsed from `src` by default |
| width | `string` | Optional, width, defaults to `100%` |
| height | `string` | Optional, height |
| ratio | `string` | Optional, aspect ratio, defaults to `16:9` |
For more `Props`, please refer to the
[artPlayer documentation](https://artplayer.org/document/start/option.html). The theme supports all available options.
## Examples
::: tip Note
The video resources in the examples are sourced from [artplayer.org](https://artplayer.org).
:::
**Input:**
```md
@[artPlayer](https://artplayer.org/assets/sample/video.mp4)
```
**Output:**
@[artPlayer](https://artplayer.org/assets/sample/video.mp4)
**Input:**
```md
<ArtPlayer
src="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
fullscreen
/>
```
**Output:**
<ArtPlayer
src="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
fullscreen
/>
## Explanation
The markdown syntax `@[artPlayer](src)` is internally converted to the `<ArtPlayer />` component, which is equivalent to:
```md
<ArtPlayer
src="src"
fullscreen
flip
playback-rate
aspect-ratio
setting
pip
/>
```

View File

@ -0,0 +1,103 @@
---
title: Audio Reader
icon: rivet-icons:audio
createTime: 2025/10/08 22:31:01
permalink: /en/guide/embed/audio/reader/
---
## Overview
The theme supports embedding audio reading capabilities in documentation.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdown-power.md).
**Audio Reader** is not a music player; it simply embeds an
(@[audioReader](https://sensearch.baidu.com/gettts?lan=en&spd=3&source=alading&text=audio))
button within content that plays an audio clip when clicked.
It is suitable for playing short audio clips, such as **word pronunciation guides**.
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
audioReader: true, // [!code ++]
},
})
})
```
## Markdown Syntax
The audio embedding markdown syntax is an inline syntax, allowing usage anywhere within markdown.
```md
@[audioReader](src)
```
With configuration options:
```md
@[audioReader type="audio/mpeg" title="title" autoplay start-time="0" end-time="10" volume="0.7"](src)
```
**Parameter Description:**
- `type`: Audio type, formatted as `audio/mpeg`.
Defaults to inference from the file extension in the audio URL. If the URL lacks an extension, declare manually.
- `title`: Audio title, displayed before the audio icon.
- `autoplay`: Whether to enable autoplay (not recommended).
- `start-time`: Audio playback start time in seconds.
- `end-time`: Audio playback end time in seconds.
- `volume`: Audio playback volume, range from `0 ~ 1`.
## Global Component
The theme provides a global component `<AudioReader />` to support more flexible and comprehensive usage.
### Props
| Field | Type | Description |
| --------- | --------- | ----------------------------------- |
| src | `string` | Required, audio source URL |
| type | `string` | Optional, audio format, parsed from `src` by default |
| autoplay | `boolean` | Optional, whether to enable autoplay (not recommended) |
| startTime | `number` | Optional, audio playback start time in seconds |
| endTime | `number` | Optional, audio playback end time in seconds |
| volume | `number` | Optional, audio playback volume, range from `0 ~ 1` |
## Examples
**Input:**
```md
audio 美 [ˈɔːdioʊ] @[audioReader](/audio/audio.mp3)
```
**Output:**
audio 美 [ˈɔːdioʊ] @[audioReader](https://sensearch.baidu.com/gettts?lan=en&spd=3&source=alading&text=audio)
**Input:**
```md
audio 美 @[audioReader title="[ˈɔːdioʊ]"](/audio/audio.mp3)
```
**Output:**
audio 美 @[audioReader title="[ˈɔːdioʊ]"](https://sensearch.baidu.com/gettts?lan=en&spd=3&source=alading&text=audio)
**Input:**
```md
audio 美 <AudioReader src="/audio/audio.mp3">[ˈɔːdioʊ]</AudioReader>
```
**Output:**
audio 美 <AudioReader src="https://sensearch.baidu.com/gettts?lan=en&spd=3&source=alading&text=audio">[ˈɔːdioʊ]</AudioReader>

View File

@ -0,0 +1,85 @@
---
title: Bilibili Video
icon: ri:bilibili-fill
createTime: 2025/10/08 12:26:47
permalink: /en/guide/embed/video/bilibili/
---
## Overview
The theme provides the capability to embed Bilibili videos.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdownPower.md).
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
bilibili: true, // [!code ++]
},
})
})
```
## Syntax
Simple syntax:
```md
@[bilibili](bvid)
```
For videos with multiple parts, add `p1`, `p2`, `p3` etc. after `bilibili`:
```md
@[bilibili p1](aid cid)
```
More options:
```md
@[bilibili p1 autoplay time="0" width="100%" height="400px" ratio="16:9"](bvid aid cid)
```
**Parameter Description:**
- bvid: Video BV ID
- aid: Video AID
- cid: Video CID
- autoplay: Whether to enable autoplay
- time: Video playback start time in seconds, or in `mm:ss` or `hh:mm:ss` format
- width: Video width
- height: Video height
- ratio: Video aspect ratio, defaults to `16:9`
For videos with multiple parts, `bvid` can be omitted, but `aid` and `cid` must be provided.
## Examples
### Widescreen Video
Input:
```md
@[bilibili](BV1EZ42187Hg)
```
Output:
@[bilibili](BV1EZ42187Hg)
### Vertical Video
Input:
```md
@[bilibili width="320px" ratio="9:16"](BV1zr42187eg)
```
Output:
@[bilibili width="320px" ratio="9:16"](BV1zr42187eg)

114
docs/en/guide/embed/pdf.md Normal file
View File

@ -0,0 +1,114 @@
---
title: PDF Reader
icon: teenyicons:pdf-outline
createTime: 2025/10/08 13:30:53
permalink: /en/guide/embed/pdf/
---
## Overview
The theme supports embedding PDF files in markdown, enabling direct PDF reading within the page.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdown-power.md).
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
pdf: true, // [!code ++]
},
})
})
```
## Syntax
The simplest syntax is as follows:
```md
@[pdf](url)
```
To open a specific page, add a page number after `pdf`:
```md
@[pdf 2](url)
```
Additional options can be added to `@[pdf ]` for more flexible control:
```md
@[pdf 2 no-toolbar width="100%" height="400px" ratio="16:9" zoom="100"](url)
```
- `no-toolbar` - Hide the toolbar
- `width` - Width, defaults to 100%
- `height` - Height, defaults to `auto`
- `ratio` - Aspect ratio, defaults to `16:9`, only effective when height is not specified
- `zoom` - Zoom level, percentage
## Examples
### Default
Input:
```md
@[pdf](https://plume.pengzhanbo.cn/files/sample.pdf)
```
Output:
@[pdf](/files/sample.pdf)
### Set Page Number to 2
Input:
```md
@[pdf 2](https://plume.pengzhanbo.cn/files/sample.pdf)
```
Output:
@[pdf 2 zoom="95"](/files/sample.pdf)
### Hide Toolbar
Input:
```md
@[pdf no-toolbar](https://plume.pengzhanbo.cn/files/sample.pdf)
```
Output:
@[pdf no-toolbar](/files/sample.pdf)
### 90% Zoom Level
Input:
```md
@[pdf zoom="90"](https://plume.pengzhanbo.cn/files/sample.pdf)
```
Output:
@[pdf zoom="90"](/files/sample.pdf)
### 21:29 Aspect Ratio
Input:
```md
@[pdf zoom="95" ratio="21:29"](https://plume.pengzhanbo.cn/files/sample.pdf)
```
Output:
@[pdf zoom="95" ratio="21:29"](/files/sample.pdf)

View File

@ -0,0 +1,65 @@
---
title: YouTube Video
icon: mdi:youtube
createTime: 2025/10/08 14:30:33
permalink: /en/guide/embed/video/youtube/
---
## Overview
The theme provides the capability to embed YouTube videos.
This feature is powered by [vuepress-plugin-md-power](../../config/plugins/markdown-power.md).
## Configuration
This feature is disabled by default. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
youtube: true, // [!code ++]
},
})
})
```
## Syntax
Simple syntax:
```md
@[youtube](id)
```
More options:
```md
@[youtube autoplay loop start="0" end="0" width="100%" height="400px" ratio="16:9"](id)
```
**Parameter Description:**
- id: Video ID
- autoplay: Whether to enable autoplay
- loop: Whether to enable loop playback
- start: Video playback start time in seconds, or in `mm:ss` or `hh:mm:ss` format
- end: Video playback end time in seconds, or in `mm:ss` or `hh:mm:ss` format
- width: Video width
- height: Video height
- ratio: Video aspect ratio, defaults to `16:9`
## Examples
### Widescreen Video
Input:
```md
@[youtube](0JJPfz5dg20)
```
Output:
@[youtube](0JJPfz5dg20)

View File

@ -0,0 +1,391 @@
---
title: Bulletin
icon: mingcute:announcement-line
createTime: 2025/10/08 21:51:22
permalink: /en/guide/features/bulletin/
---
## Overview
The bulletin is a real-time notification component that enables convenient display of notification messages within the site.
Such as the bulletin in the top-right corner.
## Usage
The theme provides very convenient and flexible methods for using the bulletin.
You can choose the appropriate configuration method based on your requirements.
### Configuration Options
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
// more options...
}
})
})
```
```ts
interface BulletinOptions {
/**
* Bulletin position
* @default 'top-right'
*/
layout?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center'
/**
* Whether to display gradient border
*
* @default true
*/
border?: boolean
/**
* Pages where the bulletin is displayed
*
* - `true` indicates all pages
* - `false` indicates no display
* - Pass a function that returns `true` when display is enabled
*/
enablePage?: boolean | ((page: Page) => boolean)
/**
* Bulletin display duration
*
* @default 'always'
*
* - `'session'`: After closing the bulletin, it won't display again during the current session cycle. It will reappear in new session cycles. Refreshing the page won't make it reappear.
* - `'always'`: Always display. After closing, refreshing the page will make it reappear.
* - `'once'`: Display only in the current cycle. After closing, it won't display again. Neither new sessions nor page refreshes will make it reappear.
*/
lifetime?: 'session' | 'always' | 'once'
/**
* Bulletin ID
*
* The bulletin display duration uses the `id` as a unique identifier
*/
id?: string
/**
* Bulletin title
*/
title?: string
/**
* Bulletin content
*
* Markdown syntax or HTML text can be used.
* When using markdown, declare `contentType` as `markdown`
*/
content?: string
/**
* Bulletin content type
*
* - `markdown` indicates using markdown syntax
* - `text` indicates using plain text (can be HTML content)
*
* @default 'text'
*/
contentType?: 'markdown' | 'text'
/**
* Pass a `markdown` or `html` file path and use the file content as bulletin content
*
* - When using `.md` files, markdown syntax will be parsed
* - When using `.html` files, only include bulletin content. Please avoid using tags like `<html>`, `<body>`, `<script>`, etc.
*/
contentFile?: string
}
```
## Simple Bulletin
When you only need to configure a simple bulletin with brief content, you can directly use `bulletin.content` to add content.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
content: 'Bulletin content',
}
})
})
```
You can also declare `bulletin.contentType` as `markdown` to use markdown syntax in the `content`.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
contentType: 'markdown', // [!code hl]
content: `\
**Update Notes**
- Added some features
- Fixed some bugs
`,
}
})
})
```
## Long Content Bulletin
When you need to configure a bulletin with lengthy content, writing long content in the configuration file
may appear bloated and difficult to read. In such cases, use `bulletin.contentFile` to specify a content
file path, separating the long content from the configuration file.
`bulletin.contentFile` requires an absolute path to a `markdown` or `html` file.
The theme will use the content of this file as the bulletin content.
::: code-tabs
@tab .vuepress/config.ts
```ts
import path from 'node:path'
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
contentFile: path.join(__dirname, '_bulletin.md'),
}
})
})
```
@tab .vuepress/_bulletin.md
```md
**Update Notes**
- Added some features
- Fixed some bugs
```
:::
The theme monitors changes to `bulletin.contentFile`. When the file content changes, the bulletin will be re-rendered.
## Bulletin with Custom Content Interactions
Using `bulletin.content` or `bulletin.contentFile` only allows writing plain text bulletin content and
some interactive content supported by `markdown` syntax. It does not support writing bulletin content with other custom interactions.
For such scenarios, the theme also provides corresponding support.
First, configure the basic content of `bulletin`. You don't need to configure `bulletin.content` or `bulletin.contentFile` at this point.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
}
})
})
```
Then, register the global component `BulletinContent` in `.vuepress/client.ts`.
The theme will automatically detect this component and use it as the bulletin content.
```ts title=".vuepress/client.ts"
import { defineClientConfig } from '@vuepress/client'
import BulletinContent from './components/BulletinContent.vue'
export default defineClientConfig({
enhance: ({ app }) => {
app.component('BulletinContent', BulletinContent)
}
})
```
Next, write the `BulletinContent.vue` component.
```vue title=".vuepress/components/BulletinContent.vue"
<script setup>
// Write custom bulletin content interactions
</script>
<template>
<div class="bulletin-content">
<!-- Custom bulletin content -->
</div>
</template>
<style scoped>
/* Custom bulletin content styles */
</style>
```
## Custom Bulletin Styles
You can directly override the bulletin styles via CSS.
Modify the following CSS variables to easily control the bulletin styles.
```css
:root {
--vp-bulletin-bg-color: var(--vp-c-bg);
--vp-bulletin-text-color: var(--vp-c-text-1);
--vp-bulletin-title-color: var(--vp-c-text-1);
--vp-bulletin-font-size: 16px;
--vp-bulletin-title-font-size: 18px;
--vp-bulletin-line-height: 24px;
--vp-bulletin-border-width: 2px;
--vp-bulletin-border: conic-gradient(var(--vp-c-important-3), var(--vp-c-danger-3), var(--vp-c-success-3), var(--vp-c-important-3));
--vp-bulletin-width: 320px;
}
```
Alternatively, override the bulletin styles globally via the `.vp-bulletin` class.
```css
.vp-bulletin {
/* Bulletin styles */
}
```
## Fully Custom Bulletin
When you don't want to use the theme's built-in bulletin at all, you can completely customize the
bulletin by registering a global `Bulletin` component.
::: code-tabs
@tab .vuepress/client.ts
```ts
import { defineClientConfig } from '@vuepress/client'
import Bulletin from './components/Bulletin.vue'
export default defineClientConfig({
enhance: ({ app }) => {
app.component('Bulletin', Bulletin)
}
})
```
:::
Next, write the `Bulletin.vue` component.
::: code-tabs
@tab .vuepress/components/Bulletin.vue
```vue
<script setup>
// Write custom bulletin
</script>
<template>
<div class="bulletin">
<!-- Custom bulletin -->
</div>
</template>
<style scoped>
/* Custom bulletin styles */
</style>
```
:::
You need to write the bulletin component from scratch. To facilitate writing the bulletin, the theme
provides the composable API `useBulletinControl()`, which you can use directly in the `Bulletin.vue` component.
```ts
import { useBulletinControl } from 'vuepress-theme-plume/composables'
const {
bulletin, // Bulletin configuration, read from theme configuration
showBulletin, // Whether to show the bulletin
enableBulletin, // Whether the bulletin is enabled on the current page
close, // Close the bulletin
} = useBulletinControl()
```
## Related Notes
### Bulletin Unique Identifier
The bulletin's unique identifier is configured via `bulletin.id`.
The unique identifier is used to distinguish bulletins and determine the validity period of `bulletin.lifetime` based on this identifier.
```ts
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
id: 'my-bulletin', // [!code hl]
}
})
})
```
When you don't explicitly configure `bulletin.id`, the theme generates a hash value based on the `bulletin` object as the unique identifier.
### Bulletin Display Duration
The bulletin display duration is configured via `bulletin.lifetime`.
- `session`: During the current session cycle, if the user doesn't close the bulletin,
it will continue to display, including into the next session cycle. When the user closes the bulletin,
it is considered expired and won't display again during the current session cycle, but will reappear in the next session cycle.
- `always`: Even if the user closes the bulletin, it will reappear not only in the next session cycle but also when refreshing the page.
- `once`: Once the bulletin is closed during the current session cycle, it won't display again afterward.
::: details What is a session?
**Session** refers to the period when you visit a site. As long as the browser tab where the site is
located remains open, the site maintains the same session, even if the page is refreshed.
:::
### Bulletin Position
The bulletin position is configured via `bulletin.layout`.
- `top-left`: Top left
- `top-right`: Top right
- `bottom-left`: Bottom left
- `bottom-right`: Bottom right
- `center`: Top center
### Pages Where Bulletin is Displayed
The pages where the bulletin is displayed are configured via `bulletin.enablePage`.
- `true` indicates all pages
- `false` indicates no display
- Pass a function that returns `true` when display is enabled
```ts
export default defineUserConfig({
theme: plumeTheme({
bulletin: {
layout: 'top-right',
title: 'Bulletin Title',
enablePage: (page) => {
return page.path === '/custom-path/'
}
}
})
})
```

View File

@ -0,0 +1,160 @@
---
title: Changelog
icon: radix-icons:activity-log
createTime: 2025/10/08 18:16:25
permalink: /en/guide/features/changelog/
---
## Overview
The theme supports adding article changelogs to better track the modification history of your articles.
Article changelogs are obtained through git commit history.
This feature is powered by [@vuepress/plugin-git](https://ecosystem.vuejs.press/zh/plugins/development/git.html).
::: warning Note
This feature relies on the `git log` command to retrieve commit history for each markdown file,
which can be relatively time-consuming, especially for large projects with extensive commit histories.
Therefore, this feature is not enabled in development environments by default and is only enabled in production environments.
However, you can still enable it in development environments by setting `theme.plugins.git` to `true` for testing purposes.
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
plugins: { git: true }
})
})
```
:::
## Usage
The theme has built-in support for the
[@vuepress/plugin-git](https://ecosystem.vuejs.press/zh/plugins/development/git.html) plugin, so you can use it without reinstalling.
Enable this feature in the theme configuration file:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Disabled by default, only takes effect when plugins.git is true
// This configuration is invalid in plume.config.ts
changelog: true,
plugins: {
// If you declare it directly as true here, it means the feature is enabled in both development and production environments
git: process.env.NODE_ENV === 'production'
}
})
})
```
## Configuration
```ts
interface ChangelogOptions {
/**
* Maximum number of change records, defaults to all records
*/
maxCount?: number
/**
* Git repository URL, e.g., https://github.com/vuepress/ecosystem
*/
repoUrl?: string
/**
* Commit URL pattern
*
* - `:repo` - Git repository URL
* - `:hash` - Commit hash
*
* @default ':repo/commit/:hash'
*/
commitUrlPattern?: string
/**
* Issue URL pattern
*
* - `:repo` - Git repository URL
* - `:issue` - Issue ID
*
* @default ':repo/issues/:issue'
*/
issueUrlPattern?: string
/**
* Tag URL pattern
* Default: ':repo/releases/tag/:tag'
*
* - `:repo` - Git repository URL
* - `:tag` - Tag name
*
* @default ':repo/releases/tag/:tag'
*/
tagUrlPattern?: string
}
```
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
changelog: {
maxCount: 10,
repoUrl: 'https://github.com/vuepress/vuepress',
commitUrlPattern: ':repo/commit/:hash',
issueUrlPattern: ':repo/issues/:issue',
tagUrlPattern: ':repo/releases/tag/:tag'
},
})
})
```
::: warning Note
Ensure that `changelog.repoUrl` is correctly configured. The default value is [docsRepo](../../config/theme.md#docsrepo).
The theme by default adapts to the URL patterns of git hosting services like `GitHub`, `GitLab`, `Gitee`, and `Bitbucket`.
If you're using a self-hosted service or others, please configure `commitUrlPattern`, `issueUrlPattern`, and `tagUrlPattern` accordingly.
:::
## Notes
This feature requires your project to be under a
[Git repository](https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository) so it can collect information from the commit history.
**When building your site, you should ensure all commit records are accessible.**
For example, CI workflows typically add the `--depth 1` parameter when cloning your repository to avoid
fetching all commit history. Therefore, you need to disable this feature so the plugin can work properly in CI.
Services like `GitHub Actions`, `Netlify`, and `Vercel` do not fetch all commit history by default.
In `GitHub Actions`, you can add the `--depth 0` parameter to ensure `GitHub Actions` can correctly fetch all commit records.
``` yaml title=".github/workflows/deploy.yml"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # [!code focus:3]
with: # [!code ++:2]
fetch-depth: 0
```
For services like `Netlify` and `Vercel`, the processing method is relatively more complex.
In such cases, you can first complete the build in `GitHub Actions` and output the artifacts to
a separate branch, then use that branch for deployment in `Netlify` or `Vercel`.
::: info
For projects created via the theme's `cli` tool, when selecting the deployment method as `GitHub Action`,
the build artifacts are output to the `gh_pages` branch. You can base your `Netlify` or `Vercel` deployment on this branch.
:::

View File

@ -0,0 +1,262 @@
---
title: Comments
icon: la:comment
createTime: 2025/10/08 11:58:59
permalink: /en/guide/features/comments/
---
## Overview
Article comments are powered by [@vuepress/plugin-comment](https://ecosystem.vuejs.press/zh/plugins/blog/comment/).
The theme has built-in plugin support, so you can use it without reinstalling.
In this theme, configure through the following fields:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
comment: {
// Service provider
provider: '', // "Artalk" | "Giscus" | "Twikoo" | "Waline"
// Whether comments are enabled by default
comment: true,
// Other configurations based on the service provider
// ...
}
})
})
```
### Service Providers
[@vuepress/plugin-comment](https://ecosystem.vuejs.press/zh/plugins/blog/comment/) supports various
comment service providers such as `"Artalk" | "Giscus" | "Twikoo" | "Waline"`.
You can configure based on your requirements.
- `Giscus` is a comment system based on GitHub Discussions, easy to set up. [View documentation](https://ecosystem.vuejs.press/zh/plugins/blog/comment/giscus/)
- `Waline` is a comment system that requires a backend, offering higher security. [View documentation](https://ecosystem.vuejs.press/zh/plugins/blog/comment/waline/)
- `Twikoo` is a concise, secure, free static website comment system based on Tencent Cloud Base. [View documentation](https://ecosystem.vuejs.press/zh/plugins/blog/comment/twikoo/)
- `Artalk` is a concise self-hosted comment system that you can easily deploy on your server and embed in frontend pages. [View documentation](https://ecosystem.vuejs.press/zh/plugins/blog/comment/artalk/)
::: tip Recommended Comment Services
- For programmers and developers: Giscus
- For the general public: Waline
:::
::: note
Examples are forked from [@vuepress/plugin-comment](https://ecosystem.vuejs.press/zh/plugins/blog/comment/),
following the [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE) license.
:::
## Giscus
Giscus is a comment system based on GitHub Discussions, easy to set up.
### Prerequisites
::: steps
1. You need to create a public repository and enable Discussions to serve as the location for storing comments.
2. You need to install the [Giscus App](https://github.com/apps/giscus) to grant it permission to access the corresponding repository.
3. After completing the above steps, go to the [Giscus page](https://giscus.app/zh-CN) to get your settings.
You only need to fill in the repository and Discussion category, then scroll to the "Enable giscus" section
at the bottom of the page to obtain the four attributes: `data-repo`, `data-repo-id`, `data-category`, and `data-category-id`.
:::
### Configuration
Set `provider: 'Giscus'` and pass `data-repo`, `data-repo-id`, `data-category`, and `data-category-id` as
plugin options to `repo`, `repoId`, `category`, and `categoryId`.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
comment: {
provider: 'Giscus', // "Artalk" | "Giscus" | "Twikoo" | "Waline"
comment: true,
repo: 'Your_Repo', // [!code ++]
repoId: 'Your_RepoId', // [!code ++]
category: 'Your_Category', // [!code ++]
categoryId: 'Your_CategoryId', // [!code ++]
}
})
})
```
### Themes
By default, Giscus uses `light` or `dark` themes (based on the dark mode state).
If you want to customize themes for light and dark modes, you can set the `lightTheme` and `darkTheme` options
using built-in theme keywords or custom CSS links starting with `https://`.
## Waline
A secure comment system with a backend.
### Installation
If you want to use Waline in the theme, you need to install `@waline/client` first.
::: npm-to
```sh
npm i @waline/client
```
:::
### LeanCloud Setup (Database)
::: steps
1. [Login](https://console.leancloud.app/login) or [Register](https://console.leancloud.app/register) for
`LeanCloud International` and enter the [Console](https://console.leancloud.app/apps)
2. Click [Create Application](https://console.leancloud.app/apps) in the upper left corner and choose a name
you like (please select the free development plan):
![Create Application](https://ecosystem.vuejs.press/assets/leancloud-1-D6GvqV4-.png)
3. Enter the application, select `Settings` > `Application Keys` in the lower left corner.
You can see your `APP ID`, `APP Key`, and `Master Key`. Please record them for later use.
![ID and Key](https://ecosystem.vuejs.press/assets/leancloud-2-B5wKvXiY.png)
:::
::: warning Domestic version requires ICP filing
If you are using the LeanCloud domestic version ([leancloud.cn](https://leancloud.cn)), we recommend
switching to the international version ([leancloud.app](https://leancloud.app)).
Otherwise, you need to bind an **already ICP-filed** domain to the application, purchase an independent IP, and complete the ICP filing process:
- Log in to the domestic version and enter the application you need to use
- Select `Settings` > `Domain Binding` > `API Access Domain` > `Bind New Domain` > Enter domain > `OK`.
- Follow the instructions on the page to complete the CNAME resolution in DNS as required.
- Purchase an independent IP and submit a work order to complete the ICP filing. (The current price for an independent IP is ¥50/month)
![Domain Settings](https://ecosystem.vuejs.press/assets/leancloud-3-D7gbeXS0.png)
:::
### Vercel Deployment (Server)
[![Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwalinejs%2Fwaline%2Ftree%2Fmain%2Fexample)
:::: steps
1. Click the button above to jump to Vercel for server-side deployment.
::: note
If you are not logged in, Vercel will prompt you to register or log in. Please use your GitHub account for quick login.
:::
2. Enter a Vercel project name you like and click `Create` to continue:
![Create Project](https://ecosystem.vuejs.press/images/comment/vercel-1.png)
3. At this point, Vercel will help you create and initialize a repository based on the Waline template,
with the repository name being the project name you entered earlier.
![deploy](https://ecosystem.vuejs.press/images/comment/vercel-3.png)
After a minute or two, a full screen of fireworks will celebrate your successful deployment.
Click `Go to Dashboard` to jump to the application's console.
![deploy](https://ecosystem.vuejs.press/images/comment/vercel-4.png)
4. Click `Settings` at the top - `Environment Variables` to enter the environment variable configuration page,
and configure the three environment variables `LEAN_ID`, `LEAN_KEY`, and `LEAN_MASTER_KEY`.
Their values correspond to the `APP ID`, `APP KEY`, and `Master Key` obtained from LeanCloud in the previous step, respectively.
![Set Environment Variables](https://ecosystem.vuejs.press/images/comment/vercel-5.png)
::: note
If you are using the LeanCloud domestic version, please additionally configure the `LEAN_SERVER`
environment variable with the value of your bound domain.
:::
5. After configuring the environment variables, click `Deployments` at the top,
then click the `Redeploy` button on the right side of the latest deployment to redeploy.
This step is to make the environment variables you just set take effect.
![redeploy](https://ecosystem.vuejs.press/images/comment/vercel-6.png)
6. At this point, it will jump to the `Overview` interface to start deployment.
Wait a moment until the `STATUS` becomes `Ready`. Then click `Visit` to jump to the deployed
website address, which is your server address.
![redeploy success](https://ecosystem.vuejs.press/images/comment/vercel-7.png)
::::
### Domain Binding (Optional)
::: steps
1. Click `Settings` - `Domains` at the top to enter the domain configuration page.
2. Enter the domain you want to bind and click `Add`.
![Add domain](https://ecosystem.vuejs.press/images/comment/vercel-8.png)
3. Add a new `CNAME` record at your domain DNS provider.
| Type | Name | Value |
| ----- | ------- | -------------------- |
| CNAME | example | cname.vercel-dns.com |
4. Wait for it to take effect. You can now access it via your own domain :tada:
- Comment system: example.your-domain.com
- Comment management: example.your-domain.com/ui
![success](https://ecosystem.vuejs.press/images/comment/vercel-9.png)
:::
### Client
#### Using the Plugin
Set `provider: "Waline"` in the plugin options, and set the server address `serverURL` to the value obtained in the previous step.
At this point, place the `<CommentService>` component in a suitable location on your website
(usually at the bottom of the page) to use the Waline comment functionality.
::: tip
You can also pass other options supported by Waline (except `el`). For details, see [Waline Configuration](https://ecosystem.vuejs.press/zh/plugins/blog/comment/waline/config.html).
:::
### Comment Management (Admin Panel)
::: steps
1. After deployment is complete, please visit `<serverURL>/ui/register` to register. The first person to register will be set as the administrator.
2. After logging in as an administrator, you can see the comment management interface. Here you can modify, mark, or delete comments.
3. Users can also register accounts through the comment box. After logging in, they will be redirected to their own profile page.
:::

View File

@ -1,9 +0,0 @@
---
title: Component
icon: radix-icons:component-2
createTime: 2025/03/24 19:52:21
outline: 2
permalink: /en/guide/features/component/
---
The original content of this document has been migrated to Component .

View File

@ -0,0 +1,329 @@
---
title: Contributors
icon: simple-icons:contributorcovenant
createTime: 2025/10/08 16:26:54
permalink: /en/guide/features/contributors/
---
## Overview
The theme supports adding contributor information to articles to better track your article contributors.
Article contributors are obtained through git commit history.
This feature is powered by [@vuepress/plugin-git](https://ecosystem.vuejs.press/zh/plugins/development/git.html).
::: warning Note
This feature relies on the `git log` command to retrieve commit history for each markdown file,
which can be relatively time-consuming, especially for large projects with extensive commit histories.
Therefore, this feature is not enabled in development environments by default and is only enabled in production environments.
However, you can still enable it in development environments by setting `theme.plugins.git` to `true` for testing purposes.
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
plugins: { git: true }
})
})
```
:::
## Usage
The theme has built-in support for the
[@vuepress/plugin-git](https://ecosystem.vuejs.press/zh/plugins/development/git.html) plugin, so you can use it without reinstalling.
Enable this feature in the theme configuration file:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// Enabled by default, only takes effect when plugins.git is true
// This configuration is invalid in plume.config.ts
contributors: true,
plugins: {
// If you declare it directly as true here, it means the feature is enabled in both development and production environments
git: process.env.NODE_ENV === 'production'
}
})
})
```
## Configuration
### mode
- Type: `'inline' | 'block'`
- Default: `'inline'`
- Description:
- `inline`: Display contributor information at the bottom of the article page,
alongside the last updated time. In this mode, only contributor names are displayed.
![contributors inline](/images/contributors-inline.png)
- `block`: Insert contributor information at the end of the article content. This mode includes contributor names, links, and avatars.
(As shown at the end of the current page's content)
In `block` mode, avatars are displayed by default for all contributors,
even if you haven't provided an avatar URL.
The plugin will generate avatar URLs from `https://gravatar.com/` based on email addresses or usernames.
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
},
})
})
```
### info
- Type: `ContributorInfo[]`
```ts
interface ContributorInfo {
/**
* Contributor's username on Git hosting service
*/
username: string
/**
* Name displayed for the contributor on the page, defaults to `username`
*/
name?: string
/**
* Contributor alias. Since the username saved in local git configuration might not match the hosting service username,
* aliases can be used to map to the actual username
*/
alias?: string[] | string
/**
* Contributor avatar URL
* If the git hosting service is `GitHub`, this can be omitted as the plugin will auto-fill it
*/
avatar?: string
/**
* Contributor profile URL
* If the git hosting service is `GitHub`, this can be omitted as the plugin will auto-fill it
*/
url?: string
}
```
- Description:
List of contributor information.
The username and email configured in the user's local git service might not match the user information
on git hosting services (like GitHub, GitLab, Gitee). You can pre-configure contributor information here.
(For non-GitHub git hosting services such as GitLab and Gitee, since avatars and user URLs cannot be
directly obtained from usernames, please supplement and complete user information here.)
::: code-tabs
@tab Github
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
info: [
{
username: 'pengzhanbo', // github username
alias: ['peng_zhan_bo'], // alias, username in local git configuration
}
]
},
})
})
```
@tab Gitlab
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
info: [
{
username: 'pengzhanbo', // gitlab username
alias: ['peng_zhan_bo'], // alias, username in local git configuration
url: 'https://gitlab.com/pengzhanbo',
avatar: 'https://gitlab.com/uploads/-/system/user/avatar/1/avatar.png',
}
]
},
})
})
```
@tab Gitee
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
info: [
{
username: 'pengzhanbo', // gitee username
alias: ['peng_zhan_bo'], // alias, username in local git configuration
url: 'https://gitee.com/pengzhanbo',
avatar: 'https://foruda.gitee.com/avatar/1234455/avatar.png',
}
]
},
})
})
```
@tab Bitbucket
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
info: [
{
username: 'pengzhanbo', // bitbucket username
alias: ['peng_zhan_bo'], // alias, username in local git configuration
url: 'https://bitbucket.org/pengzhanbo',
avatar: 'https://bitbucket.org/pengzhanbo/avatar/1234455/avatar.png',
}
]
},
})
})
```
@tab Others
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
contributors: {
mode: 'block',
info: [
{
username: 'pengzhanbo', // username
alias: ['peng_zhan_bo'], // alias, username in local git configuration
url: 'https://your-git.com/pengzhanbo',
avatar: 'https://your-git.com/avatar.png',
}
]
},
})
})
```
:::
### avatar
- Type: `boolean`
- Default: `true`
- Description: Whether to display contributor avatars
### avatarPattern
- Type: `string`
- Default: `'https://github.com/:username.png'`
- Description: Contributor avatar URL pattern
`:username` will be replaced with the contributor's username
### transform(contributors)
- Type: `(contributors: GitContributor[]) => GitContributor[]`
```ts
interface GitContributor {
name: string // Display name
username: string // Git hosting service username
email: string
commits: number // Number of commits by the contributor
avatar?: string
}
```
- Description:
Contributor transformation function. This function should return a new list of contributors.
You can add transformation logic here, such as sorting, deduplication, or completing information.
## frontmatter
### contributors
- Type: `boolean | string[]`
- Description:
Whether to display contributor information.
If your article comes from a third party and git commits cannot fully list all authors, you can supplement contributors here.
## Notes
This feature requires your project to be under a
[Git repository](https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository) so it can collect information from the commit history.
**When building your site, you should ensure all commit records are accessible.**
For example, CI workflows typically add the `--depth 1` parameter when cloning your repository to avoid
fetching all commit history. Therefore, you need to disable this feature so the plugin can work properly in CI.
Services like `GitHub Actions`, `Netlify`, and `Vercel` do not fetch all commit history by default.
In `GitHub Actions`, you can add the `--depth 0` parameter to ensure `GitHub Actions` can correctly fetch all commit records.
``` yaml title=".github/workflows/deploy.yml"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # [!code focus:3]
with: # [!code ++:2]
fetch-depth: 0
```
For services like `Netlify` and `Vercel`, the processing method is relatively more complex.
In such cases, you can first complete the build in `GitHub Actions` and output the artifacts to a
separate branch, then use that branch for deployment in `Netlify` or `Vercel`.
::: info
For projects created via the theme's `cli` tool, when selecting the deployment method as `GitHub Action`,
the build artifacts are output to the `gh_pages` branch. You can base your `Netlify` or `Vercel` deployment on this branch.
:::

View File

@ -0,0 +1,261 @@
---
title: Article Copyright
icon: lucide:creative-commons
createTime: 2025/10/08 10:52:49
permalink: /en/guide/features/copyright/
---
<script setup>
import VPCopyright from '@theme/VPCopyright.vue'
</script>
## Overview
The theme supports adding **copyright** declarations for articles.
Articles typically originate from original works, reposts, translations, etc.
Adding copyright information for different sources helps better protect intellectual property rights and avoid copyright disputes.
### Creative Commons
The theme natively supports copyright declarations using [Creative Commons](https://creativecommons.org/) licenses, including:
<style>
.doc-cc-list [class^="vpi-license-"] {
margin-left: 8px;
width: 1.4em;
height: 1.4em;
color: var(--vp-c-text-2);
transition: color var(--vp-t-color);
}
</style>
<div class="doc-cc-list">
- [CC0 1.0 Universal (CC0)](https://creativecommons.org/publicdomain/zero/1.0/)
<span class="vpi-license-zero" />
- [Attribution 4.0 International (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" />
- [Attribution-ShareAlike 4.0 International (CC-BY-SA-4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-sa" />
- [Attribution-NonCommercial 4.0 International (CC-BY-NC-4.0)](https://creativecommons.org/licenses/by-nc/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" />
- [Attribution-NoDerivatives 4.0 International (CC-BY-ND-4.0)](https://creativecommons.org/licenses/by-nd/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nd" />
- [Attribution-NonCommercial-ShareAlike 4.0 International (CC-BY-NC-SA-4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-sa" />
- [Attribution-NonCommercial-NoDerivatives 4.0 International (CC-BY-NC-ND-4.0)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-nd" />
</div>
You can select different licenses based on your requirements, or define custom licenses.
### Copyright Information
Copyright information includes:
- Copyright owner, copyright owner link
- Copyright license, copyright license link
- Original work link
This information is displayed at the bottom of articles.
::: tip It is recommended to enable the [Contributors](./contributors.md) feature when using this
functionality. For original articles, the theme automatically uses the first contributor as the
copyright owner. You can also manually specify the copyright owner in the article frontmatter.
:::
## Global Configuration
You can declare the `CC-BY-4.0` license for all articles on your site with the following configuration:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
copyright: 'CC-BY-4.0' // [!code hl]
})
})
```
You can declare a custom copyright license for all articles on your site with the following configuration:
```ts :no-line-numbers title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
copyright: { // [!code hl:6]
license: {
name: 'MIT', // License name
url: 'https://your-license-url' // License URL
},
author: {
name: 'Your Name', // Copyright owner name
url: 'https://your-author-url' // Copyright owner URL
},
creation: 'reprint' // Creation type
}
})
})
```
**Configuration Type:**
```ts
export type CopyrightLicense
= | 'CC-BY-4.0'
| 'CC-BY-SA-4.0'
| 'CC-BY-NC-4.0'
| 'CC-BY-NC-SA-4.0'
| 'CC-BY-ND-4.0'
| 'CC-BY-NC-ND-4.0'
| 'CC0'
| string
/**
* - When set to `true`, defaults to `CC-BY-4.0`
* - When set to `false`, copyright is hidden, but can be overridden in article frontmatter.copyright
*/
type CopyrightOptions = boolean | string | CopyrightLicense | {
/**
* Copyright license
*/
license?: CopyrightLicense | {
name: CopyrightLicense | string
url: string
}
/**
* Copyright owner. If not configured, defaults to obtaining from git commit records
*/
author?: {
name: string
url?: string
}
/**
* Creation type: original, translation, or repost
* @default 'original'
*/
creation?: 'original' | 'translate' | 'reprint'
}
```
::: warning Global configuration only applies to original articles. For non-original articles,
you should configure copyright information in the article frontmatter.
:::
## Article Frontmatter Configuration
You can configure copyright information for individual articles in the article frontmatter to override global configuration:
```md
---
title: My Article
copyright: CC-BY-4.0
---
```
**Configuration Type:**
```ts
/**
* When set to `false`, copyright is hidden
* When set to `true`, defaults to the global copyright configuration
*/
export type CopyrightFrontmatter = boolean | string | CopyrightLicense | {
/**
* Copyright license
*/
license?: CopyrightLicense | { name: string, url: string }
/**
* Copyright owner
* - For original articles, defaults to the first contributor
* - For non-original articles, the copyright owner must be declared
*/
author?: string | { name: string, url?: string }
/**
* Article creation type: original, translation, or repost
* @default 'original'
*/
creation?: 'original' | 'translate' | 'reprint'
/**
* Original article URL. Must be declared for non-original works
* @default ''
*/
source?: string
}
```
## Article Configuration Examples
### Original Article
```md
---
title: My Article
copyright: CC-BY-4.0
---
```
<VPCopyright license="CC-BY-4.0" />
### Reposted Article
```md
---
title: Reposted Article
copyright:
creation: reprint
license: CC-BY-4.0
source: https://example.com/origin
author:
name: Reposter
url: https://example.com/author
---
```
<VPCopyright
license="CC-BY-4.0" source="https://example.com/origin" creation="reprint"
:author="{name: 'Reposter', url: 'https://example.com/author'}"
/>
### Translated Article
```md
---
title: Translated Article
copyright:
creation: translate
license: CC-BY-4.0
source: https://example.com/origin
author:
name: Original Author
url: https://example.com/author
---
```
<VPCopyright
license="CC-BY-4.0" source="https://example.com/origin" creation="translate"
:author="{name: 'Original Author', url: 'https://example.com/author'}"
/>
### Custom License
```md
---
title: My Article
copyright:
license:
name: MIT
url: https://example.com/mit
---
```
<VPCopyright :license="{name: 'MIT', url: 'https://example.com/mit'}" />

View File

@ -0,0 +1,167 @@
---
title: Encryption
icon: mdi:encryption-outline
createTime: 2025/10/08 15:58:48
permalink: /en/guide/features/encryption/
---
## Encryption
This theme supports multiple flexible encryption methods, including **full-site encryption** and **partial encryption**.
::: warning Note
Due to the inherent limitations of `vuepress` as a static site generator,
the **encryption** only makes content *appear* invisible and excludes the content from being pre-rendered
into `html` during compilation. However, the content can still be accessed from the site's source files.
Therefore, the **encryption** feature should not be considered **secure and reliable**.
Avoid using the **encryption feature** for content that requires **strict confidentiality**.
:::
**Unlocked articles are only visible within the current session.**
## Enabling Encryption
Add the `encrypt` option in the theme configuration.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
encrypt: {
// more options...
}
})
})
```
## Full-Site Encryption
In some cases, you may need to encrypt the entire site. Configure full-site encryption using the `encrypt.
global` option, then set one or more passwords using the `encrypt.admin` option.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
encrypt: {
global: true,
admin: ['123456'],
}
})
})
```
## Partial Encryption
In most cases, you may only need to encrypt specific articles, directories, etc. Configure partial encryption using the `encrypt.rules` option.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
encrypt: {
rules: {
// Can be relative path to md file - encrypts this file
'前端/基础.md': '123456',
// Can be directory path - encrypts all articles under this directory
'/notes/vuepress-theme-plume/': '123456',
// Can be request path - encrypts all articles under this access path
'/vuepress-theme-plume/': '123456',
// Can be specific page request path - encrypts this page
'/article/f8dnci3/': '123456',
// If starting with `^`, pages matching this regex will also be encrypted
'^/(a|b)/': '123456',
}
}
})
})
```
The **keys** in `encrypt.rules` serve as matching rules, and the **values** serve as passwords for those rules. You can set one or multiple passwords.
:::tip Notes
- Passwords must be plain strings.
- If an entire directory is encrypted, unlocking applies to the entire directory, not individual articles within it.
- `encrypt.admin` can also be used to unlock **partially encrypted** pages.
- After unlocking with `encrypt.admin`, the user is considered an administrator and other locked pages are unlocked by default.
:::
### Frontmatter
Use the `password` field in Markdown file `Frontmatter` to set article passwords.
```md
---
title: Encrypted Article
password: 123456
---
```
You can also add the `passwordHint` option to set password hint information.
```md
---
title: Encrypted Article
password: 123456
passwordHint: The password is 123456
---
```
## Example
Click to visit [Encrypted Article, Password: 123456](/article/enx7c9s/)
## Related Configuration
The following configurations support use in [multilingual configuration](../../config/locales.md).
### encryptGlobalText
- **Type**: `string`
- **Default**: `'Only password can access this site'`
- **Description**:
Prompt message for full-site encryption. Supports HTML. Useful if you want to provide contact information for visitors to obtain passwords.
### encryptPageText
- **Type**: `string`
- **Default**: `'Only password can access this page'`
- **Description**:
Prompt message for partial encryption. Supports HTML. Useful if you want to provide contact information for visitors to obtain passwords.
### encryptButtonText
- **Type**: `string`
- **Default**: `'Confirm'`
- **Description**: Text for the confirmation button
### encryptPlaceholder
- **Type**: `string`
- **Default**: `'Enter password'`
- **Description**: Placeholder text for the password input field
### Example
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
locales: {
'/': {
encryptButtonText: 'Confirm',
encryptPlaceholder: 'Enter password',
encryptGlobalText: 'Only password can access this site',
encryptPageText: 'Only password can access this page',
}
}
})
})
```

View File

@ -0,0 +1,43 @@
---
title: Friend Links Page
icon: carbon:friendship
createTime: 2025/10/08 22:44:28
permalink: /en/guide/friend-links/
---
## Friend Links
The friend links page is not automatically generated, but you can create it as needed.
Create any `*.md` file in your `{sourceDir}/` directory, such as `friends.md`. Then configure it using `frontmatter` in this file.
```md title="friends.md"
---
friends: true
title: Friend Links
description: Description text for friend links
permalink: /friends/
contentPosition: after
list:
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: Even if slow, persist without stop; even if falling behind, even if failing, one must be able to reach the goal they are heading towards.
-
name: pengzhanbo
link: https://github.com/pengzhanbo
avatar: https://github.com/pengzhanbo.png
desc: Even if slow, persist without stop; even if falling behind, even if failing, one must be able to reach the goal they are heading towards.
---
Custom content <!-- Markdown content will be inserted into the friend links page -->
```
The theme will generate the friend links page based on the configuration.
You need to manually configure the entry link for the friend links page in an appropriate location in the `navbar`.
### Configuration
View the [documentation](../../config/frontmatter/friend.md) for more configuration information.

View File

@ -0,0 +1,116 @@
---
title: Icons
icon: raphael:smile2
createTime: 2025/10/08 10:45:47
permalink: /en/guide/features/icon/
---
## Overview
The theme supports icons from the following sources:
- [iconify](https://iconify.design/) - Supported by default
- [iconfont](https://www.iconfont.cn/) - Optional
- [fontawesome](https://fontawesome.com/) - Optional
Icons are used in the same way across the following theme features:
- [Navbar Icons](../../config/navbar.md#configuration)
- [Sidebar Icons](../../guide/document.md#sidebar-icons)
- [File Tree Icons](../../guide/markdown/file-tree.md)
- [Code Group Title Icons](../code/code-tabs.md#group-title-icons)
Provides syntax sugar and component support:
[Markdown Icon Syntax Sugar Support](../markdown/icons.md){.read-more}
[Icon Component Support](../components/icon.md){.read-more}
::: tip Theme Optimization for Icons
The theme employs the same parsing strategy internally for all the different ways of using icons
mentioned above. Even if you use the same icon in different locations, the same icon resources will not be loaded repeatedly.
:::
## Configuration
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
markdown: { // [!code ++:3]
icon: { provider: 'iconify' } // Supported by default
}
})
})
```
## Configuration Options
### provider
- **Type**: `'iconify' | 'iconfont' | 'fontawesome'`
- **Default**: `'iconify'`
Icon provider
### prefix
- **Type**: `string`
- **Default**: `''` (Different icon providers have different default values)
- **Details**:
Icon prefix
- When provider is `iconify`, defaults to `''`. Set the iconify collection as prefix, e.g., `mdi`.
- When provider is `iconfont`, defaults to `'iconfont icon-'`
- When provider is `fontawesome`, defaults to `'fas'`. Optional values are:
```ts
type FontAwesomePrefix
= | '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
```
### assets
- **Type**: `(string | FontAwesomeAssetBuiltin)[] | string | FontAwesomeAssetBuiltin`
```ts
type FontAwesomeAssetBuiltin = 'fontawesome' | 'fontawesome-with-brands'
```
- **Default**: `undefined`
- **Details**:
- For `iconify`: No configuration needed;
- For `iconfont`: Set to the resource URL of iconfont;
- For `fontawesome`: Set to the resource URL of fontawesome. Optional values are `fontawesome` or
`fontawesome-with-brands`, or custom resource URLs.
### size
- **Type**: `string | number`
- **Default**: `1em`
- **Details**:
Default icon size
### color
- **Type**: `string`
- **Default**: `'currentColor'`
- **Details**:
Default icon color

View File

@ -0,0 +1,46 @@
---
title: Image Preview
icon: ri:image-line
createTime: 2025/10/08 20:46:17
permalink: /en/guide/features/image-preview/
---
In the theme, images support click-to-enlarge preview by default. A preview list is generated by scanning images within the document content.
This feature is powered by the [@vuepress/plugin-photo-swipe](https://ecosystem.vuejs.press/en/plugins/features/photo-swipe.html) plugin.
## Configuration
Image preview is enabled by default. The behavior can be modified through the following configuration:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
plugins: {
photoSwipe: {
// Image selector
selector: '.vp-doc :not(a) > img:not([no-view],.no-view,.ignore)',
download: true, // Whether to show the download button
fullscreen: true, // Whether to show the fullscreen button
scrollToClose: true, // Whether to close the current image when scrolling
}
}
})
})
```
For more configuration options, please refer to [@vuepress/plugin-photo-swipe](https://ecosystem.vuejs.press/en/plugins/features/photo-swipe.html).
## Ignoring Image Preview
Image preview can be disabled by using the `no-view` or `ignore` class names, or the `no-view` attribute.
```md
![](path/to/image){.no-view}
![](path/to/image){.ignore}
![](path/to/image){no-view}
<img src="path/to/image" class="no-view">
<img src="path/to/image" class="ignore">
<img src="path/to/image" no-view>
```

View File

@ -0,0 +1,268 @@
---
title: Replace Assets
icon: lucide:replace
createTime: 2025/10/08 11:45:17
permalink: /en/guide/features/replace-assets/
badge: New
---
## Overview
This feature is powered by the [@vuepress/plugin-replace-assets](https://ecosystem.vuejs.press/en/plugins/tools/replace-assets.html) plugin.
It replaces local asset links within the site, such as images, videos, audio, PDFs, and other resources, rewriting local asset paths to new addresses.
## Why is this feature needed?
Many users choose to store site assets on CDN services to accelerate site access and improve availability.
In this process, assets typically need to be uploaded to the CDN service first, then CDN links are obtained, and finally used in the site content.
This may seem straightforward, but in practice, it often requires repeatedly performing:
```txt
Upload assets → Get asset links → Use full asset links in content
```
During this process, content creation is frequently interrupted.
This feature aims to solve this problem. During content creation,
you only need to use local asset paths directly, and the theme will handle the asset path replacement at the appropriate stage.
::: important This feature does not modify source files; replacements are only made in the compiled content.
:::
## Usage
The feature is disabled by default in the theme. You can enable it in the configuration:
```ts title=".vuepress/config.ts" twoslash
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// ReplaceAssetsPluginOptions
replaceAssets: 'https://cdn.example.com' // [!code ++]
})
})
```
==It's recommended to enable asset path replacement only for production builds, using local asset paths directly during development=={.important}
```ts title=".vuepress/config.ts" twoslash
import process from 'node:process'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
const isProd = process.env.NODE_ENV === 'production' // [!code ++]
export default defineUserConfig({
theme: plumeTheme({
// Enable only in production environment
replaceAssets: isProd ? 'https://cdn.example.com' : false // [!code ++]
})
})
```
### Asset Management
**You should store assets in the [.vuepress/public](https://v2.vuepress.vuejs.org/guide/assets.html#public-files) directory**:
```sh
./docs
├── .vuepress
│ └── public # [!code hl:6]
│ ├── images
│ │ ├── foo.jpg
│ │ └── bar.jpg
│ └── medias
│ └── foo.mp4
└── README.md
```
::: tip Why store assets in this directory?
When the site is compiled and ready for deployment, we can easily upload files from this directory directly to the CDN.
:::
In markdown, use local asset paths directly:
```md
![foo](/images/foo.jpg)
<img src="/images/foo.jpg">
```
In `javascript`:
```js
const foo = '/images/foo.jpg'
const img = document.createElement('img')
img.src = '/images/foo.jpg'
```
And in style files:
```css
.foo {
background: url('/images/foo.jpg');
}
```
The plugin will correctly identify these assets and replace them in the compiled content.
:::warning The plugin does not support identifying concatenated paths like `'/images/' + 'foo.jpg'`.
:::
## Configuration Reference
```ts
/**
* Asset link replacement configuration
*/
type ReplaceAssetsPluginOptions
= | Replacement
| ReplacementRule
| ReplacementRule[]
| ReplaceAssetsOptions
/**
* - `string`: Prepended to the original asset link
* - `function`: Returns the replaced asset link
*/
type Replacement = string | ((url: string) => string)
interface ReplacementRule {
/**
* Match asset links
*/
find: RegExp | string
/**
* Asset link replacement
*/
replacement: Replacement
}
interface ReplaceAssetsOptions {
/**
* Custom asset replacement rules
*/
rules?: ReplacementRule | ReplacementRule[]
/**
* Replacement for built-in asset matching rules
*/
all?: Replacement
/**
* Replacement for built-in image asset matching rules
*/
image?: Replacement
/**
* Replacement for built-in media asset matching rules
*/
media?: Replacement
}
```
## Built-in Asset Matching Rules
For convenience, the theme plugin provides built-in asset matching rules that you can use directly.
- `image`: Finds image assets, including local image resource links with formats `['apng','bmp','png','jpeg','jpg','jfif','pjpeg','pjp','gif','svg','ico','webp','avif','cur','jxl']`
- `media`: Finds media assets, including local media resource links with formats `['mp4','webm','ogg','mp3','wav','flac','aac','opus','mov','m4a','vtt','pdf']`
- `all`: Finds both image and media assets, combining both `image` and `media` rules
When directly passing a **asset link prefix** or **asset link replacement function**, the theme uses the `all` rule to replace asset links.
```ts title=".vuepress/config.ts"
import process from 'node:process'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// replaceAssets: 'https://cdn.example.com' // [!code hl]
replaceAssets: url => `https://cdn.example.com${url}` // [!code ++]
})
})
```
You can also apply different asset link prefixes or replacement functions to different built-in rules:
```ts title=".vuepress/config.ts"
import process from 'node:process'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// replaceAssets: { // [!code hl:4]
// image: 'https://image.cdn.com/',
// media: 'https://media.cdn.com/'
// },
replaceAssets: { // [!code ++:4]
image: url => `https://image.cdn.com${url}`,
media: url => `https://media.cdn.com${url}`
}
})
})
```
## Custom Asset Matching Rules
You can also define custom asset matching rules:
```ts title=".vuepress/config.ts"
import process from 'node:process'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
replaceAssets: { // [!code ++:4]
find: /^\/images\/.*\.(jpg|jpeg|png|gif|svg|webp|avif)$/,
replacement: url => `https://image.cdn.com${url}`
}
})
})
```
You can also define multiple matching rules:
```ts title=".vuepress/config.ts"
import process from 'node:process'
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
replaceAssets: [ // [!code ++:12]
// Find image assets
{
find: /^\/images\/.*\.(jpg|jpeg|png|gif|svg|webp|avif)$/,
replacement: 'https://image.cdn.com'
},
// Find media assets
{
find: /^\/medias\/.*\.(mp4|webm|ogg|mp3|wav|flac|aac|m3u8|m3u|flv|pdf)$/,
replacement: url => `https://media.cdn.com${url}`
},
]
})
})
```
**`find` Field Explanation**
The `find` field is used to match asset links and can be a **regular expression** or **string**.
When a `string` is provided, if it starts with `^` or ends with `$`, it will be automatically converted to a **regular expression**.
Otherwise, it will check if the asset link ends with `find` or starts with `find`.
```txt
'^/images/foo.jpg' -> /^\/images\/foo.jpg/
'/images/foo.jpg$' -> /^\/images\/foo.jpg$/
```
::: important All matched asset paths start with `/`.
:::

View File

@ -0,0 +1,234 @@
---
title: Content Search
icon: material-symbols:search
createTime: 2025/10/08 09:58:39
permalink: /en/guide/features/content-search/
---
The theme provides two approaches for content search:
- Local Content Search
- Algolia DocSearch
Note: Do not configure both approaches simultaneously. When both are configured, only Local Content Search will take effect.
## Local Content Search
Local Content Search is powered by the
[@vuepress-plume/plugin-search](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-search) plugin.
This plugin uses [minisearch](https://github.com/lucaong/minisearch) for content searching.
### Enabling
The theme enables Local Content Search by default. You can also customize its configuration.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
search: { // [!code ++:4]
provider: 'local',
// more options
}
})
})
```
This plugin generates search indexes locally based on your pages, then loads the search index files when users visit your site.
In other words, this is a lightweight built-in search capability that doesn't make any external requests.
However, when your site contains a large number of pages, the search index file can become very large and may slow down your page loading speed.
In such cases, we recommend using a more robust solution - [Algolia DocSearch](#algolia-docsearch).
## Algolia DocSearch
Site content search powered by [Algolia DocSearch](https://docsearch.algolia.com/).
### Enabling
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
search: { // [!code ++:4]
provider: 'algolia',
// more options
}
})
})
```
### Obtaining Search Index
You need to [submit your website URL](https://docsearch.algolia.com/apply/) to join the DocSearch program.
When your index is successfully created, the DocSearch team will send `apiKey` and `indexName` to your email.
You can then configure the plugin to enable DocSearch in VuePress.
Alternatively, you can [run your own crawler](https://docsearch.algolia.com/docs/run-your-own/) to create the index,
then use your own `appId`, `apiKey`, and `indexName` to configure the plugin.
Here's a crawler configuration example used by this theme. You can visit [Algolia Crawler](https://crawler.algolia.com/admin/crawlers/)
and modify it according to your needs:
```ts
new Crawler({
appId: 'YOUR_APP_ID', // [!code highlight]
apiKey: 'YOUR_API_KEY', // [!code highlight]
rateLimit: 8,
startUrls: [
// These are the initial URLs where Algolia starts crawling your site
// If your site is divided into several independent sections, you may need to set multiple entry links here
'https://YOUR_WEBSITE_URL/', // [!code highlight]
],
renderJavaScript: false,
sitemaps: [
// The theme generates sitemap by default; replace with your domain link here
'https://YOUR_WEBSITE_URL/sitemap.xml', // [!code highlight]
],
ignoreCanonicalTo: true,
discoveryPatterns: [
// This defines the scope of URLs that Algolia will crawl
'https://YOUR_WEBSITE_URL/**', // [!code highlight]
],
// Crawler execution schedule; set according to your documentation update frequency
schedule: 'at 02:00 every 1 day',
actions: [
// You can have multiple actions, especially when deploying multiple documentations under one domain
{
// Name your index appropriately
indexName: 'YOUR_INDEX_NAME', // [!code highlight]
// Paths where the index takes effect
pathsToMatch: ['https://YOUR_WEBSITE_URL/**'], // [!code highlight]
recordExtractor: ({ helpers }) => {
// Options for vuepress-theme-plume
return helpers.docsearch({
recordProps: { // [!code highlight]
lvl1: '.plume-content h1', // [!code highlight]
content: '.plume-content p, .plume-content li', // [!code highlight]
lvl0: { // [!code highlight]
selectors: [ // [!code highlight]
'.sidebar-item.is-active p', // [!code highlight]
'.content-container .page-title', // [!code highlight]
], // [!code highlight]
defaultValue: 'Documentation', // [!code highlight]
}, // [!code highlight]
lvl2: '.plume-content h2', // [!code highlight]
lvl3: '.plume-content h3', // [!code highlight]
lvl4: '.plume-content h4', // [!code highlight]
lvl5: '.plume-content h5', // [!code highlight]
}, // [!code highlight]
indexHeadings: true, // [!code highlight]
aggregateContent: true, // [!code highlight]
recordVersion: 'v3', // [!code highlight]
})
},
},
],
initialIndexSettings: {
// Controls how the index is initialized; only effective when the index hasn't been generated yet
// You may need to manually delete and regenerate the index after modifications
YOUR_INDEX_NAME: { // [!code highlight]
attributesForFaceting: ['type', 'lang'], // [!code highlight]
attributesToRetrieve: [
'hierarchy',
'content',
'anchor',
'url',
'url_without_anchor',
'type',
],
attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
attributesToSnippet: ['content:10'],
camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
searchableAttributes: [
'unordered(hierarchy_radio_camel.lvl0)',
'unordered(hierarchy_radio.lvl0)',
'unordered(hierarchy_radio_camel.lvl1)',
'unordered(hierarchy_radio.lvl1)',
'unordered(hierarchy_radio_camel.lvl2)',
'unordered(hierarchy_radio.lvl2)',
'unordered(hierarchy_radio_camel.lvl3)',
'unordered(hierarchy_radio.lvl3)',
'unordered(hierarchy_radio_camel.lvl4)',
'unordered(hierarchy_radio.lvl4)',
'unordered(hierarchy_radio_camel.lvl5)',
'unordered(hierarchy_radio.lvl5)',
'unordered(hierarchy_radio_camel.lvl6)',
'unordered(hierarchy_radio.lvl6)',
'unordered(hierarchy_camel.lvl0)',
'unordered(hierarchy.lvl0)',
'unordered(hierarchy_camel.lvl1)',
'unordered(hierarchy.lvl1)',
'unordered(hierarchy_camel.lvl2)',
'unordered(hierarchy.lvl2)',
'unordered(hierarchy_camel.lvl3)',
'unordered(hierarchy.lvl3)',
'unordered(hierarchy_camel.lvl4)',
'unordered(hierarchy.lvl4)',
'unordered(hierarchy_camel.lvl5)',
'unordered(hierarchy.lvl5)',
'unordered(hierarchy_camel.lvl6)',
'unordered(hierarchy.lvl6)',
'content',
],
distinct: true,
attributeForDistinct: 'url',
customRanking: [
'desc(weight.pageRank)',
'desc(weight.level)',
'asc(weight.position)',
],
ranking: [
'words',
'filters',
'typo',
'attribute',
'proximity',
'exact',
'custom',
],
highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
highlightPostTag: '</span>',
minWordSizefor1Typo: 3,
minWordSizefor2Typos: 7,
allowTyposOnNumericTokens: false,
minProximity: 1,
ignorePlurals: true,
advancedSyntax: true,
attributeCriteriaComputedByMinProximity: true,
removeWordsIfNoResults: 'allOptional',
},
},
})
```
The `recordProps` section contains configuration options used by this theme for index crawling.
### Configuration Options
For complete configuration, please refer to the [documentation](https://ecosystem.vuejs.press/en/plugins/search/docsearch.html).
### Configuration Example
Here's the configuration used by this theme:
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
search: { // [!code ++:6]
provider: 'algolia',
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_API_KEY',
indexName: 'YOUR_INDEX_NAME',
}
})
})
```

View File

@ -0,0 +1,111 @@
---
title: SEO
icon: tabler:seo
createTime: 2025/10/08 14:46:25
permalink: /en/guide/seo/
---
## Usage
The theme provides out-of-the-box configuration to enable SEO optimization features for your site.
To enable it, configure the following:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
hostname: 'http://your_site_url',
})
})
```
For custom SEO optimization, you can configure it through `plugins.seo`:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
plugins: {
seo: {
// ... SEO configuration
}
}
})
})
```
For complete configuration options, please refer to the [documentation](https://ecosystem.vuejs.press/en/plugins/seo/seo/config.html).
::: note
This example is forked from [@vuepress/plugin-seo](https://ecosystem.vuejs.press/en/plugins/seo/seo/),
licensed under [MIT](https://github.com/vuepress/ecosystem/blob/main/LICENSE).
:::
## Guide
The theme enhances your site's search engine optimization by injecting tags into the website's `<head>` section,
making it fully compliant with the [Open Graph Protocol (OGP)](https://ogp.me/)
and [JSON-LD 1.1](https://www.w3.org/TR/json-ld-api/).
By default, the plugin reads site configuration, theme configuration, and page frontmatter to automatically generate
metadata. Elements such as site name, page title, page type, writing date, last update date, and article tags are automatically generated.
## Default OGP Generation Logic
| Property Name | Value |
| :------------ | :---- |
| `og:url` | `options.hostname` + `path` |
| `og:site_name` | `siteConfig.title` |
| `og:title` | `page.title` |
| `og:description` | `page.frontmatter.description` \|\| auto-generated (when `autoDescription` is `true` in plugin options) |
| `og:type` | `"article"` |
| `og:image` | `options.hostname` + `page.frontmatter.image` \|\| first image in page \|\| `fallbackImage` from plugin options |
| `og:updated_time` | `page.git.updatedTime` |
| `og:locale` | `page.lang` |
| `og:locale:alternate` | Other languages included in `siteData.locales` |
| `twitter:card` | `"summary_large_image"` (only when image is found) |
| `twitter:image:alt` | `page.title` (only when image is found) |
| `article:author` | `page.frontmatter.author` \|\| `options.author` |
| `article:tag` | `page.frontmatter.tags` \|\| `page.frontmatter.tag` |
| `article:published_time` | `page.frontmatter.date` \|\| `page.git.createdTime` |
| `article:modified_time` | `page.git.updatedTime` |
## Default JSON-LD Generation Logic
| Property Name | Value |
| :------------ | :---- |
| `@context` | `"https://schema.org"` |
| `@type` | `"NewsArticle"` |
| `headline` | `page.title` |
| `image` | Images in page \|\| `options.hostname` + `page.frontmatter.image` |
| `datePublished` | `page.frontmatter.date` \|\| `page.git.createdTime` |
| `dateModified` | `page.git.updatedTime` |
| `author` | `page.frontmatter.author` \|\| `options.author` |
## SEO Introduction
Search Engine Optimization (SEO) is a method of adjusting websites by understanding search engine operation rules
to improve a target website's ranking in search engines. Since many studies have found that search engine users
often only pay attention to the top few entries in search results, many websites hope to influence search engine
rankings through various methods to achieve excellent search rankings for their sites.
The so-called "optimization for search engines" refers to making websites more easily accepted by search engines.
Search engines compare the content between websites for relevance, and then browsers present this content to searchers
in the fastest and most complete way possible. Search engine optimization follows search engine rules to create better
user experiences, with the ultimate goal of providing excellent user experience.
## Related Documentation
- [Open Graph Protocol (OGP)](https://ogp.me/)
This plugin fully supports this protocol and automatically generates compliant `<meta>` tags.
- [JSON-LD 1.1](https://www.w3.org/TR/json-ld-api/)
This plugin generates NewsArticle type tags for article pages.
- [RDFa 1.1](https://www.w3.org/TR/rdfa-primer/)
RDFa primarily marks up HTML structure.
- [Schema.Org](https://schema.org/)
Schema definitions for structured markup sites.

View File

@ -0,0 +1,35 @@
---
title: sitemap
icon: mdi:sitemap-outline
createTime: 2025/10/08 16:47:00
permalink: /en/guide/sitemap/
---
## Usage
The theme provides out-of-the-box configuration to generate a `sitemap.xml` file for the site.
To enable it, the following configuration is required:
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
hostname: 'http://your_site_url',
})
})
```
If customization of the sitemap is needed, it can be achieved through the `plugins.sitemap` configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
plugins: {
sitemap: {
// ... sitemap configurations
}
}
})
})
```
Refer to the [documentation](https://ecosystem.vuejs.press/zh/plugins/seo/sitemap/config.html) for the complete configuration.

View File

@ -0,0 +1,194 @@
---
title: Article Watermark
icon: material-symbols-light:branding-watermark-outline
createTime: 2025/10/08 20:14:57
permalink: /en/guide/features/watermark/
---
## Overview
Article watermarking is powered by [@vuepress/plugin-watermark](https://ecosystem.vuejs.press/zh/plugins/features/watermark.html).
The theme supports adding watermarks to articles. Both full-page watermarks and content-area watermarks
are supported, along with image watermarks and text watermarks.
## Enabling Watermark
Watermark functionality is disabled by default in the theme. You need to enable it in the theme configuration.
```ts title=".vuepress/config.ts"
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
// watermark: true,
watermark: {
// enabled: false, // boolean type to control global enablement
enabled: page => true, // function type to filter which pages enable watermark
delay: 500, // Delay for adding watermark. In milliseconds.
/**
* Whether to use full-page watermark, defaults to `true`.
* When set to `false`, the watermark is only displayed in the content area.
*/
fullPage: true,
/** @see https://zhensherlock.github.io/watermark-js-plus/zh/config/ */
watermarkOptions: {
content: 'your watermark',
// ...
}
}
})
})
```
### Global Enablement
When `plugins.watermark` is set to `true`, the theme enables watermark globally.
```ts
export default defineUserConfig({
theme: plumeTheme({
watermark: true,
})
})
```
### Partial Page Enablement
The theme provides two methods to control watermark enablement on specific pages.
#### watermark.enabled
```ts
export default defineUserConfig({
theme: plumeTheme({
watermark: {
// Pages returning true will enable watermark, others will disable it
enabled: page => page.path.includes('/article/'),
}
})
})
```
#### frontmatter.watermark
Add `frontmatter.watermark` as `true` in the md file:
```md
---
watermark: true
---
```
You can also customize the watermark configuration for the current page:
```md
---
watermark:
content: My Custom Content
globalAlpha: 0.2
rotate: 45
---
```
## Image Watermark
The theme supports using images as watermarks.
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
watermark: {
watermarkOptions: {
contentType: 'image',
image: '/images/watermark.png',
width: 200,
height: 200,
imageWidth: 100,
imageHeight: 100,
}
}
})
})
```
You can also add configuration in the md file to set watermark for the current page:
```md
---
watermark:
contentType: image
image: /images/watermark.png
width: 200
height: 200
imageWidth: 100
imageHeight: 100
---
```
### Example
[Image Watermark](/article/i4cuuonn/)
## Text Watermark
The theme supports using text as watermarks.
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
watermark: {
watermarkOptions: {
content: 'Custom Text',
fontColor: '#fff', // Text color
}
}
})
})
```
You can also add configuration in the md file to set watermark for the current page:
```md
---
watermark:
content: Custom Text
fontColor: #fff
---
```
## Frontmatter
The theme supports adding `frontmatter.watermark` in md files to set watermarks for individual pages.
```md
---
watermark:
content: My Custom Content
---
```
For supported configuration options, please refer to: [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/zh/config/)
Additionally, `fullPage` is supported to control whether to display the watermark full-screen.
```md
---
watermark:
fullPage: false
---
```
## Examples
- [Content Watermark](/article/2z59hh8g/)
- [Full-page Watermark](/article/97s6ha1e/)

View File

@ -1,19 +1,19 @@
--- ---
title: Abbreviation title: Abbreviations
icon: mdi:tooltip-question-outline icon: mdi:tooltip-question-outline
createTime: 2025/03/24 17:07:33 createTime: 2025/10/08 10:48:15
permalink: /en/guide/markdown/abbreviation/ permalink: /en/guide/markdown/abbreviation/
--- ---
## Overview ## Overview
**Abbreviations** refer to short forms used in Markdown, such as technical terms like W3C and ECMA. **Abbreviations** refer to acronyms used in Markdown, such as professional terms like W3C, ECMA, etc.
When hovering over an abbreviation, the full term will be displayed along with its definition or explanation. When hovering over an abbreviation, the full name of the term is displayed, and it can also include the definition and explanation of the abbreviation.
## Configuration ## Configuration
This feature is disabled by default. Enable it in `theme` configuration: This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts" ```ts title=".vuepress/config.ts"
export default defineUserConfig({ export default defineUserConfig({
@ -27,11 +27,12 @@ export default defineUserConfig({
## Syntax ## Syntax
In Markdown, define abbreviations using `*[ABBR]: Description` on separate lines. The description may include the abbreviation's definition, explanation, etc. In Markdown, use `*[Abbreviation]: Description` on a separate line to define an abbreviation.
The description can include the definition, explanation, etc., of the abbreviation.
Place the abbreviation within `[]` and its description after `:`. The description supports Markdown inline syntax. Fill in the abbreviation within `[]`, and write the description after `:`. Markdown inline syntax can be used in the description.
If the Markdown plain text contains defined abbreviations, the explanation of the abbreviation will be automatically displayed when the mouse moves over it. If defined abbreviations appear in regular Markdown text, hovering over them will automatically display the abbreviation's explanation.
**Input:** **Input:**
@ -50,5 +51,6 @@ The HTML specification is maintained by the W3C.
*[W3C]: World Wide Web Consortium *[W3C]: World Wide Web Consortium
*[ECMA]: European Computer Manufacturers Association *[ECMA]: European Computer Manufacturers Association
::: warning Abbreviations should be standalone words or phrases. For Chinese abbreviations, add spaces around the term to ensure proper detection. ::: warning
Abbreviations should be independent words or phrases. For Chinese abbreviations, add spaces on both sides of the word to distinguish them.
::: :::

View File

@ -1,19 +1,19 @@
--- ---
title: Annotation title: Annotation
icon: iconamoon:comment-add-light icon: iconamoon:comment-add-light
createTime: 2025/03/24 17:19:13 createTime: 2025/10/08 10:48:35
permalink: /en/guide/markdown/annotation/ permalink: /en/guide/markdown/annotation/
--- ---
## Description ## Description
==Annotation== is a special Markdown syntax for adding extra information, explanations, or hints in documents. ==Annotation== is a special syntax in Markdown used to add extra information, explanations, or hints to a document.
Annotations are not displayed directly in the document and require user interaction to be shown. Annotations are not directly displayed in the document; they require the user to manually click to be revealed.
## Configuration ## Configuration
This feature is not enabled by default. You need to enable it in the `theme` configuration. This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts" ```ts title=".vuepress/config.ts"
export default defineUserConfig({ export default defineUserConfig({
@ -27,120 +27,128 @@ export default defineUserConfig({
## Syntax ## Syntax
==Annotation== syntax consists of two parts: The ==Annotation== syntax consists of two parts:
### Inline Annotation ### Inline Annotation
Insert an annotation tag inline using the `[+label]` syntax. Insert an annotation marker inline using the `[+label]` syntax.
The annotation tag is composed of `[+` + `label` + `]`. To distinguish it from the content, there should be a space before `[+label]`. The annotation marker is composed of `[+` + `label` + `]`.
To distinguish it clearly from the surrounding content, a space should be present at the left edge of `[+label]`.
`label` is the annotation tag and can be any string. `label` is the tag for the annotation and can be any string.
::: important The symbol `+` is required ::: important The `+` symbol is required.
::: :::
### Define Annotation ### Defining Annotations
Define the annotation in a separate area of the document using the `[+label]:` syntax. Define annotations in a separate area of the document using the `[+label]:` syntax.
The annotation definition area is composed of `[+` + `label` + `]:` + `content`. The annotation definition area is composed of `[+` + `label` + `]:` + `Content`.
`label` should match the `[+label]` used earlier to mark the annotation. `label` should match the `[+label]` mentioned above and is used to identify the annotation tag.
**Content** can be written after the `:`: **Content** can be written directly after the `:`:
```md ```md
[+label]: Here is the content, which can use **Markdown** syntax. [+label]: This is the content. **Markdown** syntax can be used here.
``` ```
**Content** can also start on the next line with indentation, and should maintain consistent indentation for multiple lines. **Content** can also start on the next line, but it must be indented. Consistent indentation should be maintained for multiple lines.
```md ```md
[+label]: [+label]:
Here is the content. This is the content.
Indent consistently, this line is also part of the content. The indentation is consistent, so this line is also part of the content.
Even if the previous line is blank, but this line's indentation is consistent, it is still part of the content. Even with a blank line above, this line is still consistently indented, so it is also content.
You can use **Markdown** syntax. **Markdown** syntax can be used.
This line is no longer indented, so the annotation definition for this tag ends on the previous line. This line is no longer indented, so the annotation definition for this tag ended on the previous line.
``` ```
The content of the annotation definition is not rendered directly in the document, but is displayed when the `[+label]` inline annotation is clicked. The content of the annotation definition is not rendered directly in the document.
It is presented only when the inline `[+label]` marker is clicked.
## Example ## Examples
### Example 1 ### Example 1
**Input:** **Input:**
```md ```md
The site is powered by VuePress [+vuepress]. This site is powered by VuePress [+vuepress].
[+vuepress]: [+vuepress]:
VuePress is a [static site generator](https://en.wikipedia.org/wiki/Static_site_generator ) (SSG). VuePress is a [Static Site Generator](https://en.wikipedia.org/wiki/Static_site_generator) (SSG).
Designed specifically for building fast, content-focused websites. It is specifically designed for building fast, content-centric sites.
``` ```
**Output:** **Output:**
The site is powered by VuePress [+vuepress]. This site is powered by VuePress [+vuepress].
[+vuepress]: [+vuepress]:
VuePress is a [static site generator](https://en.wikipedia.org/wiki/Static_site_generator ) (SSG). VuePress is a [Static Site Generator](https://en.wikipedia.org/wiki/Static_site_generator) (SSG).
Designed specifically for building fast, content-focused websites. It is specifically designed for building fast, content-centric sites.
### Example 2 ### Example 2
**Multiple annotations for the same `label`, rendered as a list.** **Multiple annotations defined with the same `label` are rendered as a list.**
**Input:** **Input:**
```md ```md
The Four Great Classical Novels of ancient China [+classics] are widely known. The ancient Chinese **Four Great Classical Novels** [+novels] are household names.
[+classics]: [+novels]:
**Romance of the Three Kingdoms:** **"Romance of the Three Kingdoms":**
Set against the history of the Three Kingdoms period, it depicts the political and military struggles among the Wei, Shu, and Wu kingdoms, creating numerous historical figures like Zhuge Liang, Cao Cao, Guan Yu, and Liu Bei. Set against the backdrop of the Three Kingdoms period in Chinese history, it depicts the political and military struggles between the states of Wei, Shu, and Wu, shaping the images of numerous historical figures such as Zhuge Liang, Cao Cao, Guan Yu, and Liu Bei.
[+classics]: [+novels]:
**Journey to the West:** **"Journey to the West":**
Tells the story of Tang僧 and his four disciples (Sun Wukong, Zhu Bajie, Sha僧, and White Dragon Horse) on their pilgrimage to the West for Buddhist scriptures, filled with mythological elements and fantastic adventures. Tells the story of the monk Xuanzang and his three disciples (Sun Wukong, Zhu Bajie, Sha Wujing, and the White Dragon Horse) on their journey to the West to obtain sacred Buddhist texts, filled with mythological elements and fantastical adventures.
[+classics]: [+novels]:
**Dream of the Red Chamber:** **"Dream of the Red Chamber":**
Set against the rise and fall of the four major families (Jia, Shi, Wang, and Xue), it depicts the love tragedy of Jia Baoyu, Lin Daiyu, and Xue Baochai, showcasing the decadence and decline of feudal society. Set against the backdrop of the decline of the four major families Jia, Shi, Wang, and Xue, it depicts the love tragedy between Jia Baoyu, Lin Daiyu, and Xue Baochai, revealing the corruption and decay of feudal society.
[+classics]: [+novels]:
**Water Margin:** **"Water Margin":**
Depicts the story of 108 heroes led by Song Jiang gathering at Liangshan泊 to resist the court during the late Northern Song Dynasty, revealing the social reality of oppression leading to rebellion. Depicts the story of 108 outlaws, led by Song Jiang, who gather at Liangshan Marsh during the late Northern Song Dynasty to rebel against the imperial government, showcasing the social reality of oppression leading to rebellion.
``` ```
**Output:** **Output:**
The Four Great Classical Novels of ancient China [+classics] are widely known. The ancient Chinese **Four Great Classical Novels** [+novels] are household names.
[+classics]: [+novels]:
**Romance of the Three Kingdoms:** **"Romance of the Three Kingdoms":**
Set against the history of the Three Kingdoms period, it depicts the political and military struggles among the Wei, Shu, and Wu kingdoms, creating numerous historical figures like Zhuge Liang, Cao Cao, Guan Yu, and Liu Bei. Set against the backdrop of the Three Kingdoms period in Chinese history,
it depicts the political and military struggles between the states of Wei, Shu, and Wu,
shaping the images of numerous historical figures such as Zhuge Liang, Cao Cao, Guan Yu, and Liu Bei.
[+classics]: [+novels]:
**Journey to the West:** **"Journey to the West":**
Tells the story of Tang僧 and his four disciples (Sun Wukong, Zhu Bajie, Sha僧, and White Dragon Horse) on their pilgrimage to the West for Buddhist scriptures, filled with mythological elements and fantastic adventures. Tells the story of the monk Xuanzang and his three disciples (Sun Wukong, Zhu Bajie, Sha Wujing,
and the White Dragon Horse) on their journey to the West to obtain sacred Buddhist texts,
filled with mythological elements and fantastical adventures.
[+classics]: [+novels]:
**Dream of the Red Chamber:** **"Dream of the Red Chamber":**
Set against the rise and fall of the four major families (Jia, Shi, Wang, and Xue), it depicts the love tragedy of Jia Baoyu, Lin Daiyu, and Xue Baochai, showcasing the decadence and decline of feudal society. Set against the backdrop of the decline of the four major families Jia, Shi, Wang, and Xue,
it depicts the love tragedy between Jia Baoyu, Lin Daiyu, and Xue Baochai, revealing the corruption and decay of feudal society.
[+classics]: [+novels]:
**Water Margin:** **"Water Margin":**
Depicts the story of 108 heroes led by Song Jiang gathering at Liangshan泊 to resist the court during the late Northern Song Dynasty, revealing the social reality of oppression leading to rebellion. Depicts the story of 108 outlaws, led by Song Jiang, who gather at Liangshan Marsh during the late
Northern Song Dynasty to rebel against the imperial government, showcasing the social reality of oppression leading to rebellion.

View File

@ -1,19 +1,19 @@
--- ---
title: Can I Use title: Can I Use
createTime: 2025/03/24 22:10:32 createTime: 2025/10/08 14:50:55
icon: streamline:desktop-help icon: streamline:desktop-help
permalink: /en/guide/markdown/caniuse/ permalink: /en/guide/markdown/caniuse/
--- ---
## Overview ## Overview
When writing articles, you can embed the support status of [can-i-use](https://caniuse.com/) WEB features across platforms. When writing articles, this feature provides the functionality to embed platform support information for WEB features from [can-i-use](https://caniuse.com/).
This makes it easier to describe the support level of a particular WEB feature. This allows for a more intuitive representation of a feature's support level when describing a specific WEB feature.
## Configuration ## Configuration
This feature is not enabled by default. You can enable it in the configuration file. This feature is disabled by default. You can enable it in the configuration file.
```ts title=".vuepress/config.ts" ```ts title=".vuepress/config.ts"
export default defineUserConfig({ export default defineUserConfig({
@ -25,17 +25,18 @@ export default defineUserConfig({
}) })
``` ```
In your Markdown file, use the following format: In your article's markdown file, use the following format:
```md ``` md
@[caniuse](feature) @[caniuse](feature)
``` ```
For convenience, the theme provides a tool: [caniuse feature search](../../../../../notes/tools/caniuse.md), which can help generate the Markdown code. For ease of use, the theme provides tool support: [caniuse Feature Search](../../../tools/caniuse.md).
You can use this tool directly to help generate the markdown code.
## Syntax ## Syntax
```md ``` md
@[caniuse](feature) @[caniuse](feature)
@[caniuse{browser_versions}](feature) @[caniuse{browser_versions}](feature)
@[caniuse embed_type](feature) @[caniuse embed_type](feature)
@ -44,19 +45,19 @@ For convenience, the theme provides a tool: [caniuse feature search](../../../..
- `feature` - `feature`
Required. For correct values, refer to [caniuse-embed.vercel.app](https://caniuse-embed.vercel.app/zh-CN) Required. For correct values, please refer to [caniuse-embed.vercel.app](https://caniuse-embed.vercel.app/zh-CN).
- `{browser_versions}` - `{browser_versions}`
Optional. The support status of the feature across multiple browser versions. Optional. Specifies the support status of the current feature across multiple browser versions.
Default value: `{-2,1}` Default value: `{-2,1}`
Format: `{past,future}` with values ranging from `-5 ~ 3` Format: `{past,future}` Value range: `-5 ~ 3`
- Less than `0` indicates support below the current browser version - Values less than `0` indicate support status for browser versions lower than the current one.
- `0` indicates support at the current browser version - `0` indicates the support status for the current browser version.
- Greater than `0` indicates support above the current browser version - Values greater than `0` indicate support status for browser versions higher than the current one.
- `embed_type` - `embed_type`
@ -67,37 +68,40 @@ For convenience, the theme provides a tool: [caniuse feature search](../../../..
Default value: `'embed'` Default value: `'embed'`
:::caution :::caution
The use of image type is no longer recommended. Instead, use the embed type, as the theme has changed the embed implementation, offering faster loading, smaller size, and better interaction. The `image` type is no longer recommended. It is advised to use the `embed` type.
The theme has changed the implementation technology for the embed component.
The current `embed` type now offers significant advantages over the `image` type,
including faster loading speed, smaller size, and better interactive experience.
::: :::
## Examples ## Examples
**Get the browser support status for the CSS pseudo-class selector `:dir()`:** **Get the browser support for the CSS pseudo-class selector `:dir()`:**
```md ```md
@[caniuse](css-matches-pseudo) @[caniuse](css-matches-pseudo)
``` ```
Effect: Result:
@[caniuse](css-matches-pseudo) @[caniuse](css-matches-pseudo)
**Get the browser support status for the CSS pseudo-class selector `:dir()` as an image:** **Get the browser support for the CSS pseudo-class selector `:dir()` as an image:**
```md ```md
@[caniuse image](css-matches-pseudo) @[caniuse image](css-matches-pseudo)
``` ```
Effect: Result:
@[caniuse image](css-matches-pseudo) @[caniuse image](css-matches-pseudo)
**Get the browser support status for the CSS pseudo-class selector `:dir()` for specific browser versions:** **Get the browser support for the CSS pseudo-class selector `:dir()` for a specific range of browser versions:**
```md ```md
@[caniuse{-2,3}](css-matches-pseudo) @[caniuse{-2,3}](css-matches-pseudo)
``` ```
Effect: Result:
@[caniuse{-2,3}](css-matches-pseudo) @[caniuse{-2,3}](css-matches-pseudo)

View File

@ -1,26 +1,26 @@
--- ---
title: Card title: Card
createTime: 2025/03/24 19:46:13 createTime: 2025/10/08 14:37:40
icon: solar:card-broken icon: solar:card-broken
permalink: /en/guide/markdown/card/ permalink: /en/guide/markdown/card/
--- ---
## Overview ## Overview
To highlight content, place it in a card container `::: card`. Content that needs to be highlighted can be placed within a card container `::: card`.
To display multiple cards side by side when space allows, use the `card-grid` container around multiple cards. When multiple cards need to be displayed side-by-side when space permits, use the `card-grid` container to wrap multiple cards.
## Syntax ## Syntax
```md ```md
<!-- Single card --> <!-- Single Card -->
::: card title="Title" icon="twemoji:astonished-face" ::: card title="Title" icon="twemoji:astonished-face"
This is the card content. This is the card content.
::: :::
<!-- Multiple cards --> <!-- Multiple Cards -->
:::: card-grid :::: card-grid
::: card title="Card Title 1" icon="twemoji:astonished-face" ::: card title="Card Title 1" icon="twemoji:astonished-face"
@ -36,9 +36,18 @@ This is the card content.
:::: ::::
``` ```
Cards support optional `title` and `icon`. The icon can be an image URL or an Iconify icon name. ## Props
## Example :::: field-group
::: field name="title" type="string" optional default="''"
Card title
:::
::: field name="icon" type="string" optional
Card icon. Supports image URLs and [iconify](https://icon-sets.iconify.design/) icon names.
::::
## Examples
**Input:** **Input:**
@ -87,5 +96,5 @@ This is the card content.
:::: ::::
::: info ::: info
If you prefer to use cards via components, you can check out the [Card Component](/en/guide/features/component/#card). If you prefer using cards via components, you can check out the [Card Component](/guide/features/component/#card).
::: :::

View File

@ -0,0 +1,123 @@
---
title: Chat Records
icon: cil:chat-bubble
createTime: 2025/10/08 21:40:18
permalink: /en/guide/markdown/chat/
---
## Preface
::: chat title="User B"
{:2025-03-24 10:15:00}
{User B}
Putting chat screenshots in the documentation still looks pretty bad, is there a better way? \[doge\]
{.}
Sure there is, bro, definitely sure
{.}
But is it really okay to post chat records like this?
{User B}
Proof of our fiery debates is worth keeping \[doge\]
{:2025-03-24 15:32:00}
{.}
Good news: the docs now support chat records!
{.}
Bad news: I've used you as the example \[doge\]
{User B}
???
:::
## Overview
In Markdown, wrapping specially formatted text content within a `:: chat` container allows you to display ==chat records== within the documentation.
::: warning This is a feature you likely won't need most of the time.
Please consider carefully whether you should use it when the need arises.
Filter out any private or sensitive content yourself.
:::
## Enable
This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
chat: true, // [!code ++]
}
})
})
```
## Usage
Within a `::: chat` container, use specific markers to identify the sender and timestamp of messages,
which will then render the chat record in the documentation.
```md
::: chat title="Title"
{:date} <!-- Mark the starting timestamp -->
{username} <!-- Mark the sender -->
xxx <!-- The sender's message content -->
{.} <!-- Mark as sent by the current user -->
xxx <!-- The current user's message content -->
:::
```
- `{:date}` Marks the starting timestamp (Optional). Use the format `{:` + date + `}`. `date` can be in common date formats.
The theme does not process `date` in any way; it simply renders it.
- `{username}` Marks the sender of the subsequent content. Use the format `{` + username + `}`. `username` can be any string.
- `{.}` Marks the message as sent by the current user.
## Example
__Input:__
``` md
::: chat title="Title"
{:2025-03-24 10:15:00}
{User 1}
Message from User 1
{.}
Message from the current user
{User 2}
Message from User 2
{.}
Message from the current user
:::
```
__Output:__
::: chat title="Title"
{:2025-03-24 10:15:00}
{User 1}
Message from User 1
{.}
Message from the current user
{User 2}
Message from User 2
{.}
Message from the current user
:::

View File

@ -0,0 +1,170 @@
---
title: Code Tree
icon: stash:side-peek
createTime: 2025/10/08 05:59:44
permalink: /en/guide/markdown/code-tree/
badge: New
---
## Overview
In Markdown, use the `::: code-tree` container or `@[code-tree](dir_path)` syntax to display a code block area with a file tree.
Compared to code block grouping, code trees can more clearly present the organizational structure of code files and their dependency relationships.
## Enable
This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
codeTree: true, // [!code ++]
}
})
})
```
## Usage
The theme provides two usage methods:
### code-tree Container
````md
::: code-tree title="Project Name" height="400px" entry="filepath"
```lang title="filepath" :active
<!-- code content-->
```
```lang title="filepath"
<!-- code content-->
```
<!-- More code blocks -->
:::
````
Use the `::: code-tree` container to wrap multiple code blocks.
- Use `title="Project Name"` after `::: code-tree` to declare the code tree title
- Use `height="400px"` after `::: code-tree` to declare the code tree height
- Use `entry="filepath"` after `::: code-tree` to declare the default expanded file path
- Use `title="filepath"` after the code block <code>\`\`\` lang</code> to declare the current code block's file path
- If `entry="filepath"` is not declared in `::: code-tree`, you can use `:active` after the code block
<code>\`\`\` lang</code> to declare the current code block as expanded
- If no expanded file path is specified, the first file will be expanded by default
::: details Why use `title="filepath"` instead of `filepath="filepath"` on code blocks?
Because the theme already [supports title syntax on code blocks](../code/features.md#code-block-titles).
Continuing to use the existing syntax support reduces the learning curve.
:::
**Input:**
````md :collapsed-lines
::: code-tree title="Vue App" height="400px" entry="src/main.ts"
```vue title="src/components/HelloWorld.vue"
<template>
<div class="hello">
<h1>Hello World</h1>
</div>
</template>
```
```vue title="src/App.vue"
<template>
<div id="app">
<h3>vuepress-theme-plume</h3>
<HelloWorld />
</div>
</template>
```
```ts title="src/main.ts"
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
```
```json title="package.json"
{
"name": "Vue App",
"scripts": {
"dev": "vite"
}
}
```
:::
````
**Output:**
::: code-tree title="Vue App" height="400px" entry="src/main.ts"
```vue title="src/components/HelloWorld.vue"
<template>
<div class="hello">
<h1>Hello World</h1>
</div>
</template>
```
```vue title="src/App.vue"
<template>
<div id="app">
<h3>vuepress-theme-plume</h3>
<HelloWorld />
</div>
</template>
```
```ts title="src/main.ts"
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
```
```json title="package.json"
{
"name": "Vue App",
"scripts": {
"dev": "vite"
}
}
```
:::
### Importing code-tree from Directory
The theme supports importing `code-tree` from a directory using the following syntax:
```md
<!-- Simple import -->
@[code-tree](dir_path)
<!-- With additional configuration -->
@[code-tree title="Project Name" height="400px" entry="filepath"](dir_path)
```
- **dir_path**:
When an absolute path is provided (starting with `/`), the search begins from the source directory of the documentation site.
When a relative path is provided (starting with `.`), it is relative to the current Markdown file.
- **title**: Code tree title, optional, defaults to empty
- **height**: Code tree height, optional, defaults to empty
- **entry**: Default expanded file path, optional, defaults to the first file
**Input:**
```md
<!-- This directory is the theme repository's `docs/.vuepress/collections/` -->
@[code-tree title="Collections Configuration" height="400px" entry="index.ts"](/.vuepress/collections)
```
**Output:**
@[code-tree title="Collections Configuration" height="400px" entry="index.ts"](/.vuepress/collections)

View File

@ -1,22 +1,19 @@
--- ---
title: Collapsible panel title: Collapse Panel
icon: carbon:collapse-categories icon: carbon:collapse-categories
createTime: 2025/03/25 10:13:04 createTime: 2025/10/08 22:27:22
permalink: /en/guide/markdown/collapse/ permalink: /en/guide/markdown/collapse/
badge:
type: tip
text: 1.0.0-rc.137 +
--- ---
## Overview ## Overview
In Markdown, the `::: collapse` container, combined with Markdown unordered list syntax, can be used to create ==collapsible panels==. In markdown, use the `::: collapse` container with markdown unordered list syntax to implement ==collapse panels==.
- It supports setting the mode to **accordion** using the `accordion` option. - Supports ==accordion== mode via the `accordion` setting
## Enable ## Enable
This feature is not enabled by default. You need to enable it in the `theme` configuration. This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts" ```ts title=".vuepress/config.ts"
export default defineUserConfig({ export default defineUserConfig({
@ -30,13 +27,13 @@ export default defineUserConfig({
## Usage ## Usage
In Markdown, use the `::: collapse` container with Markdown unordered list syntax, where each item represents a separate collapsible area. In markdown, use the `::: collapse` container with markdown unordered list syntax. Each item represents a separate collapsible section.
```md title="collapse.md" ```md title="collapse.md"
::: collapse ::: collapse
- Title 1 <!-- Title, click to control expand/collapse --> - Title 1 <!-- Title, click to control expand/collapse -->
<!-- There must be an empty line between the title and the content --> <!-- Must have an empty line between title and content -->
Content <!-- Content, the collapsible area --> Content <!-- Content, the collapsible area -->
- Title 2 - Title 2
@ -44,225 +41,227 @@ In Markdown, use the `::: collapse` container with Markdown unordered list synta
::: :::
``` ```
For each item in the list: For each list item:
- From the **first line** to the **first empty line** is the **title**. - Everything from the __first line__ to the __first empty line__ is considered the __title__
- **After the first empty line**: The main content.
:::important Please note the correct indentation - __After the first empty line__: Content
:::important Please ensure correct indentation
::: :::
**A simple example:** __A simple example:__
**Input:** __Input:__
```md ```md
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
## Configuration ## Configuration
After the `::: collapse` container syntax, you can add configuration options: After the `::: collapse` container syntax, follow with configuration options:
- `accordion`: Sets the collapsible panels to ==accordion== mode. In accordion mode, only one panel can be expanded at a time. Clicking another panel will close the previously opened one. - `accordion`: Sets the collapse panel to ==accordion== mode. In accordion mode,
- `expand`: Expands all panels by default. This option is invalid in accordion mode. only one panel can be expanded at a time; clicking other panels will close the previously opened panel.
- `expand`: Expands panels by default. Invalid in accordion mode.
Before the title of each list item, you can use special markers `:+` or `:-` to set the initial state of the item to **expanded** or **collapsed**. In list items, before the title, use special markers `:+` / `:-` to set the initial state of the current item to __expanded / collapsed__.
## Examples ## Examples
### Basic Usage ### Basic Usage
**Input:** __Input:__
```md ```md
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
### Expand All by Default ### Expand All by Default
Add the `expand` option to expand all panels by default. Add the `expand` option to expand all panels by default.
**Input:** __Input:__
```md /expand/ ```md /expand/
::: collapse expand ::: collapse expand
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse expand ::: collapse expand
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
::: :::
### Accordion Mode ### Accordion Mode
Add the `accordion` option to set the mode to accordion, allowing only one panel to be expanded at a time. Clicking another panel will close the previously opened one. Add the `accordion` option to set accordion mode, where only one panel can be expanded at a time.
```md /accordion/ ```md /accordion/
::: collapse accordion ::: collapse accordion
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
- Title 3 - Title 3
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse accordion ::: collapse accordion
- Title 1 - Title 1
Main content Content
- Title 2 - Title 2
Main content Content
- Title 3 - Title 3
Main content Content
::: :::
### Expand Items with `:+` ### `:+` Mark Item as Expanded
By default, all collapsible panels are closed. You can use the `:+` marker to set the initial state of an item to expanded. Collapse panels are closed by default. Use `:+` to mark items with an initial expanded state.
**Input:** __Input:__
```md /:+/ ```md /:+/
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- :+ Title 2 - :+ Title 2
Main content Content
- :+ Title 3 - :+ Title 3
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse ::: collapse
- Title 1 - Title 1
Main content Content
- :+ Title 2 - :+ Title 2
Main content Content
- :+ Title 3 - :+ Title 3
Main content Content
::: :::
### Collapse Items with `:-` ### `:-` Mark Item as Collapsed
When the `expand` option is configured, all panels are expanded by default. You can use the `:-` marker to set the initial state of an item to collapsed. When collapse panel is configured with `expand`, all panels are expanded by default. Use `:-` to mark items with an initial collapsed state.
**Input:** __Input:__
```md /:-/ ```md /:-/
::: collapse expand ::: collapse expand
- Title 1 - Title 1
Main content Content
- :- Title 2 - :- Title 2
Main content Content
- Title 3 - Title 3
Main content Content
::: :::
``` ```
**Output:** __Output:__
::: collapse expand ::: collapse expand
- Title 1 - Title 1
Main content Content
- :- Title 2 - :- Title 2
Main content Content
- Title 3 - Title 3
Main content Content
::: :::

View File

@ -1,6 +1,6 @@
--- ---
title: Demo Wrapper title: Demo Container
createTime: 2025/03/24 21:49:01 createTime: 2025/10/08 14:47:12
icon: icon-park-outline:eyes icon: icon-park-outline:eyes
permalink: /en/guide/markdown/demo-wrapper/ permalink: /en/guide/markdown/demo-wrapper/
outline: 2 outline: 2
@ -8,27 +8,27 @@ outline: 2
## Overview ## Overview
Sometimes, you may need to add examples in your content but want them to be presented separately from other content. Sometimes you may need to supplement your content with examples but want them to be presented
The theme supports adding demo wrapper in Markdown files. separately from other content. The theme supports adding demo containers in Markdown files.
## Syntax ## Syntax
````md ````md
::: demo-wrapper ::: demo-wrapper
Add your example here Add your demo here
::: :::
```` ````
## Options ## Options
- `title="xxx"`: Title - `title="xxx"`: Title
- `no-padding`: No padding - `no-padding`: Remove padding
- `img`: Use when only containing an image - `img`: Use when containing only images
- `height="xxx"`: Height - `height="xxx"`: Height
## Example ## Examples
Image only: Containing only images:
```md ```md
::: demo-wrapper img no-padding ::: demo-wrapper img no-padding
@ -41,25 +41,25 @@ Image only:
![hero](/images/custom-hero.jpg) ![hero](/images/custom-hero.jpg)
::: :::
Markdown content: Containing markdown syntax:
```md ```md
::: demo-wrapper title="Title" ::: demo-wrapper title="Title"
### Third-level heading ### Level 3 Heading
This is the content within the demo wrapper. This is content inside the demo container.
::: :::
``` ```
**Output:** **Output:**
::: demo-wrapper title="Title" ::: demo-wrapper title="Title"
### Third-level heading ### Level 3 Heading
This is the content within the demo wrapper. This is content inside the demo container.
::: :::
HTML/Vue code: Containing HTML/Vue code:
```md ```md
::: demo-wrapper ::: demo-wrapper
@ -80,7 +80,7 @@ HTML/Vue code:
**Output:** **Output:**
::: demo-wrapper ::: demo-wrapper
<h1 class="your-demo-title">This is a title</h1> <h1 class="your-demo-title">This is a heading</h1>
<p class="your-demo-paragraph">This is a paragraph</p> <p class="your-demo-paragraph">This is a paragraph</p>
<style> <style>

View File

@ -0,0 +1,150 @@
---
title: Field Container
icon: solar:text-field-linear
createTime: 2025/10/08 09:55:17
permalink: /en/guide/markdown/field/
badge: New
---
## Overview
In Markdown, use the `::: field` container to describe field information, including field name,
field type, whether it's required, default value, description, and other details.
It's suitable for describing fields in configurations, component Props, and similar scenarios.
You can also use the additional `:::: field-group` container to group multiple `::: field` containers.
## Enable
This feature is disabled by default. You need to enable it in the `theme` configuration.
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
field: true, // [!code ++]
},
})
})
```
## Syntax
```md
<!-- Single Field -->
::: field name="Field Name" type="Type" required default="Default Value"
Field description information
:::
<!-- Field Group -->
:::: field-group
::: field name="Field Name" type="Type" required default="Default Value"
Field description information
:::
::: field name="Field Name" type="Type" required default="Default Value"
Field description information
:::
::::
```
## Properties
:::: field-group
::: field name="name" required type="string"
Field name
:::
::: field name="type" type="string" optional
Field type
:::
::: field name="required" type="boolean" optional
Whether the field is required
:::
::: field name="optional" type="boolean" optional
Whether the field is optional
:::
::: field name="deprecated" type="boolean" optional
Whether the field is deprecated
:::
::: field name="default" type="string" optional
Default value. If it's an empty string, use `default="''"`
:::
::::
## Examples
**Input:**
```md
::: field name="theme" type="ThemeConfig" required default="{}"
Theme configuration
:::
::: field name="enabled" type="boolean" optional default="true"
Whether enabled
:::
```
**Output:**
::: field name="theme" type="ThemeConfig" required default="{}"
Theme configuration
:::
::: field name="enabled" type="boolean" optional default="true"
Whether enabled
:::
**Input:**
```md
:::: field-group
::: field name="theme" type="ThemeConfig" required default="{ base: '/' }"
Theme configuration
:::
::: field name="enabled" type="boolean" optional default="true"
Whether enabled
:::
::: field name="callback" type="(...args: any[]) => void" optional default="() => {}"
<Badge type="tip" text="v1.0.0 New" />
Callback function
:::
::: field name="other" type="string" deprecated
<Badge type="danger" text="v0.9.0 Deprecated" />
Deprecated property
:::
::::
```
**Output:**
:::: field-group
::: field name="theme" type="ThemeConfig" required default="{ base: '/' }"
Theme configuration
:::
::: field name="enabled" type="boolean" optional default="true"
Whether enabled
:::
::: field name="callback" type="(...args: any[]) => void" optional default="() => {}"
<Badge type="tip" text="v1.0.0 New" />
Callback function
:::
::: field name="other" type="string" deprecated
<Badge type="danger" text="v0.9.0 Deprecated" />
Deprecated property
:::
::::

View File

@ -1,35 +1,38 @@
--- ---
title: File Tree title: File Tree
createTime: 2025/03/24 20:15:12 createTime: 2025/10/08 14:41:57
icon: mdi:file-tree icon: mdi:file-tree
permalink: /en/guide/markdown/file-tree/ permalink: /en/guide/markdown/file-tree/
--- ---
## Overview ## Overview
In Markdown, you can use the `file-tree` container to display a directory structure with file icons and collapsible subdirectories. In Markdown, you can use the `file-tree` container to display directory structures with file icons and collapsible subdirectories.
## Syntax ## Syntax
In the `::: file-tree` container, use the built-in **Markdown unordered list syntax** to specify the organization of files and directories. Within the `::: file-tree` container, use the built-in **Markdown unordered list syntax** to specify the organization of files and directories.
Use nested list items to create subdirectories; if you want a directory to not display specific content, simply add a slash `/` at the end of the list item. Use nested list items to create subdirectories; to indicate that a directory's contents should not be
displayed, simply add a slash `/` at the end of the list item.
The following syntax can be used to customize the appearance of the file tree: The following syntax can be used to customize the appearance of the file tree:
- Highlight files or directories by bolding their names, e.g., `**README.md**` - Emphasize file or directory names by making them bold, e.g., `**README.md**`
- Add annotations to files or directories by appending additional text after the name - Add comments to files or directories by adding additional text after the name
- Use `...` or `…` as names to add placeholder files and directories. - Mark files or directories as **added** or **deleted** by prefixing the name with `++` or `--`
- Adding `icon="simple"` or `icon="colored"` after `:::file-tree` can switch to simple icons or colored icons, with colored icons being the default. - Use `...` or `…` as the name to add placeholder files and directories.
- Add `icon="simple"` or `icon="colored"` after `:::file-tree` to switch to simple icons or colored icons. The default is colored icons.
- Add `title="xxxx"` after `:::file-tree` to add a title to the file tree.
**Input:** **Input:**
```md ```md /++/ /--/
::: file-tree ::: file-tree
- docs - docs
- .vuepress - .vuepress
- config.ts - ++ config.ts
- page1.md - -- page1.md
- README.md - README.md
- theme A **theme** directory - theme A **theme** directory
- client - client
@ -55,8 +58,8 @@ The following syntax can be used to customize the appearance of the file tree:
- docs - docs
- .vuepress - .vuepress
- config.ts - ++ config.ts
- page1.md - -- page1.md
- README.md - README.md
- theme A **theme** directory - theme A **theme** directory
- client - client
@ -73,7 +76,7 @@ The following syntax can be used to customize the appearance of the file tree:
- .gitignore - .gitignore
- README.md - README.md
- … - …
::: :::
## Using Simple Icons ## Using Simple Icons
@ -100,11 +103,11 @@ The following syntax can be used to customize the appearance of the file tree:
- page1.md - page1.md
- README.md - README.md
- package.json - package.json
::: :::
## Configuration ## Configuration
You can configure the default icon type for the file tree in the `plugins.mdPower.fileTree` option: You can configure the default icon type for file trees in the `markdown.fileTree` option:
::: code-tabs ::: code-tabs
@tab .vuepress/config.ts @tab .vuepress/config.ts
@ -123,9 +126,14 @@ export default defineUserConfig({
::: :::
::: tip Worried that colored icons will affect the build package size? ::: tip Concerned about colored icons affecting build bundle size?
You don't need to worry. The colored icons for the file tree are also parsed from `iconify`. We recommend that you install the `@iconify/json` project locally. You don't need to worry. The colored icons for the file tree are also resolved from `iconify`.
The theme will automatically parse the icon data from `@iconify/json` into local icon resources. Even if you use them multiple times on different pages, including the navbar, sidebar, icon components, etc., the same icon will only exist as one resource! We recommend installing the `@iconify/json` package locally in your project.
The theme will automatically parse the icon data from `@iconify/json` into local icon resources.
Even if you use the same icon multiple times across different pages
(including navigation bars, sidebars, icon components, etc.), only one copy of the resource will exist for each icon!
Each colored icon is approximately between `1kb ~ 2kb` in size. Even if your file tree uses 100+ different icons, the impact on the final build package size will not be significant. Each colored icon is approximately `1kb ~ 2kb` in size.
Even if your file tree extensively uses 100+ different icons, the impact on the final build bundle size
will not be significant.
::: :::

View File

@ -0,0 +1,130 @@
---
title: Flex Container
icon: material-symbols-light:flex-no-wrap
createTime: 2025/10/08 17:56:39
permalink: /en/guide/markdown/flex/
badge: New
---
## Overview
The Flex container `::: flex` provides a convenient way to apply Flexbox layout to block-level content in markdown.
## Usage
```md
::: flex [center|between|around] [start|center|end] [gap="20"] [column]
<!-- Block content 1 -->
<!-- Block content 2 -->
:::
```
## Attributes
- On the main axis, use `center` / `between` / `around` to specify alignment;
- On the cross axis, use `start` / `center` / `end` to specify alignment;
- Use `gap="20"` to specify spacing;
- Use `column` to set the main axis direction to vertical.
## Examples
### Centered Table Alignment
**Input:**
```md
::: flex center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::
```
**Output:**
::: flex center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::
### Two Tables with Left-Right Alignment
**Input:**
```md
::: flex between center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::
```
**Output:**
::: flex between center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::
### Evenly Spaced Tables
**Input:**
```md
::: flex around center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::
```
**Output:**
::: flex around center
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| 1 | 2 | 3 |
| 4 | 5 | 6 |
:::

Some files were not shown because too many files have changed in this diff Show More