From 51b5d723acc14a4bf26e919b00cd82310b963037 Mon Sep 17 00:00:00 2001 From: Fusion Date: Sun, 24 May 2026 21:15:04 +0200 Subject: [PATCH] fix: terminal clear on reset and data loss for text starting with { Frontend: - Clear xterm.js screen when receiving 'connected' status after reset - Send resize message after clearing to ensure proper dimensions - Fixes terminal artifacts after reset Backend: - Fix data loss bug: text starting with '{' but not valid JSON was silently dropped - Now writes such text to session as regular input - Fixes missing characters when user types '{' --- apps/api/src/api/terminal.py | 3 ++- apps/web/src/components/terminal.tsx | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/apps/api/src/api/terminal.py b/apps/api/src/api/terminal.py index 22541ea..38b5116 100644 --- a/apps/api/src/api/terminal.py +++ b/apps/api/src/api/terminal.py @@ -192,7 +192,8 @@ async def _write_loop(session_ref: SessionRef, websocket, instance_id: str) -> N continue except json.JSONDecodeError: - pass + # Not a valid JSON control message, treat as regular input + await session.write_input(text.encode("utf-8")) else: await session.write_input(text.encode("utf-8")) elif message["type"] == "websocket.disconnect": diff --git a/apps/web/src/components/terminal.tsx b/apps/web/src/components/terminal.tsx index e3ae4ff..fe8e969 100644 --- a/apps/web/src/components/terminal.tsx +++ b/apps/web/src/components/terminal.tsx @@ -115,6 +115,20 @@ export const TerminalComponent: React.FC = ({ if (msg.status === "connected") { setStatus("connected"); setError(null); + // Clear terminal and refit after reset/reconnect + if (termRef.current) { + termRef.current.clear(); + requestAnimationFrame(() => { + if (fitAddonRef.current && termRef.current) { + fitAddonRef.current.fit(); + const { cols, rows } = termRef.current; + const currentWs = wsRef.current; + if (currentWs?.readyState === WebSocket.OPEN) { + currentWs.send(JSON.stringify({ type: "resize", cols, rows })); + } + } + }); + } } else if (msg.status === "resetting") { setStatus("resetting"); } @@ -210,11 +224,7 @@ export const TerminalComponent: React.FC = ({ const container = terminalRef.current; let ws: WebSocket; - // Open xterm immediately - term.open(container); - ws = connectWebSocket(); - - // Fit terminal and notify backend + // Define fitTerminal before connectWebSocket so it's available in onmessage const fitTerminal = () => { if (!fitAddonRef.current || !termRef.current) return; const oldCols = termRef.current.cols; @@ -238,6 +248,10 @@ export const TerminalComponent: React.FC = ({ }); }); + // Open xterm immediately + term.open(container); + ws = connectWebSocket(); + // Refit after font load (metrics may change) document.fonts.ready.then(() => { requestAnimationFrame(() => fitTerminal());