feat: (试验性)代码高亮支持 twoslash

This commit is contained in:
pengzhanbo 2024-01-12 02:26:36 +08:00
parent de8d9e0d61
commit d0fdf79477
12 changed files with 475 additions and 84 deletions

View File

@ -72,6 +72,7 @@
"taze", "taze",
"Tongji", "Tongji",
"tsbuildinfo", "tsbuildinfo",
"twoslash",
"vite", "vite",
"vuepress", "vuepress",
"vueuse", "vueuse",

View File

@ -124,6 +124,56 @@ const obj = {
} }
``` ```
**Code Blocks TwoSlash**
```ts twoslash
// @errors: 2339
const welcome = "Tudo bem gente?"
const words = welcome.contains(" ")
```
```ts twoslash
import express from "express"
const app = express()
app.get("/", function (req, res) {
res.send
})
app.listen(3000)
```
```ts twoslash
import { getHighlighterCore } from 'shikiji/core'
const highlighter = await getHighlighterCore({})
// @log: Custom log message
const a = 1
// @error: Custom error message
const b = 1
// @warn: Custom warning message
const c = 1
// @annotate: Custom annotation message
```
```ts twoslash
// @errors: 2540
interface Todo {
title: string
}
const todo: Readonly<Todo> = {
title: 'Delete inactive users'.toUpperCase(),
// ^?
}
todo.title = 'Hello'
Number.parseInt('123', 10)
// ^|
//
//
```
**代码分组** **代码分组**
::: code-tabs ::: code-tabs

View File

@ -60,6 +60,11 @@
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.11" "vite": "^5.0.11"
}, },
"pnpm": {
"patchedDependencies": {
"@vuepress/markdown@2.0.0-rc.0": "patches/@vuepress__markdown@2.0.0-rc.0.patch"
}
},
"lint-staged": { "lint-staged": {
"*": "eslint --fix" "*": "eslint --fix"
}, },

View File

