diff --git a/apps/desktop/src/app/right-sidebar/review/index.tsx b/apps/desktop/src/app/right-sidebar/review/index.tsx
index 2e50a1f874f5..9fc203c81674 100644
--- a/apps/desktop/src/app/right-sidebar/review/index.tsx
+++ b/apps/desktop/src/app/right-sidebar/review/index.tsx
@@ -202,7 +202,7 @@ export function ReviewPane() {
) : null
) : diff ? (
-
+
) : (
{c.noDiff}
)}
diff --git a/apps/desktop/src/components/chat/diff-lines.tsx b/apps/desktop/src/components/chat/diff-lines.tsx
index eef495c7c54c..edcf08e38f70 100644
--- a/apps/desktop/src/components/chat/diff-lines.tsx
+++ b/apps/desktop/src/components/chat/diff-lines.tsx
@@ -559,9 +559,20 @@ interface FileDiffPanelProps {
/** Render an old/new line-number gutter (the full preview diff). The compact
* tool-card + inline review diff leave this off. */
showLineNumbers?: boolean
+ /** Window the rows (fixed-row virtualization) WITHOUT a gutter — for a large
+ * diff in a scrolling pane (the review panel), so only visible rows mount
+ * instead of highlighting every line. `showLineNumbers` implies windowing. */
+ virtualized?: boolean
}
-export function FileDiffPanel({ className, diff, fullText, path, showLineNumbers = false }: FileDiffPanelProps) {
+export function FileDiffPanel({
+ className,
+ diff,
+ fullText,
+ path,
+ showLineNumbers = false,
+ virtualized = false
+}: FileDiffPanelProps) {
const lines = React.useMemo(
() => (fullText != null ? parseFullFileDiff(diff, fullText) : parseDiff(diff)),
[diff, fullText]
@@ -580,66 +591,79 @@ export function FileDiffPanel({ className, diff, fullText, path, showLineNumbers
const language = shikiLanguageForFilename(path)
const canHighlight = Boolean(language) && !exceedsHighlightBudget(fullText ?? diff)
+ const windowed = showLineNumbers || virtualized
- // Full-file preview: we own the rows (tokens rendered inside) so blank lines
- // can't collapse. Compact tool/review diffs let Shiki own the rows.
- const body = !canHighlight ? (
- showLineNumbers ? (
-
- ) : (
-
- )
- ) : fullText != null ? (
+ // Windowed: we own fixed-height rows and render only the visible chunks, so a
+ // large diff never mounts (or Shiki-highlights) every line. Compact tool cards
+ // are small/clamped, so they let Shiki own the rows (SyntaxDiff).
+ const windowedBody = canHighlight ? (
+ ) : (
+
+ )
+
+ const compactBody = !canHighlight ? (
+
+ ) : fullText != null ? (
+
) : (
)
- if (!showLineNumbers) {
+ if (!windowed) {
return (
- {body}
+ {compactBody}
)
}
- // A single line-number gutter (VS Code's inline-diff style): each row shows its
- // own file's number — the new number for context/adds, the old number for
- // removals — with an overview ruler pinned to the right edge. The inner div
- // owns the scroll so the ruler (an absolute sibling) stays viewport-fixed.
+ // Windowed: a fixed-row scroller renders only the visible rows (killing the
+ // full-Shiki-of-every-line freeze on large diffs). With `showLineNumbers` a
+ // VS Code-style gutter (new number for context/adds, old for removals) sits in
+ // a left column; the scroller owns scroll so the overview ruler (an absolute
+ // sibling) stays viewport-fixed.
return (
-
-
-
- {beforeRows > 0 &&
}
- {visibleLineChunks.map(chunk => (
-
- {chunk.lines.map((line, offset) => {
- const index = chunk.start + offset
+
+ {showLineNumbers ? (
+
+
+ {beforeRows > 0 &&
}
+ {visibleLineChunks.map(chunk => (
+
+ {chunk.lines.map((line, offset) => {
+ const index = chunk.start + offset
- return (
-
- {line.newNo ?? ''}
-
- )
- })}
-
- ))}
- {afterRows > 0 &&
}
+ return (
+
+ {line.newNo ?? ''}
+
+ )
+ })}
+
+ ))}
+ {afterRows > 0 &&
}
+
+
{windowedBody}
-
{body}
-
+ ) : (
+
{windowedBody}
+ )}