Repository navigation
fix(web): reopen a thread that was following its stream at the end - #14916
santiago-ramos-02 wants to merge 1 commit into
Conversation
While a turn streams, the follow scroll trails new output, so scroll events report the thread as not at the end. The per-thread position cache saved that as a reading position, and returning to the thread restored it far above the latest message. Remember the thread at the end while live follow is latched and no anchored end space holds a first send near the top.
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused, self-contained web bug fix that corrects per-thread scroll restoration during transient live-follow lag while preserving user navigation and anchored-turn behavior. The changed production path is small and accompanied by regression coverage, with no defaults, schema, infrastructure, security, billing, or static-analysis changes. Notes:
You can add or adjust custom eligibility rules. Learn more. |
Dismissing prior approval to re-evaluate 6a9f043
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (3)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughWhen a thread is following new output, MessagesTimeline can now save its position as at-end even if the list reports that it has not reached the end. ChatView supplies the live-follow latch used for this decision. ChangesLive-follow timeline position
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Fixed issue severity: <fixed_issue_severity>Low</fixed_issue_severity> Merge Risk: ⚪ Minimal · up to The change appears ready to merge after normal checks: it restores live-edge position during active follow without treating handled user navigation as follow. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Note Grok responding on behalf of Julius. Closing as superseded by #17348, which just merged and fixes #13601. It covers the cases this PR handles (lagging follow scroll on thread switch, a gesture releasing follow before the re-render, and the anchored first send) plus the held thread-switch snapshot. Thanks for the work on this, it shaped the fix that landed. |
Problem
A thread that is following a streaming reply can be remembered as a reading position when the user switches away. While a turn streams, new output can land below the viewport before the follow scroll catches up, and a scroll event in that window saves
atEnd: false. Coming back after the reply finishes restores that old offset, far above the latest message, with "Scroll to end" showing and live follow off.Rebuilt on V2 as asked in #13602. The V2 timeline keeps up with plain streamed text, so it happens less often than before: in a sampled plain-text reply the saved position never left the end. Replies with tool calls still trigger it: in one sampled tool-heavy reply, 28 of 1,396 samples saved
atEnd: falsewhile the thread was following.Change
MessagesTimelineremembers a thread as at the end while live follow is active:atEnd: isAtEnd || (liveFollowEnabled && isLiveFollowLatched() && !anchoredEndSpace). ChatView passes its follow latch asisLiveFollowLatched, because a scroll gesture releases the latch beforeliveFollowEnabledre-renders. A user scroll, the minimap, or other navigation releases the latch, so a real reading position is still saved. A new turn held near the top by anchored end space is not following the end, so its position is kept too.ChatView's
onIsAtEndChangealready ignoresisAtEnd === falsewhile live follow is active. The remembered position now uses the same rule.Scope and approval
Fixes #13601, triaged as accepted. Replaces #13602, closed for the V2 rewrite. This does not touch #5903 or #12372: when the remembered position is at the end, a restore can still land short of it after late row measurement, with the pill hidden. That is a separate problem and is not changed here.
Verification
vp test run src/components/chat/MessagesTimeline.test.tsxfails on currentmain(expected false to be true) and passes with the change. The test scrolls with the list 500 px short of the end and checks the remembered position with the latch held, the latch released whileliveFollowEnabledis still true, follow off, and an anchored first send.MessagesTimeline.test.tsxandtimelineScrollAnchoring.test.tsx: 92 passed. Typecheck, lint, and format are clean forapps/web.atEnd: false, reopened 793 px above the end with "Scroll to end" showing.atEnd: true, reopened at the end.Before:
before.mp4
After:
after.mp4
Model: Claude Opus 5.5 (1M context), running in Claude Code inside T3 Code.