mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
Stop auto scrolling when agent responds and let scroll area handle scrolling to bottom (#4257)
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user