Skip to content

fix: chat auto-scrolls to top after thinking completes#319

Open
xoknight wants to merge 1 commit intoTHU-MAIC:mainfrom
xoknight:fix/chat-scroll-to-bottom
Open

fix: chat auto-scrolls to top after thinking completes#319
xoknight wants to merge 1 commit intoTHU-MAIC:mainfrom
xoknight:fix/chat-scroll-to-bottom

Conversation

@xoknight
Copy link
Copy Markdown

Summary

  • Fix streaming auto-scroll effect that was a no-op: scrollContainerRef has no fixed height and never overflows, so el.scrollTop = el.scrollHeight did nothing. The real scrollable ancestor is the parent div in chat-area.tsx.
  • Replace with bottomRef.scrollIntoView({ behavior: 'instant', block: 'end' }) which correctly targets the nearest scrollable ancestor.
  • Change activeBubbleId scroll from block: 'nearest' to block: 'end' — the active bubble is always the latest message, and nearest could leave the viewport mid-list after thinking transitions.

Root Cause

In chat-session.tsx, three useEffect hooks manage auto-scroll:

  1. Effect 1 (msgCount dep): bottomRef.scrollIntoView — works correctly
  2. Effect 2 (session.messages dep): scrollContainerRef.scrollTop = scrollHeightbroken because the element has no height constraint and never overflows
  3. Effect 3 (activeBubbleId dep): activeBubbleRef.scrollIntoView({ block: 'nearest' }) — works but nearest can scroll to mid-list

When thinking completes, onAgentEnd removes empty messages and onAgentStart adds new ones. If React batches these, msgCount stays the same → Effect 1 doesn't fire. Effect 2 should compensate but is a no-op → scroll position is lost during re-layout → chat jumps to top.

Test plan

  • Start a conversation, let AI think, verify scroll stays at bottom when response arrives
  • During text streaming, verify chat stays pinned to bottom
  • Scroll up manually during streaming, verify auto-scroll doesn't force back down (isAtBottomRef guard)

🤖 Generated with Claude Code

The streaming auto-scroll effect (Effect 2) used scrollContainerRef.scrollTop
to pin the viewport, but scrollContainerRef has no fixed height constraint and
never overflows — the real scrollable ancestor is the parent div in chat-area.
This made the effect a no-op, so after agent transitions (onAgentEnd removing
empty messages + onAgentStart adding new ones) the scroll position was lost.

Fix: use bottomRef.scrollIntoView({ behavior: 'instant', block: 'end' })
which correctly targets the nearest scrollable ancestor.

Also change activeBubbleId scroll from block:'nearest' to block:'end' — the
active bubble is always the latest message, and 'nearest' could leave the
viewport mid-list after thinking transitions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant