diff --git a/apps/web/src/components/terminal.tsx b/apps/web/src/components/terminal.tsx index f418772..e126688 100644 --- a/apps/web/src/components/terminal.tsx +++ b/apps/web/src/components/terminal.tsx @@ -229,7 +229,10 @@ export const TerminalComponent: React.FC = ({ fitAddonRef.current.fit(); const { cols, rows } = termRef.current; if (cols !== oldCols || rows !== oldRows) { - termRef.current.refresh(0, rows - 1); + // Delay refresh to next frame so renderer can process resize first + requestAnimationFrame(() => { + termRef.current?.refresh(0, rows - 1); + }); } if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "resize", cols, rows })); @@ -290,6 +293,19 @@ export const TerminalComponent: React.FC = ({ }); resizeObserver.observe(container); + // Also listen for window resize as fallback (ResizeObserver might miss some cases) + let windowResizeTimeout: ReturnType; + const handleWindowResize = () => { + clearTimeout(windowResizeTimeout); + windowResizeTimeout = setTimeout(() => { + requestAnimationFrame(() => { + if (!container.isConnected) return; + fitTerminal(); + }); + }, 250); + }; + window.addEventListener("resize", handleWindowResize); + // Refit after mobile header auto-hides (3s delay + 0.3s transition) const headerHideTimeout = setTimeout(() => { fitTerminal(); @@ -322,8 +338,10 @@ export const TerminalComponent: React.FC = ({ return () => { clearTimeout(resizeTimeout); + clearTimeout(windowResizeTimeout); clearTimeout(headerHideTimeout); resizeObserver.disconnect(); + window.removeEventListener("resize", handleWindowResize); document.removeEventListener("visibilitychange", handleVisibilityChange); if (ws) { ws.close();