Chat: keep view pinned to bottom while a re-opened thread settles#36
Merged
Conversation
Opening an existing conversation called scrollToBottom once after a single tick, so the view landed on the bottom of the *currently-rendered* content. But image attachments, KaTeX, highlight.js, and the inline cohort/intuition panels all paint asynchronously after that tick. As each one mounted, scrollHeight grew while scrollTop stayed put - and the last message drifted up off the bottom of the viewport. The user arrived at the conversation already scrolled away from where they expected to be. Replace the single-shot snap with a brief rAF watchdog that re-pins to bottom whenever scrollHeight grows during the post-load window. It exits as soon as scrollHeight has been stable for a handful of frames, or after a 3s hard cap, or the moment the user scrolls up (followBottom flips false), or another loadMessages supersedes it. The thread-switch path also cancels any in-flight watchdog explicitly so a slow-loading previous thread can't tug the new one back to its bottom. No changes to the streaming or send paths - those still go through the sending-gated effects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SYNOPSIS
Opening an existing conversation now lands the view on the last message even after async content finishes painting.
PURPOSE
Currently
loadMessagescallsscrollToBottomonce after a singletick(), so the view lands on the bottom of currently-rendered content. Image attachments, KaTeX, highlight.js, and the inline cohort/intuition panels paint asynchronously after that tick - each one growsscrollHeightwhilescrollTopstays put, and the latest message drifts up off the bottom of the viewport. The user arrives at the conversation already scrolled away from where they expected to be.DESCRIPTION
Before: single-shot
scrollToBottom(false)afterawait tick(). No accommodation for post-commit layout growth. The two existing scroll$effects are both gated onsending, so they don't help during thread-load.Now: a small rAF watchdog (
pinBottomWhileSettling) re-pins to bottom wheneverscrollHeightgrows during the post-load window. It exits on the first of: 6 stable frames (no growth), 3s hard cap,followBottomflipping false (user scrolled up), or anotherloadMessagessuperseding it. Thread-switch also callscancelPostLoadScroll()explicitly so a slow previous thread can't tug the new one back to its bottom.That keeps the view glued to the newest message across the window where image decode / markdown highlight / panel paint expand the transcript - so the user arrives where they intended to.
Notes:
sending-gated$effectsfollowBottom, so an early user scroll-up still winsGenerated by Claude Code