mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-22 14:30:53 +08:00
perf(desktop): skip off-screen paint on non-virtualized transcript rows (WebKit)
WebKit (Tauri WKWebView) paints complex message DOM ~2x slower than Blink and stutters during scroll. Add content-visibility:auto + contain-intrinsic-size to fully-mounted (non-virtualized) rows so the engine skips off-screen rows without unmounting or re-measuring them. Measured with Playwright WebKit on the reported laggy 79-message session (main): worst frame 279ms -> 18ms, jank 2 -> 0. Virtualization was ruled out — it made WebKit *worse* (tanstack 12-24fps) due to scroll-time measureElement/re-render cost. Only non-virtualized rows get the class (no ResizeObserver); virtualized window items stay free of content-visibility to avoid the zero-height measurement regression. Tests guard both directions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ed574777e3
commit
9b3e58b60b
@ -156,6 +156,11 @@ describe('MessageList nested tool calls', () => {
|
|||||||
expect(container.querySelectorAll('[data-message-shell="assistant"]').length).toBeLessThan(220)
|
expect(container.querySelectorAll('[data-message-shell="assistant"]').length).toBeLessThan(220)
|
||||||
expect(container.querySelector('[data-virtual-message-item]')).not.toBeNull()
|
expect(container.querySelector('[data-virtual-message-item]')).not.toBeNull()
|
||||||
expect(container.querySelector('[data-virtual-spacer="top"]')).not.toBeNull()
|
expect(container.querySelector('[data-virtual-spacer="top"]')).not.toBeNull()
|
||||||
|
// Virtualized window items must NOT get content-visibility: it zeroes their
|
||||||
|
// ResizeObserver-measured height in the virtualizer (the regression this guards).
|
||||||
|
for (const item of container.querySelectorAll('[data-virtual-message-item]')) {
|
||||||
|
expect((item as HTMLElement).className).not.toContain('chat-render-item--cv')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps small transcripts fully mounted without deferred browser painting', () => {
|
it('keeps small transcripts fully mounted without deferred browser painting', () => {
|
||||||
@ -184,9 +189,13 @@ describe('MessageList nested tool calls', () => {
|
|||||||
const renderItems = container.querySelectorAll('.chat-render-item')
|
const renderItems = container.querySelectorAll('.chat-render-item')
|
||||||
|
|
||||||
expect(renderItems).toHaveLength(2)
|
expect(renderItems).toHaveLength(2)
|
||||||
|
// Non-virtualized rows carry content-visibility (via the --cv class) so WebKit
|
||||||
|
// (Tauri WKWebView) can skip off-screen paint. Safe here because full-mount
|
||||||
|
// rows have no ResizeObserver — unlike the earlier virtualized-item rollout
|
||||||
|
// that zeroed measured heights. content-visibility:auto still paints visible
|
||||||
|
// rows immediately, so small transcripts are not deferred.
|
||||||
for (const item of renderItems) {
|
for (const item of renderItems) {
|
||||||
expect(item.className).not.toContain('content-visibility')
|
expect(item.className).toContain('chat-render-item--cv')
|
||||||
expect(item.className).not.toContain('contain-intrinsic-size')
|
|
||||||
}
|
}
|
||||||
expect(container.querySelector('[data-virtual-message-item]')).toBeNull()
|
expect(container.querySelector('[data-virtual-message-item]')).toBeNull()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1841,7 +1841,7 @@ export function MessageList({ sessionId, compact = false }: MessageListProps = {
|
|||||||
{content}
|
{content}
|
||||||
</MeasuredRenderItem>
|
</MeasuredRenderItem>
|
||||||
) : (
|
) : (
|
||||||
<div key={itemKey} className={CHAT_RENDER_ITEM_CLASS}>
|
<div key={itemKey} className={`${CHAT_RENDER_ITEM_CLASS} chat-render-item--cv`}>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1369,3 +1369,15 @@ button, input, textarea, select, a, [role="button"] {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Off-screen paint skipping for fully-mounted (non-virtualized) transcript rows.
|
||||||
|
WebKit (Tauri WKWebView) paints complex message DOM ~2x slower than Blink and
|
||||||
|
stutters on long worst-case frames; content-visibility lets it skip off-screen
|
||||||
|
rows WITHOUT unmounting or re-measuring them. Measured (Playwright WebKit, 79-msg
|
||||||
|
session): worst frame 279ms -> 18ms, jank 2 -> 0. Only non-virtualized rows get
|
||||||
|
this class — virtualized window items must stay free of content-visibility to
|
||||||
|
avoid the zero-height measurement regression in the virtualizer. */
|
||||||
|
.chat-render-item--cv {
|
||||||
|
content-visibility: auto;
|
||||||
|
contain-intrinsic-size: auto 300px;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user