Skip to content

[Bug]: Scroll-to-end pill stays hidden after thread switch strands viewport above the end #12372

Description

@bompus

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Related: #5903 (this is the stale-pill half split out per discussion there).

Area

apps/web

Steps to reproduce

  1. Open a thread with enough history to overflow the viewport.
  2. Send a prompt, then switch to a different thread while the turn runs.
  3. Return to the first thread after its response has landed.
  4. Observe the viewport: it can be stranded above the newest message, often farther up than where it was left.
  5. Observe the "Scroll to end" pill: it does not appear. Nudge the scroll up/down a little and the pill appears.

Expected behavior

If the viewport is not at the live edge, the "Scroll to end" pill is visible. The end-state tracking reflects the actual viewport position after a thread switch settles, including content whose height changes after rendering.

Actual behavior

The pill stays hidden on the stranded viewport, as if the state believes it is already scrolled to the end when it is not. Only a manual scroll nudge (which fires real scroll events) makes the pill appear.

Impact

Major degradation or frequent failure

The stranded viewport looks settled, and the missing pill removes the visible recovery path; END key is the only recourse.

Version or commit

Local checkout @ dc49767

Environment

T3 Code web/desktop client (Linux). Provider-independent; affects the shared web timeline.

Logs or stack traces

No associated exception or console error.

Screenshots, recordings, or supporting files

No response

Workaround

Nudge the scroll wheel up/down until the pill appears, or press END to jump to the live edge.

Suspected mechanism (from code reading, not runtime-verified)

  • The position cached for the background thread was saved with atEnd: true (timelineScrollAnchoring.ts), so the thread-switch effect in ChatView.tsx takes the scrollToEnd branch and hides the pill.
  • Transient isAtEnd=false scroll events during settle are suppressed by the post-open live-follow guard (onIsAtEndChange early return when the scroll generation still matches) plus the 150ms show-debounce.
  • If row heights settle after that (streamed markdown/tool output measuring late), the viewport strands with isAtEndRef stale-true and no further scroll event corrects it.
  • A manual wheel gesture bumps the generation via cancelTimelineLiveFollowForUserNavigation, letting the next isAtEnd=false through so the pill appears.

A post-settle reconciliation (re-resolve isAtEnd from the list state a frame or two after position restore completes and sync the pill) would cover this class of stranding regardless of which layout pass caused it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions