Fix auto-scroll to only trigger during streaming (#1593)

Previously, the chat auto-scrolled whenever messages changed, even when
not streaming. This caused unwanted scrolling when switching chats or
loading messages from the database.

Now auto-scroll only triggers when:
- Messages are actively streaming (isStreaming is true)
- User hasn't manually scrolled away
- User is near the bottom of the chat

Changes:
- Added isStreamingByIdAtom to track streaming state
- Modified auto-scroll useEffect to check isStreaming before scrolling
- Preserved streamCount effect for initial scroll on stream start

🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
<!-- This is an auto-generated description by cubic. -->
---

## Summary by cubic
Fixes chat auto-scroll so it only runs during active streaming,
preventing jumps when switching chats or loading message history.

- **Bug Fixes**
- Added isStreamingByIdAtom and gated the auto-scroll effect by per-chat
streaming state (updated effect deps).
  - Preserved the initial scroll on stream start via streamCount.

<!-- End of auto-generated description by cubic. -->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Limits chat auto-scroll to when a conversation is actively streaming
and the user is near the bottom.
> 
> - **Frontend**
> - **Chat auto-scroll behavior**: Only auto-scrolls during active
streaming.
> - Adds `isStreamingByIdAtom` and reads per-chat `isStreaming` state.
> - Updates auto-scroll `useEffect` to require `isStreaming` and adds it
to dependencies.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6580601cd974c2c01ddffdef78ed0ddbb2b2fa8d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Will Chen
2025-10-20 15:11:46 -07:00
committed by GitHub
parent 431b247eb2
commit 55d37b5ab9

View File

@@ -3,6 +3,7 @@ import { useAtomValue, useSetAtom } from "jotai";
import { import {
chatMessagesByIdAtom, chatMessagesByIdAtom,
chatStreamCountByIdAtom, chatStreamCountByIdAtom,
isStreamingByIdAtom,
} from "../atoms/chatAtoms"; } from "../atoms/chatAtoms";
import { IpcClient } from "@/ipc/ipc_client"; import { IpcClient } from "@/ipc/ipc_client";
@@ -30,6 +31,7 @@ export function ChatPanel({
const [isVersionPaneOpen, setIsVersionPaneOpen] = useState(false); const [isVersionPaneOpen, setIsVersionPaneOpen] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const streamCountById = useAtomValue(chatStreamCountByIdAtom); const streamCountById = useAtomValue(chatStreamCountByIdAtom);
const isStreamingById = useAtomValue(isStreamingByIdAtom);
// Reference to store the processed prompt so we don't submit it twice // Reference to store the processed prompt so we don't submit it twice
const messagesEndRef = useRef<HTMLDivElement | null>(null); const messagesEndRef = useRef<HTMLDivElement | null>(null);
@@ -131,10 +133,13 @@ export function ChatPanel({
}, [fetchChatMessages]); }, [fetchChatMessages]);
const messages = chatId ? (messagesById.get(chatId) ?? []) : []; const messages = chatId ? (messagesById.get(chatId) ?? []) : [];
// Auto-scroll effect when messages change const isStreaming = chatId ? (isStreamingById.get(chatId) ?? false) : false;
// Auto-scroll effect when messages change during streaming
useEffect(() => { useEffect(() => {
if ( if (
!isUserScrolling && !isUserScrolling &&
isStreaming &&
messagesContainerRef.current && messagesContainerRef.current &&
messages.length > 0 messages.length > 0
) { ) {
@@ -145,7 +150,7 @@ export function ChatPanel({
}); });
} }
} }
}, [messages, isUserScrolling]); }, [messages, isUserScrolling, isStreaming]);
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">