perf: shiki code block (#8763)

* perf: inlining completeLineTokens and use memo for minor improvements

* chore: bump shiki to 3.9.1

* refactor: improve token line

* refactor: add plainTokenStyle
This commit is contained in:
one 2025-08-01 20:13:37 +08:00 committed by GitHub
parent 3010f20d13
commit 43dc1e06e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 86 deletions

View File

@ -129,7 +129,7 @@
"@opentelemetry/sdk-trace-web": "^2.0.0",
"@playwright/test": "^1.52.0",
"@reduxjs/toolkit": "^2.2.5",
"@shikijs/markdown-it": "^3.7.0",
"@shikijs/markdown-it": "^3.9.1",
"@swc/plugin-styled-components": "^7.1.5",
"@tanstack/react-query": "^5.27.0",
"@tanstack/react-virtual": "^3.13.12",
@ -249,7 +249,7 @@
"remove-markdown": "^0.6.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.88.0",
"shiki": "^3.7.0",
"shiki": "^3.9.1",
"strict-url-sanitise": "^0.0.1",
"string-width": "^7.2.0",
"styled-components": "^6.1.11",

View File

@ -189,44 +189,12 @@ const CodePreview = ({ children, language, setTools }: CodePreviewProps) => {
CodePreview.displayName = 'CodePreview'
/**
* tokens
*/
function completeLineTokens(themedTokens: ThemedToken[], rawLine: string): ThemedToken[] {
// 如果出现空行,补一个空格保证行高
if (rawLine.length === 0) {
return [
{
content: ' ',
offset: 0,
color: 'inherit',
bgColor: 'inherit',
htmlStyle: {
opacity: '0.35'
}
}
]
const plainTokenStyle = {
color: 'inherit',
bgColor: 'inherit',
htmlStyle: {
opacity: '0.35'
}
const themedContent = themedTokens.map((token) => token.content).join('')
const extraContent = rawLine.slice(themedContent.length)
// 已有内容已经全部高亮,直接返回
if (!extraContent) return themedTokens
// 补全剩余内容
return [
...themedTokens,
{
content: extraContent,
offset: themedContent.length,
color: 'inherit',
bgColor: 'inherit',
htmlStyle: {
opacity: '0.35'
}
}
]
}
interface VirtualizedRowData {
@ -240,11 +208,43 @@ interface VirtualizedRowData {
*/
const VirtualizedRow = memo(
({ rawLine, tokenLine, showLineNumbers, index }: VirtualizedRowData & { index: number }) => {
// 补全代码行 tokens把原始内容拼接到高亮内容之后确保渲染出整行来。
const completeTokenLine = useMemo(() => {
// 如果出现空行,补一个空元素保证行高
if (rawLine.length === 0) {
return [
{
content: '',
offset: 0,
...plainTokenStyle
}
]
}
const currentTokens = tokenLine ?? []
const themedContentLength = currentTokens.reduce((acc, token) => acc + token.content.length, 0)
// 已有内容已经全部高亮,直接返回
if (themedContentLength >= rawLine.length) {
return currentTokens
}
// 补全剩余内容
return [
...currentTokens,
{
content: rawLine.slice(themedContentLength),
offset: themedContentLength,
...plainTokenStyle
}
]
}, [rawLine, tokenLine])
return (
<div className="line">
{showLineNumbers && <span className="line-number">{index + 1}</span>}
<span className="line-content">
{completeLineTokens(tokenLine ?? [], rawLine).map((token, tokenIndex) => (
{completeTokenLine.map((token, tokenIndex) => (
<span key={tokenIndex} style={getReactStyleFromToken(token)}>
{token.content}
</span>
@ -272,6 +272,7 @@ const ScrollContainer = styled.div<{
align-items: flex-start;
width: 100%;
line-height: ${(props) => props.$lineHeight}px;
contain: content;
.line-number {
width: var(--gutter-width, 1.2ch);

View File

@ -4923,79 +4923,79 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/core@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/core@npm:3.7.0"
"@shikijs/core@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/core@npm:3.9.1"
dependencies:
"@shikijs/types": "npm:3.7.0"
"@shikijs/types": "npm:3.9.1"
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
hast-util-to-html: "npm:^9.0.5"
checksum: 10c0/885c9d00712d350ab0aac0d239b26d8ba045f075abbaee5eda33c4a39fe841a1a2b0ca34391a3704cde0edcf1468ca583bea25fcbd37b7b8d6189b7afcf2a55d
checksum: 10c0/2267cb9b056f29d93d60b5591340161db614719f1cee8e0050af8ca048eb8ee32bac51fcfe536de65dcaeadae8697fba1157c178803daae33771a2baf6bf9672
languageName: node
linkType: hard
"@shikijs/engine-javascript@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/engine-javascript@npm:3.7.0"
"@shikijs/engine-javascript@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/engine-javascript@npm:3.9.1"
dependencies:
"@shikijs/types": "npm:3.7.0"
"@shikijs/types": "npm:3.9.1"
"@shikijs/vscode-textmate": "npm:^10.0.2"
oniguruma-to-es: "npm:^4.3.3"
checksum: 10c0/ac792fe99a2007ab076856d32d4934e191d3b03f8bd416e96f461821580c223da73de8abc199e5eceb8b56fd47e992824b2571270e7c3d55efa6b9a6d87fec80
checksum: 10c0/9d5e5e0fde46c9fc3813363f61b75cee9b06df10a676609b2006df344123993af94444f7564e44adb877c8299a33fa144c0bf35688370d0a70077249c2a5836b
languageName: node
linkType: hard
"@shikijs/engine-oniguruma@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/engine-oniguruma@npm:3.7.0"
"@shikijs/engine-oniguruma@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/engine-oniguruma@npm:3.9.1"
dependencies:
"@shikijs/types": "npm:3.7.0"
"@shikijs/types": "npm:3.9.1"
"@shikijs/vscode-textmate": "npm:^10.0.2"
checksum: 10c0/e1ec52ec2255e3330812084d62bde8853d20162b1cd285dbb63440d63d0b16c03b6ce6983982e41ac2fc2eceb3e2f6b2bc1c627d093482c4c3836c4fbb9567b0
checksum: 10c0/70eb64cccb043d01f82804a0c630ce1861ab9cb0f79eca31ea550c1f9c6e7de2f37094c4c28f0fca81b26d78b77287d11c110809e7f76a59829c443abd88ef2c
languageName: node
linkType: hard
"@shikijs/langs@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/langs@npm:3.7.0"
"@shikijs/langs@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/langs@npm:3.9.1"
dependencies:
"@shikijs/types": "npm:3.7.0"
checksum: 10c0/326e8b014e74d25ce84a63bf7fdd47d5582f85c8404d4c48d6bdacf2f32ab92ddb39b41710ee7eff3daaecbbea7ee96a6c49d427344ee8375551597c74010a81
"@shikijs/types": "npm:3.9.1"
checksum: 10c0/94351ef82e0a7a26351eaf70e33a5c0a48727ef052b907cb3c09ebbd3bb8fb1ef7825ae27c0ff2829888d5fb9da24eeca86c914178c354754eefd7fab70a613f
languageName: node
linkType: hard
"@shikijs/markdown-it@npm:^3.7.0":
version: 3.7.0
resolution: "@shikijs/markdown-it@npm:3.7.0"
"@shikijs/markdown-it@npm:^3.9.1":
version: 3.9.1
resolution: "@shikijs/markdown-it@npm:3.9.1"
dependencies:
markdown-it: "npm:^14.1.0"
shiki: "npm:3.7.0"
shiki: "npm:3.9.1"
peerDependencies:
markdown-it-async: ^2.2.0
peerDependenciesMeta:
markdown-it-async:
optional: true
checksum: 10c0/28d7ccacf241ef9b60080f232ac352694e9f29556e27c4c874226cf222b2deac3edb6f27813f46cb6316fbcaaf1b7880a76fdd6acd6f40a36cde3b3392107449
checksum: 10c0/54b7acbf1e12b8686a71fe22b988e1a1475d70bdca5434824f2cb75efc5fc929d9be793c7118e3d9a112589d39197e954b8d47dddbfc1e6981b05b5b1a28d98a
languageName: node
linkType: hard
"@shikijs/themes@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/themes@npm:3.7.0"
"@shikijs/themes@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/themes@npm:3.9.1"
dependencies:
"@shikijs/types": "npm:3.7.0"
checksum: 10c0/6887eb99b55439988edab21a1af00302eaed6ba0dd7e2bea6c844ff4dfb8879a0c6c2178ba3fcfe2dbf3fd9f3ab6105572c57ae871e147aaceaf53bcc345d0cd
"@shikijs/types": "npm:3.9.1"
checksum: 10c0/a061eec4d9dd147d83cda9c41b296263fab92d6113146279a244751b9f016f8af543f91c37dcefe33f47cff9f1a1d7898f78a80169947ac119617b32d16766d4
languageName: node
linkType: hard
"@shikijs/types@npm:3.7.0":
version: 3.7.0
resolution: "@shikijs/types@npm:3.7.0"
"@shikijs/types@npm:3.9.1":
version: 3.9.1
resolution: "@shikijs/types@npm:3.9.1"
dependencies:
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
checksum: 10c0/d7c4fcca358c0585602090e2b4ed0a3f6742b55bea340030c115cb7aa643eac79836baa095517a538d695415458bb48c08b7be7f3c8d1cf1c1c7749a58913a3f
checksum: 10c0/c726478ae36ca078a8b9d61a9b51b83fe32b7af2cfe7ae597828b2ffccbd24858d955c49d0786af13ebd04cfbb9d192067499c410a05c41eb38da57928424076
languageName: node
linkType: hard
@ -7677,7 +7677,7 @@ __metadata:
"@opentelemetry/sdk-trace-web": "npm:^2.0.0"
"@playwright/test": "npm:^1.52.0"
"@reduxjs/toolkit": "npm:^2.2.5"
"@shikijs/markdown-it": "npm:^3.7.0"
"@shikijs/markdown-it": "npm:^3.9.1"
"@strongtz/win32-arm64-msvc": "npm:^0.4.7"
"@swc/plugin-styled-components": "npm:^7.1.5"
"@tanstack/react-query": "npm:^5.27.0"
@ -7804,7 +7804,7 @@ __metadata:
rollup-plugin-visualizer: "npm:^5.12.0"
sass: "npm:^1.88.0"
selection-hook: "npm:^1.0.8"
shiki: "npm:^3.7.0"
shiki: "npm:^3.9.1"
strict-url-sanitise: "npm:^0.0.1"
string-width: "npm:^7.2.0"
styled-components: "npm:^6.1.11"
@ -19300,19 +19300,19 @@ __metadata:
languageName: node
linkType: hard
"shiki@npm:3.7.0, shiki@npm:^3.7.0":
version: 3.7.0
resolution: "shiki@npm:3.7.0"
"shiki@npm:3.9.1, shiki@npm:^3.9.1":
version: 3.9.1
resolution: "shiki@npm:3.9.1"
dependencies:
"@shikijs/core": "npm:3.7.0"
"@shikijs/engine-javascript": "npm:3.7.0"
"@shikijs/engine-oniguruma": "npm:3.7.0"
"@shikijs/langs": "npm:3.7.0"
"@shikijs/themes": "npm:3.7.0"
"@shikijs/types": "npm:3.7.0"
"@shikijs/core": "npm:3.9.1"
"@shikijs/engine-javascript": "npm:3.9.1"
"@shikijs/engine-oniguruma": "npm:3.9.1"
"@shikijs/langs": "npm:3.9.1"
"@shikijs/themes": "npm:3.9.1"
"@shikijs/types": "npm:3.9.1"
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
checksum: 10c0/a458d06cc0487da5916e10ab9daaf314e3c3c92024664c545bcacb58fad9bd3aa18cde90a200afe4f6632a2487014def57eb6f10f38ab6269e90f3420b724105
checksum: 10c0/383ca4b91b0ade1df7ce8889c4abeb9bfabead53a808f11de749e44f8400b3967d8bad7aad99a8ecf7991a2e1d1c42a71b73154d12baca6deeb979b9929376cb
languageName: node
linkType: hard