fix(v0.4): mirror size-clamp bug + session manager page

The mirror looked broken because a backgrounded tab on one device still pinned
the shared PTY to its (default 80x24) size — so the device actively viewing got
a cramped terminal.

Fix: only an ACTIVELY-VIEWING client votes on PTY size.
- attachWs no longer seeds a default size vote (a join may be a hidden mirror)
- new 'blur' client message + clearClientDims(): a tab going hidden withdraws its
  size vote (still mirrors output); show() re-casts it
- PTY size = min cols/rows across clients that have actually reported dims
- session/protocol tests cover hidden-mirror-doesn't-clamp + blur

Session manager (separate page, for the 'too many sessions' problem):
- GET stays; add DELETE /live-sessions/:id and DELETE /live-sessions[?detached=1]
- manager.killById(); LiveSessionInfo gains cols/rows
- public/manage.html + manage.ts: list/open/kill sessions, kill-all / kill-detached,
  auto-refresh; 🗂 toolbar button; bundled as a 2nd esbuild entry

Verified: two concurrent clients mirror output + shared input; manage page
lists/kills (3→2→0). 225 tests green, tsc clean.
This commit is contained in:
Yaojia Wang
2026-06-19 11:04:38 +02:00
parent 22210fadbc
commit 021a514b2d
13 changed files with 467 additions and 44 deletions

View File

@@ -24,7 +24,7 @@ export const SESSION_ID_RE =
// ─── Type whitelist ───────────────────────────────────────────────────────────
const ALLOWED_TYPES = new Set<string>(['attach', 'input', 'resize', 'approve', 'reject'])
const ALLOWED_TYPES = new Set<string>(['attach', 'input', 'resize', 'blur', 'approve', 'reject'])
// ─── parseClientMessage ───────────────────────────────────────────────────────
@@ -73,6 +73,11 @@ export function parseClientMessage(raw: string): ParseResult {
return validateInput(obj)
}
// blur (v0.4) carries no payload — withdraw this client's size vote.
if (type === 'blur') {
return { ok: true, message: { type: 'blur' } }
}
// approve/reject (H3) carry no payload.
if (type === 'approve') {
return { ok: true, message: { type: 'approve' } }