diff --git a/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx b/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx index 863b1121238..fe4b853f31a 100644 --- a/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx +++ b/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx @@ -61,7 +61,9 @@ export function CreateStep({ const radioGroupRef = useRef(null) const radioFocusFrameRef = useRef(null) const [browsingParent, setBrowsingParent] = useState(false) - const [advancedOpen, setAdvancedOpen] = useState(false) + // Why: SSH hosts need a typed remote path; hiding that field behind the + // collapsed defaults makes the create flow look impossible. + const [advancedOpen, setAdvancedOpen] = useState(manualParentEntry) const cancelRadioFocusFrame = useCallback((): void => { if (radioFocusFrameRef.current === null) { diff --git a/src/renderer/src/components/sidebar/AddRepoDialog.tsx b/src/renderer/src/components/sidebar/AddRepoDialog.tsx index 3c6ac47bb92..54730d66896 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialog.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialog.tsx @@ -320,6 +320,7 @@ const AddRepoDialog = React.memo(function AddRepoDialog() { isCloning={isCloning} sshTargets={sshTargets} selectedTargetId={selectedTargetId} + lockSshTargetSelection={selectedParsedHost?.kind === 'ssh'} remotePath={remotePath} remoteError={remoteError} isAddingRemote={isAddingRemote} diff --git a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx index 7984344abcb..0ebe217ded1 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx @@ -140,7 +140,7 @@ describe('AddRepoDialogStepContent nested imports', () => { activeRuntimeEnvironmentId: null }) - expect(html).toContain('Start a new project') + expect(html).toContain('Create a new project') expect(html).toContain('placeholder="/home/user/projects"') expect(html).not.toContain('Choose parent folder') }) @@ -155,4 +155,47 @@ describe('AddRepoDialogStepContent nested imports', () => { expect(html).toContain('Clone from URL') expect(html).toContain('aria-label="Browse server filesystem"') }) + + it('hides the SSH target chooser after a host was already selected', () => { + const html = renderStepContent({ + step: 'remote', + lockSshTargetSelection: true, + selectedTargetId: 'openclaw-2', + sshTargets: [ + { + id: 'github', + label: 'github.com', + host: 'github.com', + port: 22, + username: 'git', + state: { + targetId: 'github', + status: 'connected', + error: null, + reconnectAttempt: 0 + } + }, + { + id: 'openclaw-2', + label: 'openclaw 2', + host: 'openclaw.example.com', + port: 22, + username: 'dev', + state: { + targetId: 'openclaw-2', + status: 'connected', + error: null, + reconnectAttempt: 0 + } + } + ] + }) + + expect(html).toContain('Open remote project') + expect(html).toContain('openclaw 2') + expect(html).toContain('Remote path') + expect(html).not.toContain('SSH target') + expect(html).not.toContain('github.com') + expect(html).not.toContain('Connect') + }) }) diff --git a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx index ea2132a214d..eec96a91258 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx @@ -29,6 +29,7 @@ type AddRepoDialogStepContentProps = { isCloning: boolean sshTargets: (SshTarget & { state?: SshConnectionState })[] selectedTargetId: string | null + lockSshTargetSelection?: boolean remotePath: string remoteError: string | null isAddingRemote: boolean @@ -95,6 +96,7 @@ export function AddRepoDialogStepContent({ isCloning, sshTargets, selectedTargetId, + lockSshTargetSelection = false, remotePath, remoteError, isAddingRemote, @@ -183,6 +185,7 @@ export function AddRepoDialogStepContent({ target.id === selectedTargetId) + : null + const selectedTargetLabel = + selectedTarget?.label || + (selectedTarget ? `${selectedTarget.username}@${selectedTarget.host}` : selectedTargetId) if (browsing && selectedTargetId) { return ( @@ -76,53 +84,61 @@ export function RemoteStep({ {translate('auto.components.sidebar.AddRepoRemoteStep.91b93a90a4', 'Open remote project')} - {translate( - 'auto.components.sidebar.AddRepoRemoteStep.80557be85a', - 'Choose a connected SSH target and enter the path to a Git repository.' - )} + {lockSshTargetSelection + ? translate( + 'auto.components.sidebar.AddRepoRemoteStep.lockedDescription', + 'Enter the path to a Git repository on {{value0}}.', + { value0: selectedTargetLabel ?? 'this SSH target' } + ) + : translate( + 'auto.components.sidebar.AddRepoRemoteStep.80557be85a', + 'Choose a connected SSH target and enter the path to a Git repository.' + )}
-
- - {sshTargets.length === 0 ? ( -
-

- {translate( - 'auto.components.sidebar.AddRepoRemoteStep.df6fbcf880', - 'No SSH targets configured.' - )} -

- -
- ) : ( -
- {sshTargets.map((target) => ( - - ))} -
- )} -
+ {!lockSshTargetSelection ? ( +
+ + {sshTargets.length === 0 ? ( +
+

+ {translate( + 'auto.components.sidebar.AddRepoRemoteStep.df6fbcf880', + 'No SSH targets configured.' + )} +

+ +
+ ) : ( +
+ {sshTargets.map((target) => ( + + ))} +
+ )} +
+ ) : null}