',
+ })
+}
diff --git a/plugins/plugin-md-power/src/node/enhance/docsTitle.ts b/plugins/plugin-md-power/src/node/enhance/docsTitle.ts
new file mode 100644
index 00000000..47a77d11
--- /dev/null
+++ b/plugins/plugin-md-power/src/node/enhance/docsTitle.ts
@@ -0,0 +1,45 @@
+import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
+
+const REG_HEADING = /^#\s*?([^#\s].*)?\n/
+
+/**
+ * 适配 主题的 文档页面标题,将 markdown 中的 h1 标题提取到 frontmatter 中,并将其删除,
+ * 以避免重复显示标题。
+ */
+export function docsTitlePlugin(md: Markdown): void {
+ const render = md.render
+ md.render = (source, env: MarkdownEnv) => {
+ if (!env.filePathRelative)
+ return render(source, env)
+
+ let { matter, content } = parseSource(source.trim())
+ let title = ''
+ content = content.trim().replace(REG_HEADING, (_, match) => {
+ title = match.trim()
+ return ''
+ })
+ source = `${matter}\n${content}`
+ const result = render(source, env)
+ if (title) {
+ env.frontmatter ??= {}
+ env.frontmatter.title ??= title
+ }
+ return result
+ }
+}
+
+function parseSource(source: string) {
+ const char = '---'
+
+ if (!source.startsWith(char)) {
+ return { matter: '', content: source }
+ }
+ else {
+ const end = source.indexOf(`\n${char}`)
+ const len = char.length + 1
+ return {
+ matter: source.slice(0, end + len),
+ content: source.slice(end + len),
+ }
+ }
+}
diff --git a/plugins/plugin-md-power/src/node/plugin.ts b/plugins/plugin-md-power/src/node/plugin.ts
index dcf805a9..f7b16333 100644
--- a/plugins/plugin-md-power/src/node/plugin.ts
+++ b/plugins/plugin-md-power/src/node/plugin.ts
@@ -3,6 +3,7 @@ import type { MarkdownPowerPluginOptions } from '../shared/index.js'
import { addViteOptimizeDepsInclude } from '@vuepress/helper'
import { containerPlugin } from './container/index.js'
import { embedSyntaxPlugin } from './embed/index.js'
+import { docsTitlePlugin } from './enhance/docsTitle.js'
import { imageSizePlugin } from './enhance/imageSize.js'
import { inlineSyntaxPlugin } from './inline/index.js'
import { prepareConfigFile } from './prepareConfigFile.js'
@@ -21,11 +22,16 @@ export function markdownPowerPlugin(
extendsBundlerOptions(bundlerOptions, app) {
if (options.repl) {
- addViteOptimizeDepsInclude(bundlerOptions, app, ['shiki/core', 'shiki/wasm', 'shiki/engine/oniguruma'])
+ addViteOptimizeDepsInclude(
+ bundlerOptions,
+ app,
+ ['shiki/core', 'shiki/wasm', 'shiki/engine/oniguruma'],
+ )
}
},
extendsMarkdown: async (md, app) => {
+ docsTitlePlugin(md)
embedSyntaxPlugin(md, options)
inlineSyntaxPlugin(md, options)
diff --git a/plugins/plugin-md-power/src/node/prepareConfigFile.ts b/plugins/plugin-md-power/src/node/prepareConfigFile.ts
index 817eb353..72f2da5e 100644
--- a/plugins/plugin-md-power/src/node/prepareConfigFile.ts
+++ b/plugins/plugin-md-power/src/node/prepareConfigFile.ts
@@ -71,6 +71,8 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
import { defineClientConfig } from 'vuepress/client'
${Array.from(imports.values()).join('\n')}
+import '${CLIENT_FOLDER}styles/index.css'
+
export default defineClientConfig({
enhance({ router, app }) {
${Array.from(enhances.values())
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f37b1a07..f385c597 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -361,9 +361,6 @@ importers:
'@vuepress/plugin-git':
specifier: 2.0.0-rc.56
version: 2.0.0-rc.56(vuepress@2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)))
- '@vuepress/plugin-markdown-container':
- specifier: 2.0.0-rc.54
- version: 2.0.0-rc.54(vuepress@2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)))
'@vuepress/plugin-markdown-hint':
specifier: 2.0.0-rc.56
version: 2.0.0-rc.56(markdown-it@14.1.0)(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3))(vuepress@2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)))
@@ -2166,11 +2163,6 @@ packages:
peerDependencies:
vuepress: 2.0.0-rc.18
- '@vuepress/plugin-markdown-container@2.0.0-rc.54':
- resolution: {integrity: sha512-00TzBHfBDd6nbZlmVRgWdmLb1MFcCg22FcD39n6gwgcFym9C/oZMDcdPnxsRT+sKPGqtxrlJYxKOXdp3Z8OJCA==}
- peerDependencies:
- vuepress: 2.0.0-rc.18
-
'@vuepress/plugin-markdown-hint@2.0.0-rc.56':
resolution: {integrity: sha512-qVOlqBIMjySormRde0uo/rILIC8BP59GIz+lRk8XpO5G92ejmJlRck27Pjrzm5NngR+pOonWfZ7yjGtT35U6nA==}
peerDependencies:
@@ -8192,12 +8184,6 @@ snapshots:
execa: 9.4.1
vuepress: 2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3))
- '@vuepress/plugin-markdown-container@2.0.0-rc.54(vuepress@2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)))':
- dependencies:
- '@types/markdown-it': 14.1.2
- markdown-it-container: 4.0.0
- vuepress: 2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3))
-
'@vuepress/plugin-markdown-hint@2.0.0-rc.56(markdown-it@14.1.0)(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3))(vuepress@2.0.0-rc.18(@vuepress/bundler-vite@2.0.0-rc.18(@types/node@20.12.10)(jiti@1.21.6)(sass-embedded@1.80.3)(sass@1.80.3)(typescript@5.6.3)(yaml@2.5.1))(typescript@5.6.3)(vue@3.5.12(typescript@5.6.3)))':
dependencies:
'@mdit/plugin-alert': 0.13.1(markdown-it@14.1.0)
diff --git a/theme/package.json b/theme/package.json
index 50a5ffc0..d9725cfd 100644
--- a/theme/package.json
+++ b/theme/package.json
@@ -103,7 +103,6 @@
"@vuepress/plugin-comment": "2.0.0-rc.56",
"@vuepress/plugin-docsearch": "2.0.0-rc.56",
"@vuepress/plugin-git": "2.0.0-rc.56",
- "@vuepress/plugin-markdown-container": "2.0.0-rc.54",
"@vuepress/plugin-markdown-hint": "2.0.0-rc.56",
"@vuepress/plugin-markdown-image": "2.0.0-rc.56",
"@vuepress/plugin-markdown-math": "2.0.0-rc.56",
diff --git a/theme/src/client/styles/index.css b/theme/src/client/styles/index.css
index 101c356f..d23cfc3a 100644
--- a/theme/src/client/styles/index.css
+++ b/theme/src/client/styles/index.css
@@ -8,7 +8,6 @@
@import url("./utils.css");
@import url("./content.css");
@import url("./code.css");
-@import url("./custom-block.css");
@import url("./hint-container.css");
@import url("./twoslash.css");
@import url("./md-enhance.css");
diff --git a/theme/src/node/plugins/containerPlugins.ts b/theme/src/node/plugins/containerPlugins.ts
deleted file mode 100644
index 5abf961e..00000000
--- a/theme/src/node/plugins/containerPlugins.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import type { Plugin } from 'vuepress/core'
-import { markdownContainerPlugin as containerPlugin } from '@vuepress/plugin-markdown-container'
-
-export const customContainerPlugins: Plugin[] = [
- /**
- * :::demo-wrapper img no-padding title="xxx" height="100px"
- * :::
- */
- containerPlugin({
- type: 'demo-wrapper',
- before(info) {
- const title = resolveAttr(info, 'title')
- const wrapperClasses: string[] = ['demo-wrapper']
- let containerStyle = ''
- if (title)
- wrapperClasses.push('has-title')
-
- if (info.includes('img'))
- wrapperClasses.push('only-img')
-
- if (info.includes('no-padding'))
- wrapperClasses.push('no-padding')
-
- const height = resolveAttr(info, 'height')
- if (height) {
- const h = Number.parseFloat(height) === Number(height) ? `${height}px` : height
- containerStyle += `--demo-container-height: ${h};`
- wrapperClasses.push('has-height')
- }
-
- return `
-
-
- ${title ? `
${title}
` : ''}
-
-
\n`
- },
- after() {
- return '
'
- },
- }),
- /**
- * :::steps
- * 1. 步骤 1
- * xxx
- * 2. 步骤 2
- * xxx
- * 3. ...
- * :::
- */
- containerPlugin({
- type: 'steps',
- before() {
- return '
'
- },
- after() {
- return '
'
- },
- }),
- /**
- * ::: card title="xxx" icon="xxx"
- * xxx
- * :::
- */
- containerPlugin({
- type: 'card',
- before(info) {
- const title = resolveAttr(info, 'title')
- const icon = resolveAttr(info, 'icon')
- return `
`
- },
- after() {
- return ''
- },
- }),
-
- /**
- * :::: card-grid
- * ::: card
- * xxx
- * :::
- * ::: card
- * xxx
- * :::
- * ::::
- */
- containerPlugin({
- type: 'card-grid',
- before() {
- return '
'
- },
- after() {
- return ''
- },
- }),
-]
-
-/**
- * Resolve the specified attribute from token info
- */
-function resolveAttr(info: string, attr: string): string | null {
- // try to match specified attr mark
- const pattern = `\\b${attr}\\s*=\\s*(?
['"])(?.+?)\\k(\\s|$)`
- const regex = new RegExp(pattern, 'i')
- const match = info.match(regex)
-
- // return content if matched, null if not specified
- return match?.groups?.content ?? null
-}
diff --git a/theme/src/node/plugins/getPlugins.ts b/theme/src/node/plugins/getPlugins.ts
index 1b8d48e2..13c1f4fe 100644
--- a/theme/src/node/plugins/getPlugins.ts
+++ b/theme/src/node/plugins/getPlugins.ts
@@ -1,5 +1,6 @@
import type { App, PluginConfig } from 'vuepress/core'
import type { PlumeThemePluginOptions } from '../../shared/index.js'
+import { isPlainObject } from '@vuepress/helper'
import { cachePlugin } from '@vuepress/plugin-cache'
import { commentPlugin } from '@vuepress/plugin-comment'
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
@@ -21,8 +22,6 @@ import { type MarkdownEnhancePluginOptions, mdEnhancePlugin } from 'vuepress-plu
import { markdownPowerPlugin } from 'vuepress-plugin-md-power'
import { resolveDocsearchOptions, resolveSearchOptions } from '../config/index.js'
import { deleteAttrs } from '../utils/index.js'
-import { customContainerPlugins } from './containerPlugins.js'
-import { markdownTitlePlugin } from './markdown-title.js'
export interface SetupPluginOptions {
app: App
@@ -40,12 +39,9 @@ export function getPlugins({
const isProd = app.env.isBuild
const plugins: PluginConfig = [
- markdownTitlePlugin(),
fontsPlugin(),
contentUpdatePlugin(),
markdownHintPlugin({ hint: true, alert: true, injectStyles: false }),
-
- ...customContainerPlugins,
]
if (pluginOptions.readingTime !== false) {
@@ -132,7 +128,7 @@ export function getPlugins({
plugins.push(watermarkPlugin({
delay: 300,
enabled: true,
- ...typeof pluginOptions.watermark === 'object' ? pluginOptions.watermark : {},
+ ...isPlainObject(pluginOptions.watermark) ? pluginOptions.watermark : {},
}))
}
diff --git a/theme/src/node/plugins/index.ts b/theme/src/node/plugins/index.ts
index 9c8f78a2..62d0b452 100644
--- a/theme/src/node/plugins/index.ts
+++ b/theme/src/node/plugins/index.ts
@@ -1,2 +1 @@
-export * from './containerPlugins.js'
export * from './getPlugins.js'
diff --git a/theme/src/node/plugins/markdown-title.ts b/theme/src/node/plugins/markdown-title.ts
deleted file mode 100644
index af20b620..00000000
--- a/theme/src/node/plugins/markdown-title.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import type { Plugin } from 'vuepress/core'
-import type { MarkdownEnv } from 'vuepress/markdown'
-
-const REG_HEADING = /^#\s*?([^#\s].*)?\n/
-
-export function markdownTitlePlugin(): Plugin {
- return {
- name: '@vuepress-plume/plugin-markdown-title',
-
- extendsMarkdown(md) {
- const render = md.render
- md.render = (source, env: MarkdownEnv) => {
- if (!env.filePathRelative)
- return render(source, env)
-
- let { matter, content } = parseSource(source.trim())
- let title = ''
- content = content.trim().replace(REG_HEADING, (_, match) => {
- title = match.trim()
- return ''
- })
- source = `${matter}\n${content}`
- const result = render(source, env)
- if (title) {
- env.frontmatter ??= {}
- env.frontmatter.title ??= title
- }
- return result
- }
- },
- }
-}
-
-function parseSource(source: string) {
- const char = '---'
-
- if (!source.startsWith(char)) {
- return { matter: '', content: source }
- }
- else {
- const end = source.indexOf(`\n${char}`)
- const len = char.length + 1
- return {
- matter: source.slice(0, end + len),
- content: source.slice(end + len),
- }
- }
-}