import { open } from '@tauri-apps/plugin-dialog'; import { gitClone } from '@yaakapp-internal/git'; import { useState } from 'react'; import { openWorkspaceFromSyncDir } from '../commands/openWorkspaceFromSyncDir'; import { appInfo } from '../lib/appInfo'; import { showErrorToast } from '../lib/toast'; import { Banner } from './core/Banner'; import { Button } from './core/Button'; import { Checkbox } from './core/Checkbox'; import { IconButton } from './core/IconButton'; import { PlainInput } from './core/PlainInput'; import { VStack } from './core/Stacks'; import { promptCredentials } from './git/credentials'; interface Props { hide: () => void; } // Detect path separator from an existing path (defaults to /) function getPathSeparator(path: string): string { return path.includes('\\') ? '\\' : '/'; } export function CloneGitRepositoryDialog({ hide }: Props) { const [url, setUrl] = useState(''); const [baseDirectory, setBaseDirectory] = useState(appInfo.defaultProjectDir); const [directoryOverride, setDirectoryOverride] = useState(null); const [hasSubdirectory, setHasSubdirectory] = useState(false); const [subdirectory, setSubdirectory] = useState(''); const [isCloning, setIsCloning] = useState(false); const [error, setError] = useState(null); const repoName = extractRepoName(url); const sep = getPathSeparator(baseDirectory); const computedDirectory = repoName ? `${baseDirectory}${sep}${repoName}` : baseDirectory; const directory = directoryOverride ?? computedDirectory; const workspaceDirectory = hasSubdirectory && subdirectory ? `${directory}${sep}${subdirectory}` : directory; const handleSelectDirectory = async () => { const dir = await open({ title: 'Select Directory', directory: true, multiple: false, }); if (dir != null) { setBaseDirectory(dir); setDirectoryOverride(null); } }; const handleClone = async (e: React.FormEvent) => { e.preventDefault(); if (!url || !directory) return; setIsCloning(true); setError(null); try { const result = await gitClone(url, directory, promptCredentials); if (result.type === 'needs_credentials') { setError( result.error ?? 'Authentication failed. Please check your credentials and try again.', ); return; } // Open the workspace from the cloned directory (or subdirectory) await openWorkspaceFromSyncDir.mutateAsync(workspaceDirectory); hide(); } catch (err) { setError(String(err)); showErrorToast({ id: 'git-clone-error', title: 'Clone Failed', message: String(err), }); } finally { setIsCloning(false); } }; return ( {error && ( {error} )} } /> {hasSubdirectory && ( )} ); } function extractRepoName(url: string): string { // Handle various Git URL formats: // https://github.com/user/repo.git // git@github.com:user/repo.git // https://github.com/user/repo const match = url.match(/\/([^/]+?)(\.git)?$/); if (match?.[1]) { return match[1]; } // Fallback for SSH-style URLs const sshMatch = url.match(/:([^/]+?)(\.git)?$/); if (sshMatch?.[1]) { return sshMatch[1]; } return ''; }