From ab3641d00d67ce523bdd0649bc78e294eef7060a Mon Sep 17 00:00:00 2001 From: Augustin Date: Fri, 24 Apr 2026 17:59:48 +0200 Subject: [PATCH] fix(terminal): improve tab visibility checks and positioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add null check for container before accessing offsetHeight - Validate activeTabRef during initialization and fit operations - Check for display:none as visibility indicator - Simplify useEffect dependency array - Use absolute positioning for terminal wrapper/instance 💘 Generated with Crush Assisted-by: MiniMax-M2.7 via Crush --- web/src/components/Shell.jsx | 10 ++++++++-- web/src/styles/global.css | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/src/components/Shell.jsx b/web/src/components/Shell.jsx index d03f1d9..ca8ee6a 100644 --- a/web/src/components/Shell.jsx +++ b/web/src/components/Shell.jsx @@ -443,7 +443,12 @@ export default function Shell({ api }) { return } const container = document.getElementById(`terminal-${tab.id}`) - if (!container || container.offsetHeight === 0) { + if (!container) { + pending.push(setTimeout(() => tryInit(attempt + 1), 100)) + return + } + if (activeTabRef.current !== tab.id) return + if (container.offsetHeight === 0 || container.style.display === 'none') { pending.push(setTimeout(() => tryInit(attempt + 1), 100)) return } @@ -452,6 +457,7 @@ export default function Shell({ api }) { } requestAnimationFrame(() => { if (cancelled) return + if (activeTabRef.current !== tab.id) return const entry = tabsRef.current[tab.id] if (entry) entry.fitAddon.fit() }) @@ -462,7 +468,7 @@ export default function Shell({ api }) { cancelled = true pending.forEach(clearTimeout) } - }, [activeTab, tabs, initTerminal]) + }, [activeTab, initTerminal]) useEffect(() => { const iv = setInterval(() => { diff --git a/web/src/styles/global.css b/web/src/styles/global.css index fc8d13d..94e7be2 100644 --- a/web/src/styles/global.css +++ b/web/src/styles/global.css @@ -382,12 +382,12 @@ input::placeholder { color: var(--text-disabled); } } .shell-menu-divider { height: 1px; background: var(--border); margin: 4px 6px; } -.shell-xterm-wrapper { flex: 1; background: var(--bg); overflow: hidden; position: relative; } +.shell-xterm-wrapper { flex: 1; height: 100%; background: var(--bg); overflow: hidden; position: relative; } .shell-xterm-instance { - height: 100%; - padding: 4px; + position: absolute; + inset: 0; } -.shell-xterm-instance .xterm { height: 100%; padding: 4px; } +.shell-xterm-instance .xterm { height: 100%; } .connection-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; } .connection-dot.on { background: var(--success); box-shadow: 0 0 6px var(--success); }