From 118dcb59afec69a35f0f1b3625b8dfbee69089fc Mon Sep 17 00:00:00 2001 From: centdix Date: Sat, 21 Feb 2026 00:35:17 +0000 Subject: [PATCH] fix: defer terminal spawn until client reports actual dimensions Instead of spawning with hardcoded 120x30 on WebSocket open, wait for the client's first resize message with real fitted dimensions. Fixes terminal not taking full width/height since script+pipes PTY can't be resized after creation. Co-Authored-By: Claude Opus 4.6 --- dev-dashboard/backend/src/server.ts | 47 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/dev-dashboard/backend/src/server.ts b/dev-dashboard/backend/src/server.ts index 7837fff9d8..2bc4130e61 100644 --- a/dev-dashboard/backend/src/server.ts +++ b/dev-dashboard/backend/src/server.ts @@ -33,6 +33,7 @@ function errorResponse(message: string, status = 500): Response { interface WsData { worktree: string; + attached: boolean; } function makeCallbacks(ws: { send: (data: string) => void; readyState: number }) { @@ -59,7 +60,7 @@ Bun.serve({ const wsMatch = url.pathname.match(/^\/ws\/(.+)$/); if (wsMatch) { const worktree = decodeURIComponent(wsMatch[1]); - const upgraded = server.upgrade(req, { data: { worktree } }); + const upgraded = server.upgrade(req, { data: { worktree, attached: false } }); if (upgraded) return undefined as unknown as Response; return new Response("WebSocket upgrade failed", { status: 400 }); } @@ -72,29 +73,11 @@ Bun.serve({ }, websocket: { - async open(ws) { - const { worktree } = ws.data; - const cols = 120; - const rows = 30; - - try { - await attach(worktree, cols, rows); - - const { onData, onExit } = makeCallbacks(ws); - setCallbacks(worktree, onData, onExit); - - const scrollback = getScrollback(worktree); - if (scrollback) { - ws.send(JSON.stringify({ type: "scrollback", data: scrollback })); - } - } catch (err: unknown) { - const message = err instanceof Error ? err.message : String(err); - ws.send(JSON.stringify({ type: "error", message })); - ws.close(); - } + open(_ws) { + // Wait for the client to send its actual dimensions before spawning }, - message(ws, message) { + async message(ws, message) { try { const msg = JSON.parse(typeof message === "string" ? message : new TextDecoder().decode(message)); const { worktree } = ws.data; @@ -104,7 +87,25 @@ Bun.serve({ write(worktree, msg.data); break; case "resize": - resize(worktree, msg.cols, msg.rows); + if (!ws.data.attached) { + // First resize = client reporting actual dimensions. Spawn now. + ws.data.attached = true; + try { + await attach(worktree, msg.cols, msg.rows); + const { onData, onExit } = makeCallbacks(ws); + setCallbacks(worktree, onData, onExit); + const scrollback = getScrollback(worktree); + if (scrollback) { + ws.send(JSON.stringify({ type: "scrollback", data: scrollback })); + } + } catch (err: unknown) { + const errMsg = err instanceof Error ? err.message : String(err); + ws.send(JSON.stringify({ type: "error", message: errMsg })); + ws.close(); + } + } else { + resize(worktree, msg.cols, msg.rows); + } break; } } catch {