Skip to content

fix(chat): follow the assistant response to the bottom unless the user scrolls away - #23

Merged
devswha merged 1 commit into
mainfrom
fix/chat-scroll-follow
Sep 4, 2026
Merged

fix(chat): follow the assistant response to the bottom unless the user scrolls away#23
devswha merged 1 commit into
mainfrom
fix/chat-scroll-follow

Conversation

@devswha

@devswha devswha commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Closes #19.

What broke

After submitting at the bottom, the viewport could stay anchored near the user prompt while the assistant response grew below the fold. Two compounding causes in useChatSessionState:

  1. Follow state was inferred, not trackedhandleScroll ran setIsUserScrolledUp(!isNearBottom()) on every scroll event, including events fired by the app's own programmatic scrolls and passive layout shifts. One mid-growth event could flip follow mode off for the rest of the turn.
  2. Growth detection was narrow — auto-scroll effects reacted only to chatMessages.length and the final message's content.length, missing tool cards, reasoning blocks, images/KaTeX, and any async reflow.

Fix

New useChatFollowScroll hook (extracted, DOM-tested):

  • ResizeObserver on the transcript content pins the viewport to the bottom on any height change while following — covers tool UI, reasoning, images, and reflow, not just text growth.
  • Follow mode stops only on genuine upward scroll intent: wheel-up, downward touch drag, or ArrowUp/PageUp/Home with the container focused. Scroll events never disable it — they only resume following when the user lands within 50 px of the bottom.
  • scrollToBottom() resumes following, so the existing "new messages below" button keeps working; isUserScrolledUp, hasNewMessagesBelow, scrollToBottomAndReset, and the load-older-at-top pagination path keep their exact public behavior (isUserScrolledUp is now !isFollowing internally).

Coverage

  • New useChatFollowScroll.dom.bun.test.tsx (5 tests): growth pins to bottom; wheel-up stops follow; passive scroll events do not stop follow (the regression); returning to the bottom resumes; scrollToBottom resumes.
  • useChatSessionState.test.ts stays green; both tsconfigs and eslint clean.

Known limitation: dragging the scrollbar upward during active streaming doesn't emit wheel/touch/key intent, so follow resumes on the next content growth — wheel/touch/keyboard gestures, the common case, are all covered.

…r scrolls away

Follow mode was a boolean flipped by every scroll event, including the
app's own programmatic scrolls and passive layout shifts, so a mid-growth
event could pin the viewport at the user prompt for the rest of the turn.
It is now an explicit mode: growth of the transcript (any height change,
not just message count or final text length, via a ResizeObserver on the
content) keeps the viewport pinned to the bottom while following, and
following stops only on genuine upward scroll intent (wheel, touch drag,
ArrowUp/PageUp/Home) and resumes when the user returns to the bottom.

Closes #19
@devswha
devswha merged commit 3611ae8 into main Sep 4, 2026
2 checks passed
@devswha
devswha deleted the fix/chat-scroll-follow branch September 4, 2026 13:44
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.

bug: chat viewport stays anchored at user prompt after assistant response

1 participant