Fix transcript scrolling and live reasoning visibility during streaming - #18
Merged
Merged
Conversation
Both render paths pinned `transcript.scrollTop` to the bottom for as long as the turn was running, so scrolling up during output was impossible. Reasoning was also collapsed by default and rebuilt from scratch by every delta, so it could only be read once the turn had moved past it (issue #17). - Follow the newest output only when the reader already sits at the bottom. Sending a prompt, or returning from the settings view mid-turn, still pins the transcript back down. - Sample the scroll geometry before the DOM mutation instead of after, which also restores auto-follow for the patches that previously measured the already-grown transcript, and re-pin after a window rebuild. - Expand reasoning by default into a height-capped, internally scrolling body that follows the newest text, and keep its collapse state and scroll offset across the re-render every streaming update triggers. The reasoning behaviour is adapted from the composer-additions fork in #16. Closes #17. Co-authored-by: VaterVato <38903140+VaterVato@users.noreply.github.com>
`npm audit --audit-level=high` started failing CI on advisories published after the last release, both from the `@vscode/vsce` dev dependency: - fast-uri 3.1.5 -> 3.1.7 (high: GHSA-5jgf-p345-68v8, GHSA-f65p-4m7j-42xc, GHSA-fph4-wmhf-6fwf, GHSA-jqff-g426-hqxp), via ajv - qs 6.15.2 -> 6.16.0 (moderate: GHSA-x5fp-wj9c-mxmx, GHSA-4mjr-xmp4-gh2g), via typed-rest-client, pulling side-channel 1.1.0 -> 1.1.1 Both patched versions sit inside the ranges their parents already declare, so this is a lockfile-only update with no override or dependency bump. `npm audit` now reports 0 vulnerabilities, and packaging (the code path that actually uses ajv) still produces and verifies the VSIX.
This was referenced Sep 6, 2026
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.
Closes #17.
The report is two separate problems in the chat webview.
1. The transcript could not be scrolled up while a turn was streaming.
Both auto-scroll checks were
state.running || <near the bottom>, so as long as a turn was running every render and every transcript patch rantranscript.scrollTop = transcript.scrollHeight. Scrolling up during output was impossible — the next token pulled the view straight back down.Auto-follow now keys off the reader instead of the turn: the transcript sticks to the bottom only when it is already there, and re-attaches by itself when the reader scrolls back down. The two places that still deserve a forced jump ask for one explicitly — sending a prompt, and returning from the settings view during a live turn (hiding the transcript destroys its scroll offset).
The geometry is now sampled before the DOM mutation. The old check in
patchTranscriptran after the new nodes were appended, so oncerunningwas false it measured an already-grown transcript and skipped the follow it should have done.2. Reasoning could not be read until the turn moved past it.
renderThoughtbuilt a collapsed<details>showing only a one-line preview, andpatchTranscriptremoves and re-creates every node frompatch.starton. The streaming thought sits exactly atpatch.start, so it was rebuilt collapsed on every delta and any manual expansion was thrown away a few milliseconds later.Reasoning is now expanded by default in a height-capped (~15 line) internally scrolling body that follows the newest text, and the collapse state and scroll offset live outside the DOM so they survive the rebuild. Scrolling up inside a reasoning body parks it there; scrolling back to its bottom re-attaches the follow. Both are keyed by transcript index and dropped when the session changes.
The reasoning behaviour is adapted from @VaterVato's fork in #16 (
01cf997,bf509a3), which fixed this half of the issue but carries a large set of unrelated changes; that work is credited as co-author on the commit. The transcript scroll lock is untouched on that branch and is fixed here.Notes for review
src/transcriptScroll.tsis a new module holding the pure scroll decisions so they are testable —webview.tscannot be imported undernode --test(it callsacquireVsCodeApi()at module scope).closeSettings(), which is where the forced re-pin lives.toggleand innerscrolldo not bubble, so both listeners are registered on the transcript in the capture phase.Verification
npm run lint,npm test(80 pass, 7 new intest/transcriptScroll.test.ts),npm run test:vscode(exit 0).