pingdotgg / pingdotgg/t3code

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

Open
#12,372 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accepted bug via-triage
Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

Description

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 @ dc49767ae5

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading timelineScrollAnchoring.ts and the thread-switch and scroll-state paths in apps/web, especially ChatView.tsx and onIsAtEndChange. Reproduce the switch-and-return sequence with late-rendering content, then trace position restoration, scroll-generation guards, and the show debounce. Done means a restored viewport that is not at the live edge reliably shows the "Scroll to end" pill without requiring a manual scroll.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.