diff --git a/packages/bruno-app/src/components/Devtools/Console/TerminalTab/index.js b/packages/bruno-app/src/components/Devtools/Console/TerminalTab/index.js index 5d1046070..4395d7d32 100644 --- a/packages/bruno-app/src/components/Devtools/Console/TerminalTab/index.js +++ b/packages/bruno-app/src/components/Devtools/Console/TerminalTab/index.js @@ -183,6 +183,29 @@ const openTerminalIntoContainer = async (container, sessionId) => { } }; +let fitFrameRef; +const fitTerminal = (activeSessionId, container) => { + if (!container) return; + + const instance = terminalInstances.get(activeSessionId); + if (!instance?.fitAddon) return; + + if (fitFrameRef) { + cancelAnimationFrame(fitFrameRef); + } + + fitFrameRef = requestAnimationFrame(() => { + fitFrameRef = null; + + // Avoid fitting when hidden/0-sized (common during tab switches/layout transitions) + if (container.offsetWidth === 0 || container.offsetHeight === 0) return; + + try { + instance.fitAddon.fit(); + } catch (e) {} + }); +}; + const TerminalTab = () => { const terminalRef = useRef(null); const [sessions, setSessions] = useState([]); @@ -223,22 +246,25 @@ const TerminalTab = () => { }, []); // Create new terminal session - const createNewSession = useCallback(async (cwd = null) => { - if (!window.ipcRenderer) return null; + const createNewSession = useCallback( + async (cwd = null) => { + if (!window.ipcRenderer) return null; - try { - const options = cwd ? { cwd } : {}; - const newSessionId = await window.ipcRenderer.invoke('terminal:create', options); - if (newSessionId) { - await loadSessions(newSessionId); - setActiveSessionId(newSessionId); - return newSessionId; + try { + const options = cwd ? { cwd } : {}; + const newSessionId = await window.ipcRenderer.invoke('terminal:create', options); + if (newSessionId) { + await loadSessions(newSessionId); + setActiveSessionId(newSessionId); + return newSessionId; + } + } catch (err) { + console.error('Failed to create terminal session:', err); } - } catch (err) { - console.error('Failed to create terminal session:', err); - } - return null; - }, [loadSessions]); + return null; + }, + [loadSessions] + ); // Listen for requests to open terminal at specific CWD useEffect(() => { @@ -339,31 +365,17 @@ const TerminalTab = () => { if (mounted) { const instance = terminalInstances.get(activeSessionId); - if (instance && instance.fitAddon) { - const onResize = () => { - try { - instance.fitAddon.fit(); - } catch (e) {} - }; - - window.addEventListener('resize', onResize); - - // Initial resize - setTimeout(() => { - try { - instance.fitAddon.fit(); - const { cols, rows } = instance.terminal; - if (cols && rows && window.ipcRenderer) { - window.ipcRenderer.send('terminal:resize', activeSessionId, { cols, rows }); - } - } catch (err) { - console.warn('Failed to perform initial resize:', err); + if (instance) { + try { + const { cols, rows } = instance.terminal; + if (cols && rows && window.ipcRenderer) { + window.ipcRenderer.send('terminal:resize', activeSessionId, { cols, rows }); } - }, 100); + } catch (err) { + console.warn('Failed to perform initial resize:', err); + } return () => { - window.removeEventListener('resize', onResize); - // Park terminal element when switching sessions if (instance.terminal && instance.terminal.element) { const host = ensureParkingHost(); @@ -386,6 +398,18 @@ const TerminalTab = () => { }; }, [activeSessionId]); + const onSessionMount = useCallback( + (node) => { + if (!node) return; + terminalRef.current = node; + fitTerminal(activeSessionId, node); + const ro = new ResizeObserver(() => fitTerminal(activeSessionId, node)); + ro.observe(node.parentNode); + return () => ro.disconnect(); + }, + [activeSessionId] + ); + return (
@@ -405,13 +429,9 @@ const TerminalTab = () => {
{isLoading ? ( -
- Loading sessions... -
+
Loading sessions...
) : sessions.length === 0 ? ( -
- No active sessions -
+
No active sessions
) : ( {
)}