diff --git a/apps/web/src/components/terminal.tsx b/apps/web/src/components/terminal.tsx index 6e13cad..8e15041 100644 --- a/apps/web/src/components/terminal.tsx +++ b/apps/web/src/components/terminal.tsx @@ -229,9 +229,16 @@ export const TerminalComponent: React.FC = ({ const { cols, rows } = termRef.current; console.log('[Terminal] fitTerminal result:', cols, rows, '(was:', oldCols, oldRows + ')'); + // Force explicit resize to ensure xterm.js updates its canvas + if (cols !== oldCols || rows !== oldRows) { + termRef.current.resize(cols, rows); + } + // Always refresh on initial load or when dimensions change requestAnimationFrame(() => { - termRef.current?.refresh(0, rows - 1); + if (termRef.current) { + termRef.current.refresh(0, termRef.current.rows - 1); + } }); if (ws.readyState === WebSocket.OPEN) { @@ -254,6 +261,14 @@ export const TerminalComponent: React.FC = ({ requestAnimationFrame(() => fitTerminal()); }); + // Delayed refit after WebSocket connects to ensure backend is synced + const delayedFitTimeout = setTimeout(() => { + if (ws.readyState === WebSocket.OPEN) { + console.log('[Terminal] Delayed refit after WebSocket connect'); + fitTerminal(); + } + }, 1000); + // Handle terminal input term.onData((data) => { if (ws.readyState !== WebSocket.OPEN) return; @@ -343,6 +358,7 @@ export const TerminalComponent: React.FC = ({ clearTimeout(resizeTimeout); clearTimeout(windowResizeTimeout); clearTimeout(headerHideTimeout); + clearTimeout(delayedFitTimeout); resizeObserver.disconnect(); window.removeEventListener("resize", handleWindowResize); document.removeEventListener("visibilitychange", handleVisibilityChange);