fix(frontend): refit web agent cells after opening a new one (#61 web regression)

The web shell never adopted the desktop's #61 fix: WebAgentCell/WebWorkspace
didn't pass any refitSignal to TerminalView, so a cell opened after another
kept a stale xterm scaling — desktop "fixed" it via an incidental window
resize, but mobile has no such escape hatch.

- TerminalView: a refit landing on a transient 0x0 container now reschedules
  on the next few frames instead of giving up for good (bounded retries),
  closing the independent timing gap Architect identified. refitSignal stays
  the single explicit-refit mechanism; the terminal/PTY is never recreated,
  and resize is still pushed to the PTY only when rows/cols actually change.
- WebAgentCell: new optional refitSignal prop, forwarded to TerminalView
  as-is (no key change, no remount).
- WebWorkspace/LiveProjectPanel: new cellLayoutVersion counter, the web
  equivalent of desktop useLayout.layoutVersion, bumped on every cell
  open/close and forwarded as refitSignal to the visible WebAgentCell.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 21:21:52 +02:00
parent a197197a90
commit 3a18556ffa
6 changed files with 354 additions and 9 deletions

View File

@ -338,10 +338,27 @@ export function TerminalView({
let rafId = 0;
let lastRows = term.rows;
let lastCols = term.cols;
// A refit can land on a transient 0x0 container (mount, or a structural
// layout mutation, before the box has actually settled). Previously this
// just gave up — fine on desktop, where a later window resize always
// re-triggers the ResizeObserver and retries, but on mobile (no window
// resize possible) the cell was then stuck unfit forever. So a zero size
// now reschedules on the next few frames instead of abandoning — bounded,
// so a container that is genuinely never laid out (e.g. headless tests)
// doesn't spin forever.
const MAX_ZERO_SIZE_RETRIES = 8;
let zeroSizeRetries = 0;
const refit = () => {
rafId = 0;
if (disposed) return;
if (container.clientWidth === 0 || container.clientHeight === 0) return;
if (container.clientWidth === 0 || container.clientHeight === 0) {
if (zeroSizeRetries < MAX_ZERO_SIZE_RETRIES) {
zeroSizeRetries += 1;
rafId = requestAnimationFrame(refit);
}
return;
}
zeroSizeRetries = 0;
try {
fit.fit();
} catch {