mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 00:03:09 +00:00
Import missing project host from composer
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -316,8 +316,9 @@ Needs:
|
||||
|
||||
Current branch status: partially implemented. Ready project-host setups can be
|
||||
selected from the composer `Run on` menu, and known hosts that still need setup
|
||||
now appear as disabled rows instead of being invisible. Inline setup actions for
|
||||
those rows are still missing.
|
||||
now appear as setup-target rows instead of being invisible. The composer can
|
||||
import an existing folder for those hosts and then switch to the new ready
|
||||
setup. Inline clone/provision actions for those rows are still missing.
|
||||
|
||||
### 5. Project Setup Flow
|
||||
|
||||
@@ -474,13 +475,15 @@ Already partially landed:
|
||||
- CLI commands for listing projects/setups and creating worktrees by
|
||||
project/setup
|
||||
- composer `Run on` selector when a project has multiple ready setups
|
||||
- disabled `Run on` rows for known hosts where the selected project is not set
|
||||
up yet
|
||||
- setup-target `Run on` rows for known hosts where the selected project is not
|
||||
set up yet
|
||||
- inline `Run on` import-existing-folder setup for known hosts where the
|
||||
selected project is not set up yet
|
||||
|
||||
Not complete yet:
|
||||
|
||||
- clone/provision flows
|
||||
- inline setup actions from the composer `Run on` menu
|
||||
- inline clone/provision actions from the composer `Run on` menu
|
||||
- bulk setup flows and setup for hosts that are not already known to the client
|
||||
- project settings split into global and host-specific ownership
|
||||
- host settings/capability UI aligned with project setup
|
||||
|
||||
@@ -356,8 +356,9 @@ Needs:
|
||||
|
||||
Current branch status: partially implemented. Ready project-host setups can be
|
||||
selected from the composer `Run on` menu, and known hosts that still need setup
|
||||
now appear as disabled rows instead of being invisible. Inline setup actions for
|
||||
those rows are still missing.
|
||||
now appear as setup-target rows instead of being invisible. The composer can
|
||||
import an existing folder for those hosts and then switch to the new ready
|
||||
setup. Inline clone/provision actions for those rows are still missing.
|
||||
|
||||
### 5. Project Setup Flow
|
||||
|
||||
@@ -513,13 +514,15 @@ Already partially landed:
|
||||
- CLI commands for listing projects/setups and creating worktrees by
|
||||
project/setup
|
||||
- composer `Run on` selector when a project has multiple ready setups
|
||||
- disabled `Run on` rows for known hosts where the selected project is not set
|
||||
up yet
|
||||
- setup-target `Run on` rows for known hosts where the selected project is not
|
||||
set up yet
|
||||
- inline `Run on` import-existing-folder setup for known hosts where the
|
||||
selected project is not set up yet
|
||||
|
||||
Not complete yet:
|
||||
|
||||
- clone/provision flows
|
||||
- inline setup actions from the composer `Run on` menu
|
||||
- inline clone/provision actions from the composer `Run on` menu
|
||||
- bulk setup flows and setup for hosts that are not already known to the client
|
||||
- full project settings split into global and host-specific ownership
|
||||
- host settings/capability UI aligned with project setup
|
||||
|
||||
@@ -747,6 +747,9 @@ Landed so far:
|
||||
disabled "project not set up on this host" rows, not only as invisible missing
|
||||
options. Ready setups remain selectable and still resolve through the
|
||||
compatibility repo path.
|
||||
- Added an inline composer action for those not-yet-set-up hosts to import an
|
||||
existing folder on that host. After import, the composer switches to the new
|
||||
ready setup so workspace creation can continue on that host.
|
||||
- Added tests for local repos, SSH repos, same-provider multi-host grouping,
|
||||
no-identity same-name non-grouping, selector cache behavior, persistence
|
||||
backfill, repo mutation synchronization, renderer hydration, and runtime RPC
|
||||
@@ -768,7 +771,7 @@ Important limitation:
|
||||
|
||||
Remaining end-to-end work:
|
||||
|
||||
- add inline clone/import actions for `Run on` hosts where the project is not
|
||||
- add inline clone/provision actions for `Run on` hosts where the project is not
|
||||
set up yet
|
||||
- broaden setup-on-host flows beyond known local, SSH, and active runtime hosts
|
||||
- add clone/provision setup-on-host flows
|
||||
|
||||
@@ -13,6 +13,14 @@ import {
|
||||
Settings2
|
||||
} from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import RepoCombobox from '@/components/repo/RepoCombobox'
|
||||
import AgentCombobox from '@/components/agent/AgentCombobox'
|
||||
@@ -36,7 +44,10 @@ import SmartWorkspaceNameField, {
|
||||
} from '@/components/new-workspace/SmartWorkspaceNameField'
|
||||
import ProjectHostSetupCombobox from '@/components/new-workspace/ProjectHostSetupCombobox'
|
||||
import type { SetupConfig } from '@/lib/new-workspace'
|
||||
import type { ProjectHostSetupOption } from '@/lib/project-host-setup-options'
|
||||
import type {
|
||||
NeedsSetupProjectHostOption,
|
||||
ProjectHostSetupOption
|
||||
} from '@/lib/project-host-setup-options'
|
||||
import type { WorkspaceCreateErrorDisplay } from '@/lib/workspace-create-error-format'
|
||||
import type { SshConnectionStatus } from '../../../shared/ssh-types'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
@@ -58,6 +69,11 @@ type NewWorkspaceComposerCardProps = {
|
||||
projectHostSetupOptions: ProjectHostSetupOption[]
|
||||
selectedProjectHostSetupId: string | null
|
||||
onProjectHostSetupChange: (setupId: string) => void
|
||||
onProjectHostExistingFolderSetup: (
|
||||
option: NeedsSetupProjectHostOption,
|
||||
path: string,
|
||||
kind: 'git' | 'folder'
|
||||
) => Promise<boolean>
|
||||
primaryActionLabel: string
|
||||
name: string
|
||||
onNameValueChange: (value: string) => void
|
||||
@@ -241,6 +257,7 @@ export default function NewWorkspaceComposerCard({
|
||||
projectHostSetupOptions,
|
||||
selectedProjectHostSetupId,
|
||||
onProjectHostSetupChange,
|
||||
onProjectHostExistingFolderSetup,
|
||||
primaryActionLabel,
|
||||
name,
|
||||
onNameValueChange,
|
||||
@@ -279,6 +296,12 @@ export default function NewWorkspaceComposerCard({
|
||||
onSparseSelectPreset
|
||||
}: NewWorkspaceComposerCardProps): React.JSX.Element {
|
||||
const { isFileDragOver, dragHandlers } = useComposerFileDragOver()
|
||||
const [pendingSetupHostOptionId, setPendingSetupHostOptionId] = React.useState<string | null>(
|
||||
null
|
||||
)
|
||||
const [pendingSetupPath, setPendingSetupPath] = React.useState('')
|
||||
const [pendingSetupKind, setPendingSetupKind] = React.useState<'git' | 'folder'>('git')
|
||||
const [pendingSetupImporting, setPendingSetupImporting] = React.useState(false)
|
||||
const openModal = useAppStore((s) => s.openModal)
|
||||
const activeModal = useAppStore((s) => s.activeModal)
|
||||
const defaultTuiAgent = useAppStore((s) => s.settings?.defaultTuiAgent ?? null)
|
||||
@@ -380,6 +403,44 @@ export default function NewWorkspaceComposerCard({
|
||||
openModal('add-repo')
|
||||
}, [openModal])
|
||||
const projectDescriptionId = React.useId()
|
||||
const pendingSetupHostOption = React.useMemo(
|
||||
() =>
|
||||
projectHostSetupOptions.find(
|
||||
(option): option is NeedsSetupProjectHostOption =>
|
||||
option.kind === 'needs-setup' && option.id === pendingSetupHostOptionId
|
||||
) ?? null,
|
||||
[pendingSetupHostOptionId, projectHostSetupOptions]
|
||||
)
|
||||
const handleProjectHostSetupChange = React.useCallback(
|
||||
(setupId: string): void => {
|
||||
setPendingSetupHostOptionId(null)
|
||||
onProjectHostSetupChange(setupId)
|
||||
},
|
||||
[onProjectHostSetupChange]
|
||||
)
|
||||
const handleNeedsSetupHostSelect = React.useCallback((option: NeedsSetupProjectHostOption) => {
|
||||
setPendingSetupHostOptionId(option.id)
|
||||
}, [])
|
||||
const handleImportExistingFolder = React.useCallback(async (): Promise<void> => {
|
||||
if (!pendingSetupHostOption || !pendingSetupPath.trim()) {
|
||||
return
|
||||
}
|
||||
setPendingSetupImporting(true)
|
||||
try {
|
||||
const imported = await onProjectHostExistingFolderSetup(
|
||||
pendingSetupHostOption,
|
||||
pendingSetupPath.trim(),
|
||||
pendingSetupKind
|
||||
)
|
||||
if (imported) {
|
||||
setPendingSetupHostOptionId(null)
|
||||
setPendingSetupPath('')
|
||||
setPendingSetupKind('git')
|
||||
}
|
||||
} finally {
|
||||
setPendingSetupImporting(false)
|
||||
}
|
||||
}, [onProjectHostExistingFolderSetup, pendingSetupHostOption, pendingSetupKind, pendingSetupPath])
|
||||
useContextualTour(
|
||||
'workspace-creation',
|
||||
eligibleRepos.length > 0 && Boolean(repoId),
|
||||
@@ -473,8 +534,78 @@ export default function NewWorkspaceComposerCard({
|
||||
<ProjectHostSetupCombobox
|
||||
options={projectHostSetupOptions}
|
||||
value={selectedProjectHostSetupId}
|
||||
onValueChange={onProjectHostSetupChange}
|
||||
onValueChange={handleProjectHostSetupChange}
|
||||
onNeedsSetupHostSelect={handleNeedsSetupHostSelect}
|
||||
/>
|
||||
{pendingSetupHostOption ? (
|
||||
<div className="space-y-2 rounded-md border border-border bg-muted/20 p-2.5">
|
||||
<div className="text-xs font-medium text-foreground">
|
||||
{translate(
|
||||
'auto.components.NewWorkspaceComposerCard.setupHostExistingFolderTitle',
|
||||
'Import existing folder on {{value0}}',
|
||||
{ value0: pendingSetupHostOption.label }
|
||||
)}
|
||||
</div>
|
||||
<div className="grid min-w-0 gap-2 sm:grid-cols-[minmax(0,1fr)_7rem]">
|
||||
<Input
|
||||
value={pendingSetupPath}
|
||||
onChange={(event) => setPendingSetupPath(event.target.value)}
|
||||
placeholder={translate(
|
||||
'auto.components.NewWorkspaceComposerCard.setupHostExistingFolderPlaceholder',
|
||||
'/path/to/project/on/host'
|
||||
)}
|
||||
className="h-8 min-w-0 text-xs"
|
||||
/>
|
||||
<Select
|
||||
value={pendingSetupKind}
|
||||
onValueChange={(value) => setPendingSetupKind(value as 'git' | 'folder')}
|
||||
>
|
||||
<SelectTrigger className="h-8 min-w-0 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="git">
|
||||
{translate(
|
||||
'auto.components.NewWorkspaceComposerCard.setupKindGit',
|
||||
'Git repo'
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="folder">
|
||||
{translate(
|
||||
'auto.components.NewWorkspaceComposerCard.setupKindFolder',
|
||||
'Folder'
|
||||
)}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="min-w-0 text-[11px] text-muted-foreground">
|
||||
{translate(
|
||||
'auto.components.NewWorkspaceComposerCard.setupHostExistingFolderHelp',
|
||||
'Link a checkout that already exists there, then create this workspace on that host.'
|
||||
)}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
size="xs"
|
||||
disabled={!pendingSetupPath.trim() || pendingSetupImporting}
|
||||
onClick={() => void handleImportExistingFolder()}
|
||||
className="shrink-0"
|
||||
>
|
||||
{pendingSetupImporting
|
||||
? translate(
|
||||
'auto.components.NewWorkspaceComposerCard.importingHostSetup',
|
||||
'Importing...'
|
||||
)
|
||||
: translate(
|
||||
'auto.components.NewWorkspaceComposerCard.importHostSetup',
|
||||
'Import'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{selectedRepoRequiresConnection && selectedRepoConnectionId ? (
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import React, { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type {
|
||||
NeedsSetupProjectHostOption,
|
||||
ProjectHostSetupOption
|
||||
} from '@/lib/project-host-setup-options'
|
||||
import ProjectHostSetupCombobox from './ProjectHostSetupCombobox'
|
||||
|
||||
vi.mock('@/components/ui/popover', () => ({
|
||||
Popover: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
PopoverContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
PopoverTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>
|
||||
}))
|
||||
|
||||
vi.mock('@/components/ui/command', () => ({
|
||||
Command: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
CommandEmpty: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
CommandList: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
CommandItem: ({
|
||||
children,
|
||||
onSelect,
|
||||
value
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
onSelect?: (value: string) => void
|
||||
value: string
|
||||
}) => (
|
||||
<button type="button" data-command-value={value} onClick={() => onSelect?.(value)}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}))
|
||||
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
|
||||
const readyOption: ProjectHostSetupOption = {
|
||||
id: 'local-setup',
|
||||
kind: 'ready',
|
||||
projectId: 'project-1',
|
||||
hostId: 'local',
|
||||
repoId: 'local-repo',
|
||||
label: 'Local Mac',
|
||||
detail: 'Orca',
|
||||
path: '/Users/alice/orca'
|
||||
}
|
||||
|
||||
const needsSetupOption: NeedsSetupProjectHostOption = {
|
||||
id: 'needs-setup:ssh:builder',
|
||||
kind: 'needs-setup',
|
||||
projectId: 'project-1',
|
||||
hostId: 'ssh:builder',
|
||||
label: 'Builder',
|
||||
detail: 'Project not set up on this host'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
root.unmount()
|
||||
})
|
||||
container.remove()
|
||||
})
|
||||
|
||||
function renderCombobox({
|
||||
onValueChange = vi.fn(),
|
||||
onNeedsSetupHostSelect = vi.fn()
|
||||
}: {
|
||||
onValueChange?: (setupId: string) => void
|
||||
onNeedsSetupHostSelect?: (option: NeedsSetupProjectHostOption) => void
|
||||
} = {}): void {
|
||||
act(() => {
|
||||
root.render(
|
||||
<ProjectHostSetupCombobox
|
||||
options={[readyOption, needsSetupOption]}
|
||||
value={readyOption.id}
|
||||
onValueChange={onValueChange}
|
||||
onNeedsSetupHostSelect={onNeedsSetupHostSelect}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
describe('ProjectHostSetupCombobox', () => {
|
||||
it('routes ready setup rows through onValueChange', () => {
|
||||
const onValueChange = vi.fn()
|
||||
const onNeedsSetupHostSelect = vi.fn()
|
||||
|
||||
renderCombobox({ onValueChange, onNeedsSetupHostSelect })
|
||||
|
||||
act(() => {
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('[data-command-value="local-setup"]')
|
||||
?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(onValueChange).toHaveBeenCalledWith('local-setup')
|
||||
expect(onNeedsSetupHostSelect).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('routes hosts that need setup through onNeedsSetupHostSelect', () => {
|
||||
const onValueChange = vi.fn()
|
||||
const onNeedsSetupHostSelect = vi.fn()
|
||||
|
||||
renderCombobox({ onValueChange, onNeedsSetupHostSelect })
|
||||
|
||||
act(() => {
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('[data-command-value="needs-setup:ssh:builder"]')
|
||||
?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(onValueChange).not.toHaveBeenCalled()
|
||||
expect(onNeedsSetupHostSelect).toHaveBeenCalledWith(needsSetupOption)
|
||||
})
|
||||
})
|
||||
@@ -4,19 +4,24 @@ import { Button } from '@/components/ui/button'
|
||||
import { Command, CommandEmpty, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { ProjectHostSetupOption } from '@/lib/project-host-setup-options'
|
||||
import type {
|
||||
NeedsSetupProjectHostOption,
|
||||
ProjectHostSetupOption
|
||||
} from '@/lib/project-host-setup-options'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type ProjectHostSetupComboboxProps = {
|
||||
options: readonly ProjectHostSetupOption[]
|
||||
value: string | null
|
||||
onValueChange: (setupId: string) => void
|
||||
onNeedsSetupHostSelect?: (option: NeedsSetupProjectHostOption) => void
|
||||
}
|
||||
|
||||
export default function ProjectHostSetupCombobox({
|
||||
options,
|
||||
value,
|
||||
onValueChange
|
||||
onValueChange,
|
||||
onNeedsSetupHostSelect
|
||||
}: ProjectHostSetupComboboxProps): React.JSX.Element {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const readyOptions = options.filter((option) => option.kind === 'ready')
|
||||
@@ -24,13 +29,22 @@ export default function ProjectHostSetupCombobox({
|
||||
|
||||
const handleSelect = React.useCallback(
|
||||
(setupId: string): void => {
|
||||
if (!readyOptions.some((option) => option.id === setupId)) {
|
||||
const option = options.find((candidate) => candidate.id === setupId)
|
||||
if (!option) {
|
||||
return
|
||||
}
|
||||
if (option.kind === 'needs-setup') {
|
||||
onNeedsSetupHostSelect?.(option)
|
||||
setOpen(false)
|
||||
return
|
||||
}
|
||||
if (!readyOptions.some((candidate) => candidate.id === setupId)) {
|
||||
return
|
||||
}
|
||||
onValueChange(setupId)
|
||||
setOpen(false)
|
||||
},
|
||||
[onValueChange, readyOptions]
|
||||
[onNeedsSetupHostSelect, onValueChange, options, readyOptions]
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -76,7 +90,6 @@ export default function ProjectHostSetupCombobox({
|
||||
key={option.id}
|
||||
value={option.id}
|
||||
onSelect={() => handleSelect(option.id)}
|
||||
disabled={option.kind !== 'ready'}
|
||||
className="items-center gap-2 px-3 py-2"
|
||||
>
|
||||
<Check
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
} from '@/lib/project-host-workspace-target'
|
||||
import {
|
||||
buildProjectHostSetupOptions,
|
||||
type NeedsSetupProjectHostOption,
|
||||
type ProjectHostSetupOption
|
||||
} from '@/lib/project-host-setup-options'
|
||||
import { buildExecutionHostRegistry } from '../../../shared/execution-host-registry'
|
||||
@@ -159,6 +160,11 @@ export type ComposerCardProps = {
|
||||
projectHostSetupOptions: ProjectHostSetupOption[]
|
||||
selectedProjectHostSetupId: string | null
|
||||
onProjectHostSetupChange: (setupId: string) => void
|
||||
onProjectHostExistingFolderSetup: (
|
||||
option: NeedsSetupProjectHostOption,
|
||||
path: string,
|
||||
kind: 'git' | 'folder'
|
||||
) => Promise<boolean>
|
||||
name: string
|
||||
onNameValueChange: (value: string) => void
|
||||
onSmartGitHubItemSelect: (item: GitHubWorkItem) => void
|
||||
@@ -304,6 +310,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||
closeModal: s.closeModal,
|
||||
openSettingsPage: s.openSettingsPage,
|
||||
openSettingsTarget: s.openSettingsTarget,
|
||||
setupProjectExistingFolder: s.setupProjectExistingFolder,
|
||||
prefetchWorktreeCreateBase: s.prefetchWorktreeCreateBase,
|
||||
prefetchWorkItems: s.prefetchWorkItems,
|
||||
fetchSparsePresets: s.fetchSparsePresets
|
||||
@@ -318,6 +325,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||
closeModal,
|
||||
openSettingsPage,
|
||||
openSettingsTarget,
|
||||
setupProjectExistingFolder,
|
||||
prefetchWorktreeCreateBase,
|
||||
prefetchWorkItems,
|
||||
fetchSparsePresets
|
||||
@@ -1703,6 +1711,27 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||
},
|
||||
[handleRepoChange, projectHostSetupOptions]
|
||||
)
|
||||
const handleProjectHostExistingFolderSetup = useCallback(
|
||||
async (
|
||||
option: NeedsSetupProjectHostOption,
|
||||
path: string,
|
||||
kind: 'git' | 'folder'
|
||||
): Promise<boolean> => {
|
||||
const result = await setupProjectExistingFolder({
|
||||
projectId: option.projectId,
|
||||
hostId: option.hostId,
|
||||
path,
|
||||
kind,
|
||||
displayName: selectedRepo?.displayName
|
||||
})
|
||||
if (!result) {
|
||||
return false
|
||||
}
|
||||
handleRepoChange(result.repo.id)
|
||||
return true
|
||||
},
|
||||
[handleRepoChange, selectedRepo?.displayName, setupProjectExistingFolder]
|
||||
)
|
||||
|
||||
const showProjectRequiredError = useCallback((): void => {
|
||||
setProjectError('Choose or add a project before creating a workspace.')
|
||||
@@ -2572,6 +2601,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||
projectHostSetupOptions,
|
||||
selectedProjectHostSetupId,
|
||||
onProjectHostSetupChange: handleProjectHostSetupChange,
|
||||
onProjectHostExistingFolderSetup: handleProjectHostExistingFolderSetup,
|
||||
name,
|
||||
onNameValueChange: handleNameValueChange,
|
||||
onSmartGitHubItemSelect: handleSmartGitHubItemSelect,
|
||||
|
||||
@@ -993,7 +993,14 @@
|
||||
"7711ad5122": "Local setup command",
|
||||
"e5db1b0419": "Combined setup command",
|
||||
"runOn": "Run on",
|
||||
"addProjectBeforeWorkspace": "Add a project before creating a workspace."
|
||||
"addProjectBeforeWorkspace": "Add a project before creating a workspace.",
|
||||
"setupHostExistingFolderTitle": "Import existing folder on {{value0}}",
|
||||
"setupHostExistingFolderPlaceholder": "/path/to/project/on/host",
|
||||
"setupKindGit": "Git repo",
|
||||
"setupKindFolder": "Folder",
|
||||
"setupHostExistingFolderHelp": "Link a checkout that already exists there, then create this workspace on that host.",
|
||||
"importingHostSetup": "Importing...",
|
||||
"importHostSetup": "Import"
|
||||
},
|
||||
"NewWorkspaceComposerModal": {
|
||||
"fa90f739a5": "Choose the project, workspace name, and agent before creating the workspace."
|
||||
|
||||
@@ -993,7 +993,14 @@
|
||||
"7711ad5122": "ローカルセットアップコマンド",
|
||||
"e5db1b0419": "組み合わせセットアップコマンド",
|
||||
"runOn": "実行先",
|
||||
"addProjectBeforeWorkspace": "ワークスペースを作成する前にプロジェクトを追加します。"
|
||||
"addProjectBeforeWorkspace": "ワークスペースを作成する前にプロジェクトを追加します。",
|
||||
"setupHostExistingFolderTitle": "Import existing folder on {{value0}}",
|
||||
"setupHostExistingFolderPlaceholder": "/path/to/project/on/host",
|
||||
"setupKindGit": "Git repo",
|
||||
"setupKindFolder": "Folder",
|
||||
"setupHostExistingFolderHelp": "Link a checkout that already exists there, then create this workspace on that host.",
|
||||
"importingHostSetup": "Importing...",
|
||||
"importHostSetup": "Import"
|
||||
},
|
||||
"NewWorkspaceComposerModal": {
|
||||
"fa90f739a5": "ワークスペースを作成する前に、プロジェクト、ワークスペース名、およびエージェントを選択します。"
|
||||
|
||||
@@ -993,7 +993,14 @@
|
||||
"7711ad5122": "로컬 설정 명령",
|
||||
"e5db1b0419": "결합된 설정 명령",
|
||||
"runOn": "실행 위치",
|
||||
"addProjectBeforeWorkspace": "워크스페이스를 만들기 전에 프로젝트를 추가하세요."
|
||||
"addProjectBeforeWorkspace": "워크스페이스를 만들기 전에 프로젝트를 추가하세요.",
|
||||
"setupHostExistingFolderTitle": "Import existing folder on {{value0}}",
|
||||
"setupHostExistingFolderPlaceholder": "/path/to/project/on/host",
|
||||
"setupKindGit": "Git repo",
|
||||
"setupKindFolder": "Folder",
|
||||
"setupHostExistingFolderHelp": "Link a checkout that already exists there, then create this workspace on that host.",
|
||||
"importingHostSetup": "Importing...",
|
||||
"importHostSetup": "Import"
|
||||
},
|
||||
"NewWorkspaceComposerModal": {
|
||||
"fa90f739a5": "워크스페이스를 생성하기 전에 프로젝트, 워크스페이스 이름, 에이전트를 선택하세요."
|
||||
|
||||
@@ -993,7 +993,14 @@
|
||||
"7711ad5122": "本地设置命令",
|
||||
"e5db1b0419": "组合设置命令",
|
||||
"runOn": "运行位置",
|
||||
"addProjectBeforeWorkspace": "在创建工作区之前添加项目。"
|
||||
"addProjectBeforeWorkspace": "在创建工作区之前添加项目。",
|
||||
"setupHostExistingFolderTitle": "Import existing folder on {{value0}}",
|
||||
"setupHostExistingFolderPlaceholder": "/path/to/project/on/host",
|
||||
"setupKindGit": "Git repo",
|
||||
"setupKindFolder": "Folder",
|
||||
"setupHostExistingFolderHelp": "Link a checkout that already exists there, then create this workspace on that host.",
|
||||
"importingHostSetup": "Importing...",
|
||||
"importHostSetup": "Import"
|
||||
},
|
||||
"NewWorkspaceComposerModal": {
|
||||
"fa90f739a5": "创建工作区之前选择项目、工作区名称和代理。"
|
||||
|
||||
@@ -28,7 +28,7 @@ export type ProjectHostSetupOption =
|
||||
|
||||
export type ReadyProjectHostSetupOption = Extract<ProjectHostSetupOption, { kind: 'ready' }>
|
||||
|
||||
type NeedsSetupProjectHostOption = Extract<ProjectHostSetupOption, { kind: 'needs-setup' }>
|
||||
export type NeedsSetupProjectHostOption = Extract<ProjectHostSetupOption, { kind: 'needs-setup' }>
|
||||
|
||||
type BuildReadySetupOptionsInput = {
|
||||
projectId: string
|
||||
|
||||
Reference in New Issue
Block a user