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 (