perf(ui): enable session timeline virtualization and defer heavy tool content
Virtualize message timeline at 5+ turns. Stabilize virtualizer keys, add prepend-aware scroll compensation for "Load older", and defer expanded tool bodies by one animation frame. Tighten initial turn window to 7 turns for faster session open.
This commit is contained in:
@@ -247,32 +247,6 @@ export const useChatTimelineController = ({
|
||||
anchor: ViewportAnchor | null;
|
||||
} | null>(null);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const snap = prePrependScrollRef.current;
|
||||
const container = scrollRef.current;
|
||||
if (!snap || !container) return;
|
||||
prePrependScrollRef.current = null;
|
||||
|
||||
// Try anchor-based restoration first (pixel-perfect)
|
||||
if (snap.anchor) {
|
||||
const anchorEl = container.querySelector<HTMLElement>(
|
||||
`[data-message-id="${snap.anchor.messageId}"]`,
|
||||
);
|
||||
if (anchorEl) {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const anchorTop = anchorEl.getBoundingClientRect().top - containerRect.top;
|
||||
container.scrollTop += anchorTop - snap.anchor.offsetTop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: height-delta compensation
|
||||
const delta = container.scrollHeight - snap.height;
|
||||
if (delta > 0) {
|
||||
container.scrollTop = snap.top + delta;
|
||||
}
|
||||
}, [renderedMessages, scrollRef]);
|
||||
|
||||
const captureViewportAnchor = React.useCallback((): ViewportAnchor | null => {
|
||||
return messageListRef.current?.captureViewportAnchor() ?? null;
|
||||
}, [messageListRef]);
|
||||
@@ -281,6 +255,26 @@ export const useChatTimelineController = ({
|
||||
return messageListRef.current?.restoreViewportAnchor(anchor) ?? false;
|
||||
}, [messageListRef]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const snap = prePrependScrollRef.current;
|
||||
const container = scrollRef.current;
|
||||
if (!snap || !container) return;
|
||||
prePrependScrollRef.current = null;
|
||||
|
||||
// When a viewport anchor is available, delegate to MessageList
|
||||
// restoreViewportAnchor which falls back to virtualizer-aware
|
||||
// scrollHistoryIndexIntoView when the element is not in the DOM.
|
||||
if (snap.anchor && restoreViewportAnchor(snap.anchor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: height-delta compensation
|
||||
const delta = container.scrollHeight - snap.height;
|
||||
if (delta > 0) {
|
||||
container.scrollTop = snap.top + delta;
|
||||
}
|
||||
}, [renderedMessages, scrollRef, restoreViewportAnchor]);
|
||||
|
||||
const revealBufferedTurns = React.useCallback(async (): Promise<boolean> => {
|
||||
if (turnStartRef.current <= 0 || pendingRevealWorkRef.current) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user