From f26ea6a4a521fb0b05759d05768df8418afd4f3f Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:07:27 -0700 Subject: [PATCH] fix(terminal): keep SSH reconnect banner above the xterm canvas (#10242) Portal the non-blocking reconnect banner into each pane container so WebGL compositing cannot paint terminal text through it, raise z-index above pane-local chrome, and use a fully opaque card background. --- .../components/terminal-pane/TerminalPane.tsx | 27 ++++++++++++------- .../TerminalSshReconnectOverlay.test.tsx | 2 +- .../TerminalSshReconnectOverlay.tsx | 5 ++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/components/terminal-pane/TerminalPane.tsx b/src/renderer/src/components/terminal-pane/TerminalPane.tsx index 3c556118d..a7543e8df 100644 --- a/src/renderer/src/components/terminal-pane/TerminalPane.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalPane.tsx @@ -2896,16 +2896,23 @@ export default function TerminalPane({ onRestartDaemon={() => daemonActions.setPending('restart')} /> )} - {showSshReconnectOverlay && sshReconnectTargetId && sshReconnectStatus ? ( - - ) : null} + {/* Why: portal into the pane so the banner stacks above the xterm canvas (sibling mount painted under WebGL). */} + {showSshReconnectOverlay && sshReconnectTargetId && sshReconnectStatus + ? managedPanes.map((pane) => + createPortal( + , + pane.container, + `ssh-reconnect-${pane.id}` + ) + ) + : null} {isActive && ( { expect(screen.getByText(/This terminal is waiting for devbox/)).toBeInTheDocument() expect(screen.getByRole('status')).toBeInTheDocument() const banner = container.querySelector('[data-terminal-ssh-reconnect-banner="disconnected"]') - expect(banner).toHaveClass('inset-x-3', 'bottom-3') + expect(banner).toHaveClass('inset-x-3', 'bottom-3', 'z-40') expect(banner).not.toHaveClass('inset-0', 'bg-background/75') await user.click(screen.getByRole('button', { name: 'Connect' })) diff --git a/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx b/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx index a90e16c2a..b0506a28a 100644 --- a/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx @@ -136,13 +136,14 @@ export function TerminalSshReconnectOverlay({ } }, [isConnecting, mountedRef, setSshConnectionState, sshOwnerEnvironmentId, targetId]) + // Why: z-40 clears pane-local chrome (focus rim z-30); bg-card is fully opaque so terminal text cannot paint through. return (