From 57ecdc2ca289430740297fea8c236d8bcc6bbf20 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:06:39 -0700 Subject: [PATCH] fix(ssh): say what plain SSH is for beside its button, and polish the plain SSH window (#31732) * fix(ssh): point unclearable move conflicts at plain SSH, and polish the plain SSH window Conflicts retrying can't clear now say how to keep working on the host. The plain SSH terminal paints its theme background past the grid, and a failed open shows only its error, not an empty terminal. Co-Authored-By: Claude * fix(ssh): say what plain SSH is for beside its button, not in main's refusal text Main's refusal text also reaches clients without a plain SSH terminal, so the next step is shown next to the button wherever it renders. Also: a translucent theme isn't painted twice, and a failed open shrinks to its error. Co-Authored-By: Claude * fix(ssh): treat only opacity below 1 as a translucent plain SSH background Co-Authored-By: Claude --------- Co-authored-by: Claude --- .../orcad-migration-conflict-guidance.test.ts | 38 +++++++++++++++++++ .../OpenPlainSshTerminalButton.tsx | 25 ++++++++---- .../PlainSshTerminalPanels.test.tsx | 1 + .../PlainSshTerminalPanels.tsx | 8 +++- .../PlainSshTerminalView.tsx | 15 +++++++- src/renderer/src/i18n/locales/en.json | 3 +- 6 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 src/main/ssh/orcad-migration-conflict-guidance.test.ts diff --git a/src/main/ssh/orcad-migration-conflict-guidance.test.ts b/src/main/ssh/orcad-migration-conflict-guidance.test.ts new file mode 100644 index 00000000000..a9c4e942be7 --- /dev/null +++ b/src/main/ssh/orcad-migration-conflict-guidance.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest' +import { orcadMigrationConflictGuidance } from './orcad-migration-conflict-guidance' + +const NO_DESKTOP_STEP = [ + 'project_group_id', + 'folder_workspace_id', + 'dormant_id', + 'dormant_focus', + 'client_state', + 'close_intent_destination', + 'snapshot_destination' +] + +describe('orcad migration conflict guidance', () => { + // Why no UI words here: this text also reaches clients that have no plain SSH terminal. + it('names the code for every conflict retrying can’t clear, and no client-only action', () => { + for (const kind of NO_DESKTOP_STEP) { + const text = orcadMigrationConflictGuidance( + new Error(`orcad_migration_${kind}_conflict:ui:workspaceHostScope`) + ) + expect(text).toContain('Retrying won’t clear it.') + expect(text).not.toContain('plain SSH') + expect(text).toContain(`orcad_migration_${kind}_conflict`) + } + }) + + it('keeps the specific way out where the desktop has one', () => { + expect( + orcadMigrationConflictGuidance( + new Error('orcad_migration_repository_path_conflict:/home/me/app') + ) + ).toContain('remove that project from this desktop and connect again') + }) + + it('is null for anything that is not a conflict', () => { + expect(orcadMigrationConflictGuidance(new Error('ECONNRESET'))).toBeNull() + }) +}) diff --git a/src/renderer/src/components/plain-ssh-terminal/OpenPlainSshTerminalButton.tsx b/src/renderer/src/components/plain-ssh-terminal/OpenPlainSshTerminalButton.tsx index 75f6197a195..8d3a7b8c7f7 100644 --- a/src/renderer/src/components/plain-ssh-terminal/OpenPlainSshTerminalButton.tsx +++ b/src/renderer/src/components/plain-ssh-terminal/OpenPlainSshTerminalButton.tsx @@ -23,13 +23,22 @@ export function OpenPlainSshTerminalButton({ return null } return ( - + + + {/* The next step for any block, including ones retrying can't clear. */} + + {translate( + 'auto.components.plainSshTerminal.openHint', + 'Keeps you working on the host while Orca can’t set up there.' + )} + + ) } diff --git a/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.test.tsx b/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.test.tsx index c653b30619c..bf8e667e4d3 100644 --- a/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.test.tsx +++ b/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.test.tsx @@ -241,6 +241,7 @@ describe('plain SSH terminal', () => { render() await user.click(screen.getByRole('button', { name: 'Open plain SSH terminal' })) expect(await screen.findByText(/couldn’t find OpenSSH/)).toBeInTheDocument() + expect(document.querySelector('[data-plain-ssh-screen]')).toHaveClass('hidden') }) it('hangs up when its window closes', async () => { diff --git a/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.tsx b/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.tsx index 638efb15489..e9404d35602 100644 --- a/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.tsx +++ b/src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.tsx @@ -76,6 +76,8 @@ function PlainSshTerminalCard({ panel }: { panel: PlainSshPanel }): React.JSX.El // Why hidden, not unmounted: minimizing must keep the SSH session and its scrollback. className={cn( 'pointer-events-auto flex h-[26rem] w-[min(44rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-floating', + // A failed open has only its error to show. + phase.kind === 'failed' && 'h-auto', panel.minimized && 'hidden' )} > @@ -105,7 +107,11 @@ function PlainSshTerminalCard({ panel }: { panel: PlainSshPanel }): React.JSX.El {message ? (

{message}

) : null} -
+ {/* An empty terminal under a failed open's error would read as a live session. */} +