Stop auto scrolling when agent responds and let scroll area handle scrolling to bottom (#4257)

This commit is contained in:
Zane
2025-08-22 07:15:09 -07:00
committed by GitHub
parent 7f6177b268
commit 69ad4cad63
3 changed files with 18 additions and 53 deletions
+1 -27
View File
@@ -42,7 +42,7 @@
* while remaining flexible enough to support different UI contexts (Hub vs Pair).
*/
import React, { useEffect, useContext, createContext, useRef, useCallback } from 'react';
import React, { useEffect, useContext, createContext, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import { SearchView } from './conversation/SearchView';
import { AgentHeader } from './AgentHeader';
@@ -163,13 +163,6 @@ function BaseChatContent({
chat,
setChat,
onMessageStreamFinish: () => {
// Auto-scroll to bottom when message stream finishes
setTimeout(() => {
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
}
}, 300);
// Call the original callback if provided
onMessageStreamFinish?.();
},
@@ -303,11 +296,6 @@ function BaseChatContent({
scrollRef.current.scrollToBottom();
}
}, 200);
} else {
// Immediate scroll for regular submit
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
}
}
};
@@ -319,18 +307,6 @@ function BaseChatContent({
}
append(text);
};
// Callback to handle scroll to bottom from ProgressiveMessageList
const handleScrollToBottom = useCallback(() => {
// Only auto-scroll if user is not actively typing
const isUserTyping = document.activeElement?.id === 'dynamic-textarea';
if (!isUserTyping) {
setTimeout(() => {
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
}
}, 100);
}
}, []);
// Listen for global scroll-to-bottom requests (e.g., from MCP UI prompt actions)
useEffect(() => {
@@ -433,7 +409,6 @@ function BaseChatContent({
setMessages(updatedMessages);
}}
isUserMessage={isUserMessage}
onScrollToBottom={handleScrollToBottom}
isStreamingMessage={chatState !== ChatState.Idle}
onMessageUpdate={onMessageUpdate}
/>
@@ -450,7 +425,6 @@ function BaseChatContent({
setMessages(updatedMessages);
}}
isUserMessage={isUserMessage}
onScrollToBottom={handleScrollToBottom}
isStreamingMessage={chatState !== ChatState.Idle}
onMessageUpdate={onMessageUpdate}
/>
@@ -30,7 +30,6 @@ interface ProgressiveMessageListProps {
append?: (value: string) => void; // Make optional
appendMessage?: (message: Message) => void; // Make optional
isUserMessage: (message: Message) => boolean;
onScrollToBottom?: () => void;
batchSize?: number;
batchDelay?: number;
showLoadingThreshold?: number; // Only show loading if more than X messages
@@ -47,7 +46,6 @@ export default function ProgressiveMessageList({
append = () => {},
appendMessage = () => {},
isUserMessage,
onScrollToBottom,
batchSize = 20,
batchDelay = 20,
showLoadingThreshold = 50,
@@ -99,10 +97,6 @@ export default function ProgressiveMessageList({
if (nextCount >= messages.length) {
setIsLoading(false);
// Trigger scroll to bottom
window.setTimeout(() => {
onScrollToBottom?.();
}, 100);
} else {
// Schedule next batch
timeoutRef.current = window.setTimeout(loadNextBatch, batchDelay);
@@ -121,14 +115,7 @@ export default function ProgressiveMessageList({
timeoutRef.current = null;
}
};
}, [
messages.length,
batchSize,
batchDelay,
showLoadingThreshold,
onScrollToBottom,
renderedCount,
]);
}, [messages.length, batchSize, batchDelay, showLoadingThreshold, renderedCount]);
// Cleanup on unmount
useEffect(() => {
@@ -203,9 +190,6 @@ export default function ProgressiveMessageList({
chatId={chat.id}
workingDir={window.appConfig.get('GOOSE_WORKING_DIR') as string}
contextType={getContextHandlerType!(message)}
onSummaryComplete={() => {
window.setTimeout(() => onScrollToBottom?.(), 100);
}}
/>
) : (
!hasOnlyToolResponses(message) && (
@@ -222,9 +206,6 @@ export default function ProgressiveMessageList({
chatId={chat.id}
workingDir={window.appConfig.get('GOOSE_WORKING_DIR') as string}
contextType={getContextHandlerType!(message)}
onSummaryComplete={() => {
window.setTimeout(() => onScrollToBottom?.(), 100);
}}
/>
) : (
<GooseMessage
@@ -265,7 +246,6 @@ export default function ProgressiveMessageList({
onMessageUpdate,
hasContextHandlerContent,
getContextHandlerType,
onScrollToBottom,
]);
return (
+16 -5
View File
@@ -67,17 +67,28 @@ const ScrollArea = React.forwardRef<ScrollAreaHandle, ScrollAreaProps>(
const { scrollHeight, scrollTop, clientHeight } = viewport;
const scrollBottom = scrollTop + clientHeight;
// Allow a small tolerance (2px) for rounding errors
const isAtBottom = scrollHeight - scrollBottom <= 2;
const isAtBottom = scrollHeight - scrollBottom <= 10;
setIsFollowing(isAtBottom);
setIsScrolled(scrollTop > 0);
}, []);
React.useEffect(() => {
if (!autoScroll || !isFollowing) return;
// Track previous scroll height to detect content changes
const prevScrollHeightRef = React.useRef<number>(0);
scrollToBottom();
React.useEffect(() => {
if (!autoScroll || !isFollowing || !viewportRef.current) return;
const viewport = viewportRef.current;
const currentScrollHeight = viewport.scrollHeight;
// Only auto-scroll if content has actually grown (new content added)
// and we were already following (at the bottom)
if (currentScrollHeight > prevScrollHeightRef.current) {
scrollToBottom();
}
prevScrollHeightRef.current = currentScrollHeight;
}, [children, autoScroll, isFollowing, scrollToBottom]);
// Add scroll event listener