mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
* feat: remove shiki plugin * chore: tweak * chore: tweak --------- Co-authored-by: pengzhanbo <volodymyr@foxmail.com>
This commit is contained in:
parent
de5469201a
commit
b8b32201ef
@ -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: {
|
||||
|
||||
@ -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 {
|
||||
|
||||
效果:
|
||||
|
||||
<!-- @include: ../../snippet/whitespace.snippet.md{18-24} -->
|
||||
<!-- @include: ../../snippet/whitespace.snippet.md{17-23} -->
|
||||
|
||||
### collapseLines
|
||||
|
||||
@ -160,7 +112,7 @@ interface CopyCodeOptions {
|
||||
|
||||
将代码块折叠到指定行数。
|
||||
|
||||
### codeTransformers
|
||||
### transformers
|
||||
|
||||
- 类型: `ShikiTransformer[]`
|
||||
- 默认值: `[]`
|
||||
|
||||
@ -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 重要变更 <Badge type="danger" text="1.0.0-rc.136" />
|
||||
|
||||
因此,如果比较在意 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` 配置代码块主题。
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
<Badge type="warning" text="v1.0.0-rc.105 更新" />
|
||||
随着 `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]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
</book>
|
||||
</catalog>
|
||||
```
|
||||
```
|
||||
````
|
||||
|
||||
**输出:**
|
||||
@ -57,7 +56,6 @@ function foo( ) {
|
||||
return 'Hello World'
|
||||
}
|
||||
```
|
||||
```
|
||||
````
|
||||
|
||||
**输出:**
|
||||
|
||||
@ -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.
|
||||
@ -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<string, string>
|
||||
/**
|
||||
* Setup Shikiji instance
|
||||
*/
|
||||
shikiSetup?: (shikiji: Highlighter) => void | Promise<void>
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
```
|
||||
@ -1,456 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`collapsedLinesPlugin > should not work with false 1`] = `
|
||||
"<div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`collapsedLinesPlugin > should not work with includes styles 1`] = `
|
||||
"<div class="language-" style="color: red;"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;color: red;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;color: red;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;color: red;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language-" style="color: red;"><pre><code>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
|
||||
</code></pre>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`collapsedLinesPlugin > should work with default 1`] = `
|
||||
"<div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`collapsedLinesPlugin > should work with number 1`] = `
|
||||
"<div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`collapsedLinesPlugin > should work with true 1`] = `
|
||||
"<div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:15;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language- has-collapsed collapsed" style="--vp-collapsed-lines:10;"><pre><code>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
|
||||
</code></pre>
|
||||
<div class="collapsed-lines"></div></div><div class="language-"><pre><code>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
|
||||
</code></pre>
|
||||
</div>"
|
||||
`;
|
||||
@ -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 => `<pre><code>${str}</code></pre>`
|
||||
const fence = md.renderer.rules.fence!
|
||||
md.renderer.rules.fence = (...args) =>
|
||||
`<div class="language-"${hasStyles ? ' style="color: red;"' : ''}>${fence(...args)}</div>`
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
@ -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 => `<pre><code>${str}</code></pre>`
|
||||
const fence = md.renderer.rules.fence!
|
||||
md.renderer.rules.fence = (...args) => `<div class="language-lang">${fence(...args)}</div>`
|
||||
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('<button class="copy"')
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', { filePathRelative: '/zh/test.md' })).toContain('<button class="copy"')
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', { filePathRelative: '/xxx/test.md' })).toContain('<button class="copy"')
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', {})).toContain('<button class="copy"')
|
||||
})
|
||||
|
||||
it('should work with true', () => {
|
||||
const md = createMarkdown(true)
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', { filePathRelative: '/test.md' })).toContain('<button class="copy"')
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', {})).toContain('<button class="copy"')
|
||||
})
|
||||
|
||||
it('should not work with disable', () => {
|
||||
const md = createMarkdown(false)
|
||||
|
||||
expect(md.render('```js\nconst a = 1\n```', { filePathRelative: '/test.js' })).not.toContain('<button class="copy"')
|
||||
})
|
||||
})
|
||||
@ -1,63 +0,0 @@
|
||||
{
|
||||
"name": "@vuepress-plume/plugin-shikiji",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.135",
|
||||
"description": "The Plugin for VuePress 2 - shiki",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git",
|
||||
"directory": "plugins/plugin-shikiji"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/node/index.d.ts",
|
||||
"import": "./lib/node/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "lib/node/index.js",
|
||||
"types": "./lib/node/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm run copy && pnpm run tsup",
|
||||
"clean": "rimraf --glob ./lib",
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||
"tsup": "tsup --config tsup.config.ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vuepress": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@shikijs/transformers": "^3.1.0",
|
||||
"@shikijs/twoslash": "^3.1.0",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@vuepress/helper": "catalog:",
|
||||
"@vueuse/core": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"floating-vue": "^5.2.2",
|
||||
"mdast-util-from-markdown": "^2.0.2",
|
||||
"mdast-util-gfm": "^3.1.0",
|
||||
"mdast-util-to-hast": "^13.2.0",
|
||||
"nanoid": "catalog:",
|
||||
"shiki": "^3.1.0",
|
||||
"twoslash": "^0.3.1",
|
||||
"twoslash-vue": "^0.3.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keyword": [
|
||||
"VuePress",
|
||||
"vuepress plugin",
|
||||
"shiki",
|
||||
"vuepress-plugin-shiki"
|
||||
]
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
|
||||
export function useCollapsedLines({
|
||||
selector = 'div[class*="language-"] > .collapsed-lines',
|
||||
}: { selector?: string } = {}): void {
|
||||
useEventListener('click', (e) => {
|
||||
const el = e.target as HTMLElement
|
||||
if (el.matches(selector)) {
|
||||
const parent = el.parentElement
|
||||
if (parent?.classList.toggle('collapsed')) {
|
||||
parent.scrollIntoView({ block: 'center', behavior: 'instant' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
import { useClipboard, useEventListener } from '@vueuse/core'
|
||||
|
||||
const SHELL_RE = /language-(?:shellscript|shell|bash|sh|zsh)/
|
||||
const IGNORE_NODES = ['.vp-copy-ignore', '.diff.remove']
|
||||
|
||||
interface CopyCodeOptions {
|
||||
selector?: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
export function useCopyCode({
|
||||
selector = 'div[class*="language-"] > button.copy',
|
||||
duration = 2000,
|
||||
}: CopyCodeOptions = {}): void {
|
||||
if (__VUEPRESS_SSR__)
|
||||
return
|
||||
|
||||
const timeoutIdMap = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()
|
||||
const { copy } = useClipboard({ legacy: true })
|
||||
|
||||
useEventListener('click', (e) => {
|
||||
const el = e.target as HTMLElement
|
||||
if (el.matches(selector)) {
|
||||
const parent = el.parentElement
|
||||
const sibling = el.nextElementSibling
|
||||
if (!parent || !sibling)
|
||||
return
|
||||
|
||||
const isShell = SHELL_RE.test(parent.className)
|
||||
|
||||
// Clone the node and remove the ignored nodes
|
||||
const clone = sibling.cloneNode(true) as HTMLElement
|
||||
clone
|
||||
.querySelectorAll(IGNORE_NODES.join(','))
|
||||
.forEach(node => node.remove())
|
||||
|
||||
let text = clone.textContent || ''
|
||||
|
||||
if (isShell)
|
||||
text = text.replace(/^ *(\$|>) /gm, '').trim()
|
||||
|
||||
copy(text).then(() => {
|
||||
if (duration <= 0)
|
||||
return
|
||||
|
||||
el.classList.add('copied')
|
||||
clearTimeout(timeoutIdMap.get(el))
|
||||
const timeoutId = setTimeout(() => {
|
||||
el.classList.remove('copied')
|
||||
el.blur()
|
||||
timeoutIdMap.delete(el)
|
||||
}, duration)
|
||||
timeoutIdMap.set(el, timeoutId)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
import type { App } from 'vue'
|
||||
import FloatingVue, { recomputeAllPoppers } from 'floating-vue'
|
||||
import 'floating-vue/dist/style.css'
|
||||
|
||||
const isMobile = typeof navigator !== 'undefined' && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
|
||||
export type FloatingVueConfig = Parameters<(typeof FloatingVue)['install']>[1]
|
||||
|
||||
export function enhanceTwoslash(app: App) {
|
||||
if (typeof window !== 'undefined') {
|
||||
// Recompute poppers when clicking on a tab
|
||||
window.addEventListener('click', (e) => {
|
||||
const path = e.composedPath()
|
||||
if (path.some((el: any) => el?.classList?.contains?.('vp-code-group') || el?.classList?.contains?.('tabs')))
|
||||
recomputeAllPoppers()
|
||||
}, { passive: true })
|
||||
}
|
||||
app.use(FloatingVue, {
|
||||
themes: {
|
||||
'twoslash': {
|
||||
$extend: 'dropdown',
|
||||
triggers: isMobile ? ['touch'] : ['hover', 'touch'],
|
||||
popperTriggers: isMobile ? ['touch'] : ['hover', 'touch'],
|
||||
placement: 'bottom-start',
|
||||
overflowPadding: 10,
|
||||
delay: 0,
|
||||
handleResize: false,
|
||||
autoHide: true,
|
||||
instantMove: true,
|
||||
flip: false,
|
||||
arrowPadding: 8,
|
||||
autoBoundaryMaxSize: true,
|
||||
},
|
||||
'twoslash-query': {
|
||||
$extend: 'twoslash',
|
||||
triggers: ['click'],
|
||||
popperTriggers: ['click'],
|
||||
autoHide: false,
|
||||
},
|
||||
'twoslash-completion': {
|
||||
$extend: 'twoslash-query',
|
||||
triggers: ['click'],
|
||||
popperTriggers: ['click'],
|
||||
autoHide: false,
|
||||
distance: 0,
|
||||
arrowOverflow: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
import type { LocaleConfig } from 'vuepress'
|
||||
import type { CopyCodeLocaleOptions } from '../types.js'
|
||||
|
||||
/** Multi language config for copy code button */
|
||||
export const copyCodeButtonLocales: LocaleConfig<CopyCodeLocaleOptions>
|
||||
= {
|
||||
'/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',
|
||||
},
|
||||
}
|
||||
@ -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('><pre', `>${copyCodeButton}<pre`)
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import type { App, LocaleConfig } from 'vuepress'
|
||||
import type {
|
||||
CopyCodeLocaleOptions,
|
||||
CopyCodeOptions,
|
||||
} from '../types.js'
|
||||
import {
|
||||
getLocalePaths,
|
||||
inferRootLocalePath,
|
||||
isPlainObject,
|
||||
} from '@vuepress/helper'
|
||||
import { ensureLeadingSlash, resolveLocalePath } from 'vuepress/shared'
|
||||
import { copyCodeButtonLocales } from './copyCodeButtonLocales.js'
|
||||
|
||||
export function createCopyCodeButtonRender(app: App, options?: boolean | CopyCodeOptions): ((filePathRelative: string) => string) | null {
|
||||
if (options === false)
|
||||
return null
|
||||
|
||||
const { className = 'copy', locales: userLocales = {} }
|
||||
= isPlainObject(options) ? options : {}
|
||||
|
||||
const root = inferRootLocalePath(app)
|
||||
const locales: LocaleConfig<CopyCodeLocaleOptions> = {
|
||||
// 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 `<button class="${className}" title="${title}" data-copied="${copied}"></button>`
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './copyCodeButtonLocales.js'
|
||||
export * from './copyCodeButtonPlugin.js'
|
||||
export * from './createCopyCodeButtonRender.js'
|
||||
@ -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
|
||||
}
|
||||
@ -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<string, string>()
|
||||
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<string, string>) {
|
||||
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<string, string>, twoslash: boolean) {
|
||||
mustaches.forEach((marker, match) => {
|
||||
s = s.replaceAll(marker, match)
|
||||
})
|
||||
|
||||
if (twoslash)
|
||||
s = s.replace(/\{/g, '{')
|
||||
|
||||
return `${s}\n`
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './highlight.js'
|
||||
export * from './resolveTsPaths.js'
|
||||
export * from './scanLanguages.js'
|
||||
@ -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<Record<string, string[]> | 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
|
||||
}
|
||||
}
|
||||
@ -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<string[]> {
|
||||
const source = app.dir.source()
|
||||
const pattern = ['**/*.md', '!.vuepress', '!node_modules']
|
||||
const files = await fg(pattern, {
|
||||
cwd: source,
|
||||
})
|
||||
const langs = new Set<string>()
|
||||
|
||||
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))
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { shikiPlugin } from './shikiPlugin.js'
|
||||
|
||||
export * from './shikiPlugin.js'
|
||||
export * from './types.js'
|
||||
|
||||
export default shikiPlugin
|
||||
@ -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>'), code.indexOf('</code>')).split('\n').length
|
||||
|
||||
const startLines
|
||||
= typeof collapsedLinesInfo === 'number' ? collapsedLinesInfo : 15
|
||||
|
||||
if (lines < startLines) {
|
||||
return code
|
||||
}
|
||||
|
||||
const collapsedLinesCode = `<div class="collapsed-lines"></div>`
|
||||
const styles = `--vp-collapsed-lines:${startLines};`
|
||||
|
||||
const finalCode = code
|
||||
.replace(/<\/div>$/, `${collapsedLinesCode}</div>`)
|
||||
.replace(/"(language-[^"]*)"/, '"$1 has-collapsed collapsed"')
|
||||
.replace(/^<div[^>]*>/, (match) => {
|
||||
if (!match.includes('style=')) {
|
||||
return `${match.slice(0, -1)} style="${styles}">`
|
||||
}
|
||||
return match.replace(/(style=")/, `$1${styles}`)
|
||||
})
|
||||
|
||||
return finalCode
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export * from './collapsedLinesPlugin.js'
|
||||
export * from './highlightLinesPlugin.js'
|
||||
export * from './lineNumberPlugin.js'
|
||||
export * from './preWrapperPlugin.js'
|
||||
@ -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('<code>'),
|
||||
rawCode.indexOf('</code>'),
|
||||
)
|
||||
|
||||
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(() => `<div class="line-number"></div>`)
|
||||
.join('')
|
||||
|
||||
const lineNumbersWrapperCode = `<div class="line-numbers" aria-hidden="true" ${lineNumbersStyle}>${lineNumbersCode}</div>`
|
||||
|
||||
const finalCode = rawCode
|
||||
.replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`)
|
||||
.replace(/"(language-[^"]*)"/, '"$1 line-numbers-mode"')
|
||||
|
||||
return finalCode
|
||||
}
|
||||
}
|
||||
@ -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 `<code>` attributes
|
||||
result = result.replace(/<code[\s\S]*?>/, '<code>')
|
||||
result = `<pre class="${classes.join(' ')}"${result.slice('<pre'.length)}`
|
||||
return result
|
||||
}
|
||||
const attrs: string[] = [
|
||||
`data-ext="${lang}"`,
|
||||
`data-title="${title}"`,
|
||||
]
|
||||
|
||||
return `<div class="${classes.join(' ')}" ${attrs.join(' ')}>${result}</div>`
|
||||
}
|
||||
}
|
||||
@ -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<string> {
|
||||
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()
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
}
|
||||
@ -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'],
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -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 [
|
||||
<Element>{
|
||||
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<T extends ElementContent>(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 <Element>{
|
||||
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
|
||||
}
|
||||
@ -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
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -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<BundledLanguage>
|
||||
| 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<string, string>
|
||||
/**
|
||||
* Setup Shikiji instance
|
||||
*/
|
||||
shikiSetup?: (shiki: Highlighter) => void | Promise<void>
|
||||
/**
|
||||
* 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 `<pre>` tag with an extra `<div>` 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
|
||||
*
|
||||
* `<button title="{title}" class="{className}"></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<CopyCodeLocaleOptions>
|
||||
}
|
||||
|
||||
export interface CopyCodeLocaleOptions {
|
||||
/**
|
||||
* Title of the button
|
||||
*
|
||||
* @default 'Copy code'
|
||||
*/
|
||||
title?: string
|
||||
|
||||
/**
|
||||
* Copied text
|
||||
*
|
||||
* @default 'Copied!'
|
||||
*/
|
||||
copied?: string
|
||||
}
|
||||
@ -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'],
|
||||
}))
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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'
|
||||
@ -1,39 +0,0 @@
|
||||
// adapted from https://stackoverflow.com/a/46432113/11613622
|
||||
|
||||
export class LRUCache<K, V> {
|
||||
private max: number
|
||||
private cache: Map<K, V>
|
||||
|
||||
constructor(max: number = 10) {
|
||||
this.max = max
|
||||
this.cache = new Map<K, V>()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -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*(?<quote>['"])(?<content>.+?)\\k<quote>(\\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
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
const RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/
|
||||
const RE_CODE_BLOCKS = /^[\w\-]*(\s*:[\w\-]*)?(\s*\{[\w\-,\s]+\})?\s*/
|
||||
|
||||
export function resolveAttrs(info: string): {
|
||||
attrs: Record<string, string | boolean>
|
||||
rawAttrs: string
|
||||
} {
|
||||
if (!info)
|
||||
return { rawAttrs: '', attrs: {} }
|
||||
info = info.replace(RE_CODE_BLOCKS, '').trim()
|
||||
if (!info)
|
||||
return { rawAttrs: '', attrs: {} }
|
||||
|
||||
const attrs: Record<string, string | boolean> = {}
|
||||
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 }
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
const VUE_RE = /-vue$/
|
||||
|
||||
export function resolveLanguage(info: string): string {
|
||||
return info
|
||||
.match(/^([^ :[{]+)/)?.[1]
|
||||
?.replace(VUE_RE, '')
|
||||
.toLowerCase() ?? ''
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
})
|
||||
190
pnpm-lock.yaml
generated
190
pnpm-lock.yaml
generated
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:",
|
||||
|
||||
@ -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: "<color>";
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
/**
|
||||
* 代码高亮 配置
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user