History

Defer chat auto-scroll to animation frame

- Queue tail-to-bottom scrolling with `requestAnimationFrame` to avoid redundant scroll calls - Preserve user scroll position by checking auto-scroll intent before running - Cancel any pending frame on unmount to prevent stale callbacks

Julius Marminge committed 8 months ago

Commit 01accc01 parent 0d9c2681 file · +19 −2Browse files

Unified · Split

apps/web/src/components/ChatView.tsx+19−2
@@ -402,6 +402,7 @@ export default function ChatView({ threadId }: ChatViewProps) { · 401 unmodified lines
402402 >(() => readLastInvokedScriptByProjectFromStorage());
403403 const messagesScrollRef = useRef<HTMLDivElement>(null);
404404 const shouldAutoScrollRef = useRef(true);
405+ const pendingAutoScrollFrameRef = useRef<number | null>(null);
405406 const textareaRef = useRef<HTMLTextAreaElement>(null);
406407 const composerCommandInputRef = useRef<HTMLInputElement>(null);
407408 const composerImagesRef = useRef<ComposerImageAttachment[]>([]);
@@ -1102,6 +1103,14 @@ export default function ChatView({ threadId }: ChatViewProps) { · 694 unmodified lines
11021103 scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior });
11031104 shouldAutoScrollRef.current = true;
11041105 }, []);
1106+ const requestAutoTailToBottom = useCallback(() => {
1107+ if (pendingAutoScrollFrameRef.current !== null) return;
1108+ pendingAutoScrollFrameRef.current = window.requestAnimationFrame(() => {
1109+ pendingAutoScrollFrameRef.current = null;
1110+ if (!shouldAutoScrollRef.current) return;
1111+ scrollMessagesToBottom();
1112+ });
1113+ }, [scrollMessagesToBottom]);
11051114 const onMessagesScroll = useCallback(() => {
11061115 const scrollContainer = messagesScrollRef.current;
11071116 if (!scrollContainer) return;
@@ -1113,8 +1122,16 @@ export default function ChatView({ threadId }: ChatViewProps) { · 5 unmodified lines
11131122 }, [activeThread?.id, scrollMessagesToBottom]);
11141123 useLayoutEffect(() => {
11151124 if (!shouldAutoScrollRef.current) return;
1116− scrollMessagesToBottom();
1117− }, [activeThread?.messages, workLogCount, scrollMessagesToBottom]);
1125+ requestAutoTailToBottom();
1126+ }, [activeThread?.messages, workLogCount, requestAutoTailToBottom]);
1127+ useEffect(
1128+ () => () => {
1129+ if (pendingAutoScrollFrameRef.current === null) return;
1130+ window.cancelAnimationFrame(pendingAutoScrollFrameRef.current);
1131+ pendingAutoScrollFrameRef.current = null;
1132+ },
1133+ [],
1134+ );
11181135
11191136 useEffect(() => {
11201137 setExpandedWorkGroups({});