@ -0,0 +1,13 @@
diff --git a/dist/index.js b/dist/index.js
index 996b0d16dac39667cc25496e52adcc9dd2b2befa..a4c9f5ba3a20967d9a561fcc73178d9e84f48279 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -245,7 +245,7 @@ var codePlugin = (md, {
const info = token.info ? md.utils.unescapeAll(token.info).trim() : "";
const language = resolveLanguage(info);
const languageClass = `${options.langPrefix}${language.name}`;
- const code = options.highlight?.(token.content, language.name, "") || md.utils.escapeHtml(token.content);
+ const code = options.highlight?.(token.content, language.name, info || "") || md.utils.escapeHtml(token.content);
token.attrJoin("class", languageClass);
let result = code.startsWith("<pre") ? code : `<pre${slf.renderAttrs(token)}><code>${code}</code></pre>`;
const useVPre = resolveVPre(info) ?? vPreBlock;

View File

@ -8,7 +8,7 @@ const options = __COPY_CODE_OPTIONS__
const RE_LANGUAGE = /language-([\w]+)/ const RE_LANGUAGE = /language-([\w]+)/
const RE_START_CODE = /^ *(\$|>)/gm const RE_START_CODE = /^ *(\$|>)/gm
const shells = ['shellscript', 'shell', 'bash', 'sh', 'zsh'] const shells = ['shellscript', 'shell', 'bash', 'sh', 'zsh']
const ignoredNodes = ['.diff.remove'] const ignoredNodes = ['.diff.remove', '.vp-copy-ignore']
function isMobile(): boolean { function isMobile(): boolean {
return navigator return navigator

View File

@ -37,7 +37,8 @@
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"shikiji": "^0.9.18", "shikiji": "^0.9.18",
"shikiji-transformers": "^0.9.18" "shikiji-transformers": "^0.9.18",
"shikiji-twoslash": "^0.9.18"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -15,7 +15,9 @@ import {
transformerNotationFocus, transformerNotationFocus,
transformerNotationHighlight, transformerNotationHighlight,
} from 'shikiji-transformers' } from 'shikiji-transformers'
import { rendererRich, transformerTwoSlash } from 'shikiji-twoslash'
import type { HighlighterOptions, ThemeOptions } from './types.js' import type { HighlighterOptions, ThemeOptions } from './types.js'
import { resolveAttrs } from './resolveAttrs.js'
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
@ -88,9 +90,7 @@ export async function highlight(
lang = defaultLang lang = defaultLang
} }
} }
const { attrs: attributes, rawAttrs } = resolveAttrs(attrs || '')
// const lineOptions = attrsToLines(attrs)
const mustaches = new Map<string, string>() const mustaches = new Map<string, string>()
const removeMustache = (s: string) => { const removeMustache = (s: string) => {
@ -110,32 +110,75 @@ export async function highlight(
mustaches.forEach((marker, match) => { mustaches.forEach((marker, match) => {
s = s.replaceAll(marker, match) s = s.replaceAll(marker, match)
}) })
return s
}
const fillEmptyHighlightedLine = (s: string) => { return `${s}\n`
return `${s.replace(
/(<span class="line highlighted">)(<\/span>)/g,
'$1<wbr>$2',
).replace(/(\/\/\s*?\[)\\(!code.*?\])/g, '$1$2')}\n`
} }
str = removeMustache(str).trimEnd() str = removeMustache(str).trimEnd()
const highlighted = highlighter.codeToHtml(str, { const inlineTransformers: ShikijiTransformer[] = [
lang, {
transformers: [ name: 'vuepress-shikiji:empty-line',
...transformers, pre(hast) {
...userTransformers, hast.children.forEach((code) => {
], if (code.type === 'element' && code.tagName === 'code') {
meta: { code.children.forEach((span) => {
__raw: attrs, if (
span.type === 'element'
&& span.tagName === 'span'
&& Array.isArray(span.properties.class)
&& span.properties.class.includes('line')
&& span.children.length === 0
) {
span.children.push({
type: 'element',
tagName: 'wbr',
properties: {},
children: [],
})
}
})
}
})
},
}, },
...(typeof theme === 'object' && 'light' in theme && 'dark' in theme {
? { themes: theme, defaultColor: false } name: 'vuepress-shikiji:remove-escape',
: { theme }), postprocess(code) {
}) return code.replace(/\[\\\!code/g, '[!code')
},
},
]
return fillEmptyHighlightedLine(restoreMustache(highlighted)) if (attributes.twoslash) {
inlineTransformers.push(transformerTwoSlash({
renderer: rendererRich({
classExtra: 'vp-copy-ignore',
}),
}))
}
try {
const highlighted = highlighter.codeToHtml(str, {
lang,
transformers: [
...transformers,
...inlineTransformers,
...userTransformers,
],
meta: {
__raw: rawAttrs,
},
...(typeof theme === 'object' && 'light' in theme && 'dark' in theme
? { themes: theme, defaultColor: false }
: { theme }),
})
return restoreMustache(highlighted)
}
catch (e) {
logger.error(e)
return str
}
} }
} }

View File

