Fix auto scroll to bottom during chat (#4923)

This commit is contained in:
Zane
2025-10-01 14:23:03 -07:00
committed by GitHub
parent 8cb6eb4185
commit 345cc65bd3
3 changed files with 167 additions and 83 deletions
+20 -52
View File
@@ -117,50 +117,6 @@ function BaseChatContent({
const [currentRecipeTitle, setCurrentRecipeTitle] = React.useState<string | null>(null);
const { isCompacting, handleManualCompaction } = useContextManager();
// Timeout ref for debouncing auto-scroll
const autoScrollTimeoutRef = useRef<number | null>(null);
// Track if user was following when agent started responding
const wasFollowingRef = useRef<boolean>(true);
const isNearBottom = React.useCallback(() => {
if (!scrollRef.current) return false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const viewport = scrollRef.current as any;
if (!viewport.viewportRef?.current) return false;
const viewportElement = viewport.viewportRef.current;
const { scrollHeight, scrollTop, clientHeight } = viewportElement;
const scrollBottom = scrollTop + clientHeight;
const distanceFromBottom = scrollHeight - scrollBottom;
return distanceFromBottom <= 100;
}, []);
// Function to auto-scroll if user was following when agent started
const conditionalAutoScroll = React.useCallback(() => {
// Clear any existing timeout
if (autoScrollTimeoutRef.current) {
clearTimeout(autoScrollTimeoutRef.current);
}
// Debounce the auto-scroll to prevent jumpy behavior and prevent multiple rapid scrolls
autoScrollTimeoutRef.current = window.setTimeout(() => {
// Only auto-scroll if user was following when the agent started responding
if (wasFollowingRef.current && scrollRef.current) {
scrollRef.current.scrollToBottom();
}
}, 150);
}, []);
useEffect(() => {
return () => {
if (autoScrollTimeoutRef.current) {
clearTimeout(autoScrollTimeoutRef.current);
}
};
}, []);
// Use shared chat engine
const {
messages,
@@ -187,14 +143,10 @@ function BaseChatContent({
chat,
setChat,
onMessageStreamFinish: () => {
conditionalAutoScroll();
// Call the original callback if provided
onMessageStreamFinish?.();
},
onMessageSent: () => {
wasFollowingRef.current = isNearBottom();
// Mark that user has started using the recipe
if (recipeConfig) {
setHasStartedUsingRecipe(true);
@@ -275,12 +227,23 @@ function BaseChatContent({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Track if this is the initial render for session resuming
const initialRenderRef = useRef(true);
// Auto-scroll when messages are loaded (for session resuming)
const handleRenderingComplete = React.useCallback(() => {
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
// Only force scroll on the very first render
if (initialRenderRef.current && messages.length > 0) {
initialRenderRef.current = false;
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
}
} else if (scrollRef.current?.isFollowing) {
if (scrollRef.current?.scrollToBottom) {
scrollRef.current.scrollToBottom();
}
}
}, []);
}, [messages.length]);
// Handle submit
const handleSubmit = (e: React.FormEvent) => {
@@ -441,7 +404,12 @@ function BaseChatContent({
onClick={async () => {
clearError();
await handleManualCompaction(messages, setMessages, append, chat.sessionId);
await handleManualCompaction(
messages,
setMessages,
append,
chat.sessionId
);
}}
>
Summarize Conversation