Hide redundant SSH target chooser

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo-H
2026-06-11 14:51:16 -07:00
co-authored by Orca
parent 9e1e86fb01
commit 42a47c0cac
10 changed files with 120 additions and 50 deletions
@@ -61,7 +61,9 @@ export function CreateStep({
const radioGroupRef = useRef<HTMLDivElement>(null)
const radioFocusFrameRef = useRef<number | null>(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) {
@@ -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}
@@ -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')
})
})
@@ -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({
<RemoteStep
sshTargets={sshTargets}
selectedTargetId={selectedTargetId}
lockSshTargetSelection={lockSshTargetSelection}
remotePath={remotePath}
remoteError={remoteError}
isAddingRemote={isAddingRemote}
@@ -11,6 +11,7 @@ import { translate } from '@/i18n/i18n'
type RemoteStepProps = {
sshTargets: (SshTarget & { state?: SshConnectionState })[]
selectedTargetId: string | null
lockSshTargetSelection?: boolean
remotePath: string
remoteError: string | null
isAddingRemote: boolean
@@ -26,6 +27,7 @@ type RemoteStepProps = {
export function RemoteStep({
sshTargets,
selectedTargetId,
lockSshTargetSelection = false,
remotePath,
remoteError,
isAddingRemote,
@@ -38,6 +40,12 @@ export function RemoteStep({
onStopNestedScan
}: RemoteStepProps): React.JSX.Element {
const [browsing, setBrowsing] = useState(false)
const selectedTarget = selectedTargetId
? sshTargets.find((target) => 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')}
</DialogTitle>
<DialogDescription>
{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.'
)}
</DialogDescription>
</DialogHeader>
<div className="space-y-3 pt-1">
<div className="space-y-1">
<label className="text-[11px] font-medium text-muted-foreground">
{translate('auto.components.sidebar.AddRepoRemoteStep.44637f43bd', 'SSH target')}
</label>
{sshTargets.length === 0 ? (
<div className="space-y-1.5 py-1">
<p className="text-xs text-muted-foreground">
{translate(
'auto.components.sidebar.AddRepoRemoteStep.df6fbcf880',
'No SSH targets configured.'
)}
</p>
<Button
variant="outline"
size="sm"
className="h-7 text-xs"
onClick={onOpenSshSettings}
>
<Settings className="size-3.5" />
{translate(
'auto.components.sidebar.AddRepoRemoteStep.0416bde073',
'Add in Settings'
)}
</Button>
</div>
) : (
<div className="space-y-1.5 max-h-64 overflow-y-auto pr-1 scrollbar-sleek">
{sshTargets.map((target) => (
<SshTargetRow
key={target.id}
target={target}
isSelected={selectedTargetId === target.id}
onSelect={onSelectTarget}
onConnect={onConnectTarget}
/>
))}
</div>
)}
</div>
{!lockSshTargetSelection ? (
<div className="space-y-1">
<label className="text-[11px] font-medium text-muted-foreground">
{translate('auto.components.sidebar.AddRepoRemoteStep.44637f43bd', 'SSH target')}
</label>
{sshTargets.length === 0 ? (
<div className="space-y-1.5 py-1">
<p className="text-xs text-muted-foreground">
{translate(
'auto.components.sidebar.AddRepoRemoteStep.df6fbcf880',
'No SSH targets configured.'
)}
</p>
<Button
variant="outline"
size="sm"
className="h-7 text-xs"
onClick={onOpenSshSettings}
>
<Settings className="size-3.5" />
{translate(
'auto.components.sidebar.AddRepoRemoteStep.0416bde073',
'Add in Settings'
)}
</Button>
</div>
) : (
<div className="space-y-1.5 max-h-64 overflow-y-auto pr-1 scrollbar-sleek">
{sshTargets.map((target) => (
<SshTargetRow
key={target.id}
target={target}
isSelected={selectedTargetId === target.id}
onSelect={onSelectTarget}
onConnect={onConnectTarget}
/>
))}
</div>
)}
</div>
) : null}
<div className="space-y-1">
<label className="text-[11px] font-medium text-muted-foreground">
+2 -1
View File
@@ -3120,7 +3120,8 @@
"007651bdf9": "Navigate to a directory and click Select to choose it.",
"dd3ff65486": "Browse remote filesystem",
"36d427bb66": "Add remote project",
"35831a7312": "Adding..."
"35831a7312": "Adding...",
"lockedDescription": "Enter the path to a Git repository on {{value0}}."
},
"AddRepoServerStartStep": {
"ae990c86a0": "Back to add options",
+2 -1
View File
@@ -3114,7 +3114,8 @@
"007651bdf9": "Navegue hasta un directorio y haga clic en Seleccionar para elegirlo.",
"dd3ff65486": "Explorar el sistema de archivos remoto",
"36d427bb66": "Agregar proyecto remoto",
"35831a7312": "Añadiendo..."
"35831a7312": "Añadiendo...",
"lockedDescription": "Enter the path to a Git repository on {{value0}}."
},
"AddRepoServerStartStep": {
"ae990c86a0": "Volver para agregar opciones",
+2 -1
View File
@@ -3101,7 +3101,8 @@
"007651bdf9": "ディレクトリに移動し、「選択」をクリックして選択します。",
"dd3ff65486": "リモートファイルシステムを参照する",
"36d427bb66": "リモートプロジェクトを追加",
"35831a7312": "追加中..."
"35831a7312": "追加中...",
"lockedDescription": "Enter the path to a Git repository on {{value0}}."
},
"AddRepoServerStartStep": {
"ae990c86a0": "オプションの追加に戻る",
+2 -1
View File
@@ -3101,7 +3101,8 @@
"007651bdf9": "디렉터리로 이동한 후 선택을 클릭하여 선택합니다.",
"dd3ff65486": "원격 파일 시스템 찾아보기",
"36d427bb66": "원격 프로젝트 추가",
"35831a7312": "첨가..."
"35831a7312": "첨가...",
"lockedDescription": "Enter the path to a Git repository on {{value0}}."
},
"AddRepoServerStartStep": {
"ae990c86a0": "옵션 추가로 돌아가기",
+2 -1
View File
@@ -3101,7 +3101,8 @@
"007651bdf9": "导航到目录并单击“选择”以选择它。",
"dd3ff65486": "浏览远程文件系统",
"36d427bb66": "添加远程项目",
"35831a7312": "添加..."
"35831a7312": "添加...",
"lockedDescription": "Enter the path to a Git repository on {{value0}}."
},
"AddRepoServerStartStep": {
"ae990c86a0": "返回添加选项",