Repository navigation
[Bug]: Scrolling over an inline HTML render doesn't stop live-follow, so the thread snaps back to the bottom #17056
Description
Activity
Note
Grok responding on behalf of Julius.
Thanks for the precise report and measurements. I checked this against
mainat04ad174, which includes9fba2093e5, and the mechanism you describe holds in the code.What I confirmed
- Live-follow only turns off for input the parent document can see. The opt-out listeners are
wheel,touchmove, andpointerdownon the timeline scroll node, pluskeydownondocument(ChatView.tsx#L6529-L6633). Input inside a sandboxed, opaque-origin frame is delivered to the frame's own document, so none of these fire. The frame is BrowserDocumentFrame.tsx#L123-L128, withsandbox="allow-scripts allow-forms". - Scroll position alone does not end follow mode, on purpose. A plain
scrollreacheshandleScroll→onIsAtEndChange(false)(MessagesTimeline.tsx#L1084-L1111). While following,onIsAtEndChangereturns early: it hides the scroll-to-bottom pill and leaves follow on (ChatView.tsx#L6748-L6756). This guards against streaming growth makingisAtEndflicker (see the comment at L6515-L6526 and fix: scrolling up during a running thread no longer snaps back to the bottom #5566). The side effect is that a scroll that chains up out of the frame moves the viewport whiletimelineLiveFollowEnabledstaystrue. - LegendList then pins back to the end. While
liveFollowEnabledis true,maintainScrollAtEndstays on (MessagesTimeline.tsx#L1418-L1428), so the next layout change snaps the timeline back. The frame itself can trigger that change: when the page reportsui/notifications/size-changed, the row resizes (HtmlRenderFrame.tsx#L47-L50, L102). That fits your "worst right after opening" observation.
How #16283 is involved. #16283 doesn't forward scroll by
postMessage. It sizes the frame to the page's reported height (htmlRender.ts#L118-L132), up to the 2000px cap. The page then has nothing to scroll, and the browser's native scroll chaining moves the timeline. So #16283 makes this path reliable rather than introducing the follow gap. The follow logic never accounted for input inside a frame, and before #16283 the frame likely trapped the scroll instead (inferred, see below). I'd call #16283 the trigger that exposed this, not the root cause.McpAppFrame. It embeds the same kind of sandboxed, opaque-origin iframe in a timeline row (McpAppFrame.tsx#L531-L536), and the opt-out listeners can't see its input either. Whether it hits this in practice depends on whether the app's page chains scroll instead of scrolling itself. I haven't reproduced that.Fix direction (not yet tried)
- Host side, covers every frame: while following, treat a timeline
scrollthat lowersscrollTopwith no programmatic scroll in flight as the user leaving follow mode. Content growth shouldn't lowerscrollTop, so this likely wouldn't bring back fix: scrolling up during a running thread no longer snaps back to the bottom #5566. Content shrinking (collapsing a disclosure) can lower it through clamping, though, so it would need a guard or a check of whether the content got shorter. This also covers renders published before fix(web): inline HTML renders no longer trap the thread's scroll #16283, since their bootstrap is frozen at publish time. - Bootstrap bridge: have the injected bootstrap (htmlRender.ts#L346), which already posts
size-changed, also post upward wheel/touch intent. The host would callcancelTimelineLiveFollowForUserNavigation. The same bridge could carry the edge-of-frame wheel delta proposed in [Bug]: Wheel scroll stays stuck on a tall inline HTML render instead of chaining to the thread #16977. Limits: it only covers new renders, doesn't cover MCP apps (third-party pages), and any page can send the message. Spoofing it can only turn follow off, which is low risk.
Option 1 looks like the smaller, more general fix. Option 2 pairs naturally with #16977.
Related
- [Bug]: Wheel scroll stays stuck on a tall inline HTML render instead of chaining to the thread #16977 (open): the opposite symptom for tall (>2000px or agent-capped) renders. The frame scrolls its own page, and the wheel gesture doesn't chain to the thread until the wheel pauses.
- [Bug]: Android inline HTML traps chat scrolling when its content overflows #16989 (open): Android inline HTML traps chat scrolling. A different platform and path.
- fix: scrolling up during a running thread no longer snaps back to the bottom #5566 / fix(web): keep following the stream after scrolling back to the live edge #6519 (merged): the earlier live-follow snap-back fixes that this gesture-only detection comes from.
- [Bug]: Returning to a long thread lands at the top or the middle instead of where I left it #14051 / fix(web): reopen a thread that was following its stream at the end #14916 (open): scroll restore when reopening a thread. Not the same bug.
Not verified here: I haven't run the reproduction, so the 0-of-2 wheel events / 0 px measurements come from the report. I also haven't confirmed the claim that before #16283 the frame trapped the scroll in this exact scenario; that comes from #16283's description and the code.
- Live-follow only turns off for input the parent document can see. The opt-out listeners are
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 8, 2026 - added a commit that references this issue
on Oct 8, 2026
Before submitting
Area
apps/web
Summary
When you scroll up with the pointer over an agent's inline HTML render (
html_render), the thread keeps following the end and snaps back to the bottom at the next layout change. Scrolling over plain chat text in the same spot works. It is intermittent because a single wheel, click, or key press anywhere outside the frame turns live-follow off for good.It shows up most on threads that end with a tall render. The frame fills the viewport, so the pointer is almost always over it. It is easiest to hit right after opening such a thread, while the frame, its CDN scripts and fonts, and images are still settling and causing layout changes.
Steps to reproduce
html_renderpage taller than the viewport, for example a KaTeX derivation about 1,700 px tall. Opening the thread puts the timeline in live-follow at the end.Expected behavior
Scrolling up over the render stops live-follow, as it does over chat text. The timeline stays where I scrolled.
Actual behavior
The timeline jumps back to the bottom. Repeated attempts keep snapping back until a wheel, click, or key press lands outside the frame.
Measured with a real mouse wheel (Playwright,
chrome-headless-shell154) against a dev server onmain(9fba2093e5). Each run scrolled up two wheel ticks, 300 px total, then resized the window by 20 px:html_renderiframeRecording on
main, using a neutral fixture thread. The red dot is the real pointer, and the bottom-right counter is the distance from the end. In the first half the pointer is over the HTML render: it scrolls 300 px up, and a 20 px window resize snaps it back to 0. In the second half the pointer is over chat text: same scroll and resize, and it stays at 300 px.MP4 version
Root cause
Live-follow is turned off only by user gestures on the timeline's scroll node. In
ChatView.tsx, the comment at line 6477 says "Live-follow stays active after send/thread-open until an actual list scroll gesture opts out". The listeners arewheel,touchmove, andpointerdownonscrollNode, pluskeydownondocument(lines 6529–6633). WhileliveFollowEnabledstays true,MessagesTimelinekeepsmaintainScrollAtEndon (line 1418), and LegendList re-pins to the end on the next layout change.HtmlRenderDocumentis a sandboxed, cross-origin iframe (BrowserDocumentFrame.tsx:128,sandbox="allow-scripts allow-forms"). Wheel and touch events over it go to the frame's own document and never reach the parent's listeners. When the page can't scroll itself, the browser passes the scroll up to the timeline. The timeline moves without any event its follow logic can see, so it stays in follow mode while away from the end.McpAppFrameembeds an iframe in the timeline the same way and is probably affected too.Possible fix directions
ui/notifications/size-changed, also post a message when the reader scrolls inside the frame.Impact
Minor bug or occasional failure
Version or commit
T3 Code
0.0.46-nightly.20261008.2801. Reproduced onmainat9fba2093e5.Environment
T3 Code (Nightly) desktop app on macOS 26, connected to
t3 serveon Ubuntu 26.04. Reproduced in Chromium against a local dev server.Claude Opus 5.5 via Claude Code in T3 Code