fix(tui): prevent transcript loss across reflows - #3684
Conversation
Keep resize and append boundaries durable only when the rendered prefix is proven unchanged, preserving wrapped rows, viewport state, and terminal history without replaying prior content. Tested: bun test focused TUI regression, viewport, write-boundary, fixture, and resize-storm suites Tested: bun x biome check focused TUI files
721f5ae to
77a4b1d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 721f5aefb7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Hostile disposition: MERGE_READY — awaiting terminal native/viewport checks Exact head Hostile verification: focused exact-head TUI suites passed 120 tests with 858 assertions locally, including viewport scroll, render regressions, resize replay storms, width settling, render commits, and render goldens. Fixture churn is deterministic and tied to explicit render-golden cases; no unexplained broad golden rewrite was found. The implementation preserves native scrollback by avoiding clear/replay for real process terminals and marked multiplexers, while retaining full redraw for headless terminals and explicit legacy override behavior. The remaining hosted checks are not red: native-build and viewport/resize focused checks are pending. No P0/P1 findings. Merge is authorized only after the exact current-head CI becomes terminal green; no merge was performed. — |
The #3684 chain removed the shouldUseViewportRepaintForHost gate from the width-change viewport-repaint intercept, making ALL terminals unconditionally redirect width-changed renders to a live viewport repaint. On plain terminals (non-multiplexer, non-process-terminal), this bypassed fullRender which properly clears scrollback and replays the full transcript. Korean/CJK prose wrapping broke at narrow widths because the viewport repaint only painted visible rows without committing the full frame — the suffix was split across viewport boundaries with selector content interleaved between its halves. Restored the gate: compute useViewportRepaintPath before the intercept and only redirect to viewportRepaint when the host is actually a viewport-repaint host. Also restored the fullRender fallback for the firstChanged < viewportTop branch and propagated IME cursor write failure from #writeRenderBufferAndReanchorImeCursor so callers detect terminal detach. Updated affected #3684-added tests to use isProcessTerminal: true on their VirtualTerminals where they test viewport-repaint-specific behavior, and regenerated render golden fixtures. Issue: #1979 Confidence: high Scope-risk: narrow Reversibility: revert-commit Tested: full TUI suite + issue-1979 Korean wrap test Not-tested: real multiplexer session (CI covers via env injection)
The #3684 chain removed the shouldUseViewportRepaintForHost gate from the width-change viewport-repaint intercept, making ALL terminals unconditionally redirect width-changed renders to a live viewport repaint. On plain terminals (non-multiplexer, non-process-terminal), this bypassed fullRender which properly clears scrollback and replays the full transcript. Korean/CJK prose wrapping broke at narrow widths because the viewport repaint only painted visible rows without committing the full frame — the suffix was split across viewport boundaries with selector content interleaved between its halves. Restored the gate: compute useViewportRepaintPath before the intercept and only redirect to viewportRepaint when the host is actually a viewport-repaint host. Also restored the fullRender fallback for the firstChanged < viewportTop branch and propagated IME cursor write failure from #writeRenderBufferAndReanchorImeCursor so callers detect terminal detach. Updated affected #3684-added tests to use isProcessTerminal: true on their VirtualTerminals where they test viewport-repaint-specific behavior, and regenerated render golden fixtures. Issue: #1979 Confidence: high Scope-risk: narrow Reversibility: revert-commit Tested: full TUI suite + issue-1979 Korean wrap test Not-tested: real multiplexer session (CI covers via env injection)
The #3684 chain removed the shouldUseViewportRepaintForHost gate from the width-change viewport-repaint intercept, making ALL terminals unconditionally redirect width-changed renders to a live viewport repaint. On plain terminals (non-multiplexer, non-process-terminal), this bypassed fullRender which properly clears scrollback and replays the full transcript. Korean/CJK prose wrapping broke at narrow widths because the viewport repaint only painted visible rows without committing the full frame — the suffix was split across viewport boundaries with selector content interleaved between its halves. Restored the gate: compute useViewportRepaintPath before the intercept and only redirect to viewportRepaint when the host is actually a viewport-repaint host. Also restored the fullRender fallback for the firstChanged < viewportTop branch and propagated IME cursor write failure from #writeRenderBufferAndReanchorImeCursor so callers detect terminal detach. Updated affected #3684-added tests to use isProcessTerminal: true on their VirtualTerminals where they test viewport-repaint-specific behavior, and regenerated render golden fixtures. Issue: #1979 Confidence: high Scope-risk: narrow Reversibility: revert-commit Tested: full TUI suite + issue-1979 Korean wrap test Not-tested: real multiplexer session (CI covers via env injection)
#3691) The #3684 chain removed the shouldUseViewportRepaintForHost gate from the width-change viewport-repaint intercept, making ALL terminals unconditionally redirect width-changed renders to a live viewport repaint. On plain terminals (non-multiplexer, non-process-terminal), this bypassed fullRender which properly clears scrollback and replays the full transcript. Korean/CJK prose wrapping broke at narrow widths because the viewport repaint only painted visible rows without committing the full frame — the suffix was split across viewport boundaries with selector content interleaved between its halves. Restored the gate: compute useViewportRepaintPath before the intercept and only redirect to viewportRepaint when the host is actually a viewport-repaint host. Also restored the fullRender fallback for the firstChanged < viewportTop branch and propagated IME cursor write failure from #writeRenderBufferAndReanchorImeCursor so callers detect terminal detach. Updated affected #3684-added tests to use isProcessTerminal: true on their VirtualTerminals where they test viewport-repaint-specific behavior, and regenerated render golden fixtures. Issue: #1979 Confidence: high Scope-risk: narrow Reversibility: revert-commit Tested: full TUI suite + issue-1979 Korean wrap test Not-tested: real multiplexer session (CI covers via env injection) Co-authored-by: gaebal-gajae <gaebal-gajae@users.noreply.github.com>
Problem
Fix TUI transcript clipping, disappearance, and duplicate Tool output when resize/reflow is coalesced with streaming output.
bd5aea72(origin/devduring the observed run).ROW 034andROW 124.Reproduction
The developer-local interactive dogfood harness uses the production
ProcessTerminal,TUI,ToolExecutionComponent, and bottom-pinnedCustomEditor.Change
Verification
Interactive dogfood
The production-component terminal run displayed
ROW 001throughROW 180exactly once and in order. It exercised the ToolExecution card and bottom-pinned input rather than a synthetic text-only renderer.The current TUI patch is unchanged after rebasing onto upstream
dev; it is byte-identical to the locally dogfooded TUI revision.Automated
bun --cwd=packages/tui run checkpassed.Scope
The behavioral change is confined to TUI frame admission, reflow, repaint, and temporary ownership-transfer handling.
Upstream
devbase:1ccb5f84350ad4e3f0be286e91ded6fc39982df5Head:
77a4b1d567c7213671045ce291a98ec4d2e38ca5