@ -0,0 +1,43 @@
const RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w\d-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/
const RE_CODE_BLOCKS = /^[\w\d-]*(\s*:[\w\d-]*)?(\s*\{[\d\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 }
}

View File

@ -2,9 +2,6 @@ import type { Plugin } from '@vuepress/core'
import { highlight } from './highlight.js' import { highlight } from './highlight.js'
import type { HighlighterOptions } from './types' import type { HighlighterOptions } from './types'
/**
* Options of @vuepress/plugin-shiki
*/
export type ShikijiPluginOptions = HighlighterOptions export type ShikijiPluginOptions = HighlighterOptions
export function shikijiPlugin(options: ShikijiPluginOptions = {}): Plugin { export function shikijiPlugin(options: ShikijiPluginOptions = {}): Plugin {

123
pnpm-lock.yaml generated
View File

@ -4,6 +4,11 @@ settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
patchedDependencies:
'@vuepress/markdown@2.0.0-rc.0':
hash: zvwmijlesxcdy3i4trojywcm7y
path: patches/@vuepress__markdown@2.0.0-rc.0.patch
importers: importers:
.: .:
@ -16,7 +21,7 @@ importers:
version: 18.4.4 version: 18.4.4
'@pengzhanbo/eslint-config-vue': '@pengzhanbo/eslint-config-vue':
specifier: ^1.5.3 specifier: ^1.5.3
version: 1.5.3(@vue/compiler-sfc@3.4.5)(eslint@8.56.0)(typescript@5.3.3) version: 1.5.3(@vue/compiler-sfc@3.4.7)(eslint@8.56.0)(typescript@5.3.3)
'@pengzhanbo/stylelint-config': '@pengzhanbo/stylelint-config':
specifier: ^1.5.3 specifier: ^1.5.3
version: 1.5.3(stylelint@16.1.0) version: 1.5.3(stylelint@16.1.0)
@ -79,7 +84,7 @@ importers:
version: 2.0.0-rc.0(@types/node@20.9.1)(sass@1.69.7)(typescript@5.3.3) version: 2.0.0-rc.0(@types/node@20.9.1)(sass@1.69.7)(typescript@5.3.3)
'@vuepress/bundler-webpack': '@vuepress/bundler-webpack':
specifier: 2.0.0-rc.0 specifier: 2.0.0-rc.0
version: 2.0.0-rc.0(@vue/compiler-sfc@3.4.5)(typescript@5.3.3) version: 2.0.0-rc.0(@vue/compiler-sfc@3.4.7)(typescript@5.3.3)
'@vuepress/cli': '@vuepress/cli':
specifier: 2.0.0-rc.0 specifier: 2.0.0-rc.0
version: 2.0.0-rc.0(typescript@5.3.3) version: 2.0.0-rc.0(typescript@5.3.3)
@ -361,6 +366,9 @@ importers:
shikiji-transformers: shikiji-transformers:
specifier: ^0.9.18 specifier: ^0.9.18
version: 0.9.18 version: 0.9.18
shikiji-twoslash:
specifier: ^0.9.18
version: 0.9.18(typescript@5.3.3)
theme: theme:
dependencies: dependencies:
@ -3803,7 +3811,7 @@ packages:
'@parcel/watcher-win32-x64': 2.3.0 '@parcel/watcher-win32-x64': 2.3.0
dev: false dev: false
/@pengzhanbo/eslint-config-vue@1.5.3(@vue/compiler-sfc@3.4.5)(eslint@8.56.0)(typescript@5.3.3): /@pengzhanbo/eslint-config-vue@1.5.3(@vue/compiler-sfc@3.4.7)(eslint@8.56.0)(typescript@5.3.3):
resolution: {integrity: sha512-5QdiHebq5wQGLBC1nZKJGK4rjB1fMM3RcWWLNBn2NqbYMOKRllj9CVrnHvjhKoO9KTUNV89doRDBmnIIQoortA==} resolution: {integrity: sha512-5QdiHebq5wQGLBC1nZKJGK4rjB1fMM3RcWWLNBn2NqbYMOKRllj9CVrnHvjhKoO9KTUNV89doRDBmnIIQoortA==}
peerDependencies: peerDependencies:
'@unocss/eslint-plugin': '>=0.50.0' '@unocss/eslint-plugin': '>=0.50.0'
@ -3820,7 +3828,7 @@ packages:
eslint: 8.56.0 eslint: 8.56.0
eslint-merge-processors: 0.1.0(eslint@8.56.0) eslint-merge-processors: 0.1.0(eslint@8.56.0)
eslint-plugin-vue: 9.19.2(eslint@8.56.0) eslint-plugin-vue: 9.19.2(eslint@8.56.0)
eslint-processor-vue-blocks: 0.1.1(@vue/compiler-sfc@3.4.5)(eslint@8.56.0) eslint-processor-vue-blocks: 0.1.1(@vue/compiler-sfc@3.4.7)(eslint@8.56.0)
vue-eslint-parser: 9.3.2(eslint@8.56.0) vue-eslint-parser: 9.3.2(eslint@8.56.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/compiler-sfc' - '@vue/compiler-sfc'
@ -4386,6 +4394,13 @@ packages:
resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==}
dev: false dev: false
/@types/lz-string@1.5.0:
resolution: {integrity: sha512-s84fKOrzqqNCAPljhVyC5TjAo6BH4jKHw9NRNFNiRUY5QSgZCmVm5XILlWbisiKl+0OcS7eWihmKGS5akc2iQw==}
deprecated: This is a stub types definition. lz-string provides its own type definitions, so you do not need this installed.
dependencies:
lz-string: 1.5.0
dev: false
/@types/markdown-it-emoji@2.0.4: /@types/markdown-it-emoji@2.0.4:
resolution: {integrity: sha512-H6ulk/ZmbDxOayPwI/leJzrmoW1YKX1Z+MVSCHXuYhvqckV4I/c+hPTf6UiqJyn2avWugfj30XroheEb6/Ekqg==} resolution: {integrity: sha512-H6ulk/ZmbDxOayPwI/leJzrmoW1YKX1Z+MVSCHXuYhvqckV4I/c+hPTf6UiqJyn2avWugfj30XroheEb6/Ekqg==}
dependencies: dependencies:
@ -4699,6 +4714,28 @@ packages:
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
dev: true dev: true
/@typescript/twoslash@3.2.4(typescript@5.3.3):
resolution: {integrity: sha512-/TCIOuPQaKltzUUT1qJo6mplYwjbAxkaSFvkeZD3FeFt3Ovt+HJi8xisu8rcLyDRmM3VJ0+jAx+AAICwn4Zlhw==}
peerDependencies:
typescript: '*'
dependencies:
'@types/lz-string': 1.5.0
'@typescript/vfs': 1.5.0
debug: 4.3.4(supports-color@9.2.2)
lz-string: 1.5.0
typescript: 5.3.3
transitivePeerDependencies:
- supports-color
dev: false
/@typescript/vfs@1.5.0:
resolution: {integrity: sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==}
dependencies:
debug: 4.3.4(supports-color@9.2.2)
transitivePeerDependencies:
- supports-color
dev: false
/@ungap/structured-clone@1.2.0: /@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: true dev: true
@ -4756,15 +4793,6 @@ packages:
vue: 3.4.7(typescript@5.3.3) vue: 3.4.7(typescript@5.3.3)
dev: false dev: false
/@vue/compiler-core@3.4.5:
resolution: {integrity: sha512-Daka7P1z2AgKjzuueWXhwzIsKu0NkLB6vGbNVEV2iJ8GJTrzraZo/Sk4GWCMRtd/qVi3zwnk+Owbd/xSZbwHtQ==}
dependencies:
'@babel/parser': 7.23.6
'@vue/shared': 3.4.5
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.0.2
/@vue/compiler-core@3.4.7: /@vue/compiler-core@3.4.7:
resolution: {integrity: sha512-hhCaE3pTMrlIJK7M/o3Xf7HV8+JoNTGOQ/coWS+V+pH6QFFyqtoXqQzpqsNp7UK17xYKua/MBiKj4e1vgZOBYw==} resolution: {integrity: sha512-hhCaE3pTMrlIJK7M/o3Xf7HV8+JoNTGOQ/coWS+V+pH6QFFyqtoXqQzpqsNp7UK17xYKua/MBiKj4e1vgZOBYw==}
dependencies: dependencies:
@ -4773,33 +4801,12 @@ packages:
entities: 4.5.0 entities: 4.5.0
estree-walker: 2.0.2 estree-walker: 2.0.2
source-map-js: 1.0.2 source-map-js: 1.0.2
dev: false
/@vue/compiler-dom@3.4.5:
resolution: {integrity: sha512-J8YlxknJVd90SXFJ4HwGANSAXsx5I0lK30sO/zvYV7s5gXf7gZR7r/1BmZ2ju7RGH1lnc6bpBc6nL61yW+PsAQ==}
dependencies:
'@vue/compiler-core': 3.4.5
'@vue/shared': 3.4.5
/@vue/compiler-dom@3.4.7: /@vue/compiler-dom@3.4.7:
resolution: {integrity: sha512-qDKBAIurCTub4n/6jDYkXwgsFuriqqmmLrIq1N2QDfYJA/mwiwvxi09OGn28g+uDdERX9NaKDLji0oTjE3sScg==} resolution: {integrity: sha512-qDKBAIurCTub4n/6jDYkXwgsFuriqqmmLrIq1N2QDfYJA/mwiwvxi09OGn28g+uDdERX9NaKDLji0oTjE3sScg==}
dependencies: dependencies:
'@vue/compiler-core': 3.4.7 '@vue/compiler-core': 3.4.7
'@vue/shared': 3.4.7 '@vue/shared': 3.4.7
dev: false
/@vue/compiler-sfc@3.4.5:
resolution: {integrity: sha512-jauvkDuSSUbP0ebhfNqljhShA90YEfX/0wZ+w40oZF43IjGyWYjqYaJbvMJwGOd+9+vODW6eSvnk28f0SGV7OQ==}
dependencies:
'@babel/parser': 7.23.6
'@vue/compiler-core': 3.4.5
'@vue/compiler-dom': 3.4.5
'@vue/compiler-ssr': 3.4.5
'@vue/shared': 3.4.5
estree-walker: 2.0.2
magic-string: 0.30.5
postcss: 8.4.33
source-map-js: 1.0.2
/@vue/compiler-sfc@3.4.7: /@vue/compiler-sfc@3.4.7:
resolution: {integrity: sha512-Gec6CLkReVswDYjQFq79O5rktri4R7TsD/VPCiUoJw40JhNNxaNJJa8mrQrWoJluW4ETy6QN0NUyC/JO77OCOw==} resolution: {integrity: sha512-Gec6CLkReVswDYjQFq79O5rktri4R7TsD/VPCiUoJw40JhNNxaNJJa8mrQrWoJluW4ETy6QN0NUyC/JO77OCOw==}
@ -4813,20 +4820,12 @@ packages:
magic-string: 0.30.5 magic-string: 0.30.5
postcss: 8.4.33 postcss: 8.4.33
source-map-js: 1.0.2 source-map-js: 1.0.2
dev: false
/@vue/compiler-ssr@3.4.5:
resolution: {integrity: sha512-DDdEcDzj2lWTMfUMMtEpLDhURai9LhM0zSZ219jCt7b2Vyl0/jy3keFgCPMitG0V1S1YG4Cmws3lWHWdxHQOpg==}
dependencies:
'@vue/compiler-dom': 3.4.5
'@vue/shared': 3.4.5
/@vue/compiler-ssr@3.4.7: /@vue/compiler-ssr@3.4.7:
resolution: {integrity: sha512-PvYeSOvnCkST5mGS0TLwEn5w+4GavtEn6adcq8AspbHaIr+mId5hp7cG3ASy3iy8b+LuXEG2/QaV/nj5BQ/Aww==} resolution: {integrity: sha512-PvYeSOvnCkST5mGS0TLwEn5w+4GavtEn6adcq8AspbHaIr+mId5hp7cG3ASy3iy8b+LuXEG2/QaV/nj5BQ/Aww==}
dependencies: dependencies:
'@vue/compiler-dom': 3.4.7 '@vue/compiler-dom': 3.4.7
'@vue/shared': 3.4.7 '@vue/shared': 3.4.7
dev: false
/@vue/devtools-api@6.5.1: /@vue/devtools-api@6.5.1:
resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
@ -4867,12 +4866,8 @@ packages:
resolution: {integrity: sha512-/zYUwiHD8j7gKx2argXEMCUXVST6q/21DFU0sTfNX0URJroCe3b1UF6vLJ3lQDfLNIiiRl2ONp7Nh5UVWS6QnA==} resolution: {integrity: sha512-/zYUwiHD8j7gKx2argXEMCUXVST6q/21DFU0sTfNX0URJroCe3b1UF6vLJ3lQDfLNIiiRl2ONp7Nh5UVWS6QnA==}
dev: false dev: false
/@vue/shared@3.4.5:
resolution: {integrity: sha512-6XptuzlMvN4l4cDnDw36pdGEV+9njYkQ1ZE0Q6iZLwrKefKaOJyiFmcP3/KBDHbt72cJZGtllAc1GaHe6XGAyg==}
/@vue/shared@3.4.7: /@vue/shared@3.4.7:
resolution: {integrity: sha512-G+i4glX1dMJk88sbJEcQEGWRQnVm9eIY7CcQbO5dpdsD9SF8jka3Mr5OqZYGjczGN1+D6EUwdu6phcmcx9iuPA==} resolution: {integrity: sha512-G+i4glX1dMJk88sbJEcQEGWRQnVm9eIY7CcQbO5dpdsD9SF8jka3Mr5OqZYGjczGN1+D6EUwdu6phcmcx9iuPA==}
dev: false
/@vuepress/bundler-vite@2.0.0-rc.0(@types/node@20.9.1)(sass@1.69.7)(typescript@5.3.3): /@vuepress/bundler-vite@2.0.0-rc.0(@types/node@20.9.1)(sass@1.69.7)(typescript@5.3.3):
resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==} resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==}
@ -4904,7 +4899,7 @@ packages:
- typescript - typescript
dev: false dev: false
/@vuepress/bundler-webpack@2.0.0-rc.0(@vue/compiler-sfc@3.4.5)(typescript@5.3.3): /@vuepress/bundler-webpack@2.0.0-rc.0(@vue/compiler-sfc@3.4.7)(typescript@5.3.3):
resolution: {integrity: sha512-PUbjaQCTE+pwkmHkozT4CCjdEiAEO89XOXKTO/VwEsv6hWNeT97fi7TnScV/x8R/9WeA45QrW3eHipMwkKJ8uQ==} resolution: {integrity: sha512-PUbjaQCTE+pwkmHkozT4CCjdEiAEO89XOXKTO/VwEsv6hWNeT97fi7TnScV/x8R/9WeA45QrW3eHipMwkKJ8uQ==}
dependencies: dependencies:
'@types/express': 4.17.21 '@types/express': 4.17.21
@ -4926,7 +4921,7 @@ packages:
postcss-loader: 7.3.3(postcss@8.4.31)(webpack@5.89.0) postcss-loader: 7.3.3(postcss@8.4.31)(webpack@5.89.0)
style-loader: 3.3.3(webpack@5.89.0) style-loader: 3.3.3(webpack@5.89.0)
vue: 3.4.7(typescript@5.3.3) vue: 3.4.7(typescript@5.3.3)
vue-loader: 17.3.1(@vue/compiler-sfc@3.4.5)(vue@3.4.7)(webpack@5.89.0) vue-loader: 17.3.1(@vue/compiler-sfc@3.4.7)(vue@3.4.7)(webpack@5.89.0)
vue-router: 4.2.5(vue@3.4.7) vue-router: 4.2.5(vue@3.4.7)
webpack: 5.89.0 webpack: 5.89.0
webpack-chain: 6.5.1 webpack-chain: 6.5.1
@ -4980,7 +4975,7 @@ packages:
resolution: {integrity: sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA==} resolution: {integrity: sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA==}
dependencies: dependencies:
'@vuepress/client': 2.0.0-rc.0(typescript@5.3.3) '@vuepress/client': 2.0.0-rc.0(typescript@5.3.3)
'@vuepress/markdown': 2.0.0-rc.0 '@vuepress/markdown': 2.0.0-rc.0(patch_hash=zvwmijlesxcdy3i4trojywcm7y)
'@vuepress/shared': 2.0.0-rc.0 '@vuepress/shared': 2.0.0-rc.0
'@vuepress/utils': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0
vue: 3.4.7(typescript@5.3.3) vue: 3.4.7(typescript@5.3.3)
@ -4990,7 +4985,7 @@ packages:
- typescript - typescript
dev: false dev: false
/@vuepress/markdown@2.0.0-rc.0: /@vuepress/markdown@2.0.0-rc.0(patch_hash=zvwmijlesxcdy3i4trojywcm7y):
resolution: {integrity: sha512-USmqdKKMT6ZFHYRztTjKUlO8qgGfnEygMAAq4AzC/uYXiEfrbMBLAWJhteyGS56P3rGLj0OPAhksE681bX/wOg==} resolution: {integrity: sha512-USmqdKKMT6ZFHYRztTjKUlO8qgGfnEygMAAq4AzC/uYXiEfrbMBLAWJhteyGS56P3rGLj0OPAhksE681bX/wOg==}
dependencies: dependencies:
'@mdit-vue/plugin-component': 1.0.0 '@mdit-vue/plugin-component': 1.0.0
@ -5012,6 +5007,7 @@ packages:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: false dev: false
patched: true
/@vuepress/plugin-active-header-links@2.0.0-rc.0(typescript@5.3.3): /@vuepress/plugin-active-header-links@2.0.0-rc.0(typescript@5.3.3):
resolution: {integrity: sha512-UJdXLYNGL5Wjy5YGY8M2QgqT75bZ95EHebbqGi8twBdIJE9O+bM+dPJyYtAk2PIVqFORiw3Hj+PchsNSxdn9+g==} resolution: {integrity: sha512-UJdXLYNGL5Wjy5YGY8M2QgqT75bZ95EHebbqGi8twBdIJE9O+bM+dPJyYtAk2PIVqFORiw3Hj+PchsNSxdn9+g==}
@ -5033,7 +5029,7 @@ packages:
dependencies: dependencies:
'@types/markdown-it': 13.0.7 '@types/markdown-it': 13.0.7
'@vuepress/core': 2.0.0-rc.0(typescript@5.3.3) '@vuepress/core': 2.0.0-rc.0(typescript@5.3.3)
'@vuepress/markdown': 2.0.0-rc.0 '@vuepress/markdown': 2.0.0-rc.0(patch_hash=zvwmijlesxcdy3i4trojywcm7y)
'@vuepress/shared': 2.0.0-rc.0 '@vuepress/shared': 2.0.0-rc.0
'@vuepress/utils': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0
markdown-it: 13.0.2 markdown-it: 13.0.2
@ -5074,7 +5070,7 @@ packages:
dependencies: dependencies:
'@vuepress/client': 2.0.0-rc.0(typescript@5.3.3) '@vuepress/client': 2.0.0-rc.0(typescript@5.3.3)
'@vuepress/core': 2.0.0-rc.0(typescript@5.3.3) '@vuepress/core': 2.0.0-rc.0(typescript@5.3.3)
'@vuepress/markdown': 2.0.0-rc.0 '@vuepress/markdown': 2.0.0-rc.0(patch_hash=zvwmijlesxcdy3i4trojywcm7y)
'@vuepress/shared': 2.0.0-rc.0 '@vuepress/shared': 2.0.0-rc.0
'@vuepress/utils': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0
vue: 3.4.7(typescript@5.3.3) vue: 3.4.7(typescript@5.3.3)
@ -8610,13 +8606,13 @@ packages:
- supports-color - supports-color
dev: true dev: true
/eslint-processor-vue-blocks@0.1.1(@vue/compiler-sfc@3.4.5)(eslint@8.56.0): /eslint-processor-vue-blocks@0.1.1(@vue/compiler-sfc@3.4.7)(eslint@8.56.0):
resolution: {integrity: sha512-9+dU5lU881log570oBwpelaJmOfOzSniben7IWEDRYQPPWwlvaV7NhOtsTuUWDqpYT+dtKKWPsgz4OkOi+aZnA==} resolution: {integrity: sha512-9+dU5lU881log570oBwpelaJmOfOzSniben7IWEDRYQPPWwlvaV7NhOtsTuUWDqpYT+dtKKWPsgz4OkOi+aZnA==}
peerDependencies: peerDependencies:
'@vue/compiler-sfc': ^3.3.0 '@vue/compiler-sfc': ^3.3.0
eslint: ^8.50.0 eslint: ^8.50.0
dependencies: dependencies:
'@vue/compiler-sfc': 3.4.5 '@vue/compiler-sfc': 3.4.7
eslint: 8.56.0 eslint: 8.56.0
dev: true dev: true
@ -11611,6 +11607,11 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
/lz-string@1.5.0:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
hasBin: true
dev: false
/macos-release@3.1.0: /macos-release@3.1.0:
resolution: {integrity: sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==} resolution: {integrity: sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -14365,6 +14366,16 @@ packages:
shikiji: 0.9.18 shikiji: 0.9.18
dev: false dev: false
/shikiji-twoslash@0.9.18(typescript@5.3.3):
resolution: {integrity: sha512-+Np6QAdV244p6CVofefszrRWdcmbUNvTXaGV4V+wjKRyfp8jVTUtxjuVjtG7e3vyA8Nu989K99CCjhcTQOuEaw==}
dependencies:
'@typescript/twoslash': 3.2.4(typescript@5.3.3)
shikiji-core: 0.9.18
transitivePeerDependencies:
- supports-color
- typescript
dev: false
/shikiji@0.9.18: /shikiji@0.9.18:
resolution: {integrity: sha512-/tFMIdV7UQklzN13VjF0/XFzmii6C606Jc878hNezvB8ZR8FG8FW9j0I4J9EJre0owlnPntgLVPpHqy27Gs+DQ==} resolution: {integrity: sha512-/tFMIdV7UQklzN13VjF0/XFzmii6C606Jc878hNezvB8ZR8FG8FW9j0I4J9EJre0owlnPntgLVPpHqy27Gs+DQ==}
dependencies: dependencies:
@ -16031,7 +16042,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/vue-loader@17.3.1(@vue/compiler-sfc@3.4.5)(vue@3.4.7)(webpack@5.89.0): /vue-loader@17.3.1(@vue/compiler-sfc@3.4.7)(vue@3.4.7)(webpack@5.89.0):
resolution: {integrity: sha512-nmVu7KU8geOyzsStyyaxID/uBGDMS8BkPXb6Lu2SNkMawriIbb+hYrNtgftHMKxOSkjjjTF5OSSwPo3KP59egg==} resolution: {integrity: sha512-nmVu7KU8geOyzsStyyaxID/uBGDMS8BkPXb6Lu2SNkMawriIbb+hYrNtgftHMKxOSkjjjTF5OSSwPo3KP59egg==}
peerDependencies: peerDependencies:
'@vue/compiler-sfc': '*' '@vue/compiler-sfc': '*'
@ -16043,7 +16054,7 @@ packages:
vue: vue:
optional: true optional: true
dependencies: dependencies:
'@vue/compiler-sfc': 3.4.5 '@vue/compiler-sfc': 3.4.7
chalk: 4.1.2 chalk: 4.1.2
hash-sum: 2.0.0 hash-sum: 2.0.0
vue: 3.4.7(typescript@5.3.3) vue: 3.4.7(typescript@5.3.3)

View File

@ -6,6 +6,8 @@
@import "utils"; @import "utils";
@import "content"; @import "content";
@import "code"; @import "code";
@import "twoslash";
@import "md-enhance"; @import "md-enhance";
@import "search"; @import "search";
@import "@vuepress/plugin-palette/style"; @import "@vuepress/plugin-palette/style";

View File

@ -0,0 +1,225 @@
/* stylelint-disable no-descending-specificity */
/* ===== Basic ===== */
:root {
--twoslash-border-color: var(--vp-c-divider);
--twoslash-jsdoc-color: #888;
--twoslash-underline-color: currentcolor;
--twoslash-popup-bg: var(--vp-c-neutral-inverse);
--twoslash-popup-shadow: var(--vp-shadow-2);
--twoslash-matched-color: inherit;
--twoslash-unmatched-color: #888;
--twoslash-cursor-color: #8888;
--twoslash-error-color: var(--vp-c-danger-1);
--twoslash-error-bg: var(--vp-c-danger-soft);
--twoslash-tag-color: var(--vp-c-tip-1);
--twoslash-tag-bg: var(--vp-c-tip-soft);
--twoslash-tag-warn-color: var(--vp-c-warning-1);
--twoslash-tag-warn-bg: var(--vp-c-warning-soft);
--twoslash-tag-annotate-color: var(--vp-c-green-1);
--twoslash-tag-annotate-bg: var(--vp-c-green-soft);
}
div[class*="language-"].line-numbers-mode:has(> .twoslash) {
.line-numbers {
display: none;
}
pre {
padding-left: 1.5rem;
margin-left: 0;
}
}
/* 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;
}
.twoslash .twoslash-popup-info {
position: absolute;
z-index: 10;
display: inline-block;
padding: 4px 6px;
text-align: left;
pointer-events: none;
user-select: none;
background: var(--twoslash-popup-bg);
border: 1px solid var(--twoslash-border-color);
border-radius: 4px;
box-shadow: var(--twoslash-popup-shadow);
opacity: 0;
transition: opacity 0.3s;
transform: translateY(1.5em);
}
.twoslash .twoslash-query-presisted .twoslash-popup-info {
left: 50%;
z-index: 9;
transform: translate(-1.3em, 1.8em);
}
.twoslash .twoslash-hover:hover .twoslash-popup-info,
.twoslash .twoslash-query-presisted .twoslash-popup-info {
pointer-events: auto;
opacity: 1;
}
.twoslash .twoslash-popup-info:hover {
user-select: auto;
}
.twoslash .twoslash-popup-arrow {
position: absolute;
top: -4px;
left: 1em;
width: 6px;
height: 6px;
pointer-events: none;
background: var(--twoslash-popup-bg);
border-top: 1px solid var(--twoslash-border-color);
border-right: 1px solid var(--twoslash-border-color);
transform: rotate(-45deg);
}
.twoslash .twoslash-popup-jsdoc {
padding-top: 6px;
padding-bottom: 2px;
font-family: sans-serif;
font-size: 0.8em;
color: var(--twoslash-jsdoc-color);
}
/* ===== 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-completions-list {
position: relative;
}
.twoslash .twoslash-completions-list ul {
position: absolute;
top: 0;
left: 0;
z-index: 8;
display: inline-block;
display: flex;
flex-direction: column;
gap: 4px;
width: 240px;
padding: 4px;
margin: 3px 0 0 -1px;
font-size: 0.8rem;
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 .twoslash-completions-list ul:hover {
user-select: auto;
}
.twoslash .twoslash-completions-list ul::before {
position: absolute;
top: -1.6em;
left: -1px;
width: 2px;
height: 1.4em;
content: " ";
background-color: var(--twoslash-cursor-color);
}
.twoslash .twoslash-completions-list ul li {
display: flex;
gap: 0.25em;
align-items: center;
overflow: hidden;
line-height: 1em;
}
.twoslash .twoslash-completions-list ul li span.twoslash-completions-unmatched {
color: var(--twoslash-unmatched-color);
}
.twoslash .twoslash-completions-list ul .deprecated {
text-decoration: line-through;
opacity: 0.5;
}
.twoslash .twoslash-completions-list ul li span.twoslash-completions-matched {
color: var(--twoslash-matched-color);
}
/* Icons */
.twoslash .twoslash-completions-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;
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);
}