perf: migrate chat rendering to virtua (#1651)

* refactor: migrate chat history virtualization to virtua

* refactor: render loaded chat history directly

* refactor: finish virtua migration

* perf: defer tool body rendering

* perf: queue deferred tool body mounts

* perf: quiet and defer markdown file probes

* perf: defer markdown code highlighting

* perf: stabilize markdown plugin lists

* perf: defer mermaid markdown rendering

* perf: delay markdown file reference annotation

* perf: attach markdown table listeners on demand

* perf: trim markdown render overhead
This commit is contained in:
Bohdan Triapitsyn
2026-06-15 03:29:40 +03:00
committed by GitHub
parent e372c8d8cb
commit a45376d585
24 changed files with 621 additions and 671 deletions
@@ -3,14 +3,14 @@
*
* Replaces per-line <SyntaxHighlighter> with:
* 1. ONE Prism.highlight() call to tokenize all code at once
* 2. @tanstack/react-virtual to only render visible rows
* 2. virtua to only render visible rows
*
* This drops mount cost from O(N * Prism) to O(1 * Prism) + O(visible_rows).
* For a 2000-line file, ~2000 SyntaxHighlighter instances → ~30 plain <div>s.
*/
import React from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { Virtualizer } from 'virtua';
import Prism from 'prismjs';
// Ensure common languages are loaded (react-syntax-highlighter lazy-loads them,
@@ -244,52 +244,31 @@ const VirtualizedRows: React.FC<VirtualizedRowsProps> = React.memo(({
lineStyles,
}) => {
const parentRef = React.useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: lines.length,
getScrollElement: () => parentRef.current,
estimateSize: () => ROW_HEIGHT,
overscan: 20, // render 20 extra rows above/below viewport
});
const viewportHeight = `min(${lines.length * ROW_HEIGHT}px, ${maxHeight})`;
return (
<div
ref={parentRef}
className="typography-code font-mono w-full min-w-0 oc-virtualized-prism"
style={{ maxHeight, overflow: 'auto' }}
style={{ height: viewportHeight, maxHeight, overflow: 'auto' }}
>
{prismThemeCss ? <style>{prismThemeCss}</style> : null}
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
<Virtualizer
data={lines}
itemSize={ROW_HEIGHT}
bufferSize={ROW_HEIGHT * 20}
scrollRef={parentRef}
>
{virtualizer.getVirtualItems().map((vItem) => {
const line = lines[vItem.index];
return (
<div
key={vItem.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${vItem.size}px`,
transform: `translateY(${vItem.start}px)`,
}}
>
<Row
line={line}
language={language}
showLineNumbers={showLineNumbers}
style={lineStyles?.(line)}
/>
</div>
);
})}
</div>
{(line, index) => (
<Row
key={index}
line={line}
language={language}
showLineNumbers={showLineNumbers}
style={lineStyles?.(line)}
/>
)}
</Virtualizer>
</div>
);
});