Skip to content

fix(terminal): flush pending PTY resize on teardown (tmux pane-bleed)#1010

Open
griffinwork40 wants to merge 2 commits into
crynta:mainfrom
griffinwork40:fix/tmux-winsize-desync
Open

fix(terminal): flush pending PTY resize on teardown (tmux pane-bleed)#1010
griffinwork40 wants to merge 2 commits into
crynta:mainfrom
griffinwork40:fix/tmux-winsize-desync

Conversation

@griffinwork40

@griffinwork40 griffinwork40 commented Jul 16, 2026

Copy link
Copy Markdown

What

The terminal PTY winsize could lag the rendered xterm grid after a resize, so tmux (and any multiplexer) drew pane content across the dividers. This commits any pending PTY resize on every renderer-pool teardown path so the winsize always converges to the grid.

Why

Closes #981.

The renderer pool refits the grid synchronously on resize but debounces the PTY resize by PTY_RESIZE_DEBOUNCE_MS (256ms). If a tmux leaf is hidden/evicted inside that window, detachSlotFromLeaf cleared the pending timer without flushing it, so the queued winsize update was dropped. releaseSlot then reported the uncommitted grid size as the session size, and a retained fast rebind suppressed the now-size-matched resizePty, so the PTY and grid stayed diverged until some unrelated later resize resynced them. tmux positions panes by absolute coordinates against the size it was told, so the stale winsize smeared content across the pane dividers.

How

  • New pure helper pendingPtyResize(grid, committed) in rendererResize.ts: returns the size still owed to the PTY, or null when they already agree, and never emits a degenerate (<= 0) resize.
  • rendererPool.ts: flushPty (the debounce timer) and detachSlotFromLeaf (every teardown path) now both route through a single commitPendingResize() that calls the helper and updates slot.lastCols/lastRows. Dropping the queued resize on teardown is no longer possible, so the winsize cannot lag the grid.

Single concern, 3 files, no src-tauri/ changes, no new dependencies.

Testing

  • pnpm lint clean
  • pnpm check-types clean
  • pnpm test clean (264 tests; new rendererResize.test.ts covers match / grow / shrink, the degenerate-dimensions deny path, and the interrupted-debounce regression)
  • pnpm build and pnpm size clean (size well within budget)

Verification level is unit test plus a static trace of the resize path (ResizeObserver -> debounced resizePty -> pty_resize -> master.resize), matching the triage note in #981. I have not captured a live before/after tmux recording: the trigger is timing-dependent (hide/show a leaf within the 256ms debounce) and does not reproduce deterministically in a headless run. Glad to add an integration-level test around detachSlotFromLeaf if you prefer that over the pure-helper unit test.

  • pnpm lint clean
  • pnpm check-types clean
  • pnpm test clean
  • Manual smoke-test of the affected feature (timing-dependent; see note above)
  • (If you touched src-tauri/) cargo clippy - N/A, frontend only
  • (If you touched src-tauri/) cargo nextest run - N/A, frontend only
  • (If you changed a #[tauri::command] signature) - N/A, no command changes
  • (If UI) tested in pnpm tauri dev - N/A, no UI/visual change
  • Platforms tested: macOS (the logic is platform-agnostic; the desync is not platform-specific)
  • Shells tested (if relevant): N/A, not shell-specific
  • pnpm build + pnpm size clean (extra CI gates)

Screenshots / GIFs

N/A - no visual/UI change; this is terminal-to-PTY size plumbing.

Notes for reviewer

  • Follows the existing functional-core pattern: the size decision is a pure, tested function and the pool wiring stays thin.
  • This is the "minimal, single-concern fix plus a unit test" offered in Terminal: tmux content bleeds across panes after a resize (xterm grid vs PTY winsize desync) #981. detachSlotFromLeaf is the teardown path that dropped the resize; the debounce timer now shares the same commitPendingResize() so there is one source of truth for "what the PTY was last told".
  • Flagging that I understand the terminal subsystem is load-bearing and you may prefer to discuss before merging; happy to adjust scope or test shape.

Summary by CodeRabbit

  • Bug Fixes
    • Improved synchronization between the terminal’s rendered grid and its underlying PTY size, including during teardown to prevent content bleed.
  • Tests
    • Added new test coverage for pending PTY resize behavior, including growth, shrink, interrupted resize debounce, and invalid grid dimensions.

@griffinwork40
griffinwork40 requested a review from crynta as a code owner July 16, 2026 14:09
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c893a24f-a2d4-4f6a-864b-08d0bac19885

📥 Commits

Reviewing files that changed from the base of the PR and between bf63e0c and 588a7f8.

📒 Files selected for processing (3)
  • src/modules/terminal/lib/rendererPool.ts
  • src/modules/terminal/lib/rendererResize.test.ts
  • src/modules/terminal/lib/rendererResize.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/modules/terminal/lib/rendererResize.ts
  • src/modules/terminal/lib/rendererResize.test.ts
  • src/modules/terminal/lib/rendererPool.ts

📝 Walkthrough

Walkthrough

Changes

PTY resize synchronization

Layer / File(s) Summary
Pending resize contract and validation
src/modules/terminal/lib/rendererResize.ts, src/modules/terminal/lib/rendererResize.test.ts
Adds GridSize and pendingPtyResize, with tests for changed, unchanged, grow, shrink, and invalid dimensions.
Resize flush and teardown commits
src/modules/terminal/lib/rendererPool.ts
Centralizes PTY resize commits and applies pending dimensions during normal debounce flushing and slot detachment.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses Conventional Commits and accurately summarizes the terminal PTY resize teardown fix.
Linked Issues check ✅ Passed The changes implement the required teardown flush for pending PTY resizes and add coverage for the desync fix in #981.
Out of Scope Changes check ✅ Passed The PR stays within the terminal resize fix and test coverage, with no unrelated dependency or surface-area changes.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The renderer pool refits the xterm grid synchronously on resize but
debounces the PTY winsize update by PTY_RESIZE_DEBOUNCE_MS (256ms). If a
leaf was hidden or evicted inside that window, detachSlotFromLeaf cleared
the pending resize timer without flushing it, so the queued winsize
update was dropped and the PTY stayed behind the rendered grid.
releaseSlot then reported the uncommitted grid size as the session size,
and a retained fast rebind suppressed the size-matched resizePty, so the
two stayed diverged until an unrelated later resize resynced them.

tmux positions every pane by absolute coordinates against the size it was
told, so the stale winsize made it draw pane content across the dividers.

Extract the grid/PTY delta into a pure pendingPtyResize() helper and
commit it on every teardown path, not only when the debounce timer fires,
so the winsize can never lag the grid.

Refs crynta#981
@griffinwork40
griffinwork40 force-pushed the fix/tmux-winsize-desync branch from bf63e0c to 588a7f8 Compare July 16, 2026 14:15
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.

Terminal: tmux content bleeds across panes after a resize (xterm grid vs PTY winsize desync)

1 participant