fix: prevent blank chat viewport when switching sessions (#1553)
When switching to a session with long context (especially in Electron desktop when changing servers), the chat viewport could render blank until the user scrolled. Two interacting issues caused this: 1. historyVirtualRows memo never recomputed after the first render because historyVirtualizer (from useVirtualizer's useState) is a stable reference. Frozen range meant items rendered at the top while paddingBottom filled the visible viewport after scrolling. 2. pendingInitialRestoreRef replay ran in useEffect (after paint), showing a frame at scrollTop:0 with the stale virtualizer range. Fixed by: adding a virtualVersion counter driven by useVirtualizer's onChange to bust the memo; switching the replay to useLayoutEffect so scroll position is set before the browser paints.
This commit is contained in:
@@ -1372,6 +1372,8 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
scrollEl.scrollTop += prependedHeight;
|
||||
});
|
||||
|
||||
const [virtualVersion, bumpVirtualVersion] = React.useReducer((v: number) => v + 1, 0);
|
||||
|
||||
const historyVirtualizer = useVirtualizer({
|
||||
count: historyEntries.length,
|
||||
getScrollElement: resolveScrollContainer,
|
||||
@@ -1381,6 +1383,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
useAnimationFrameWithResizeObserver: true,
|
||||
overscan: MESSAGE_LIST_OVERSCAN,
|
||||
enabled: shouldVirtualizeHistory,
|
||||
onChange: bumpVirtualVersion,
|
||||
});
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
@@ -1437,7 +1440,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
|
||||
const historyVirtualRows = React.useMemo(
|
||||
() => (shouldVirtualizeHistory ? historyVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ROWS),
|
||||
[historyVirtualizer, shouldVirtualizeHistory],
|
||||
[historyVirtualizer, shouldVirtualizeHistory, virtualVersion],
|
||||
);
|
||||
|
||||
const allEntries = React.useMemo(() => {
|
||||
|
||||
@@ -411,7 +411,9 @@ export const useChatAutoFollow = ({
|
||||
}, [sessionIsWorking, startFollowLoop]);
|
||||
|
||||
// Replay a deferred restoreSnapshot once ChatViewport mounts.
|
||||
React.useEffect(() => {
|
||||
// useLayoutEffect ensures scroll position is set before the browser paints,
|
||||
// preventing a visible flash of content at the wrong scroll position.
|
||||
React.useLayoutEffect(() => {
|
||||
if (!containerEl) return;
|
||||
if (pendingInitialRestoreRef.current && pendingInitialRestoreRef.current === currentSessionId) {
|
||||
void restoreSnapshot();
|
||||
|
||||
Reference in New Issue
Block a user