Skip to content

Fix transcript scrolling and live reasoning visibility during streaming - #18

Merged
SivanCola merged 2 commits into
mainfrom
fix/live-transcript-and-reasoning-scroll
Sep 6, 2026
Merged

SivanCola merged 2 commits into
mainfrom
fix/live-transcript-and-reasoning-scroll

Conversation

@SivanCola

Copy link
Copy Markdown
Owner

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 ran transcript.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 patchTranscript ran after the new nodes were appended, so once running was 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.
renderThought built a collapsed <details> showing only a one-line preview, and patchTranscript removes and re-creates every node from patch.start on. The streaming thought sits exactly at patch.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.ts is a new module holding the pure scroll decisions so they are testable — webview.ts cannot be imported under node --test (it calls acquireVsCodeApi() at module scope).
  • The two identical settings-close handlers were folded into closeSettings(), which is where the forced re-pin lives.
  • toggle and inner scroll do not bubble, so both listeners are registered on the transcript in the capture phase.

Verification

  • npm run lint, npm test (80 pass, 7 new in test/transcriptScroll.test.ts), npm run test:vscode (exit 0).
  • Not verified by watching a live stream in a real Extension Host — the DOM wiring around the pure helpers is reviewed but untested.

SivanCola and others added 2 commits September 7, 2026 03:58
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.
@SivanCola
SivanCola merged commit cc1de3d into main Sep 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't scroll up while model is outputting text, and can't see the thinking output unless the model closes it first

1 participant