feat(frontend): surfaces live web (workstate live, background, inbox, reconnect) (#13)

Lot F5 du chantier server/client mode : le client web consomme les frames
event.domain (B7) pour animer ses surfaces live.

- webLive.ts : consommation du flux event.domain (workstate, background,
  inbox).
- useLiveReconnect.ts : reconnexion du flux live.
- wsLiveClient.ts / index.ts : câblage du transport live.
- WebWorkspace.tsx : surfaces live branchées.
- Tests : WebWorkspaceLive.test.tsx, wsLiveClientReconnect.test.ts,
  WebApp.test.tsx.

Validé : frontend 753 tests verts, desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 23:37:02 +02:00
parent 1bc5217dbc
commit dd1d083a1a
8 changed files with 457 additions and 112 deletions

View File

@ -263,15 +263,19 @@ export class WsLiveClient {
this.setConnState("closed");
return;
}
// Only reconnect when there is a live terminal to re-attach; otherwise stay
// idle until the next explicit use.
this.setConnState("reconnecting");
// Reconnect while anything still needs the socket: a live terminal to
// re-attach (F3) OR an active domain-event subscription (F5 live surfaces).
// Otherwise stay idle until the next explicit use.
if (this.terminals.size > 0) {
this.setConnState("reconnecting");
for (const sub of this.terminals.values()) sub.onData(notice("déconnecté — reconnexion…"));
this.scheduleReconnect();
} else {
this.setConnState("reconnecting");
}
if (this.needsReconnect()) this.scheduleReconnect();
}
/** Whether the socket should auto-reconnect (a terminal or a live subscription). */
private needsReconnect(): boolean {
return this.terminals.size > 0 || this.domainEventHandler !== null;
}
private scheduleReconnect(): void {
@ -293,8 +297,8 @@ export class WsLiveClient {
await this.connect();
await this.reattachAll();
} catch {
// Still down: back off and retry (unless everything was detached/closed).
if (this.terminals.size > 0) this.scheduleReconnect();
// Still down: back off and retry (unless nothing needs the socket anymore).
if (this.needsReconnect()) this.scheduleReconnect();
}
}