diff --git a/src/server.ts b/src/server.ts index 4b9a0ee..1b155e3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,8 +1,6 @@ // Core server: HTTP + WebSocket + SSE import type { ServerWebSocket } from "bun"; -import { ansiToHtml } from "./ansi"; -import { splitAnsiCarryover } from "./ansi-carryover"; import { appendOutput, createDevice, @@ -19,7 +17,12 @@ import { updateLastSeen, updateSessionStats, } from "./db"; -import { createTerminal, disposeTerminal, type TerminalSession } from "./terminal"; +import { + createTerminal, + disposeTerminal, + serializeAsHTML, + type TerminalSession, +} from "./terminal"; import type { AnswerResponse, ClientMessage, @@ -494,35 +497,29 @@ const server = Bun.serve({ // Handle output message if (msg.type === "output") { const sessionId = ws.data.sessionId; - // Join with any saved carryover from previous chunk - const prevCarry = ansiCarryovers.get(sessionId) || ""; - const combined = prevCarry ? prevCarry + msg.data : msg.data; - // Determine if new tail is an incomplete control sequence and split - const [body, carry] = splitAnsiCarryover(combined); - if (carry) { - console.debug( - `Session ${sessionId}: ANSI carryover detected (${carry.length} bytes)`, - ); - ansiCarryovers.set(sessionId, carry); - } else if (prevCarry) { - console.debug(`Session ${sessionId}: ANSI carryover resolved`); - // Clear carry if previously set and now resolved - ansiCarryovers.delete(sessionId); - } - - appendOutput(sessionId, body); // Store raw ANSI without trailing incomplete fragment - - // Write to terminal emulator const termSession = sessionTerminals.get(sessionId); - if (termSession) { - termSession.terminal.write(msg.data); + + if (!termSession) { + console.error(`No terminal for session ${sessionId}`); + return; } + // Write to terminal emulator (handles all ANSI sequences) + termSession.terminal.write(msg.data); + + // Store raw ANSI in database + appendOutput(sessionId, msg.data); + + // Serialize current terminal state as HTML + const html = serializeAsHTML(termSession); + + // Broadcast to dashboards broadcastSSE({ type: "output", session_id: sessionId, - data: ansiToHtml(body), // Parse for display + data: html, }); + return; }