cmux-tui: preserve smallest sizing through web reattach

Merged
LA
3mo ago
+4800-554

Follow-up to https://github.com/manaflow-ai/cmux/pull/8031.

The web client now republishes its unchanged local viewport whenever an overflow recovery creates a new attach stream. Both byte and render attachments use bounded recovery, so the server never loses the client from the smallest-size calculation. The byte fallback also uses Menlo, matching the styled renderer and Ghostty default.

The first commit is the failing behavior test. The second commit fixes it.

Verified:

  • 114 web tests and production build
  • 23 TypeScript SDK tests and build
  • strict Rust workspace Clippy
  • live 120x40 plus 80x50 clients resolve to 80x40
  • concurrent TUI input, smaller-client detach expansion, and unsized-tab inheritance
  • Cmux Lite and web share Monokai chrome, terminal background, Menlo, and blinking bar cursor

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Preserves the smallest shared terminal grid across recovery and visibility changes, matching tmux sizing semantics and bounds. Client presence now refreshes after remote recovery, and remote subscription recovery is linearized and coalesced so pane labels and menus stay accurate.

  • New Features

    • Per‑pane controls in the web UI: bottom‑left clients indicator and nested menu to include/exclude, “use only this client”, “use all”, or detach.
    • Protocol/CLI: set-client-sizing (with exclusive) and release-surface-size; list-clients adds size_participating and transport: "local" | "unix" | "ws".
    • SDK/UI: CmuxClient#setClientSizing, useOnlyClientSizing, useAllClientSizing; Rust release_surface_size; pane border label shows clients and shared minimum; render attach events include OverflowEvent.
  • Bug Fixes

    • Route all resizes through shared client sizing; clamp terminal sizes (max 10,000); match tmux ignore‑size defaults; ignore unattached fits.
    • Attach recovery: bounded retry with stabilization; apply fits only with an active stream; re‑publish unchanged local fit after reattach.
    • Remote recovery: linearize and coalesce subscription refresh; update client presence so labels and client menus stay correct.
    • UI: nested context menus stay in the viewport with stable focus/hover; coalesced refresh prevents overlapping presence updates; Menlo added to the byte fallback font; preserve sidebar focus during plugin sync.

Summary by CodeRabbit

  • Bug Fixes
    • Improved render/terminal attachment recovery after overflow events using retry + stabilization, with safer cleanup to prevent resize during teardown.
    • Prevented terminal fit/surface “echo back” loops by clarifying resize behavior and only applying fit while an attachment is active.
    • Stopped key/mouse/paste handling from unintentionally reasserting viewer sizes, and fixed sidebar focus retention during plugin sync.
    • Improved web terminal font fallback by adding Menlo to the monospace stack.
  • Tests
    • Added coverage for sizing during overflow reattachment and verifying no resize after disconnect.
    • Added coverage ensuring user inputs never trigger unintended viewer size reassertions.
  • Chores
    • Tightened a sidebar smoke-test assertion for more reliable matching.