Files
fusion/packages/dashboard/app/components/TerminalModal.css
gsxdsm 9f10767254 feat: share terminal sessions across browsers
Terminal PTYs already lived in a server-side registry whose WebSocket attach
supports several viewers, but the tab list is per-browser localStorage and a
browser with no stored tabs skipped the session listing entirely (FN-7686's
cold-open optimization) and spawned its own PTY. Two people on one Fusion —
or one person in a second browser — therefore never saw each other's
terminals and quietly accumulated parallel sessions.

- A zero-tab client adopts the server's sessions, oldest first, so every
  client converges on the same set. A client with stored tabs still only
  validates them; adopting there would resurrect tabs the user closed here.
- FN-7686's guarantee weakens from "never waits" to "waits at most
  ADOPT_LIST_TIMEOUT_MS (1.5s), then behaves exactly as before": auto-create
  fires on a 0ms timer, so a background list can never win that race. Its
  regression test and comment now state the bounded contract.
- Closing a tab asks which close was meant — detach here (PTY keeps running
  for other viewers) or end the session for everyone. alwaysAsk is set: this
  gates an informed choice and both wrong answers are destructive. Desktop
  and mobile close controls both route through it.
- A footer Reopen control lists sessions the server still runs that this
  browser is not showing and reattaches to them, so detaching is not a
  one-way door.

Also fixes a multi-viewer data bug: the attach called
getScrollbackAndClearPending(), discarding queued output and deleting a slice
of every already-attached viewer's live stream. It now flushes pending output
to existing viewers first, then reads scrollback for the newcomer, so each
viewer receives it exactly once.

Drive-by: closeTab's dependency array was empty while its body reads
projectId, freezing it at the first render's project.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:13:53 -07:00

60 KiB