Fix the chat not scrolling to the end of an answer#537
Merged
Conversation
A finished answer could be left with its tail, usually the citations, below the fold, and nothing would bring it back: the view stayed put with the question still on screen above an answer whose end you had to scroll to by hand. The chat log followed the answer by hand: a tick every 150ms measured the log and scrolled it to the bottom, with a pair of flags deciding whether the user had scrolled away and wanted to be left alone. Measuring is the problem. While the model streams, the answer often still fits, so there is nothing to scroll; it only outgrows the pane when the answer finishes and renders the tail it had buffered plus the citations. Markdown mounts those asynchronously, so the last scroll of the turn ran against a widget that had not grown yet, did nothing, and left no tick behind to fix it. Textual has this: an anchored widget stays scrolled to the bottom as content arrives, until the user scrolls away. It re-pins the scroll while it arranges the layout, off the size it just computed, so there is no moment where the view is behind the content and no measurement of ours to get wrong. It also handles the scrollbar and the wheel, which our version never did. So the follower goes, and a turn anchors the log instead. This is the second bug in that hand-written follower, after the auto-follow disabling itself in #294. Tests: the scroll ones drove _scroll_to_bottom directly, so they are now written against what the reader sees, and all of them set up by sending a question rather than anchoring the log themselves, which is what lets them fail when the anchoring goes missing.
tobocop2
force-pushed
the
fix/chat-final-scroll-after-layout
branch
from
July 16, 2026 03:17
2814cc2 to
48d4c10
Compare
The scroll tests read scroll_y and max_scroll_y back after a single pilot.pause(), which settles none of what they depend on. The scroll bindings animate, so scroll_y samples somewhere on the easing curve at whatever point wall-clock allowed: on a loaded runner it had not moved at all yet and "the reader scrolled up" asserted 135 < 135. A bubble also only buffers appended content until compose hands it a content widget, so an answer grown before that lands leaves max_scroll_y at 0 and the preconditions compare 0 to 0. Wait on Textual's own wait_for_scheduled_animations, which drains pending messages, runs the animations out, and drains again. The turn helper now also asserts the bubble composed, so a test that would otherwise pass against an empty bubble fails instead.
AGENTS.md asks TUI tests to go through pilot.press() rather than call action_* methods, and this is a good demonstration of why. Pressing PgUp does not scroll the log at all: the chat input holds focus and takes the key, since the binding is not a priority one. Calling action_scroll_up() jumped over the binding and hid that. Escape first, then PgUp, which is what the reader does and what the modal design intends: you type in insert mode, and escape to read. Focus moves to the log there, so PgUp and G land on it.
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.
Problem
When an answer finished, the chat stopped short of the end of it. The last part, usually the sources, sat below the visible area and you had to scroll down by hand to read it.
Solution
Textual can keep a view pinned to the bottom as content arrives, and let go once the reader scrolls away. The chat now uses that, replacing the hand-written scrolling it had, which measured the answer at the wrong moment and left the end of it off screen.
This is the second bug in that scrolling code, after #294, so it is replaced rather than patched again.