diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index ec158726..631ad842 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -14,7 +14,6 @@ export const theme: Theme = plumeTheme({ shiki: { twoslash: true, lineNumbers: 10, - languages: ['sh', 'ts', 'md', 'html', 'js', 'go', 'kotlin', 'rust', 'vue', 'css', 'json', 'scss', 'yaml', 'bash', 'c++', 'java', 'py', 'ruby', 'make', 'objc', 'swift', 'php', 'rs', 'sql', 'xml', 'zig', 'pug', 'http', 'less', 'styl', 'jsx', 'tsx', 'astro', 'svelte', 'wasm', 'vb', 'bat', 'cs', 'cpp'], }, markdownEnhance: { diff --git a/docs/notes/theme/config/plugins/代码高亮.md b/docs/notes/theme/config/plugins/代码高亮.md index 02c90203..5629dfed 100644 --- a/docs/notes/theme/config/plugins/代码高亮.md +++ b/docs/notes/theme/config/plugins/代码高亮.md @@ -9,22 +9,13 @@ permalink: /config/plugins/code-highlight/ 主题内置的代码高亮插件, 对代码块进行代码高亮。 +关联插件:[@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/markdown/shiki.html) + 主题 使用 [Shiki](https://github.com/shikijs/shiki) 在 Markdown 代码块中使用彩色文本实现语法高亮。 Shiki 支持多种编程语言。 在 Shiki 的代码仓库中,可以找到 [合法的编程语言列表](https://shiki.style/languages) 。 -关联插件: [@vuepress-plume/plugin-shikiji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-shikiji) - -::: details 为什么不用 官方的 @vuepress/plugin-shiki ? - -你可以认为本插件是 官方 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html) 的 -一个分支版本,但本插件更为激进,支持更多新的特性。 - -同时,我也是 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html) 的主要维护者之一 -,在 `@vuepress-plume/plugin-shikiji` 插件中新增的试验性的新特性,会在未来合适的时候合并到 官方插件中。 -::: - ## 特性 - [代码行高亮](../../guide/代码/特性支持.md#在代码块中实现行高亮) @@ -50,7 +41,7 @@ export default defineUserConfig({ theme: plumeTheme({ plugins: { shiki: { - theme: { light: 'vitesse-light', dark: 'vitesse-dark' }, + themes: { light: 'vitesse-light', dark: 'vitesse-dark' }, }, } }), @@ -59,16 +50,16 @@ export default defineUserConfig({ ::: -### theme +### themes -- 类型: `string | { light: string, dark: string }` +- 类型: `{ light: string, dark: string }` - 默认值: `{ light: 'vitesse-light', dark: 'vitesse-dark' }` 代码高亮主题,支持 浅色/暗色 双主题。 可在 支持的 [主题列表](https://shiki.style/themes) 中选择你喜欢的主题。 -### languages +### langs - 类型: `string[]` - 默认值: `[]` @@ -78,12 +69,12 @@ export default defineUserConfig({ 在 Shiki 的代码仓库中,可以找到 [合法的编程语言列表](https://shiki.style/languages) 。 -### defaultHighlightLang +### defaultLang - 类型: `string` - 默认值: `text` -默认高亮的编程语言。当代码块未指定语言时使用。 +指定的语言不可用时所使用的备选语言。 ### lineNumbers @@ -96,51 +87,12 @@ export default defineUserConfig({ `false`: 不显示行号\ `number`: 指定需要显式代码行号的最小行数。 -### copyCode - -- 类型: `boolean | CopyCodeOptions` -- 默认值: `true` - -是否允许复制代码。启用时,会在代码块右侧显示复制按钮。 - -```ts -interface CopyCodeOptions { - /** - * 复制成功后提示文本持续时间 - * - * @default 2000 - */ - duration?: number - - /** - * 多语言配置 - */ - locales?: { - [localePath: string]: { - /** - * 复制按钮标题 - * - * @default 'Copy code' - */ - title?: string - - /** - * 复制成功提示 - * - * @default 'Copied' - */ - copied?: string - } - } -} -``` - ### twoslash -- 类型: `boolean` +- 类型: `boolean | ShikiTwoslashOptions` - 默认值: `false` -实验性功能,是否启用 对 `typescript` 和 `vue` 语言的 类型提示 支持。 +是否启用 对 `typescript` 和 `vue` 语言的 类型提示 支持。 ### whitespace @@ -151,7 +103,7 @@ interface CopyCodeOptions { 效果: - + ### collapseLines @@ -160,7 +112,7 @@ interface CopyCodeOptions { 将代码块折叠到指定行数。 -### codeTransformers +### transformers - 类型: `ShikiTransformer[]` - 默认值: `[]` diff --git a/docs/notes/theme/guide/代码/介绍.md b/docs/notes/theme/guide/代码/介绍.md index cc6c8555..d9d0a131 100644 --- a/docs/notes/theme/guide/代码/介绍.md +++ b/docs/notes/theme/guide/代码/介绍.md @@ -1,6 +1,5 @@ --- title: 介绍 -author: pengzhanbo icon: ic:outline-code createTime: 2024/04/04 10:35:45 permalink: /guide/code/intro/ @@ -10,41 +9,22 @@ permalink: /guide/code/intro/ 主题 使用 [Shiki](https://shiki.style/) 在 Markdown 代码块实现语法高亮。 -:::info -主题默认 加载了 [Shiki](https://shiki.style/) 支持的超过 190+ 的 语言,这可能导致 在启用 vuepress 服务时, -需要多等待 **300ms ~ 600ms** 左右的时间来加载所有的 语言。 +::: important 重要变更 -因此,如果比较在意 vuepress 启动时间,建议修改配置为仅加载 您所需要的 语言。 +从 ==1.0.0-rc.136== 版本开始,主题已将代码高亮插件从主题内部实现的 `@vuepress-plume/plugin-shikiji` 迁移 +到了 [vuepress ecosystem](https://github.com/vuepress/ecosystem) 提供的 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/markdown/shiki.html)。 -示例: +_(无需担心变化太大,我也是 `@vuepress/plugin-shiki` 的主要开发者之一,它实现了与主题原插件一致的功能)_ -::: code-tabs -@tab .vuepress/config.ts +其中涉及到部分配置项需要调整变更: -```ts -export default defineUserConfig({ - theme: plumeTheme({ - plugins: { - shiki: { - languages: ['javascript', 'typescript', 'vue', 'bash', 'sh'], // [!code highlight] - } - } - }) -}) -``` +- `languages` 配置变更为 `langs` 选项。无需再手动添加你所使用的语言,插件将会自动识别并按需加载语言包。 +- `themes` 配置变更为: + - 当使用单主题配置时,使用 `theme` 配置代码块主题 + - 当使用双主题配置时,使用 `themes` 配置代码块主题。 ::: ---- - - -随着 `shiki` 支持的语言越来越丰富,默认加载全部的语言所花费的时间越来越多了, -因此 **强烈建议您 手动配置 `languages`** ,仅加载 您需要的 语言。 - -同时,在您未配置 `languages` 时,主题会在启动时尝试分析 `markdown` 文件中的代码块所使用的语言, -并将它们作为 `languages` 传入给 shiki,这也极大的减少了加载时间。然而随着 项目 `markdown` 文件数量和内容的增长, -这种方式带来额外的 i/o 开销和解析开销也会越来越长,因此 **强烈建议您手动配置 `languages`** 。 - ## 语言 [Shiki](https://shiki.style/) 支持 超过 190+ 种语言, @@ -53,7 +33,8 @@ export default defineUserConfig({ 你可以通过以下语法为你使用的 语言所编写的代码 实现高亮效果: ````md -```[lang] +``` [lang] + ``` ```` @@ -62,7 +43,8 @@ export default defineUserConfig({ 示例: ````md -```js +// [!code word:js] +``` js const a = 1 console.log(a) ``` @@ -87,7 +69,7 @@ export default defineUserConfig({ theme: plumeTheme({ plugins: { shiki: { - theme: { light: 'vitesse-light', dark: 'vitesse-dark' }, // [!code highlight] + themes: { light: 'vitesse-light', dark: 'vitesse-dark' }, // [!code highlight] } } }) diff --git a/docs/notes/theme/snippet/whitespace.snippet.md b/docs/notes/theme/snippet/whitespace.snippet.md index c7cdbcb2..54e1b3a8 100644 --- a/docs/notes/theme/snippet/whitespace.snippet.md +++ b/docs/notes/theme/snippet/whitespace.snippet.md @@ -10,7 +10,6 @@ ``` -``` ```` **输出:** @@ -57,7 +56,6 @@ function foo( ) { return 'Hello World' } ``` -``` ```` **输出:** diff --git a/plugins/plugin-shikiji/LICENSE b/plugins/plugin-shikiji/LICENSE deleted file mode 100644 index 9f677c90..00000000 --- a/plugins/plugin-shikiji/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (C) 2021 - PRESENT by pengzhanbo - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/plugins/plugin-shikiji/README.md b/plugins/plugin-shikiji/README.md deleted file mode 100644 index f4c751d5..00000000 --- a/plugins/plugin-shikiji/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# `@vuepress-plume/plugin-shikiji` - -使用 [`shiki`](https://shiki.style/) 为 Markdown 代码块启用代码高亮。 - -> [!WARNING] -> 相比于 官方的 [@vuepress/plugin-shiki](https://ecosystem.vuejs.press/zh/plugins/shiki.html), -> 本插件做了很多各种各样的调整,你可以认为这是试验性的。 - -## Install - -```sh -npm install @vuepress-plume/plugin-shikiji -# or -pnpm add @vuepress-plume/plugin-shikiji -# or -yarn add @vuepress-plume/plugin-shikiji -``` - -## Usage - -``` js -// .vuepress/config.[jt]s -import { shikiPlugin } from '@vuepress-plume/plugin-shikiji' - -export default { - // ... - plugins: [ - shikiPlugin() - ] - // ... -} -``` - -## Options - -```ts -interface ShikijiOptions { - /** - * Custom theme for syntax highlighting. - * - * You can also pass an object with `light` and `dark` themes to support dual themes. - * - * @example { theme: 'github-dark' } - * @example { theme: { light: 'github-light', dark: 'github-dark' } } - * - * You can use an existing theme. - * @see https://shiki.style/themes - * Or add your own theme. - * @see https://shiki.style/guide/load-theme - */ - theme?: ThemeOptions - /** - * Languages for syntax highlighting. - * @see https://shiki.style/languages - */ - languages?: LanguageInput[] - /** - * Custom language aliases. - * - * @example { 'my-lang': 'js' } - * @see https://shiki.style/guide/load-lang#custom-language-aliases - */ - languageAlias?: Record - /** - * Setup Shikiji instance - */ - shikiSetup?: (shikiji: Highlighter) => void | Promise - /** - * Fallback language when the specified language is not available. - */ - defaultHighlightLang?: string - /** - * Transformers applied to code blocks - * @see https://shiki.style/guide/transformers - */ - codeTransformers?: ShikiTransformer[] - /** - * Enable transformerRenderWhitespace - * @default false - */ - whitespace?: boolean -} -``` diff --git a/plugins/plugin-shikiji/__test__/__snapshots__/collapsedLinesPlugin.spec.ts.snap b/plugins/plugin-shikiji/__test__/__snapshots__/collapsedLinesPlugin.spec.ts.snap deleted file mode 100644 index cc26df76..00000000 --- a/plugins/plugin-shikiji/__test__/__snapshots__/collapsedLinesPlugin.spec.ts.snap +++ /dev/null @@ -1,456 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`collapsedLinesPlugin > should not work with false 1`] = ` -"
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
" -`; - -exports[`collapsedLinesPlugin > should not work with includes styles 1`] = ` -"
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
" -`; - -exports[`collapsedLinesPlugin > should work with default 1`] = ` -"
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
" -`; - -exports[`collapsedLinesPlugin > should work with number 1`] = ` -"
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
" -`; - -exports[`collapsedLinesPlugin > should work with true 1`] = ` -"
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-
-
const line1 = 1
-const line2 = 2
-const line3 = 3
-const line4 = 4
-const line5 = 5
-const line6 = 6
-const line7 = 7
-const line8 = 8
-const line9 = 9
-const line10 = 10
-const line11 = 11
-const line12 = 12
-const line13 = 13
-const line14 = 14
-const line15 = 15
-const line16 = 16
-const line17 = 17
-const line18 = 18
-const line19 = 19
-const line20 = 20
-
-
" -`; diff --git a/plugins/plugin-shikiji/__test__/collapsedLinesPlugin.spec.ts b/plugins/plugin-shikiji/__test__/collapsedLinesPlugin.spec.ts deleted file mode 100644 index f66a56bd..00000000 --- a/plugins/plugin-shikiji/__test__/collapsedLinesPlugin.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import MarkdownIt from 'markdown-it' -import { describe, expect, it } from 'vitest' -import { collapsedLinesPlugin } from '../src/node/markdown/collapsedLinesPlugin.js' - -const FENCE = '```' - -function createMarkdown(collapsedLines?: boolean | number, hasStyles = false) { - const md = new MarkdownIt() - - md.options.highlight = str => `
${str}
` - const fence = md.renderer.rules.fence! - md.renderer.rules.fence = (...args) => - `
${fence(...args)}
` - - md.use(collapsedLinesPlugin, collapsedLines ? { collapsedLines } : undefined) - - return md -} - -describe('collapsedLinesPlugin', () => { - const genLines = (n: number) => - Array.from({ length: n }).map((_, i) => `const line${i + 1} = ${i + 1}`).join('\n') - const code = `\ -${FENCE} -${genLines(10)} -${FENCE} - -${FENCE}js -${genLines(20)} -${FENCE} - -${FENCE}js :collapsed-lines -${genLines(20)} -${FENCE} - -${FENCE}js :collapsed-lines=10 -${genLines(12)} -${FENCE} - -${FENCE}js :no-collapsed-lines -${genLines(20)} -${FENCE} -` - it('should work with default', () => { - const md = createMarkdown() - expect(md.render(code)).toMatchSnapshot() - }) - - it('should work with true', () => { - const md = createMarkdown(true) - expect(md.render(code)).toMatchSnapshot() - }) - - it('should work with number', () => { - const md = createMarkdown(10) - expect(md.render(code)).toMatchSnapshot() - }) - - it('should not work with includes styles', () => { - const md = createMarkdown(true, true) - expect(md.render(code)).toMatchSnapshot() - }) - - it('should not work with false', () => { - const md = createMarkdown(false) - expect(md.render(code)).toMatchSnapshot() - }) -}) diff --git a/plugins/plugin-shikiji/__test__/copyCodeButton.spec.ts b/plugins/plugin-shikiji/__test__/copyCodeButton.spec.ts deleted file mode 100644 index 88003d3b..00000000 --- a/plugins/plugin-shikiji/__test__/copyCodeButton.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { App } from 'vuepress/core' -import type { CopyCodeOptions } from '../src/node/types.js' -import MarkdownIt from 'markdown-it' -import { describe, expect, it } from 'vitest' -import { copyCodeButtonPlugin } from '../src/node/copy-code-button/index.js' - -function createMarkdown(options: boolean | CopyCodeOptions = {}, lang = 'en-US') { - const md = new MarkdownIt() - md.options.highlight = str => `
${str}
` - const fence = md.renderer.rules.fence! - md.renderer.rules.fence = (...args) => `
${fence(...args)}
` - const app = { - env: { isDebug: false }, - siteData: { - lang, - locales: { '/': { lang }, '/zh/': { lang: 'zh-CN' }, '/en/': { lang: 'en-US' }, '/xxx/': { lang: 'unknown' } }, - }, - } as unknown as App - copyCodeButtonPlugin(md, app, options) - return md -} - -describe('copy code button plugin', () => { - it('should work with default', () => { - const md = createMarkdown() - - expect(md.render('```js\nconst a = 1\n```', { filePathRelative: '/test.md' })).toContain('` - } -} diff --git a/plugins/plugin-shikiji/src/node/copy-code-button/index.ts b/plugins/plugin-shikiji/src/node/copy-code-button/index.ts deleted file mode 100644 index 996bef2c..00000000 --- a/plugins/plugin-shikiji/src/node/copy-code-button/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './copyCodeButtonLocales.js' -export * from './copyCodeButtonPlugin.js' -export * from './createCopyCodeButtonRender.js' diff --git a/plugins/plugin-shikiji/src/node/highlight/getLanguage.ts b/plugins/plugin-shikiji/src/node/highlight/getLanguage.ts deleted file mode 100644 index ce19d0cf..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/getLanguage.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { isPlainLang, isSpecialLang } from 'shiki' -import { colors as c, logger } from 'vuepress/utils' -import { resolveLanguage } from '../utils/index.js' - -export function getLanguage( - loadedLanguages: string[], - language: string, - defaultLang: string, -): string { - let lang = resolveLanguage(language) || defaultLang - - if (lang) { - const langLoaded = loadedLanguages.includes(lang as any) - if (!langLoaded && !isPlainLang(lang) && !isSpecialLang(lang)) { - logger.warn( - c.yellow( - `\nThe language '${lang}' is not loaded, falling back to '${defaultLang || 'txt' - }' for syntax highlighting.`, - ), - ) - lang = defaultLang - } - } - return lang -} diff --git a/plugins/plugin-shikiji/src/node/highlight/highlight.ts b/plugins/plugin-shikiji/src/node/highlight/highlight.ts deleted file mode 100644 index 667da5fe..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/highlight.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { HighlighterOptions, ThemeOptions } from '../types.js' -import { customAlphabet } from 'nanoid' -import { bundledLanguages, createHighlighter } from 'shiki' -import { colors, logger } from 'vuepress/utils' -import { getLanguage } from './getLanguage.js' -import { baseTransformers, getInlineTransformers } from './transformers.js' - -const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) -const mustacheRE = /\{\{.*?\}\}/g - -export async function highlight( - theme: ThemeOptions, - options: HighlighterOptions, -): Promise<(str: string, lang: string, attrs: string) => string> { - const { - defaultHighlightLang: defaultLang = '', - codeTransformers: userTransformers = [], - whitespace = false, - languages = Object.keys(bundledLanguages), - } = options - - const highlighter = await createHighlighter({ - themes: - typeof theme === 'object' && 'light' in theme && 'dark' in theme - ? [theme.light, theme.dark] - : [theme], - langs: languages, - langAlias: options.languageAlias, - }) - - await options.shikiSetup?.(highlighter) - - const loadedLanguages = highlighter.getLoadedLanguages() - - return (str: string, language: string, attrs: string = '') => { - const lang = getLanguage(loadedLanguages, language, defaultLang) - - const enabledTwoslash = attrs.includes('twoslash') && !!options.twoslash - - const mustaches = new Map() - str = removeMustache(str, mustaches).trimEnd() - - try { - const highlighted = highlighter.codeToHtml(str, { - lang, - transformers: [ - ...baseTransformers, - ...getInlineTransformers({ - attrs, - lang, - enabledTwoslash, - whitespace, - twoslash: options.twoslash, - }), - ...userTransformers, - ], - meta: { __raw: attrs }, - ...(typeof theme === 'object' && 'light' in theme && 'dark' in theme - ? { themes: theme, defaultColor: false } - : { theme }), - }) - - const rendered = restoreMustache(highlighted, mustaches, enabledTwoslash) - - return rendered - } - catch (e) { - logger.error( - (e as Error)?.message, - '\n', - (e as Error)?.stack ? colors.gray(String((e as Error)?.stack)) : '', - ) - return str - } - } -} - -function removeMustache(s: string, mustaches: Map) { - return s.replace(mustacheRE, (match) => { - let marker = mustaches.get(match) - if (!marker) { - marker = nanoid() - mustaches.set(match, marker) - } - return marker - }) -} - -function restoreMustache(s: string, mustaches: Map, twoslash: boolean) { - mustaches.forEach((marker, match) => { - s = s.replaceAll(marker, match) - }) - - if (twoslash) - s = s.replace(/\{/g, '{') - - return `${s}\n` -} diff --git a/plugins/plugin-shikiji/src/node/highlight/index.ts b/plugins/plugin-shikiji/src/node/highlight/index.ts deleted file mode 100644 index 6e06de95..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './highlight.js' -export * from './resolveTsPaths.js' -export * from './scanLanguages.js' diff --git a/plugins/plugin-shikiji/src/node/highlight/resolveTsPaths.ts b/plugins/plugin-shikiji/src/node/highlight/resolveTsPaths.ts deleted file mode 100644 index 0988456e..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/resolveTsPaths.ts +++ /dev/null @@ -1,28 +0,0 @@ -import fs from 'node:fs/promises' -import path from 'node:path' -import process from 'node:process' - -export async function resolveTsPaths(): Promise | undefined> { - const tsconfigPath = path.join(process.cwd(), 'tsconfig.json') - try { - const tsconfig = JSON.parse(await fs.readFile(tsconfigPath, 'utf-8')) - const paths = tsconfig.compilerOptions?.paths ?? undefined - const baseUrl = tsconfig.compilerOptions?.baseUrl - - if (baseUrl && paths) { - const dirname = path.join(process.cwd(), baseUrl) - for (const key in paths) { - const value = paths[key] - if (Array.isArray(value)) - paths[key] = value.map(v => path.resolve(dirname, v)) - else if (value) - paths[key] = path.resolve(dirname, value) - } - } - - return paths - } - catch { - return undefined - } -} diff --git a/plugins/plugin-shikiji/src/node/highlight/scanLanguages.ts b/plugins/plugin-shikiji/src/node/highlight/scanLanguages.ts deleted file mode 100644 index 219cc0a9..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/scanLanguages.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { App } from 'vuepress' -import fs from 'node:fs/promises' -import path from 'node:path' -import fg from 'fast-glob' -import { bundledLanguages } from 'shiki' - -const languages = Object.keys(bundledLanguages) -const RE_FENCE = /`{3,}[^`]*?(\s|$)/g - -export async function scanLanguages(app: App): Promise { - const source = app.dir.source() - const pattern = ['**/*.md', '!.vuepress', '!node_modules'] - const files = await fg(pattern, { - cwd: source, - }) - const langs = new Set() - - for (const file of files) { - const filepath = path.join(source, file) - const content = await fs.readFile(filepath, 'utf-8') - const matched = content.match(RE_FENCE) - if (matched) { - for (const match of matched) { - let lang = match.replace(/`{3,}/, '').trim() - if (lang.includes(':')) - lang = lang.split(':')[0] - if (lang) - langs.add(lang) - } - } - } - - return Array.from(langs).filter(lang => languages.includes(lang)) -} diff --git a/plugins/plugin-shikiji/src/node/highlight/transformers.ts b/plugins/plugin-shikiji/src/node/highlight/transformers.ts deleted file mode 100644 index 0a071db9..00000000 --- a/plugins/plugin-shikiji/src/node/highlight/transformers.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { TransformerTwoslashOptions } from '@shikijs/twoslash/core' -import type { ShikiTransformer } from 'shiki' -import type { VueSpecificOptions } from 'twoslash-vue' -import type { WhitespacePosition } from '../utils/index.js' -import process from 'node:process' -import { - transformerCompactLineOptions, - transformerNotationDiff, - transformerNotationErrorLevel, - transformerNotationFocus, - transformerNotationHighlight, - transformerNotationWordHighlight, - transformerRemoveNotationEscape, - transformerRenderWhitespace, -} from '@shikijs/transformers' -import { defaultTwoslashOptions } from '@shikijs/twoslash/core' -import { addClassToHast } from 'shiki' -import { isPlainObject } from 'vuepress/shared' -import { defaultHoverInfoProcessor, transformerTwoslash } from '../twoslash/rendererTransformer.js' -import { attrsToLines, resolveWhitespacePosition } from '../utils/index.js' - -const decorationsRE = /^\/\/ @decorations:(.*)\n/ - -export const baseTransformers: ShikiTransformer[] = [ - transformerNotationDiff({ - matchAlgorithm: 'v3', - }), - transformerNotationFocus({ - matchAlgorithm: 'v3', - classActiveLine: 'has-focus', - classActivePre: 'has-focused-lines', - }), - transformerNotationHighlight({ - matchAlgorithm: 'v3', - }), - transformerNotationErrorLevel({ - matchAlgorithm: 'v3', - }), - transformerNotationWordHighlight({ - matchAlgorithm: 'v3', - }), - { - name: 'vuepress:add-class', - pre(node) { - addClassToHast(node, 'vp-code') - }, - }, - { - name: 'vuepress:clean-up', - pre(node) { - delete node.properties.tabindex - delete node.properties.style - }, - }, - { - name: 'shiki:inline-decorations', - preprocess(code, options) { - code = code.replace(decorationsRE, (match, decorations) => { - options.decorations ||= [] - options.decorations.push(...JSON.parse(decorations)) - return '' - }) - return code - }, - }, - transformerRemoveNotationEscape(), -] - -const vueRE = /-vue$/ -export function getInlineTransformers({ attrs, lang, enabledTwoslash, whitespace, twoslash }: { - attrs: string - lang: string - enabledTwoslash: boolean - whitespace: boolean | WhitespacePosition - twoslash?: boolean | TransformerTwoslashOptions['twoslashOptions'] & VueSpecificOptions -}): ShikiTransformer[] { - const vPre = vueRE.test(lang) ? '' : 'v-pre' - const inlineTransformers: ShikiTransformer[] = [ - transformerCompactLineOptions(attrsToLines(attrs)), - ] - - if (enabledTwoslash) { - const { compilerOptions, ...twoslashOptions } = isPlainObject(twoslash) ? twoslash : {} - const defaultOptions = defaultTwoslashOptions() - inlineTransformers.push(transformerTwoslash({ - processHoverInfo(info) { - return defaultHoverInfoProcessor(info) - }, - twoslashOptions: { - ...defaultOptions, - ...twoslashOptions, - compilerOptions: { - baseUrl: process.cwd(), - ...compilerOptions, - }, - }, - })) - } - else { - inlineTransformers.push({ - name: 'vuepress:v-pre', - pre(node) { - if (vPre) - node.properties['v-pre'] = '' - }, - }) - } - - const position = resolveWhitespacePosition(attrs, whitespace) - if (position) - inlineTransformers.push(transformerRenderWhitespace({ position })) - - return inlineTransformers -} diff --git a/plugins/plugin-shikiji/src/node/index.ts b/plugins/plugin-shikiji/src/node/index.ts deleted file mode 100644 index 0d96bfc3..00000000 --- a/plugins/plugin-shikiji/src/node/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { shikiPlugin } from './shikiPlugin.js' - -export * from './shikiPlugin.js' -export * from './types.js' - -export default shikiPlugin diff --git a/plugins/plugin-shikiji/src/node/markdown/collapsedLinesPlugin.ts b/plugins/plugin-shikiji/src/node/markdown/collapsedLinesPlugin.ts deleted file mode 100644 index ab8480f0..00000000 --- a/plugins/plugin-shikiji/src/node/markdown/collapsedLinesPlugin.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { Markdown } from 'vuepress/markdown' -import { resolveCollapsedLines } from '../utils/collapsedLines.js' - -export interface MarkdownItCollapsedLinesOptions { - /** - * Whether to collapse code blocks when they exceed a certain number of lines, - * - * - If `number`, collapse starts from line `number`. - * - If `true`, collapse starts from line 15 by default. - * - If `false`, disable collapse. - * @default false - */ - collapsedLines?: boolean | number -} - -export function collapsedLinesPlugin(md: Markdown, { - collapsedLines: collapsedLinesOptions = false, -}: MarkdownItCollapsedLinesOptions = {}): void { - const rawFence = md.renderer.rules.fence! - - md.renderer.rules.fence = (...args) => { - const [tokens, index] = args - const token = tokens[index] - // get token info - const info = token.info ? md.utils.unescapeAll(token.info).trim() : '' - const code = rawFence(...args) - - // resolve collapsed-lines mark from token info - const collapsedLinesInfo - = resolveCollapsedLines(info) ?? collapsedLinesOptions - - if (collapsedLinesInfo === false) { - return code - } - - const lines - = code.slice(code.indexOf(''), code.indexOf('')).split('\n').length - - const startLines - = typeof collapsedLinesInfo === 'number' ? collapsedLinesInfo : 15 - - if (lines < startLines) { - return code - } - - const collapsedLinesCode = `
` - const styles = `--vp-collapsed-lines:${startLines};` - - const finalCode = code - .replace(/<\/div>$/, `${collapsedLinesCode}`) - .replace(/"(language-[^"]*)"/, '"$1 has-collapsed collapsed"') - .replace(/^]*>/, (match) => { - if (!match.includes('style=')) { - return `${match.slice(0, -1)} style="${styles}">` - } - return match.replace(/(style=")/, `$1${styles}`) - }) - - return finalCode - } -} diff --git a/plugins/plugin-shikiji/src/node/markdown/highlightLinesPlugin.ts b/plugins/plugin-shikiji/src/node/markdown/highlightLinesPlugin.ts deleted file mode 100644 index 71a72d59..00000000 --- a/plugins/plugin-shikiji/src/node/markdown/highlightLinesPlugin.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Modified from https://github.com/egoist/markdown-it-highlight-lines -// Now this plugin is only used to normalize line attrs. -// The else part of line highlights logic is in '../highlight.ts'. - -import type { Markdown } from 'vuepress/markdown' - -const HIGHLIGHT_LINES_REGEXP = /\{([\d,-]+)\}/ - -export function highlightLinesPlugin(md: Markdown): void { - const rawFence = md.renderer.rules.fence! - - md.renderer.rules.fence = (...args) => { - const [tokens, idx] = args - const token = tokens[idx] - - let lines: string | null = null - - const rawInfo = token.info - const result = rawInfo?.match(HIGHLIGHT_LINES_REGEXP) - - if (!result) - return rawFence(...args) - - // ensure the next plugin get the correct lang - token.info = rawInfo.replace(HIGHLIGHT_LINES_REGEXP, '').trim() - - lines = result[1] - - token.info += ` ${lines}` - return rawFence(...args) - } -} diff --git a/plugins/plugin-shikiji/src/node/markdown/index.ts b/plugins/plugin-shikiji/src/node/markdown/index.ts deleted file mode 100644 index 1ce8a03d..00000000 --- a/plugins/plugin-shikiji/src/node/markdown/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './collapsedLinesPlugin.js' -export * from './highlightLinesPlugin.js' -export * from './lineNumberPlugin.js' -export * from './preWrapperPlugin.js' diff --git a/plugins/plugin-shikiji/src/node/markdown/lineNumberPlugin.ts b/plugins/plugin-shikiji/src/node/markdown/lineNumberPlugin.ts deleted file mode 100644 index 71192642..00000000 --- a/plugins/plugin-shikiji/src/node/markdown/lineNumberPlugin.ts +++ /dev/null @@ -1,63 +0,0 @@ -// markdown-it plugin for generating line numbers. -// It depends on preWrapper plugin. - -import type { Markdown } from 'vuepress/markdown' -import type { LineNumberOptions } from '../types.js' - -const LINE_NUMBERS_REGEXP = /:line-numbers\b/ -const NO_LINE_NUMBERS_REGEXP = /:no-line-numbers\b/ -const LINE_NUMBERS_START_REGEXP = /:line-numbers=(\d+)\b/ - -export function lineNumberPlugin(md: Markdown, { lineNumbers = true }: LineNumberOptions = {}): void { - const rawFence = md.renderer.rules.fence! - - md.renderer.rules.fence = (...args) => { - const rawCode = rawFence(...args) - - const [tokens, idx] = args - const info = tokens[idx].info - const enableLineNumbers = LINE_NUMBERS_REGEXP.test(info) - const disableLineNumbers = NO_LINE_NUMBERS_REGEXP.test(info) - - if (info.includes('twoslash')) - return rawCode - - if ( - (!lineNumbers && !enableLineNumbers) - || (lineNumbers && disableLineNumbers) - ) { - return rawCode - } - - const code = rawCode.slice( - rawCode.indexOf(''), - rawCode.indexOf(''), - ) - - const lines = code.split('\n') - - if ( - typeof lineNumbers === 'number' - && lines.length < lineNumbers - && !enableLineNumbers - ) { - return rawCode - } - - const startNumbers - = Number(info.match(LINE_NUMBERS_START_REGEXP)?.[1] ?? 1) - 1 - const lineNumbersStyle = `style="counter-reset:line-number ${startNumbers}"` - - const lineNumbersCode = [...Array.from({ length: lines.length })] - .map(() => `
`) - .join('') - - const lineNumbersWrapperCode = `` - - const finalCode = rawCode - .replace(/<\/div>$/, `${lineNumbersWrapperCode}`) - .replace(/"(language-[^"]*)"/, '"$1 line-numbers-mode"') - - return finalCode - } -} diff --git a/plugins/plugin-shikiji/src/node/markdown/preWrapperPlugin.ts b/plugins/plugin-shikiji/src/node/markdown/preWrapperPlugin.ts deleted file mode 100644 index b90080e8..00000000 --- a/plugins/plugin-shikiji/src/node/markdown/preWrapperPlugin.ts +++ /dev/null @@ -1,39 +0,0 @@ -// markdown-it plugin for generating line numbers. -// v-pre block logic is in `../highlight.ts` -import type { Markdown } from 'vuepress/markdown' -import type { PreWrapperOptions } from '../types.js' -import { resolveAttr, resolveLanguage } from '../utils/index.js' - -export function preWrapperPlugin( - md: Markdown, - { preWrapper = true }: PreWrapperOptions = {}, -): void { - const rawFence = md.renderer.rules.fence! - - md.renderer.rules.fence = (...args) => { - const [tokens, idx, options] = args - const token = tokens[idx] - - // get token info - const info = token.info ? md.utils.unescapeAll(token.info).trim() : '' - - const lang = resolveLanguage(info) - const title = resolveAttr(info, 'title') || lang - const classes: string[] = [`${options.langPrefix}${lang}`] - - let result = rawFence(...args) - - if (!preWrapper) { - // remove `` attributes - result = result.replace(//, '') - result = `
${result}`
-  }
-}
diff --git a/plugins/plugin-shikiji/src/node/prepareClientConfigFile.ts b/plugins/plugin-shikiji/src/node/prepareClientConfigFile.ts
deleted file mode 100644
index 04460f7b..00000000
--- a/plugins/plugin-shikiji/src/node/prepareClientConfigFile.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import type { App } from 'vuepress'
-import { ensureEndingSlash } from '@vuepress/helper'
-import { getDirname, path } from 'vuepress/utils'
-
-const __dirname = getDirname(import.meta.url)
-
-const CLIENT_FOLDER = ensureEndingSlash(
-  path.resolve(__dirname, '../client'),
-)
-
-export async function prepareClientConfigFile(app: App, {
-  copyCode,
-  twoslash,
-}: { copyCode: boolean, twoslash: boolean }): Promise {
-  return await app.writeTemp(
-    'internal/plugin-shiki/client.js',
-    `\
-${twoslash ? `import { enhanceTwoslash } from '${CLIENT_FOLDER}composables/twoslash.js'` : ''}
-${copyCode ? `import { useCopyCode } from '${CLIENT_FOLDER}composables/copy-code.js'` : ''}
-import { useCollapsedLines } from '${CLIENT_FOLDER}composables/collapsed-lines.js'
-
-export default {
-  ${twoslash
-    ? `enhance({ app }) {
-    enhanceTwoslash(app)
-  },`
-    : ''}
-  setup() {
-    ${copyCode
-      ? `useCopyCode({
-      selector: __CC_SELECTOR__,
-      duration: __CC_DURATION__,
-    })`
-      : ''}
-    useCollapsedLines()
-  },
-}
-`,
-  )
-}
diff --git a/plugins/plugin-shikiji/src/node/shikiPlugin.ts b/plugins/plugin-shikiji/src/node/shikiPlugin.ts
deleted file mode 100644
index 1cd97904..00000000
--- a/plugins/plugin-shikiji/src/node/shikiPlugin.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import type { Plugin } from 'vuepress/core'
-import type {
-  CopyCodeOptions,
-  HighlighterOptions,
-  LineNumberOptions,
-  PreWrapperOptions,
-} from './types.js'
-import { addViteOptimizeDepsInclude } from '@vuepress/helper'
-import { isPlainObject } from 'vuepress/shared'
-import { colors } from 'vuepress/utils'
-import { copyCodeButtonPlugin } from './copy-code-button/index.js'
-import { highlight, resolveTsPaths, scanLanguages } from './highlight/index.js'
-import {
-  collapsedLinesPlugin,
-  highlightLinesPlugin,
-  lineNumberPlugin,
-  preWrapperPlugin,
-} from './markdown/index.js'
-import { prepareClientConfigFile } from './prepareClientConfigFile.js'
-import { logger } from './utils/index.js'
-
-export interface ShikiPluginOptions
-  extends HighlighterOptions, LineNumberOptions, PreWrapperOptions {
-  /**
-   * Add copy code button
-   *
-   * @default true
-   */
-  copyCode?: boolean | CopyCodeOptions
-}
-
-export function shikiPlugin({
-  preWrapper = true,
-  lineNumbers = true,
-  copyCode = true,
-  collapsedLines = false,
-  ...options
-}: ShikiPluginOptions = {}): Plugin {
-  const copyCodeOptions: CopyCodeOptions = isPlainObject(copyCode) ? copyCode : {}
-
-  return {
-    name: '@vuepress-plume/plugin-shikiji',
-
-    define: {
-      __CC_DURATION__: copyCodeOptions.duration ?? 2000,
-      __CC_SELECTOR__: `div[class*="language-"] > button.${copyCodeOptions.className || 'copy'}`,
-    },
-
-    clientConfigFile: app => prepareClientConfigFile(app, {
-      copyCode: copyCode !== false,
-      twoslash: !!options.twoslash,
-    }),
-
-    extendsMarkdown: async (md, app) => {
-      const start = performance.now()
-      const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' }
-
-      if (!options.languages || !options.languages.length) {
-        options.languages = await scanLanguages(app)
-        if (options.languages.length) {
-          logger.warn(`You have not configured \`${colors.cyan('plugins.shiki.languages')}\`. It has been detected that you are using \`${colors.green(JSON.stringify(options.languages))}\`. Please add it to the \`${colors.cyan('plugins.shiki.languages')}\` configuration.`)
-        }
-        if (app.env.isDebug) {
-          logger.info(`scan languages in: ${(performance.now() - start).toFixed(2)}ms`)
-        }
-      }
-
-      if (options.twoslash) {
-        const paths = await resolveTsPaths()
-        if (paths) {
-          options.twoslash = isPlainObject(options.twoslash) ? options.twoslash : {}
-          options.twoslash.compilerOptions ??= {}
-          options.twoslash.compilerOptions.paths = {
-            ...paths,
-            ...options.twoslash.compilerOptions.paths,
-          }
-        }
-      }
-
-      md.options.highlight = await highlight(theme, options)
-      if (app.env.isDebug) {
-        logger.info(`highlight Loaded in: ${(performance.now() - start).toFixed(2)}ms`)
-      }
-
-      md.use(highlightLinesPlugin)
-      md.use(preWrapperPlugin, { preWrapper })
-
-      if (preWrapper) {
-        copyCodeButtonPlugin(md, app, copyCode)
-        md.use(lineNumberPlugin, { lineNumbers })
-        md.use(collapsedLinesPlugin, { collapsedLines })
-      }
-    },
-
-    extendsMarkdownOptions: (options) => {
-      // 注入 floating-vue 后,需要关闭 代码块 的 v-pre 配置
-      if ((options as any).vPre !== false) {
-        const vPre = isPlainObject(options.vPre) ? options.vPre : { block: true }
-        if (vPre.block) {
-          options.vPre ??= {}
-          ;(options as any).vPre.block = false
-        }
-      }
-    },
-
-    extendsBundlerOptions: (bundlerOptions, app) => {
-      if (options.twoslash) {
-        addViteOptimizeDepsInclude(
-          bundlerOptions,
-          app,
-          ['floating-vue'],
-        )
-      }
-    },
-  }
-}
diff --git a/plugins/plugin-shikiji/src/node/twoslash/renderer-floating-vue.ts b/plugins/plugin-shikiji/src/node/twoslash/renderer-floating-vue.ts
deleted file mode 100644
index 24814dc8..00000000
--- a/plugins/plugin-shikiji/src/node/twoslash/renderer-floating-vue.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-import type { RendererRichOptions, TwoslashRenderer } from '@shikijs/twoslash'
-import type { Element, ElementContent, Text } from 'hast'
-import type { ShikiTransformerContextCommon } from 'shiki'
-import { defaultHoverInfoProcessor, rendererRich } from '@shikijs/twoslash'
-import { fromMarkdown } from 'mdast-util-from-markdown'
-import { gfmFromMarkdown } from 'mdast-util-gfm'
-import { defaultHandlers, toHast } from 'mdast-util-to-hast'
-
-export { defaultHoverInfoProcessor }
-
-export interface TwoslashFloatingVueOptions {
-  classCopyIgnore?: string
-  classFloatingPanel?: string
-  classCode?: string
-  classMarkdown?: string
-
-  floatingVueTheme?: string
-  floatingVueThemeQuery?: string
-  floatingVueThemeCompletion?: string
-}
-
-export interface TwoslashFloatingVueRendererOptions extends RendererRichOptions {
-  /**
-   * Class and themes for floating-vue specific nodes
-   */
-  floatingVue?: TwoslashFloatingVueOptions
-}
-
-export function rendererFloatingVue(options: TwoslashFloatingVueRendererOptions = {}): TwoslashRenderer {
-  const {
-    classCopyIgnore = 'vp-copy-ignore',
-    classFloatingPanel = 'twoslash-floating',
-    classCode = 'vp-code',
-    classMarkdown = 'vp-doc',
-    floatingVueTheme = 'twoslash',
-    floatingVueThemeQuery = 'twoslash-query',
-    floatingVueThemeCompletion = 'twoslash-completion',
-  } = options.floatingVue || {}
-
-  const {
-    errorRendering = 'line',
-  } = options
-
-  const hoverBasicProps = {
-    'class': 'twoslash-hover',
-    'popper-class': ['shiki', classFloatingPanel, classCopyIgnore, classCode].join(' '),
-    'theme': floatingVueTheme,
-  }
-
-  const rich = rendererRich({
-    classExtra: classCopyIgnore,
-    ...options,
-    renderMarkdown,
-    renderMarkdownInline,
-    hast: {
-      hoverToken: {
-        tagName: 'v-menu',
-        properties: hoverBasicProps,
-      },
-      hoverCompose: compose,
-      queryToken: {
-        tagName: 'v-menu',
-        properties: {
-          ...hoverBasicProps,
-          ':shown': 'true',
-          'theme': floatingVueThemeQuery,
-        },
-      },
-      queryCompose: compose,
-      popupDocs: {
-        class: `twoslash-popup-docs ${classMarkdown}`,
-      },
-      popupDocsTags: {
-        class: `twoslash-popup-docs twoslash-popup-docs-tags ${classMarkdown}`,
-      },
-      popupError: {
-        class: `twoslash-popup-error ${classMarkdown}`,
-      },
-      errorToken: errorRendering === 'line'
-        ? undefined
-        : {
-            tagName: 'v-menu',
-            properties: {
-              ...hoverBasicProps,
-              class: 'twoslash-error twoslash-error-hover',
-            },
-          },
-      errorCompose: compose,
-      completionCompose({ popup, cursor }) {
-        return [
-          {
-            type: 'element',
-            tagName: 'v-menu',
-            properties: {
-              'popper-class': ['shiki twoslash-completion', classCopyIgnore, classFloatingPanel],
-              'theme': floatingVueThemeCompletion,
-              ':shown': 'true',
-            },
-            children: [
-              cursor,
-              {
-                type: 'element',
-                tagName: 'template',
-                properties: {
-                  'v-slot:popper': '{}',
-                },
-                content: {
-                  type: 'root',
-                  children: [vPre(popup)],
-                },
-              },
-            ],
-          },
-        ]
-      },
-    },
-  })
-
-  return rich
-}
-
-function compose(parts: { token: Element | Text, popup: Element }): Element[] {
-  return [
-    {
-      type: 'element',
-      tagName: 'span',
-      properties: {},
-      children: [parts.token],
-    },
-    {
-      type: 'element',
-      tagName: 'template',
-      properties: {
-        'v-slot:popper': '{}',
-      },
-      content: {
-        type: 'root',
-        children: [vPre(parts.popup)],
-      },
-      children: [],
-    },
-  ]
-}
-
-function vPre(el: T): T {
-  if (el.type === 'element') {
-    el.properties = el.properties || {}
-    el.properties['v-pre'] = ''
-  }
-  return el
-}
-
-function renderMarkdown(this: ShikiTransformerContextCommon, md: string): ElementContent[] {
-  const mdast = fromMarkdown(
-    md.replace(/\{@link ([^}]*)\}/g, '$1'), // replace jsdoc links
-    { mdastExtensions: [gfmFromMarkdown()] },
-  )
-
-  return (toHast(
-    mdast,
-    {
-      handlers: {
-        code: (state, node) => {
-          const lang = node.lang || ''
-          if (lang) {
-            return {
-              type: 'element',
-              tagName: 'div',
-              properties: {
-                'class': `language-${lang}`,
-                'data-ext': lang,
-              },
-              children: this.codeToHast(
-                node.value,
-                {
-                  ...this.options,
-                  transformers: [],
-                  lang,
-                  structure: node.value.trim().includes('\n') ? 'classic' : 'inline',
-                },
-              ).children,
-            }
-          }
-          return defaultHandlers.code(state, node)
-        },
-      },
-    },
-  ) as Element).children
-}
-
-function renderMarkdownInline(this: ShikiTransformerContextCommon, md: string, context?: string): ElementContent[] {
-  if (context === 'tag:param')
-    md = md.replace(/^([\w$-]+)/, '`$1` ')
-
-  const children = renderMarkdown.call(this, md)
-  if (children.length === 1 && children[0].type === 'element' && children[0].tagName === 'p')
-    return children[0].children
-  return children
-}
diff --git a/plugins/plugin-shikiji/src/node/twoslash/rendererTransformer.ts b/plugins/plugin-shikiji/src/node/twoslash/rendererTransformer.ts
deleted file mode 100644
index e2cf32e0..00000000
--- a/plugins/plugin-shikiji/src/node/twoslash/rendererTransformer.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { TransformerTwoslashOptions } from '@shikijs/twoslash/core'
-import type { ShikiTransformer } from 'shiki'
-import type { VueSpecificOptions } from 'twoslash-vue'
-import type { TwoslashFloatingVueRendererOptions } from './renderer-floating-vue.js'
-import process from 'node:process'
-import { createTransformerFactory } from '@shikijs/twoslash/core'
-import { removeTwoslashNotations } from 'twoslash'
-import { createTwoslasher } from 'twoslash-vue'
-import { rendererFloatingVue } from './renderer-floating-vue.js'
-
-export * from './renderer-floating-vue.js'
-
-interface TransformerTwoslashVueOptions extends TransformerTwoslashOptions {
-  twoslashOptions?: TransformerTwoslashOptions['twoslashOptions'] & VueSpecificOptions
-}
-
-export interface VuePressTwoslashOptions extends TransformerTwoslashVueOptions, TwoslashFloatingVueRendererOptions {
-  /**
-   * Requires adding `twoslash` to the code block explicitly to run twoslash
-   * @default true
-   */
-  explicitTrigger?: TransformerTwoslashOptions['explicitTrigger']
-}
-
-/**
- * Create a Shiki transformer for VuePress to enable twoslash integration
- */
-export function transformerTwoslash(options: VuePressTwoslashOptions = {}): ShikiTransformer {
-  const {
-    explicitTrigger = true,
-  } = options
-
-  const onError = (error: any, code: string) => {
-    const isCI = typeof process !== 'undefined' && process?.env?.CI
-    const isDev = typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development'
-    const shouldThrow = (options.throws || isCI || !isDev) && options.throws !== false
-    console.error(`\n\n--------\nTwoslash error in code:\n--------\n${code.split(/\n/g).slice(0, 15).join('\n').trim()}\n--------\n`)
-    if (shouldThrow)
-      throw error
-    else
-      console.error(error)
-    return removeTwoslashNotations(code)
-  }
-
-  const twoslash = createTransformerFactory(
-    createTwoslasher(options.twoslashOptions),
-  )({
-    langs: ['ts', 'tsx', 'js', 'jsx', 'json', 'vue'],
-    renderer: rendererFloatingVue(options),
-    onTwoslashError: onError,
-    onShikiError: onError,
-    ...options,
-    explicitTrigger,
-  })
-
-  const trigger = explicitTrigger instanceof RegExp
-    ? explicitTrigger
-    : /\btwoslash\b/
-
-  return {
-    ...twoslash,
-    name: '@shiki/vuepress-twoslash',
-    preprocess(code, options) {
-      const cleanup = options.transformers?.find(i => i.name === 'vuepress:clean-up')
-      if (cleanup)
-        options.transformers?.splice(options.transformers.indexOf(cleanup), 1)
-
-      // Disable v-pre for twoslash, because we need render it with FloatingVue
-      if (!explicitTrigger || options.meta?.__raw?.match(trigger)) {
-        const vPre = options.transformers?.find(i => i.name === 'vuepress:v-pre')
-        if (vPre)
-          options.transformers?.splice(options.transformers.indexOf(vPre), 1)
-      }
-
-      return twoslash.preprocess!.call(this, code, options)
-    },
-    postprocess(html) {
-      if (this.meta.twoslash)
-        return html.replace(/\{/g, '{')
-
-      return html
-    },
-  }
-}
diff --git a/plugins/plugin-shikiji/src/node/types.ts b/plugins/plugin-shikiji/src/node/types.ts
deleted file mode 100644
index ee62105b..00000000
--- a/plugins/plugin-shikiji/src/node/types.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import type { TransformerTwoslashOptions } from '@shikijs/twoslash/core'
-import type {
-  BuiltinTheme,
-  BundledLanguage,
-  Highlighter,
-  LanguageInput,
-  ShikiTransformer,
-  SpecialLanguage,
-  StringLiteralUnion,
-  ThemeRegistration,
-} from 'shiki'
-import type { VueSpecificOptions } from 'twoslash-vue'
-import type { LocaleConfig } from 'vuepress/shared'
-
-export type ShikiLang =
-  | LanguageInput
-  | StringLiteralUnion
-  | SpecialLanguage
-
-export type ThemeOptions =
-  | ThemeRegistration
-  | BuiltinTheme
-  | {
-    light: ThemeRegistration | BuiltinTheme
-    dark: ThemeRegistration | BuiltinTheme
-  }
-
-export interface HighlighterOptions {
-  /**
-   * Custom theme for syntax highlighting.
-   *
-   * You can also pass an object with `light` and `dark` themes to support dual themes.
-   *
-   * You can use an existing theme.
-   *
-   * @see https://shiki.style/themes
-   *
-   * Or add your own theme.
-   *
-   * @see https://shiki.style/guide/load-theme
-   *
-   * @example { theme: 'github-dark' }
-   * @example { theme: { light: 'github-light', dark: 'github-dark' } }
-   *
-   */
-  theme?: ThemeOptions
-  /**
-   * Languages for syntax highlighting.
-   * @see https://shiki.style/languages
-   */
-  languages?: ShikiLang[]
-  /**
-   * Custom language aliases.
-   *
-   * @example { 'my-lang': 'js' }
-   * @see https://shiki.style/guide/load-lang#custom-language-aliases
-   */
-  languageAlias?: Record
-  /**
-   * Setup Shikiji instance
-   */
-  shikiSetup?: (shiki: Highlighter) => void | Promise
-  /**
-   * Fallback language when the specified language is not available.
-   */
-  defaultHighlightLang?: string
-  /**
-   * Transformers applied to code blocks
-   * @see https://shiki.style/guide/transformers
-   */
-  codeTransformers?: ShikiTransformer[]
-
-  /**
-   * @experiment
-   * Enable transformerTwoslash
-   * @default false
-   */
-  twoslash?: boolean | TransformerTwoslashOptions['twoslashOptions'] & VueSpecificOptions
-
-  /**
-   * Enable transformerRenderWhitespace
-   * @default false
-   */
-  whitespace?: boolean | 'all' | 'boundary' | 'trailing'
-}
-
-export interface LineNumberOptions {
-  /**
-   * Show line numbers in code blocks
-   * @default true
-   */
-  lineNumbers?: boolean | number
-}
-
-export interface PreWrapperOptions {
-  /**
-   * Wrap the `
` tag with an extra `
` or not. Do not disable it unless you - * understand what's it for - * - * - Required for `lineNumbers` - * - Required for title display of default theme - */ - preWrapper?: boolean - - /** - * Hide extra rows when exceeding a specific number of lines. - * - * `true` is equivalent to `15` . - * - * @default false - */ - collapsedLines?: number | boolean -} - -/** - * Options for copy code button - * - * `` - */ -export interface CopyCodeOptions { - /** - * Class name of the button - * - * @default 'copy' - */ - className?: string - - /** - * Duration of the copied text - * - * @default 2000 - */ - duration?: number - - /** - * Locale config for copy code button - */ - locales?: LocaleConfig -} - -export interface CopyCodeLocaleOptions { - /** - * Title of the button - * - * @default 'Copy code' - */ - title?: string - - /** - * Copied text - * - * @default 'Copied!' - */ - copied?: string -} diff --git a/plugins/plugin-shikiji/src/node/utils/attrsToLines.ts b/plugins/plugin-shikiji/src/node/utils/attrsToLines.ts deleted file mode 100644 index 131f4c11..00000000 --- a/plugins/plugin-shikiji/src/node/utils/attrsToLines.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { TransformerCompactLineOption } from '@shikijs/transformers' - -/** - * 2 steps: - * - * 1. convert attrs into line numbers: - * {4,7-13,16,23-27,40} -> [4,7,8,9,10,11,12,13,16,23,24,25,26,27,40] - * 2. convert line numbers into line options: - * [{ line: number, classes: string[] }] - */ -export function attrsToLines(attrs: string): TransformerCompactLineOption[] { - // eslint-disable-next-line regexp/optimal-quantifier-concatenation, regexp/no-super-linear-backtracking - attrs = attrs.replace(/^(?:\[.*?\])?.*?([\d,-]+).*/, '$1').trim() - - const result: number[] = [] - - if (!attrs) - return [] - - attrs - .split(',') - .map(v => v.split('-').map(v => Number.parseInt(v, 10))) - .forEach(([start, end]) => { - if (start && end) { - result.push( - ...Array.from({ length: end - start + 1 }, (_, i) => start + i), - ) - } - else { - result.push(start) - } - }) - - return result.map(line => ({ - line, - classes: ['highlighted'], - })) -} diff --git a/plugins/plugin-shikiji/src/node/utils/collapsedLines.ts b/plugins/plugin-shikiji/src/node/utils/collapsedLines.ts deleted file mode 100644 index dddc8c7c..00000000 --- a/plugins/plugin-shikiji/src/node/utils/collapsedLines.ts +++ /dev/null @@ -1,24 +0,0 @@ -const COLLAPSED_LINES_REGEXP = /:collapsed-lines\b/ -const COLLAPSED_LINES_START_REGEXP = /:collapsed-lines=(\d+)\b/ -const NO_COLLAPSED_LINES_REGEXP = /:no-collapsed-lines\b/ - -/** - * Resolve the `:collapsed-lines` `:collapsed-lines=num` / `:no-collapsed-lines` mark from token info - */ -export function resolveCollapsedLines(info: string): boolean | number | null { - const lines = COLLAPSED_LINES_START_REGEXP.exec(info)?.[1] - - if (lines) { - return Number(lines) - } - - if (COLLAPSED_LINES_REGEXP.test(info)) { - return true - } - - if (NO_COLLAPSED_LINES_REGEXP.test(info)) { - return false - } - - return null -} diff --git a/plugins/plugin-shikiji/src/node/utils/index.ts b/plugins/plugin-shikiji/src/node/utils/index.ts deleted file mode 100644 index a8a899dd..00000000 --- a/plugins/plugin-shikiji/src/node/utils/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Logger } from '@vuepress/helper' - -export const logger = new Logger('@vuepress-plume/plugin-shikiji') - -export * from './attrsToLines.js' -export * from './collapsedLines.js' -export * from './lru.js' -export * from './resolveAttr.js' -export * from './resolveLanguage.js' -export * from './whitespace.js' diff --git a/plugins/plugin-shikiji/src/node/utils/lru.ts b/plugins/plugin-shikiji/src/node/utils/lru.ts deleted file mode 100644 index 191a6f0c..00000000 --- a/plugins/plugin-shikiji/src/node/utils/lru.ts +++ /dev/null @@ -1,39 +0,0 @@ -// adapted from https://stackoverflow.com/a/46432113/11613622 - -export class LRUCache { - private max: number - private cache: Map - - constructor(max: number = 10) { - this.max = max - this.cache = new Map() - } - - get(key: K): V | undefined { - const item = this.cache.get(key) - if (item !== undefined) { - // refresh key - this.cache.delete(key) - this.cache.set(key, item) - } - return item - } - - set(key: K, val: V): void { - // refresh key - if (this.cache.has(key)) - this.cache.delete(key) - // evict oldest - else if (this.cache.size === this.max) - this.cache.delete(this.first()!) - this.cache.set(key, val) - } - - first(): K | undefined { - return this.cache.keys().next().value - } - - clear(): void { - this.cache.clear() - } -} diff --git a/plugins/plugin-shikiji/src/node/utils/resolveAttr.ts b/plugins/plugin-shikiji/src/node/utils/resolveAttr.ts deleted file mode 100644 index f787df2c..00000000 --- a/plugins/plugin-shikiji/src/node/utils/resolveAttr.ts +++ /dev/null @@ -1,9 +0,0 @@ -export 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/plugins/plugin-shikiji/src/node/utils/resolveAttrs.ts b/plugins/plugin-shikiji/src/node/utils/resolveAttrs.ts deleted file mode 100644 index a2acb2e7..00000000 --- a/plugins/plugin-shikiji/src/node/utils/resolveAttrs.ts +++ /dev/null @@ -1,43 +0,0 @@ -const RE_ATTR_VALUE = /(?:^|\s+)(?[\w-]+)(?:=\s*(?['"])(?.+?)\k)?(?:\s+|$)/ -const RE_CODE_BLOCKS = /^[\w\-]*(\s*:[\w\-]*)?(\s*\{[\w\-,\s]+\})?\s*/ - -export function resolveAttrs(info: string): { - attrs: Record - rawAttrs: string -} { - if (!info) - return { rawAttrs: '', attrs: {} } - info = info.replace(RE_CODE_BLOCKS, '').trim() - if (!info) - return { rawAttrs: '', attrs: {} } - - const attrs: Record = {} - const rawAttrs = info - - let matched: RegExpMatchArray | null - - // eslint-disable-next-line no-cond-assign - while (matched = info.match(RE_ATTR_VALUE)) { - const { attr, value } = matched.groups || {} - attrs[attr] = value ?? true - info = info.slice(matched[0].length) - } - - Object.keys(attrs).forEach((key) => { - let value = attrs[key] - value = typeof value === 'string' ? value.trim() : value - if (value === 'true') - value = true - else if (value === 'false') - value = false - - attrs[key] = value - - if (key.includes('-')) { - const _key = key.replace(/-(\w)/g, (_, c) => c.toUpperCase()) - attrs[_key] = value - } - }) - - return { attrs, rawAttrs } -} diff --git a/plugins/plugin-shikiji/src/node/utils/resolveLanguage.ts b/plugins/plugin-shikiji/src/node/utils/resolveLanguage.ts deleted file mode 100644 index 9afae671..00000000 --- a/plugins/plugin-shikiji/src/node/utils/resolveLanguage.ts +++ /dev/null @@ -1,8 +0,0 @@ -const VUE_RE = /-vue$/ - -export function resolveLanguage(info: string): string { - return info - .match(/^([^ :[{]+)/)?.[1] - ?.replace(VUE_RE, '') - .toLowerCase() ?? '' -} diff --git a/plugins/plugin-shikiji/src/node/utils/whitespace.ts b/plugins/plugin-shikiji/src/node/utils/whitespace.ts deleted file mode 100644 index b39d0a09..00000000 --- a/plugins/plugin-shikiji/src/node/utils/whitespace.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const WHITESPACE_REGEXP = /:whitespace(?:=(all|boundary|trailing)?)?\b/ -export const NO_WHITESPACE_REGEXP = /:no-whitespace\b/ - -export type WhitespacePosition = 'all' | 'boundary' | 'trailing' - -export function resolveWhitespacePosition(info: string, defaultPosition?: boolean | WhitespacePosition): WhitespacePosition | false { - if (NO_WHITESPACE_REGEXP.test(info)) { - return false - } - - const position = defaultPosition === true ? undefined : defaultPosition - - const match = info.match(WHITESPACE_REGEXP) - if (match) { - return (match[1] || position || 'all') as WhitespacePosition - } - return defaultPosition === true ? 'all' : defaultPosition ?? false -} diff --git a/plugins/plugin-shikiji/tsup.config.ts b/plugins/plugin-shikiji/tsup.config.ts deleted file mode 100644 index d61383ec..00000000 --- a/plugins/plugin-shikiji/tsup.config.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Options } from 'tsup' -import { defineConfig } from 'tsup' -import { argv } from '../../scripts/tsup-args.js' - -export default defineConfig(() => { - const DEFAULT_OPTIONS: Options = { - dts: true, - sourcemap: false, - splitting: false, - format: 'esm', - } - - const options: Options[] = [] - - if (argv.node) { - options.push({ - ...DEFAULT_OPTIONS, - entry: ['./src/node/index.ts'], - outDir: './lib/node', - target: 'node18', - }) - } - if (argv.client) { - options.push(...[ - // client/composables/ - { - ...DEFAULT_OPTIONS, - entry: [ - 'copy-code.ts', - 'twoslash.ts', - 'collapsed-lines.ts', - ].map(file => `./src/client/composables/${file}`), - outDir: './lib/client/composables', - external: [/.*\.css$/], - }, - ]) - } - return options -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6cff6d45..d63c6c57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,9 @@ catalogs: '@vuepress/plugin-comment': specifier: 2.0.0-rc.79 version: 2.0.0-rc.79 + '@vuepress/plugin-copy-code': + specifier: 2.0.0-rc.79 + version: 2.0.0-rc.79 '@vuepress/plugin-docsearch': specifier: 2.0.0-rc.79 version: 2.0.0-rc.79 @@ -51,12 +54,18 @@ catalogs: '@vuepress/plugin-seo': specifier: 2.0.0-rc.79 version: 2.0.0-rc.79 + '@vuepress/plugin-shiki': + specifier: 2.0.0-rc.79 + version: 2.0.0-rc.79 '@vuepress/plugin-sitemap': specifier: 2.0.0-rc.79 version: 2.0.0-rc.79 '@vuepress/plugin-watermark': specifier: 2.0.0-rc.79 version: 2.0.0-rc.79 + '@vuepress/shiki-twoslash': + specifier: 2.0.0-rc.79 + version: 2.0.0-rc.79 '@vueuse/core': specifier: ^12.7.0 version: 12.7.0 @@ -409,54 +418,6 @@ importers: specifier: 'catalog:' version: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) - plugins/plugin-shikiji: - dependencies: - '@shikijs/transformers': - specifier: ^3.1.0 - version: 3.1.0 - '@shikijs/twoslash': - specifier: ^3.1.0 - version: 3.1.0(typescript@5.8.2) - '@types/hast': - specifier: ^3.0.4 - version: 3.0.4 - '@vuepress/helper': - specifier: 'catalog:' - version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) - '@vueuse/core': - specifier: 'catalog:' - version: 12.7.0(typescript@5.8.2) - fast-glob: - specifier: 'catalog:' - version: 3.3.3 - floating-vue: - specifier: ^5.2.2 - version: 5.2.2(vue@3.5.13(typescript@5.8.2)) - mdast-util-from-markdown: - specifier: ^2.0.2 - version: 2.0.2 - mdast-util-gfm: - specifier: ^3.1.0 - version: 3.1.0 - mdast-util-to-hast: - specifier: ^13.2.0 - version: 13.2.0 - nanoid: - specifier: 'catalog:' - version: 5.1.2 - shiki: - specifier: ^3.1.0 - version: 3.1.0 - twoslash: - specifier: ^0.3.1 - version: 0.3.1(typescript@5.8.2) - twoslash-vue: - specifier: ^0.3.1 - version: 0.3.1(typescript@5.8.2) - vuepress: - specifier: 'catalog:' - version: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) - theme: dependencies: '@iconify/utils': @@ -474,9 +435,6 @@ importers: '@vuepress-plume/plugin-search': specifier: workspace:* version: link:../plugins/plugin-search - '@vuepress-plume/plugin-shikiji': - specifier: workspace:* - version: link:../plugins/plugin-shikiji '@vuepress/helper': specifier: 'catalog:' version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) @@ -486,6 +444,9 @@ importers: '@vuepress/plugin-comment': specifier: 'catalog:' version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + '@vuepress/plugin-copy-code': + specifier: 'catalog:' + version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) '@vuepress/plugin-docsearch': specifier: 'catalog:' version: 2.0.0-rc.79(@algolia/client-search@5.18.0)(search-insights@2.17.3)(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) @@ -516,12 +477,18 @@ importers: '@vuepress/plugin-seo': specifier: 'catalog:' version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + '@vuepress/plugin-shiki': + specifier: 'catalog:' + version: 2.0.0-rc.79(@vuepress/shiki-twoslash@2.0.0-rc.79(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))))(@vueuse/core@12.7.0(typescript@5.8.2))(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) '@vuepress/plugin-sitemap': specifier: 'catalog:' version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) '@vuepress/plugin-watermark': specifier: 'catalog:' version: 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + '@vuepress/shiki-twoslash': + specifier: 'catalog:' + version: 2.0.0-rc.79(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) '@vueuse/core': specifier: 'catalog:' version: 12.7.0(typescript@5.8.2) @@ -2227,8 +2194,8 @@ packages: '@vue/devtools-shared@7.7.2': resolution: {integrity: sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==} - '@vue/language-core@2.2.4': - resolution: {integrity: sha512-eGGdw7eWUwdIn9Fy/irJ7uavCGfgemuHQABgJ/hU1UgZFnbTg9VWeXvHQdhY+2SPQZWJqWXvRWIg67t4iWEa+Q==} + '@vue/language-core@2.1.10': + resolution: {integrity: sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -2273,6 +2240,15 @@ packages: peerDependencies: vuepress: 2.0.0-rc.20 + '@vuepress/highlighter-helper@2.0.0-rc.79': + resolution: {integrity: sha512-zfNZT2bmBLcYxm7TscW7J5KBDdW8MIrnIr+eclym1L5aQL0+oLrc+WtACctJ+TkkF7ZYBKv3U30piLUMfHPwCg==} + peerDependencies: + '@vueuse/core': ^12.7.0 + vuepress: 2.0.0-rc.20 + peerDependenciesMeta: + '@vueuse/core': + optional: true + '@vuepress/markdown@2.0.0-rc.20': resolution: {integrity: sha512-Q/zsW9Kp1BDsLaTxP6J9sVUtH8FfIYkEb6vMT8HHhNvEcIcoLBQRWMZp0VG3yuYRW5sMzjMU5IPD3BdSi52ayw==} @@ -2296,6 +2272,11 @@ packages: twikoo: optional: true + '@vuepress/plugin-copy-code@2.0.0-rc.79': + resolution: {integrity: sha512-Jub18jjYW+EFPqxQfaG0BEL9Yg29IQV/H+nyjTSBWSR49ZJl5um+3H7ZwOLDzXDB1fv4ZIXYXIEfEybklqBXAg==} + peerDependencies: + vuepress: 2.0.0-rc.20 + '@vuepress/plugin-docsearch@2.0.0-rc.79': resolution: {integrity: sha512-9f9CNu0zYsw6pF550/7/S5PvLLH+EXUw7Ni1ugKR/jZB54opNJX+1XAvYc6xWZC/7DNcYH96N7zOQGnbEj9JEg==} peerDependencies: @@ -2368,6 +2349,15 @@ packages: peerDependencies: vuepress: 2.0.0-rc.20 + '@vuepress/plugin-shiki@2.0.0-rc.79': + resolution: {integrity: sha512-QpbNJwrTABjD+jVB/yPaU4M89dwaWRUFp0Keofl6p494QRbDc3vLsots6SXE/dqPU7gXoF5ujg5XyyrjM6t8kw==} + peerDependencies: + '@vuepress/shiki-twoslash': 2.0.0-rc.79 + vuepress: 2.0.0-rc.20 + peerDependenciesMeta: + '@vuepress/shiki-twoslash': + optional: true + '@vuepress/plugin-sitemap@2.0.0-rc.79': resolution: {integrity: sha512-g0wrWNOCoah0aNPnPlKESbBkCecFuwnQ0ddCAnopHs6ctnMqU3OR6vsyG1/z09aPZtfHZGS+yv64eHEHYu41Pg==} peerDependencies: @@ -2381,6 +2371,11 @@ packages: '@vuepress/shared@2.0.0-rc.20': resolution: {integrity: sha512-fMCJxO9tqEGZJ85cYLz4pIP6TnUpC7kUgGJtpSGivro0NA7tqTVv4MVQwQ5J3w4YkQfEJirhlAYEOTrlols52Q==} + '@vuepress/shiki-twoslash@2.0.0-rc.79': + resolution: {integrity: sha512-b0pPS6C6hfc6bpfr/ZTJ/ZBX+tiz8uDniYUQDB0SSGKO6lZr9jcSB3RX7ho9pftlskeBdnAVP8gVHpxdpichhg==} + peerDependencies: + vuepress: 2.0.0-rc.20 + '@vuepress/utils@2.0.0-rc.20': resolution: {integrity: sha512-X3KL2tQrmrnyzQeQhIx7E9j0ssvfddLNrEu8pqUYevuYH3xrnrIT5XBNiTqvnDEFYDYcD2R5gFBCGtLs3uYo6g==} @@ -2461,8 +2456,8 @@ packages: resolution: {integrity: sha512-/tfpK2A4FpS0o+S78o3YSdlqXr0MavJIDlFK3XZrlXLy7vaRXJvW5jYg3v5e/wCaF8y0IpMjkYLhoV6QqfpOgw==} engines: {node: '>= 14.0.0'} - alien-signals@1.0.4: - resolution: {integrity: sha512-DJqqQD3XcsaQcQ1s+iE2jDUZmmQpXwHiR6fCAim/w87luaW+vmLY8fMlrdkmRwzaFXhkxf3rqPCR59tKVv1MDw==} + alien-signals@0.2.2: + resolution: {integrity: sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==} ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} @@ -6102,13 +6097,21 @@ packages: typescript: optional: true + twoslash-protocol@0.2.12: + resolution: {integrity: sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg==} + twoslash-protocol@0.3.1: resolution: {integrity: sha512-BMePTL9OkuNISSyyMclBBhV2s9++DiOCyhhCoV5Kaht6eaWLwVjCCUJHY33eZJPsyKeZYS8Wzz0h+XI01VohVw==} - twoslash-vue@0.3.1: - resolution: {integrity: sha512-9/PS0/iL2m8G6N2ILdI18sZ8l6ex+W2nN5jIaTpfFPlnY0MOX2G5UxEVs+AuNimM9SwEnwfiIuDY9ubDCIQpSQ==} + twoslash-vue@0.2.12: + resolution: {integrity: sha512-kxH60DLn2QBcN2wjqxgMDkyRgmPXsytv7fJIlsyFMDPSkm1/lMrI/UMrNAshNaRHcI+hv8x3h/WBgcvlb2RNAQ==} peerDependencies: - typescript: ^5.5.0 + typescript: '*' + + twoslash@0.2.12: + resolution: {integrity: sha512-tEHPASMqi7kqwfJbkk7hc/4EhlrKCSLcur+TcvYki3vhIfaRMXnXjaYFgXpoZRbT6GdprD4tGuVBEmTpUgLBsw==} + peerDependencies: + typescript: '*' twoslash@0.3.1: resolution: {integrity: sha512-OGqMTGvqXTcb92YQdwGfEdK0nZJA64Aj/ChLOelbl3TfYch2IoBST0Yx4C0LQ7Lzyqm9RpgcpgDxeXQIz4p2Kg==} @@ -8277,13 +8280,13 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/language-core@2.2.4(typescript@5.8.2)': + '@vue/language-core@2.1.10(typescript@5.8.2)': dependencies: '@volar/language-core': 2.4.11 '@vue/compiler-dom': 3.5.13 '@vue/compiler-vue2': 2.7.16 '@vue/shared': 3.5.13 - alien-signals: 1.0.4 + alien-signals: 0.2.2 minimatch: 9.0.5 muggle-string: 0.4.1 path-browserify: 1.0.1 @@ -8447,6 +8450,12 @@ snapshots: transitivePeerDependencies: - typescript + '@vuepress/highlighter-helper@2.0.0-rc.79(@vueuse/core@12.7.0(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': + dependencies: + vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) + optionalDependencies: + '@vueuse/core': 12.7.0(typescript@5.8.2) + '@vuepress/markdown@2.0.0-rc.20': dependencies: '@mdit-vue/plugin-component': 2.1.3 @@ -8484,6 +8493,15 @@ snapshots: transitivePeerDependencies: - typescript + '@vuepress/plugin-copy-code@2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': + dependencies: + '@vuepress/helper': 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + '@vueuse/core': 12.7.0(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.2) + vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) + transitivePeerDependencies: + - typescript + '@vuepress/plugin-docsearch@2.0.0-rc.79(@algolia/client-search@5.18.0)(search-insights@2.17.3)(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': dependencies: '@docsearch/css': 3.9.0 @@ -8602,6 +8620,21 @@ snapshots: transitivePeerDependencies: - typescript + '@vuepress/plugin-shiki@2.0.0-rc.79(@vuepress/shiki-twoslash@2.0.0-rc.79(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))))(@vueuse/core@12.7.0(typescript@5.8.2))(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': + dependencies: + '@shikijs/transformers': 3.1.0 + '@vuepress/helper': 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + '@vuepress/highlighter-helper': 2.0.0-rc.79(@vueuse/core@12.7.0(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + nanoid: 5.1.2 + shiki: 3.1.0 + synckit: 0.9.2 + vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) + optionalDependencies: + '@vuepress/shiki-twoslash': 2.0.0-rc.79(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + transitivePeerDependencies: + - '@vueuse/core' + - typescript + '@vuepress/plugin-sitemap@2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': dependencies: '@vuepress/helper': 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) @@ -8624,6 +8657,23 @@ snapshots: dependencies: '@mdit-vue/types': 2.1.0 + '@vuepress/shiki-twoslash@2.0.0-rc.79(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))': + dependencies: + '@shikijs/twoslash': 3.1.0(typescript@5.8.2) + '@vuepress/helper': 2.0.0-rc.79(typescript@5.8.2)(vuepress@2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))) + floating-vue: 5.2.2(vue@3.5.13(typescript@5.8.2)) + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm: 3.1.0 + mdast-util-to-hast: 13.2.0 + twoslash: 0.2.12(typescript@5.8.2) + twoslash-vue: 0.2.12(typescript@5.8.2) + vuepress: 2.0.0-rc.20(@vuepress/bundler-vite@2.0.0-rc.20(@types/node@22.13.8)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.1)(stylus@0.64.0)(typescript@5.8.2)(yaml@2.7.0))(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)) + transitivePeerDependencies: + - '@nuxt/kit' + - supports-color + - typescript + - vue + '@vuepress/utils@2.0.0-rc.20': dependencies: '@types/debug': 4.1.12 @@ -8711,7 +8761,7 @@ snapshots: '@algolia/requester-fetch': 5.18.0 '@algolia/requester-node-http': 5.18.0 - alien-signals@1.0.4: {} + alien-signals@0.2.2: {} ansi-escapes@4.3.2: dependencies: @@ -12713,13 +12763,23 @@ snapshots: - tsx - yaml + twoslash-protocol@0.2.12: {} + twoslash-protocol@0.3.1: {} - twoslash-vue@0.3.1(typescript@5.8.2): + twoslash-vue@0.2.12(typescript@5.8.2): dependencies: - '@vue/language-core': 2.2.4(typescript@5.8.2) - twoslash: 0.3.1(typescript@5.8.2) - twoslash-protocol: 0.3.1 + '@vue/language-core': 2.1.10(typescript@5.8.2) + twoslash: 0.2.12(typescript@5.8.2) + twoslash-protocol: 0.2.12 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + twoslash@0.2.12(typescript@5.8.2): + dependencies: + '@typescript/vfs': 1.6.1(typescript@5.8.2) + twoslash-protocol: 0.2.12 typescript: 5.8.2 transitivePeerDependencies: - supports-color diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6eef9453..10938750 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,6 +9,7 @@ catalog: '@vuepress/helper': 2.0.0-rc.79 '@vuepress/plugin-cache': 2.0.0-rc.79 '@vuepress/plugin-comment': 2.0.0-rc.79 + '@vuepress/plugin-copy-code': 2.0.0-rc.79 '@vuepress/plugin-docsearch': 2.0.0-rc.79 '@vuepress/plugin-git': 2.0.0-rc.79 '@vuepress/plugin-markdown-hint': 2.0.0-rc.79 @@ -19,8 +20,10 @@ catalog: '@vuepress/plugin-photo-swipe': 2.0.0-rc.79 '@vuepress/plugin-reading-time': 2.0.0-rc.79 '@vuepress/plugin-seo': 2.0.0-rc.79 + '@vuepress/plugin-shiki': 2.0.0-rc.79 '@vuepress/plugin-sitemap': 2.0.0-rc.79 '@vuepress/plugin-watermark': 2.0.0-rc.79 + '@vuepress/shiki-twoslash': 2.0.0-rc.79 '@vueuse/core': ^12.7.0 '@vueuse/integrations': ^12.7.0 chokidar: 3.6.0 diff --git a/theme/package.json b/theme/package.json index 9ced55ee..b2272017 100644 --- a/theme/package.json +++ b/theme/package.json @@ -63,6 +63,7 @@ }, "peerDependencies": { "@iconify/json": "^2", + "@vuepress/shiki-twoslash": "catalog:", "mathjax-full": "^3.2.2", "sass": "^1.85.0", "sass-embedded": "^1.85.0", @@ -74,6 +75,9 @@ "@iconify/json": { "optional": true }, + "@vuepress/shiki-twoslash": { + "optional": true + }, "mathjax-full": { "optional": true }, @@ -96,10 +100,10 @@ "@pengzhanbo/utils": "^1.2.0", "@vuepress-plume/plugin-fonts": "workspace:*", "@vuepress-plume/plugin-search": "workspace:*", - "@vuepress-plume/plugin-shikiji": "workspace:*", "@vuepress/helper": "catalog:", "@vuepress/plugin-cache": "catalog:", "@vuepress/plugin-comment": "catalog:", + "@vuepress/plugin-copy-code": "catalog:", "@vuepress/plugin-docsearch": "catalog:", "@vuepress/plugin-git": "catalog:", "@vuepress/plugin-markdown-hint": "catalog:", @@ -110,6 +114,7 @@ "@vuepress/plugin-photo-swipe": "catalog:", "@vuepress/plugin-reading-time": "catalog:", "@vuepress/plugin-seo": "catalog:", + "@vuepress/plugin-shiki": "catalog:", "@vuepress/plugin-sitemap": "catalog:", "@vuepress/plugin-watermark": "catalog:", "@vueuse/core": "catalog:", diff --git a/theme/src/client/styles/code.css b/theme/src/client/styles/code.css index 4387a8cd..6fd0f655 100644 --- a/theme/src/client/styles/code.css +++ b/theme/src/client/styles/code.css @@ -66,6 +66,9 @@ html:not([data-theme="dark"]) .vp-code span { padding: 20px 0; margin: 0; overflow-x: auto; + font-family: inherit; + font-size: inherit; + line-height: inherit; background: transparent; } @@ -100,6 +103,10 @@ html:not([data-theme="dark"]) .vp-code span { display: none; } +.vp-doc div[class*="language-"].line-numbers-mode::after { + display: none; +} + .vp-doc div[class*="language-"].line-numbers-mode { /* rtl:ignore */ padding-left: 32px; @@ -129,6 +136,7 @@ html:not([data-theme="dark"]) .vp-code span { .vp-doc div[class*="language-"].line-numbers-mode .line-numbers .line-number { position: relative; z-index: 3; + font-family: var(--vp-font-family-mono); user-select: none; } @@ -160,7 +168,7 @@ html:not([data-theme="dark"]) .vp-code span { padding: 0 24px; margin: 0 -24px; background-color: var(--vp-code-line-highlight-color); - transition: background-color 0.5s; + transition: background-color var(--vp-t-color); } .vp-doc div[class*="language-"] code .highlighted.error { @@ -201,7 +209,7 @@ html:not([data-theme="dark"]) .vp-code span { width: calc(100% + 48px); padding: 0 24px; margin: 0 -24px; - transition: background-color 0.5s; + transition: background-color var(--vp-t-color); } .vp-doc div[class*="language-"] code .diff::before { @@ -217,7 +225,6 @@ html:not([data-theme="dark"]) .vp-code span { .vp-doc div[class*="language-"] code .diff.remove::before { color: var(--vp-code-line-diff-remove-symbol-color); content: "-"; - transform: translateX(-6px); } .vp-doc div[class*="language-"] code .diff.add { @@ -227,96 +234,21 @@ html:not([data-theme="dark"]) .vp-code span { .vp-doc div[class*="language-"] code .diff.add::before { color: var(--vp-code-line-diff-add-symbol-color); content: "+"; - transform: translateX(-6px); } -.vp-doc div[class*="language-"] .has-focused-lines .line:not(.has-focus) { - filter: blur(0.095rem); - opacity: 0.7; - transition: filter 0.35s, opacity 0.35s; +/** + * Copy Code Button + */ +.vp-copy-code-button { + --copy-code-c-text: var(--vp-code-block-color); + + top: 1em; + line-height: initial; + transition: opacity var(--vp-t-color), background-color var(--vp-t-color); } -.vp-doc div[class*="language-"]:hover .has-focused-lines .line:not(.has-focus) { - filter: blur(0); - opacity: 1; -} - -.vp-doc div[class*="language-"] button.copy { - position: absolute; - top: 12px; - - /* rtl:ignore */ - right: 12px; - z-index: 3; - width: 40px; - height: 40px; - cursor: pointer; - background-color: var(--vp-code-copy-code-bg); - background-image: var(--vp-icon-copy); - background-repeat: no-repeat; - background-position: 50%; - background-size: 20px; - border: 1px solid var(--vp-code-copy-code-border-color); - border-radius: 4px; - opacity: 0; - transition: border-color 0.25s, background-color 0.25s, opacity 0.25s; - - /* rtl:ignore */ - direction: ltr; -} - -.vp-doc div[class*="language-"]:hover > button.copy, -.vp-doc div[class*="language-"] > button.copy:focus, -.vp-doc div[class*="language-"] > button.copy.copied { - opacity: 1; -} - -.vp-doc div[class*="language-"] > button.copy:hover, -.vp-doc div[class*="language-"] > button.copy.copied { - background-color: var(--vp-code-copy-code-hover-bg); - border-color: var(--vp-code-copy-code-hover-border-color); -} - -.vp-doc div[class*="language-"] > button.copy.copied, -.vp-doc div[class*="language-"] > button.copy:hover.copied { - background-color: var(--vp-code-copy-code-hover-bg); - background-image: var(--vp-icon-copied); - - /* rtl:ignore */ - border-radius: 0 4px 4px 0; -} - -.vp-doc div[class*="language-"] > button.copy.copied::before, -.vp-doc div[class*="language-"] > button.copy:hover.copied::before { - position: relative; - top: -1px; - display: flex; - align-items: center; - justify-content: center; - width: fit-content; - height: 40px; - padding: 0 10px; - font-size: 12px; - font-weight: 500; - color: var(--vp-code-copy-code-active-text); - text-align: center; - white-space: nowrap; - content: attr(data-copied); - background-color: var(--vp-code-copy-code-hover-bg); - border: 1px solid var(--vp-code-copy-code-hover-border-color); - - /* rtl:ignore */ - border-right: 0; - border-radius: 4px 0 0 4px; - - /* rtl:ignore */ - transform: translateX(calc(-100% - 1px)); -} - -@media (max-width: 419px) { - .vp-doc div[class*="language-"] > button.copy { - display: none; - } +.vp-copy-code-button.copied::after { + height: 2.5rem; } /* @@ -324,79 +256,27 @@ html:not([data-theme="dark"]) .vp-code span { -------------------------------------------------------------------------- */ -.vp-doc div[class*="language-"].has-collapsed.collapsed { - height: calc(var(--vp-collapsed-lines) * var(--vp-code-line-height) * var(--vp-code-font-size) + 62px); - overflow-y: hidden; -} +.vp-doc div[class*="language-"].has-collapsed-lines .collapsed-lines, +[data-theme="dark"] .vp-doc div[class*="language-"].has-collapsed-lines .collapsed-lines { + --vp-collapsed-lines-bg: var(--vp-code-block-bg); -@property --vp-code-bg-collapsed-lines { - inherits: false; - initial-value: #fff; - syntax: ""; -} - -.vp-doc div[class*="language-"].has-collapsed .collapsed-lines { - --vp-code-bg-collapsed-lines: var(--vp-code-block-bg); - - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 4; - display: flex; - align-items: center; - justify-content: center; height: 44px; - cursor: pointer; - background: linear-gradient(to bottom, transparent 0%, var(--vp-code-bg-collapsed-lines) 50%, var(--vp-code-bg-collapsed-lines) 100%); - transition: --vp-code-bg-collapsed-lines var(--vp-t-color); + background: linear-gradient(to bottom, transparent 0%, var(--vp-collapsed-lines-bg) 50%, var(--vp-collapsed-lines-bg) 100%); } -.vp-doc div[class*="language-"].has-collapsed .collapsed-lines:hover { - --vp-code-bg-collapsed-lines: var(--vp-c-default-soft); +.vp-doc div[class*="language-"].has-collapsed-lines .collapsed-lines:hover, +[data-theme="dark"] .vp-doc div[class*="language-"].has-collapsed-lines .collapsed-lines:hover { + --vp-collapsed-lines-bg: var(--vp-c-default-soft); } -.vp-doc div[class*="language-"].has-collapsed .collapsed-lines::before { - --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='m18 12l-6 6l-6-6m12-6l-6 6l-6-6'/%3E%3C/svg%3E"); - --trans-rotate: 0deg; - - display: inline-block; - width: 24px; - height: 24px; - pointer-events: none; - content: ""; - background-color: var(--vp-code-block-color); - -webkit-mask-image: var(--icon); - mask-image: var(--icon); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: 50%; - mask-position: 50%; - -webkit-mask-size: 20px; - mask-size: 20px; - animation: code-collapsed-lines 1.2s infinite alternate-reverse ease-in-out; +.vp-doc div[class*="language-"].has-collapsed-lines.collapsed { + height: calc(var(--vp-collapsed-lines) * var(--vp-code-line-height) * var(--vp-code-font-size) + 62px); } -.vp-doc div[class*="language-"].has-collapsed:not(.collapsed) code { +.vp-doc div[class*="language-"].has-collapsed-lines:not(.collapsed) code { padding-bottom: 20px; } -.vp-doc div[class*="language-"].has-collapsed:not(.collapsed) .collapsed-lines:hover { - --vp-code-bg-collapsed-lines: transparent; -} - -.vp-doc div[class*="language-"].has-collapsed:not(.collapsed) .collapsed-lines::before { - --trans-rotate: 180deg; -} - -@keyframes code-collapsed-lines { - 0% { - opacity: 0.3; - transform: translateY(-2px) rotate(var(--trans-rotate)); - } - - 100% { - opacity: 1; - transform: translateY(2px) rotate(var(--trans-rotate)); - } +.vp-doc div[class*="language-"].has-collapsed-lines:not(.collapsed) .collapsed-lines:hover { + --vp-collapsed-lines-bg: transparent; } diff --git a/theme/src/client/styles/twoslash.css b/theme/src/client/styles/twoslash.css index 08753c4c..a6c12266 100644 --- a/theme/src/client/styles/twoslash.css +++ b/theme/src/client/styles/twoslash.css @@ -26,282 +26,3 @@ --twoslash-matched-color: var(--vp-c-brand-1); --twoslash-unmatched-color: var(--vp-c-text-2); } - -/* Respect people's wishes to not have animations */ -@media (prefers-reduced-motion: reduce) { - .twoslash * { - transition: none !important; - } -} - -/* ===== Hover Info ===== */ -.twoslash:hover .twoslash-hover { - border-color: var(--twoslash-underline-color); -} - -.twoslash .twoslash-hover { - position: relative; - border-bottom: 1px dotted transparent; - transition: border-color 0.3s; - transition-timing-function: ease; -} - -/* ===== Error Line ===== */ -.twoslash .twoslash-error-line { - position: relative; - padding: 6px; - margin: 0.2em 0; - color: var(--twoslash-error-color); - background-color: var(--twoslash-error-bg); - border-left: 3px solid var(--twoslash-error-color); -} - -.twoslash .twoslash-error { - padding-bottom: 2px; - background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") repeat-x bottom left; -} - -/* ===== Completeions ===== */ -.twoslash .twoslash-completion-cursor { - position: relative; -} - -.twoslash .twoslash-completion-cursor .twoslash-completion-list { - position: absolute; - top: 0; - left: 0; - z-index: 8; - display: inline-block; - margin: 3px 0 0 -1px; - user-select: none; - background: var(--twoslash-popup-bg); - border: 1px solid var(--twoslash-border-color); - box-shadow: var(--twoslash-popup-shadow); - transform: translate(0, 1.2em); -} - -.twoslash-completion-list { - display: flex; - flex-direction: column; - gap: 4px; - width: 240px; - padding: 4px; - font-size: 0.8rem; -} - -.twoslash-completion-list:hover { - user-select: auto; -} - -.twoslash-completion-list::before { - position: absolute; - top: -1.6em; - left: -1px; - width: 2px; - height: 1.4em; - content: " "; - background-color: var(--twoslash-cursor-color); -} - -.twoslash-completion-list li { - display: flex; - gap: 0.25em; - align-items: center; - overflow: hidden; - line-height: 1em; -} - -.twoslash-completion-list li span.twoslash-completions-unmatched { - color: var(--twoslash-unmatched-color); -} - -.twoslash-completion-list .deprecated { - text-decoration: line-through; - opacity: 0.5; -} - -.twoslash-completion-list li span.twoslash-completions-matched { - color: var(--twoslash-matched-color); -} - -/* Highlights */ -.twoslash-highlighted { - padding: 1px 2px; - margin: -1px -3px; - background-color: var(--twoslash-highlighted-bg); - border: 1px solid var(--twoslash-highlighted-border); - border-radius: 4px; -} - -/* Icons */ -.twoslash-completion-list .twoslash-completions-icon { - flex: none; - width: 1em; - color: var(--twoslash-unmatched-color); -} - -/* Custom Tags */ -.twoslash .twoslash-tag-line { - position: relative; - display: flex; - gap: 0.3em; - align-items: center; - padding: 6px 10px; - margin: 0.2em 0; - color: var(--twoslash-tag-color); - background-color: var(--twoslash-tag-bg); - border-left: 3px solid var(--twoslash-tag-color); -} - -.twoslash .twoslash-tag-line .twoslash-tag-icon { - width: 1.1em; - color: inherit; -} - -.twoslash .twoslash-tag-line.twoslash-tag-error-line { - color: var(--twoslash-error-color); - background-color: var(--twoslash-error-bg); - border-left: 3px solid var(--twoslash-error-color); -} - -.twoslash .twoslash-tag-line.twoslash-tag-warn-line { - color: var(--twoslash-tag-warn-color); - background-color: var(--twoslash-tag-warn-bg); - border-left: 3px solid var(--twoslash-tag-warn-color); -} - -.twoslash .twoslash-tag-line.twoslash-tag-annotate-line { - color: var(--twoslash-tag-annotate-color); - background-color: var(--twoslash-tag-annotate-bg); - border-left: 3px solid var(--twoslash-tag-annotate-color); -} - -/* ========== floating vue ================== */ -.v-popper--theme-twoslash { - z-index: calc(var(--vp-z-index-local-nav) - 1); -} - -.v-popper--theme-twoslash .v-popper__inner { - color: var(--twoslash-popup-color); - background: var(--twoslash-popup-bg); - border-color: var(--twoslash-border-color); -} - -.v-popper--theme-twoslash .v-popper__arrow-outer { - border-color: var(--twoslash-border-color); -} - -.v-popper--theme-twoslash .v-popper__arrow-inner { - border-color: var(--twoslash-popup-bg); -} - -.twoslash-popup-container { - transform: translateY(1.5em); -} - -.twoslash-query-presisted .twoslash-popup-container { - transform: translateY(1.8em); -} - -.twoslash .v-popper { - display: inline-block; -} - -.twoslash-completion-list .twoslash-completions-icon { - color: var(--twoslash-unmatched-color) !important; -} - -.twoslash-floating .twoslash-popup-code { - display: block; - width: fit-content; - min-width: 100%; - max-width: 600px; - padding: 6px 12px; - font-size: var(--twoslash-code-size); - line-height: var(--vp-code-line-height); - white-space: pre-wrap; - transition: color 0.5s; -} - -.twoslash-floating .twoslash-popup-docs, -.twoslash-floating .twoslash-popup-error { - max-width: 700px; - max-height: 500px; - padding: 12px !important; - overflow: hidden auto; - font-family: var(--twoslash-docs-font); - font-size: 0.9em; - text-wrap: balance; -} - -.twoslash-floating .twoslash-popup-docs p:first-child, -.twoslash-floating .twoslash-popup-error p:first-child { - margin-top: 0; -} - -.twoslash-floating .twoslash-popup-docs p:last-child, -.twoslash-floating .twoslash-popup-error p:last-child { - margin-bottom: 0; -} - -.twoslash-floating .twoslash-popup-docs { - color: var(--twoslash-docs-color); - border-top: 1px solid var(--twoslash-border-color); -} - -.twoslash-floating .twoslash-popup-error { - color: var(--twoslash-error-color); -} - -.twoslash-floating .twoslash-popup-error.twoslash-error-level-warning { - color: var(--twoslash-warn-color); -} - -.twoslash-floating .twoslash-popup-docs p, -.twoslash-floating .twoslash-popup-error p { - margin: 6px 0; - text-wrap: balance; -} - -.twoslash-floating .twoslash-popup-docs pre .twoslash-floating .twoslash-popup-error pre { - padding: 12px; - margin: 6px -2px; - overflow-x: auto; - background-color: var(--vp-code-block-bg); - border-radius: 8px; -} - -.twoslash-floating .twoslash-popup-docs-tags { - display: flex; - flex-direction: column; - padding: 8px 12px !important; -} - -.twoslash-floating .twoslash-popup-docs-tags .twoslash-popup-docs-tag-name { - margin-right: 0.5em; - font-family: var(--twoslash-code-font); - color: var(--twoslash-unmatched-color); -} - -.twoslash-completion-cursor { - display: inline-block; - width: 2px; - height: 1.2em; - margin-bottom: -0.2em; - user-select: none; - background: var(--twoslash-cursor-color); -} - -.twoslash-floating.twoslash-completion .v-popper__arrow-container { - display: none; -} - -.twoslash-floating.twoslash-completion .twoslash-completion-list { - padding: 6px; - font-family: var(--twoslash-code-font); - font-size: var(--twoslash-code-size) !important; -} - -.twoslash-floating.twoslash-completion .twoslash-completion-list li { - padding: 3px 0; -} diff --git a/theme/src/client/styles/vars.css b/theme/src/client/styles/vars.css index 067c3a45..17463586 100644 --- a/theme/src/client/styles/vars.css +++ b/theme/src/client/styles/vars.css @@ -376,14 +376,6 @@ --vp-code-line-warning-color: var(--vp-c-yellow-soft); --vp-code-line-error-color: var(--vp-c-red-soft); - - --vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E"); - --vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E"); - --vp-code-copy-code-border-color: var(--vp-c-divider); - --vp-code-copy-code-bg: var(--vp-c-bg-soft); - --vp-code-copy-code-hover-border-color: var(--vp-c-divider); - --vp-code-copy-code-hover-bg: var(--vp-c-bg); - --vp-code-copy-code-active-text: var(--vp-c-text-2); --vp-code-tab-divider: var(--vp-code-block-divider-color); --vp-code-tab-text-color: var(--vp-c-text-2); --vp-code-tab-bg: var(--vp-code-block-bg); diff --git a/theme/src/node/plugins/getPlugins.ts b/theme/src/node/plugins/getPlugins.ts index 99a1b98d..db037226 100644 --- a/theme/src/node/plugins/getPlugins.ts +++ b/theme/src/node/plugins/getPlugins.ts @@ -1,13 +1,15 @@ import type { SeoPluginOptions } from '@vuepress/plugin-seo' import type { SitemapPluginOptions } from '@vuepress/plugin-sitemap' +import type { ThemeOptions } from 'vuepress-plugin-md-power' import type { App, PluginConfig } from 'vuepress/core' import type { PlumeThemePluginOptions } from '../../shared/index.js' +import { uniq } from '@pengzhanbo/utils' import { fontsPlugin } from '@vuepress-plume/plugin-fonts' import { searchPlugin } from '@vuepress-plume/plugin-search' -import { shikiPlugin } from '@vuepress-plume/plugin-shikiji' import { isPlainObject } from '@vuepress/helper' import { cachePlugin } from '@vuepress/plugin-cache' import { commentPlugin } from '@vuepress/plugin-comment' +import { copyCodePlugin } from '@vuepress/plugin-copy-code' import { docsearchPlugin } from '@vuepress/plugin-docsearch' import { gitPlugin } from '@vuepress/plugin-git' import { markdownHintPlugin } from '@vuepress/plugin-markdown-hint' @@ -18,6 +20,7 @@ import { nprogressPlugin } from '@vuepress/plugin-nprogress' import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe' import { readingTimePlugin } from '@vuepress/plugin-reading-time' import { seoPlugin } from '@vuepress/plugin-seo' +import { shikiPlugin } from '@vuepress/plugin-shiki' import { sitemapPlugin } from '@vuepress/plugin-sitemap' import { watermarkPlugin } from '@vuepress/plugin-watermark' import { mdEnhancePlugin } from 'vuepress-plugin-md-enhance' @@ -102,13 +105,39 @@ export function getPlugins({ plugins.push(searchPlugin(pluginOptions.search || {})) } - const shikiOption = pluginOptions.shiki - let shikiTheme: any = { light: 'vitesse-light', dark: 'vitesse-dark' } - if (shikiOption !== false) { - shikiTheme = shikiOption?.theme ?? shikiTheme + if (pluginOptions.copyCode !== false) { + const { ignoreSelector = [], ...copyCodeOptions } = pluginOptions.copyCode || {} + plugins.push(copyCodePlugin({ + ignoreSelector: uniq(['.vp-copy-ignore', '.diff.remove', ...ignoreSelector]), + ...copyCodeOptions, + })) + } + + const shikiOptions = pluginOptions.shiki + + const shikiTheme = shikiOptions && 'theme' in shikiOptions ? shikiOptions.theme : shikiOptions && 'themes' in shikiOptions ? shikiOptions.themes : { light: 'vitesse-light', dark: 'vitesse-dark' } + + if (shikiOptions !== false) { + const { twoslash, ...restShikiOptions } = isPlainObject(shikiOptions) ? shikiOptions : {} + const twoslashOptions = twoslash === true ? {} : twoslash plugins.push(shikiPlugin({ - theme: shikiTheme, - ...(shikiOption ?? {}), + // enable some default features + notationDiff: true, + notationErrorLevel: true, + notationFocus: true, + notationHighlight: true, + notationWordHighlight: true, + highlightLines: true, + collapsedLines: false, + twoslash: isPlainObject(twoslashOptions) + ? { + ...twoslashOptions, + // inject markdown class + floatingVue: { classMarkdown: 'vp-doc', ...twoslashOptions.floatingVue }, + } + : twoslashOptions, + ...('theme' in restShikiOptions ? {} : { themes: { light: 'vitesse-light', dark: 'vitesse-dark' } }), + ...restShikiOptions, })) } @@ -121,8 +150,9 @@ export function getPlugins({ plot: true, icons: true, ...pluginOptions.markdownPower || {}, + // TODO: repl 代码主题 配置仅支持 主题名,不支持注册自定义主题 repl: pluginOptions.markdownPower?.repl - ? { theme: shikiTheme, ...pluginOptions.markdownPower?.repl } + ? { theme: shikiTheme as ThemeOptions, ...pluginOptions.markdownPower?.repl } : pluginOptions.markdownPower?.repl, })) } diff --git a/theme/src/shared/options/plugins.ts b/theme/src/shared/options/plugins.ts index cc60d9a9..32a2eaf7 100644 --- a/theme/src/shared/options/plugins.ts +++ b/theme/src/shared/options/plugins.ts @@ -1,12 +1,13 @@ import type { SearchPluginOptions } from '@vuepress-plume/plugin-search' -import type { ShikiPluginOptions } from '@vuepress-plume/plugin-shikiji' import type { CommentPluginOptions } from '@vuepress/plugin-comment' +import type { CopyCodePluginOptions } from '@vuepress/plugin-copy-code' import type { DocSearchOptions } from '@vuepress/plugin-docsearch' import type { MarkdownImagePluginOptions } from '@vuepress/plugin-markdown-image' import type { MarkdownIncludePluginOptions } from '@vuepress/plugin-markdown-include' import type { MarkdownMathPluginOptions } from '@vuepress/plugin-markdown-math' import type { ReadingTimePluginOptions } from '@vuepress/plugin-reading-time' import type { SeoPluginOptions } from '@vuepress/plugin-seo' +import type { ShikiPluginOptions } from '@vuepress/plugin-shiki' import type { SitemapPluginOptions } from '@vuepress/plugin-sitemap' import type { WatermarkPluginOptions } from '@vuepress/plugin-watermark' import type { MarkdownEnhancePluginOptions } from 'vuepress-plugin-md-enhance' @@ -23,6 +24,11 @@ export interface PlumeThemePluginOptions { */ docsearch?: false | DocSearchOptions + /** + * 代码块复制按钮配置 + */ + copyCode?: false | CopyCodePluginOptions + /** * 代码高亮 配置 */