fix(#552): release stick-to-bottom on any upward user scroll

The chat viewport previously only released stick-to-bottom when the
user scrolled up AND was already more than 200px from the bottom.
While reading near the end of a streaming response, any upward scroll
inside the bottom 200px did nothing — the ResizeObserver then yanked
the viewport back to the bottom on the next streaming chunk, producing
the 'can't scroll up' tug-of-war reported in #552.

Fix: any user-initiated upward scroll releases stick-to-bottom
immediately. Re-stick only when the user has stopped scrolling up
AND is at the bottom (<=NEAR_BOTTOM_THRESHOLD). Applied symmetrically
in ChatContainerRoot (handleScroll) and in chat-message-list's
handleUserScroll mirror.
This commit is contained in:
Aurora
2026-06-05 11:13:30 -04:00
parent 40828fc30d
commit 0a6d1bccb0
2 changed files with 15 additions and 3 deletions

View File

@@ -48,13 +48,21 @@ function ChatContainerRoot({
if (!element) return
const handleScroll = () => {
// Track stick-to-bottom internally based on actual scroll position
// Track stick-to-bottom internally based on actual scroll position.
// Bug #552: previously we only released stick-to-bottom when the user
// both scrolled up AND was already >200px from bottom. That meant any
// upward scroll within the bottom 200px did nothing — and during heavy
// streaming the ResizeObserver immediately yanked the viewport back to
// the bottom on the next content growth, producing the "can't scroll up"
// tug-of-war. Fix: ANY user-initiated upward scroll releases stick. Only
// re-stick when the user has stopped scrolling up AND is right at the
// bottom (≤NEAR_BOTTOM_THRESHOLD).
const distFromBottom =
element.scrollHeight - element.scrollTop - element.clientHeight
const wasScrollingUp = element.scrollTop < lastScrollTopRef.current - 5
lastScrollTopRef.current = element.scrollTop
if (wasScrollingUp && distFromBottom > NEAR_BOTTOM_THRESHOLD) {
if (wasScrollingUp) {
stickToBottomRef.current = false
} else if (distFromBottom <= NEAR_BOTTOM_THRESHOLD) {
stickToBottomRef.current = true

View File

@@ -722,7 +722,11 @@ function ChatMessageListComponent({
const wasScrollingUp = metrics.scrollTop < lastScrollTopRef.current - 5
lastScrollTopRef.current = metrics.scrollTop
if (wasScrollingUp && !nearBottom) {
// Bug #552: any user-initiated upward scroll releases stick-to-bottom
// (previously required >200px from bottom, which let streaming yank the
// viewport back down during near-bottom reading). Re-stick only when the
// user lands back at the bottom.
if (wasScrollingUp) {
stickToBottomRef.current = false
isNearBottomRef.current = false
} else if (nearBottom) {