- = {
- '/en/': {
- title: 'Copy code',
- copied: 'Copied',
- },
-
- '/zh/': {
- title: '复制代码',
- copied: '已复制',
- },
-
- '/zh-tw/': {
- title: '複製代碼',
- copied: '已複製',
- },
-
- '/de/': {
- title: 'Kopiere den Code.',
- copied: 'Kopiert',
- },
-
- '/de-at/': {
- title: 'Kopiere den Code.',
- copied: 'Kopierter',
- },
-
- '/vi/': {
- title: 'Sao chép code',
- copied: 'Đã sao chép',
- },
-
- '/uk/': {
- title: 'Скопіюйте код',
- copied: 'Скопійовано',
- },
-
- '/ru/': {
- title: 'Скопировать код',
- copied: 'Скопировано',
- },
-
- '/br/': {
- title: 'Copiar o código',
- copied: 'Código',
- },
-
- '/pl/': {
- title: 'Skopiuj kod',
- copied: 'Skopiowane',
- },
-
- '/sk/': {
- title: 'Skopíruj kód',
- copied: 'Skopírované',
- },
-
- '/fr/': {
- title: 'Copier le code',
- copied: 'Copié',
- },
-
- '/es/': {
- title: 'Copiar código',
- copied: 'Copiado',
- },
-
- '/ja/': {
- title: 'コードをコピー',
- copied: 'コピーしました',
- },
-
- '/tr/': {
- title: 'Kodu kopyala',
- copied: 'Kopyalandı',
- },
-
- '/ko/': {
- title: '코드 복사',
- copied: '복사됨',
- },
-
- '/fi/': {
- title: 'Kopioi koodi',
- copied: 'Kopioitu',
- },
-
- '/hu/': {
- title: 'Kód másolása',
- copied: 'Másolva',
- },
-
- '/id/': {
- title: 'Salin kode',
- copied: 'Disalin',
- },
-
- '/nl/': {
- title: 'Kopieer code',
- copied: 'Gekopieerd',
- },
- }
diff --git a/plugins/plugin-shikiji/src/node/copy-code-button/copyCodeButtonPlugin.ts b/plugins/plugin-shikiji/src/node/copy-code-button/copyCodeButtonPlugin.ts
deleted file mode 100644
index 1219ff41..00000000
--- a/plugins/plugin-shikiji/src/node/copy-code-button/copyCodeButtonPlugin.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { App } from 'vuepress'
-import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
-import type { CopyCodeOptions } from '../types.js'
-import { createCopyCodeButtonRender } from './createCopyCodeButtonRender.js'
-
-/**
- * This plugin should work after `preWrapperPlugin`,
- * and if `preWrapper` is disabled, this plugin should not be called either.
- */
-export function copyCodeButtonPlugin(md: Markdown, app: App, options?: boolean | CopyCodeOptions): void {
- const render = createCopyCodeButtonRender(app, options)
-
- if (!render)
- return
-
- const fence = md.renderer.rules.fence!
-
- md.renderer.rules.fence = (...args) => {
- const [, , , env] = args
-
- const result = fence(...args)
- const { filePathRelative } = env as MarkdownEnv
- // resolve copy code button
- const copyCodeButton = render(filePathRelative ?? '')
-
- return result.replace('>${copyCodeButton} string) | null {
- if (options === false)
- return null
-
- const { className = 'copy', locales: userLocales = {} }
- = isPlainObject(options) ? options : {}
-
- const root = inferRootLocalePath(app)
- const locales: LocaleConfig = {
- // fallback locale
- '/': userLocales['/'] || copyCodeButtonLocales[root],
- }
-
- getLocalePaths(app).forEach((path) => {
- locales[path]
- = userLocales[path] || copyCodeButtonLocales[path === '/' ? root : path]
- })
-
- return (filePathRelative: string) => {
- const relativePath = ensureLeadingSlash(filePathRelative)
- const localePath = resolveLocalePath(locales, relativePath)
- const { title, copied } = locales[localePath] || locales['/']
-
- return ``
- }
-}
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 = `
${lineNumbersCode}
`
-
- 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
+
/**
* 代码高亮 配置
*/