From 3ec2ff557a80cd69378337f49e192596d1463af8 Mon Sep 17 00:00:00 2001
From: Neil <4138956+nwparker@users.noreply.github.com>
Date: Fri, 13 Nov 2026 10:06:25 -0800
Subject: [PATCH] fix(cloud): unreachable GitHub repository on a remote-server
project says so with More access (UNREACHABLE-REPO-NO-ROUTE) (#730)
* fix(cloud): Orca Cloud on a remote-server project whose GitHub repository it can't reach says so with More access, not the folder wording (UNREACHABLE-REPO-NO-ROUTE)
* fix(cloud): UNREACHABLE-REPO-NO-ROUTE design-squad: 'Give Orca access' link, muted (not red) precondition line, fixture
* test(cloud): OrcaCloudNeedsRepoSubmit fixtures press the footer's Create
* fix(cloud): UNREACHABLE-REPO-NO-ROUTE the Project field's ring is amber while Orca Cloud waits for a GitHub repository (design-squad); aria-invalid kept
* fix(cloud): UNREACHABLE-REPO-NO-ROUTE ring classes stay literal (localization audit), drop the unreferenced remoteServerDetail key
---
...omposerProjectSection.add-project.test.tsx | 1 +
...orkspaceComposerProjectSection.fixture.tsx | 1 +
.../NewWorkspaceComposerProjectSection.tsx | 4 ++
.../new-workspace/ProjectCombobox.tsx | 8 +--
.../new-workspace/project-combobox-props.ts | 12 ++++
.../run-on/OrcaCloudNeedsRepoLine.tsx | 36 +++++++++++-
.../OrcaCloudNeedsRepoSubmit.fixture.tsx | 41 ++++++++++++--
.../new-workspace/run-on/cloud-start-view.ts | 19 +++++--
.../orca-cloud-unreachable-repo.test.tsx | 55 +++++++++++++++++++
.../run-on/use-cloud-only-repo.ts | 7 ++-
.../run-on/use-cloud-start-composer.ts | 1 +
.../src/i18n/en-runtime-required.json | 7 ---
src/renderer/src/i18n/locales/en.json | 2 +-
13 files changed, 169 insertions(+), 25 deletions(-)
create mode 100644 src/renderer/src/components/new-workspace/run-on/orca-cloud-unreachable-repo.test.tsx
diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.add-project.test.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.add-project.test.tsx
index 02639b9a238..3adbcbc692d 100644
--- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.add-project.test.tsx
+++ b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.add-project.test.tsx
@@ -25,6 +25,7 @@ function cloudOnly(
picked: null,
folderProject: false,
needsGitHubPick: false,
+ remoteServerProject: false,
githubStep: null,
pick: () => {},
clear: () => {},
diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx
index 89cd74fef6c..e54d3b3b8be 100644
--- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx
+++ b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx
@@ -99,6 +99,7 @@ const handoffCloudStart = (githubHandoff: 'waiting' | 'timed-out'): CloudStartVi
picked: null,
folderProject: false,
needsGitHubPick: false,
+ remoteServerProject: false,
githubStep: null,
pick: () => {},
clear: () => {},
diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx
index 948b0a9839d..551bfef169a 100644
--- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx
+++ b/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx
@@ -236,6 +236,8 @@ export function NewWorkspaceComposerProjectSection({
}
triggerClassName="h-9 w-full border-input text-sm focus-visible:border-focus-ring focus-visible:ring-1 focus-visible:ring-focus-ring"
invalid={Boolean(projectError) || Boolean(needsRepo)}
+ // Waiting for a GitHub repository is a fixable precondition: amber, not red.
+ invalidTone={!projectError && needsRepo ? 'warning' : 'error'}
describedBy={
!projectError && needsRepo ? ORCA_CLOUD_NEEDS_REPO_ID : projectDescriptionId
}
@@ -255,6 +257,8 @@ export function NewWorkspaceComposerProjectSection({
diff --git a/src/renderer/src/components/new-workspace/ProjectCombobox.tsx b/src/renderer/src/components/new-workspace/ProjectCombobox.tsx index 5927bfae436..52e4b34c265 100644 --- a/src/renderer/src/components/new-workspace/ProjectCombobox.tsx +++ b/src/renderer/src/components/new-workspace/ProjectCombobox.tsx @@ -23,7 +23,7 @@ import { import { ProjectOptionMark, ProjectOptionRow } from './ProjectComboboxRow' import { useRecentProjectIds } from './use-recent-project-ids' import { isWithinComboboxRoot, useTypeAheadCombobox } from './use-type-ahead-combobox' -import type { ProjectComboboxProps } from './project-combobox-props' +import { projectFieldInvalidClass, type ProjectComboboxProps } from './project-combobox-props' import { COMBOBOX_FIELD_SHELL, COMBOBOX_POPOVER_SURFACE } from './type-ahead-combobox-styles' import { cloudRepoKey, @@ -48,6 +48,7 @@ export default function ProjectCombobox({ placeholder = 'Choose project', triggerClassName, invalid = false, + invalidTone = 'error', describedBy, cloudRepos = null, cloudRepoValue = null, @@ -218,9 +219,8 @@ export default function ProjectCombobox({ }} className={cn( COMBOBOX_FIELD_SHELL, - // Invalid stays red while focused, so the field and its error read as one. - invalid && - 'border-destructive ring-destructive/20 focus-within:border-destructive focus-within:ring-destructive/40 dark:ring-destructive/40', + // Invalid stays marked while focused, so the field and its line read as one. + invalid && projectFieldInvalidClass(invalidTone), triggerClassName )} > diff --git a/src/renderer/src/components/new-workspace/project-combobox-props.ts b/src/renderer/src/components/new-workspace/project-combobox-props.ts index 72401f1d6fe..0b37d12577e 100644 --- a/src/renderer/src/components/new-workspace/project-combobox-props.ts +++ b/src/renderer/src/components/new-workspace/project-combobox-props.ts @@ -13,6 +13,11 @@ export type ProjectComboboxProps = { placeholder?: string triggerClassName?: string invalid?: boolean + /** + * `warning`: a fixable precondition (Orca Cloud waiting for a GitHub repository), ringed amber + * rather than red; still aria-invalid for screen readers. Defaults to `error`. + */ + invalidTone?: 'error' | 'warning' describedBy?: string /** D4: GitHub repositories with no clone here, listed while Orca Cloud is the Run on target. */ cloudRepos?: readonly OrcaCloudGitHubRepository[] | null @@ -32,3 +37,10 @@ export type ProjectComboboxProps = { */ focusSignal?: number } + +/** The field's invalid ring: red for an error, amber for a fixable precondition (glossary). */ +export function projectFieldInvalidClass(tone: 'error' | 'warning'): string { + return tone === 'warning' + ? 'border-status-warning ring-status-warning/20 focus-within:border-status-warning focus-within:ring-status-warning/40 dark:ring-status-warning/40' + : 'border-destructive ring-destructive/20 focus-within:border-destructive focus-within:ring-destructive/40 dark:ring-destructive/40' +} diff --git a/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoLine.tsx b/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoLine.tsx index ccf006cba43..bf3859a60b3 100644 --- a/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoLine.tsx +++ b/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoLine.tsx @@ -10,12 +10,44 @@ import { ORCA_CLOUD_NEEDS_REPO_ID } from './cloud-start-view' */ export function OrcaCloudNeedsRepoLine({ hasRepos, - onAddRepository + onAddRepository, + unreachable = false, + onMoreAccess }: { hasRepos: boolean onAddRepository: () => void + /** UNREACHABLE-REPO-NO-ROUTE: the project's own GitHub repository is out of Orca Cloud's reach. */ + unreachable?: boolean + /** Opens GitHub to give Orca access to more repositories ("Give Orca access"). */ + onMoreAccess?: () => void }): React.JSX.Element { const t = useTranslate() + if (unreachable) { + return ( +
+ {t( + 'components.newWorkspace.cloudOnly.remoteServerUnreachable', + 'Orca Cloud starts from a GitHub repository and can’t reach this project’s. Give Orca access to it, or pick another repository.' + )} +
+ {onMoreAccess ? ( ++
{t( 'components.newWorkspace.cloudOnly.needsRepository', 'Orca Cloud needs a GitHub repository.' diff --git a/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoSubmit.fixture.tsx b/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoSubmit.fixture.tsx index 7a6370ac1cb..8a947d9b74d 100644 --- a/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoSubmit.fixture.tsx +++ b/src/renderer/src/components/new-workspace/run-on/OrcaCloudNeedsRepoSubmit.fixture.tsx @@ -37,17 +37,40 @@ const orcaCloud: EphemeralVmRecipeOption = { create: 'orca-cloud create' } const NEEDS_REPO = { hasRepos: true } +// UNREACHABLE-REPO-NO-ROUTE: Hello-World cloned only on a paired server, out of Orca Cloud's reach. +const UNREACHABLE = { hasRepos: true, unreachable: true } -function Composer(): React.JSX.Element { +function Composer({ unreachable = false }: { unreachable?: boolean }): React.JSX.Element { const [project, setProject] = useState('folder') const [created, setCreated] = useState(false) - const needsRepo = project === 'folder' ? NEEDS_REPO : null + const needsRepo = project === 'folder' ? (unreachable ? UNREACHABLE : NEEDS_REPO) : null const guard = useNeedsRepoSubmitGuard(needsRepo, () => setCreated(true)) const cloudStart = { onSelectOrcaCloud: () => {}, onUserPick: () => {}, folderProject: project === 'folder', - needsGitHubRepo: guard.shown + needsGitHubRepo: guard.shown, + ...(unreachable + ? { + cloudOnly: { + repos: [], + showMore: null, + retryRepos: null, + retryingRepos: false, + picked: null, + folderProject: false, + needsGitHubPick: true, + remoteServerProject: true, + githubStep: null, + pick: () => {}, + clear: () => {}, + moreAccess: () => {}, + githubHandoff: 'idle' as const, + checkGitHubAgain: () => {}, + start: async () => false + } + } + : {}) } return (