feat(web): parité layout AppImage desktop + switch TUI naïf/CLI custom — #208

This commit is contained in:
2026-08-15 09:08:42 +02:00
parent 780f91a211
commit 7cdb97ae89
7 changed files with 914 additions and 661 deletions

View File

@ -0,0 +1,83 @@
import { describe, it, expect } from "vitest";
import type { WebSocketLike } from "./wsLiveClient";
import { WsLiveClient } from "./wsLiveClient";
class FakeSocket implements WebSocketLike {
sent: string[] = [];
onopen: (() => void) | null = null;
onmessage: ((event: { data: string }) => void) | null = null;
onerror: ((event: unknown) => void) | null = null;
onclose: (() => void) | null = null;
send(data: string): void {
this.sent.push(data);
}
close(): void {
this.onclose?.();
}
receive(frame: unknown): void {
this.onmessage?.({ data: JSON.stringify(frame) });
}
}
function connectedClient(): { client: WsLiveClient; socket: FakeSocket } {
const socket = new FakeSocket();
const client = new WsLiveClient({
wsUrl: "wss://host",
socketFactory: () => {
queueMicrotask(() => socket.onopen?.());
return socket;
},
});
return { client, socket };
}
describe("WsLiveClient structured chat routing", () => {
it("routes chat.output to the per-session chat sink", async () => {
const { client, socket } = connectedClient();
await client.ensureConnected();
const received: unknown[] = [];
client.setChatSink("chat-1", (chunk) => received.push(chunk));
socket.receive({
kind: "chat.output",
payload: {
sessionId: "chat-1",
chunk: { kind: "final", content: "done" },
},
});
expect(received).toEqual([{ kind: "final", content: "done" }]);
client.removeChatSink("chat-1");
socket.receive({
kind: "chat.output",
payload: {
sessionId: "chat-1",
chunk: { kind: "final", content: "ignored" },
},
});
expect(received).toEqual([{ kind: "final", content: "done" }]);
});
it("clears structured chat sinks on disconnect", async () => {
const { client, socket } = connectedClient();
await client.ensureConnected();
const received: unknown[] = [];
client.setChatSink("chat-1", (chunk) => received.push(chunk));
client.disconnect();
expect(client.getConnectionState()).toBe("closed");
socket.receive({
kind: "chat.output",
payload: {
sessionId: "chat-1",
chunk: { kind: "final", content: "dropped" },
},
});
expect(received).toEqual([]);
});
});