From 5c998f5bf9b448d12bbd93908e1462c11c7ae7aa Mon Sep 17 00:00:00 2001 From: Fusion Date: Sun, 24 May 2026 20:13:10 +0200 Subject: [PATCH] fix: revert all terminal resize fixes that caused layout issues Reverted terminal.tsx, terminal_session.py, and terminal.py to clean state from before the resize debugging saga. Removed: - Debug console.log statements - Explicit term.resize() calls that broke xterm.js - position: relative CSS overrides on .xterm - stty -echo wrapper and asyncio.sleep delay - Extra requestAnimationFrame refresh calls Kept: - Mobile terminal features (special keys, modifiers, font size) - ResizeObserver for container resize detection - Basic fit() and WebSocket resize messaging --- apps/api/src/services/terminal_session.py | 18 +++------ apps/web/src/components/terminal.tsx | 49 ++--------------------- 2 files changed, 9 insertions(+), 58 deletions(-) diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index f66632a..396f9d1 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -145,27 +145,21 @@ class TerminalSession: # Only resize if dimensions actually changed if cols == self._cols and rows == self._rows: - logger.info(f"resize() skipped for session {self.session_id}: already {cols}x{rows}") return self._cols = cols self._rows = rows logger.info(f"resize() called for session {self.session_id}: {cols}x{rows}") - - # Set host PTY size self._set_terminal_size(cols, rows) # Docker exec doesn't forward PTY resize to the container process, # so we need to explicitly set the size inside the container shell. - # Send on every resize so the container shell always matches the frontend. - # Use stty -echo to hide command output, then re-enable echo - # Add small delay to ensure shell is ready to receive commands - await asyncio.sleep(0.1) - stty_cmd = ( - f"stty -echo; stty cols {cols} rows {rows}; stty echo\n" - ).encode() - await self.write_input(stty_cmd) - logger.info(f"Sent stty resize to container for session {self.session_id}: {cols}x{rows}") + # Only do this on the first resize to avoid interfering with user input. + if not getattr(self, '_stty_sent', False): + self._stty_sent = True + stty_cmd = f"stty cols {cols} rows {rows}\n".encode() + await self.write_input(stty_cmd) + logger.info(f"Sent stty command to container for session {self.session_id}: {cols}x{rows}") async def reset(self) -> None: """Reset the session by killing the process and clearing state.""" diff --git a/apps/web/src/components/terminal.tsx b/apps/web/src/components/terminal.tsx index 8e15041..68434e4 100644 --- a/apps/web/src/components/terminal.tsx +++ b/apps/web/src/components/terminal.tsx @@ -216,36 +216,17 @@ export const TerminalComponent: React.FC = ({ // Fit terminal and notify backend const fitTerminal = () => { - if (!fitAddonRef.current || !termRef.current) { - console.log('[Terminal] fitTerminal: refs not ready'); - return; - } + if (!fitAddonRef.current || !termRef.current) return; const oldCols = termRef.current.cols; const oldRows = termRef.current.rows; - - console.log('[Terminal] fitTerminal called, container dims:', container?.offsetWidth, container?.offsetHeight); - fitAddonRef.current.fit(); 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 + // Force refresh if dimensions changed if (cols !== oldCols || rows !== oldRows) { - termRef.current.resize(cols, rows); + termRef.current.refresh(0, rows - 1); } - - // Always refresh on initial load or when dimensions change - requestAnimationFrame(() => { - if (termRef.current) { - termRef.current.refresh(0, termRef.current.rows - 1); - } - }); - if (ws.readyState === WebSocket.OPEN) { - console.log('[Terminal] Sending resize:', cols, rows); ws.send(JSON.stringify({ type: "resize", cols, rows })); - } else { - console.log('[Terminal] WebSocket not open, state:', ws.readyState); } }; @@ -261,14 +242,6 @@ 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; @@ -311,19 +284,6 @@ 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(); @@ -356,11 +316,8 @@ export const TerminalComponent: React.FC = ({ return () => { clearTimeout(resizeTimeout); - clearTimeout(windowResizeTimeout); clearTimeout(headerHideTimeout); - clearTimeout(delayedFitTimeout); resizeObserver.disconnect(); - window.removeEventListener("resize", handleWindowResize); document.removeEventListener("visibilitychange", handleVisibilityChange); if (ws) { ws.close();