Skip to content

fix: scrolling up during a running thread no longer snaps back to the bottom - #5566

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/preserve-scroll-position
Aug 7, 2026
Merged

fix: scrolling up during a running thread no longer snaps back to the bottom#5566
t3dotgg merged 2 commits into
mainfrom
t3code/preserve-scroll-position

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Aug 7, 2026

Copy link
Copy Markdown
Member

Scrolling up while a thread was streaming would yank you back to the bottom on the next update, on both web and mobile. You had no way to read earlier messages until the turn finished.

Now scrolling up holds your position, and scrolling back down to the bottom re-enables the stick-to-bottom behavior.

What was wrong:

  • Mobile had no notion of "the user scrolled away". LegendList's maintainScrollAtEnd re-pinned on pure geometry — anywhere within ~10% of a viewport of the bottom (or any thread shorter than the screen) snapped back on every stream chunk. Added a follow latch: a user drag (through its momentum) that leaves the bottom zone disables the maintain; returning to the bottom, sending, or switching threads re-arms it.
  • Web re-armed live-follow via LegendList's isNearEnd, which is true up to half a viewport above the bottom, so a modest scroll-up still counted as "following". The library's own maintain also ran independently of the app's follow state, the escape listeners attached in one animation frame with no retry, and keyboard scrolling never broke follow at all. Follow now re-arms only within a tight band at the true content bottom, gates the library maintain, and the opt-out listeners retry attachment, cover PageUp/Home/ArrowUp, and are direction/position-aware.
  • Turn completion could re-fire the sent message anchor after user navigation had cancelled it. That restarted send-time positioning and clamped the viewport to the bottom. Anchor positioning now ignores remeasurement unless the timeline is still in its explicit send-time anchoring phase.

Browser-verified in Chrome with a live 17-second GPT-5.6-Luna turn: the viewport remained exactly 1,100 px from the bottom through completion, then resumed normal follow after using Scroll to end.

Verified with typecheck, lint, and unit tests on both apps (1821 web, 615 mobile).


🤖 Latest fix generated with GPT-5.6-Sol in T3 Code using the Codex harness.


Note

Medium Risk
Changes core chat scroll-follow behavior on web and mobile during streaming; localized to timeline UX but easy to regress edge cases (keyboard, touch, anchor sends).

Overview
Fixes thread timelines yanking back to the bottom on every stream chunk after you scroll up to read earlier messages. Live follow is now an explicit latch that gates LegendList’s maintainScrollAtEnd instead of leaving the library to re-pin on its own geometry.

On mobile (ThreadFeed), follow turns off only during a real user drag/momentum that leaves the end band, and turns back on when you return to the bottom, switch threads, or anchor on send. On web (ChatView / MessagesTimeline), a liveFollowEnabled flag does the same gating; opt-out listeners retry if the list isn’t mounted yet, and wheel/touch/pointer/keyboard handlers only break follow when the viewport can actually move away from the live edge (avoiding spurious breaks while still pinned).

resolveTimelineIsAtEnd no longer treats LegendList’s broad isNearEnd as “at end”; it uses a 40px band at the true content bottom (with composer end inset), so modest scroll-up no longer re-arms follow. Send-time anchor positioning is also guarded so remeasures after a turn completes don’t restart anchoring once you’ve scrolled away.

Reviewed by Cursor Bugbot for commit f7fa313. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix chat scroll so scrolling up during a running thread no longer snaps back to the bottom

  • Introduces a "live-follow latch" on both web (ChatView.tsx, MessagesTimeline.tsx) and mobile (ThreadFeed.tsx) that gates maintainScrollAtEnd on a flag rather than always keeping it enabled.
  • On web, follow is cancelled only by intentional user navigation away from the bottom: wheel-up, touch drag, scrollbar drag, or keyboard shortcuts (PageUp/Home/ArrowUp). Follow re-arms when the viewport returns within 40px of the content bottom.
  • On mobile, follow breaks only during an active user scroll session and re-arms on thread switches or when an anchor message is present.
  • resolveTimelineIsAtEnd in MessagesTimeline.logic.ts now uses geometry-based evaluation with a 40px (TIMELINE_FOLLOW_REARM_THRESHOLD_PX) re-arm band, replacing the previous isNearEnd check.
  • Behavioral Change: anchor remeasurement no longer repositions the scroll view after the user has scrolled away from the end.

Macroscope summarized f7fa313.

On both web and mobile, scrolling up while a thread streamed would snap
back to the bottom on the next chunk. Mobile had no user-scroll latch at
all (LegendList's maintainScrollAtEnd re-pinned on pure geometry); web
re-armed live-follow within half a viewport of the end and let the
library's own maintain re-pin regardless of the app's follow state.

Follow now breaks when the user scrolls away and re-arms only when the
viewport actually returns to the bottom, on send, or on thread switch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 40db45d0-14fa-49fc-9d07-d1e1c95ea738

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 7, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 7, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved f7fa313

This is a well-scoped bug fix for scroll behavior that prevents the timeline from yanking users back to the bottom while they're reading history. Changes are self-contained to scroll handling logic across mobile and web, include unit tests for the threshold logic, and have clear explanatory comments.

You can customize Macroscope's approvability policy. Learn more.

@t3dotgg t3dotgg added the DO NOT MERGE Experimental pull request. Do not merge. label Aug 7, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 7, 2026 04:08

Dismissing prior approval to re-evaluate f7fa313

@t3dotgg
t3dotgg merged commit 1c7d059 into main Aug 7, 2026
17 checks passed
@t3dotgg
t3dotgg deleted the t3code/preserve-scroll-position branch August 7, 2026 04:32
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 7, 2026
## What's Changed
* fix: prevent reconnect loops during server stalls by @gfsaaser24 in pingdotgg/t3code#5561
* fix(server): settle stopped Claude subagents by @t3dotgg in pingdotgg/t3code#5568
* fix: scrolling up during a running thread no longer snaps back to the bottom by @t3dotgg in pingdotgg/t3code#5566

## New Contributors
* @gfsaaser24 made their first contribution in pingdotgg/t3code#5561

**Full Changelog**: pingdotgg/t3code@v0.0.32-nightly.20260807.1020...v0.0.32-nightly.20260807.1021

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.32-nightly.20260807.1021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE Experimental pull request. Do not merge. size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant