diff --git a/src/renderer/src/components/CodeBlockView/CodePreview.tsx b/src/renderer/src/components/CodeBlockView/CodePreview.tsx
index 3a95a68cd8..3df9491e13 100644
--- a/src/renderer/src/components/CodeBlockView/CodePreview.tsx
+++ b/src/renderer/src/components/CodeBlockView/CodePreview.tsx
@@ -9,6 +9,7 @@ import { debounce } from 'lodash'
import { ChevronsDownUp, ChevronsUpDown, Text as UnWrapIcon, WrapText as WrapIcon } from 'lucide-react'
import React, { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { ThemedToken } from 'shiki/core'
import styled from 'styled-components'
interface CodePreviewProps {
@@ -150,7 +151,8 @@ const CodePreview = ({ children, language, setTools }: CodePreviewProps) => {
{
'--gutter-width': `${gutterDigits}ch`,
fontSize: `${fontSize - 1}px`,
- maxHeight: shouldCollapse ? MAX_COLLAPSE_HEIGHT : undefined
+ maxHeight: shouldCollapse ? MAX_COLLAPSE_HEIGHT : undefined,
+ overflowY: shouldCollapse ? 'auto' : 'hidden'
} as React.CSSProperties
}>
{
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 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 {
rawLine: string
- tokenLine?: any[]
+ tokenLine?: ThemedToken[]
showLineNumbers: boolean
}
@@ -210,17 +252,11 @@ const VirtualizedRow = memo(
{showLineNumbers && {index + 1}}
- {tokenLine ? (
- // 渲染高亮后的内容
- tokenLine.map((token, tokenIndex) => (
-
- {token.content}
-
- ))
- ) : (
- // 渲染原始内容
- {rawLine || ' '}
- )}
+ {completeLineTokens(tokenLine ?? [], rawLine).map((token, tokenIndex) => (
+
+ {token.content}
+
+ ))}
)
@@ -234,7 +270,7 @@ const ScrollContainer = styled.div<{
$lineHeight?: number
}>`
display: block;
- overflow: auto;
+ overflow-x: auto;
position: relative;
border-radius: inherit;
padding: 0.5em 1em;
@@ -264,10 +300,6 @@ const ScrollContainer = styled.div<{
overflow-wrap: ${(props) => (props.$wrap ? 'break-word' : 'normal')};
}
}
-
- .line-content-raw {
- opacity: 0.35;
- }
}
`