Skip to content

fix(runtime): disable size debounce on emulator path so term.resize() reflows synchronously (14764)#6

Merged
beorn merged 1 commit into
mainfrom
fix/termless-resize-14764
May 21, 2026
Merged

fix(runtime): disable size debounce on emulator path so term.resize() reflows synchronously (14764)#6
beorn merged 1 commit into
mainfrom
fix/termless-resize-14764

Conversation

@beorn
Copy link
Copy Markdown
Owner

@beorn beorn commented May 19, 2026

Summary

  • 4 resize tests in tests/features/termless-coverage.test.tsx (red→green) — termless term.resize(cols, rows) now propagates to layout within the test's 100ms settle window
  • Surface bug: km-silvery.termless-resize-reflow-4-fails — wider/narrower/shorter/taller resizes left layout frozen on pre-resize geometry
  • Root cause: the autoTerm wraps the caller's stdout with createSize which applies a 200ms trailing-edge debounce. That coalescing is right for real terminals (tmux/cmux/Ghostty SIGWINCH bursts) but in emulator paths the resize is driven explicitly via term.resize(...) — no burst possible. The 200ms delay just pushes the resize event past the test's settle.
  • Fix: thread a resizeCoalesceMs option from run.tsx emulator branch through app.run → autoTerm → createNodeTermcreateSize. Emulator branch passes 0; options-path with fake real-stream keeps the 200ms default (run-writable.test.tsx burst-coalescing tests still green). Real-PTY callers go through run.tsx's Term branch where term is injected as a provider — autoTerm path isn't used, debounce contract preserved end-to-end.

Files (+25 LOC of code, ~50 LOC of doc comments)

  • packages/ag-term/src/ansi/types.ts — new CreateTermOptions.resizeCoalesceMs
  • packages/ag-term/src/ansi/term.ts — propagate option to createSize
  • packages/ag-term/src/runtime/create-app.tsx — new AppRunOptions.resizeCoalesceMs; thread to autoTerm creation
  • packages/ag-term/src/runtime/run.tsx — emulator branch passes resizeCoalesceMs: 0
  • tests/features/termless-coverage.test.tsx — flip 4 test.failstest, update comments to "regression guard"

Test plan

  • Resize tests pass: bun vitest run --project vendor vendor/silvery/tests/features/termless-coverage.test.tsx (15/15)
  • Features suite green: bun vitest run --project vendor vendor/silvery/tests/features/ (2441 pass | 1 expected fail | 4 skipped)
  • Full silvery suite green: 6591 pass | 2 expected fail (run-writable burst-coalescing tests still pass — options path retains 200ms default)
  • No new lint/typecheck errors
  • Verified RED→GREEN — pre-fix run with test.fails flipped showed 4 distinct failures (border-width unchanged, Right Column missing at narrow width, top border missing after shrink, Row 9 missing after grow)

… reflows synchronously (14764)

The autoTerm created inside createApp wraps the caller's stdout with `createSize`
which applies a 200ms trailing-edge debounce on `resize` events. That coalescing
is correct for real terminals receiving SIGWINCH bursts from tmux/cmux/Ghostty
multiplexer resizes, but in emulator-backed runs (`run(<App />, createTermless())`)
the resize is driven explicitly via `term.resize(cols, rows)` — no burst possible.
The 200ms delay just pushes the resize event past the test's 100ms settle window,
leaving the layout frozen on the pre-resize geometry.

Surface symptom: km-silvery.termless-resize-reflow-4-fails — 4 tests in
termless-coverage.test.tsx that resize the terminal and assert post-resize content
all failed because the runtime never re-laid-out: top borders stay at the old
width, columns clipped to the old narrow viewport never reflow, taller terminals
show stale buffer content instead of the new full-height layout.

Fix: thread a `resizeCoalesceMs` option from run.tsx emulator branch through
app.run → autoTerm → createNodeTerm → createSize. The emulator branch passes
0 (no debouncing); the options-path with a fake real-stream still uses the
200ms default so existing burst-coalescing regression tests
(run-writable.test.tsx "Screen sees only final coalesced width") keep their
contract. Real-PTY callers go through run.tsx's Term branch where the caller
injects their own Term — autoTerm path isn't used at all, so the default
debounce is preserved end-to-end.

Tests:
- vendor/silvery/tests/features/termless-coverage.test.tsx — 4 resize tests
  flipped from test.fails → test, all passing
- 2441 features tests pass | 1 expected fail (unrelated termless test.fails)
- run-writable.test.tsx burst-coalescing assertions still pass (options path
  retains 200ms default)

Touched files (+25 LOC of code, ~50 LOC of doc comments):
- packages/ag-term/src/ansi/types.ts — CreateTermOptions.resizeCoalesceMs
- packages/ag-term/src/ansi/term.ts — propagate option to createSize
- packages/ag-term/src/runtime/create-app.tsx — AppRunOptions.resizeCoalesceMs;
  thread to autoTerm
- packages/ag-term/src/runtime/run.tsx — emulator branch passes 0
- tests/features/termless-coverage.test.tsx — flip test.fails to test, update
  comments to "regression guard"
@beorn beorn merged commit 767de21 into main May 21, 2026
4 checks passed
@beorn beorn deleted the fix/termless-resize-14764 branch May 21, 2026 17:39
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.

1 participant