fix(electrobun): wizard creation flow + GitLab probe + shell detection + dropdown flip
- Git probe tries GitHub then GitLab for owner/repo shorthand - Shows "Found on GitHub/GitLab" with platform indicator - system.shells RPC detects installed shells (bash/zsh/fish/sh/dash) - CustomDropdown flip logic uses 200px threshold for flip-up - Project creation properly persists all wizard fields + adds card
This commit is contained in:
parent
021feba3ed
commit
e61473b025
7 changed files with 112 additions and 21 deletions
|
|
@ -37,6 +37,7 @@
|
|||
let provider = $state<string>('claude'); let model = $state(''); let permissionMode = $state('default');
|
||||
let systemPrompt = $state(''); let autoStart = $state(false); let modelConfig = $state<Record<string, unknown>>({});
|
||||
let detectedProviders = $state<ProviderInfo[]>([]); let providerModels = $state<Array<{ id: string; name: string; provider: string }>>([]); let modelsLoading = $state(false);
|
||||
let githubPlatform = $state<'github' | 'gitlab' | null>(null);
|
||||
let pathTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let probeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let githubTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
|
@ -69,17 +70,41 @@
|
|||
});
|
||||
$effect(() => {
|
||||
if (sourceType === 'github' && githubRepo.trim()) {
|
||||
if (githubTimer) clearTimeout(githubTimer); githubInfo = null; githubProbeStatus = 'idle';
|
||||
if (githubTimer) clearTimeout(githubTimer); githubInfo = null; githubProbeStatus = 'idle'; githubPlatform = null;
|
||||
const input = githubRepo.trim();
|
||||
let probeUrl: string;
|
||||
if (input.startsWith('http://') || input.startsWith('https://') || input.startsWith('git@')) { probeUrl = input; }
|
||||
else if (isValidGithubRepo(input)) { probeUrl = `https://github.com/${input}.git`; } else { return; }
|
||||
const isFullUrl = input.startsWith('http://') || input.startsWith('https://') || input.startsWith('git@');
|
||||
const isShorthand = !isFullUrl && isValidGithubRepo(input);
|
||||
if (!isFullUrl && !isShorthand) return;
|
||||
githubLoading = true; githubProbeStatus = 'probing';
|
||||
githubTimer = setTimeout(async () => {
|
||||
try {
|
||||
const r = await appRpc.request['git.probe']({ url: probeUrl });
|
||||
if (r?.ok) { githubProbeStatus = 'ok'; if (isValidGithubRepo(input)) { try { const res = await fetch(`https://api.github.com/repos/${input}`, { signal: AbortSignal.timeout(5000) }); if (res.ok) { const d = await res.json(); githubInfo = { stars: d.stargazers_count ?? 0, description: d.description ?? '', defaultBranch: d.default_branch ?? 'main' }; } } catch { /* optional */ } } }
|
||||
else { githubProbeStatus = 'error'; }
|
||||
if (isFullUrl) {
|
||||
// Full URL — probe directly, detect platform from hostname
|
||||
const r = await appRpc.request['git.probe']({ url: input });
|
||||
if (r?.ok) {
|
||||
githubProbeStatus = 'ok';
|
||||
if (input.includes('gitlab.com')) githubPlatform = 'gitlab';
|
||||
else if (input.includes('github.com')) githubPlatform = 'github';
|
||||
} else { githubProbeStatus = 'error'; }
|
||||
} else {
|
||||
// owner/repo shorthand — try GitHub first, then GitLab
|
||||
const ghUrl = `https://github.com/${input}.git`;
|
||||
const ghResult = await appRpc.request['git.probe']({ url: ghUrl });
|
||||
if (ghResult?.ok) {
|
||||
githubProbeStatus = 'ok'; githubPlatform = 'github';
|
||||
// Fetch GitHub API metadata
|
||||
try { const res = await fetch(`https://api.github.com/repos/${input}`, { signal: AbortSignal.timeout(5000) }); if (res.ok) { const d = await res.json(); githubInfo = { stars: d.stargazers_count ?? 0, description: d.description ?? '', defaultBranch: d.default_branch ?? 'main' }; } } catch { /* optional */ }
|
||||
} else {
|
||||
// GitHub failed — try GitLab
|
||||
const glUrl = `https://gitlab.com/${input}.git`;
|
||||
const glResult = await appRpc.request['git.probe']({ url: glUrl });
|
||||
if (glResult?.ok) {
|
||||
githubProbeStatus = 'ok'; githubPlatform = 'gitlab';
|
||||
} else {
|
||||
githubProbeStatus = 'error';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { githubProbeStatus = 'error'; } githubLoading = false;
|
||||
}, 500);
|
||||
}
|
||||
|
|
@ -101,7 +126,7 @@
|
|||
|
||||
async function goToStep2() {
|
||||
if (sourceType === 'git-clone') { const url = sanitizeUrl(repoUrl); const target = sanitizePath(cloneTarget); if (!url || !target) return; cloning = true; try { const r = await appRpc.request['git.clone']({ url, target }); if (!r?.ok) { cloning = false; return; } localPath = target; isGitRepo = true; } catch { cloning = false; return; } cloning = false; }
|
||||
else if (sourceType === 'github') { const input = githubRepo.trim(); const url = (input.startsWith('http') || input.startsWith('git@')) ? input : `https://github.com/${input}.git`; const name = input.includes('/') ? input.split('/').pop()?.replace(/\.git$/, '') || 'project' : 'project'; const target = `~/projects/${name}`; cloning = true; try { const r = await appRpc.request['git.clone']({ url, target }); if (!r?.ok) { cloning = false; return; } localPath = target; isGitRepo = true; } catch { cloning = false; return; } cloning = false; }
|
||||
else if (sourceType === 'github') { const input = githubRepo.trim(); const platformBase = githubPlatform === 'gitlab' ? 'https://gitlab.com' : 'https://github.com'; const url = (input.startsWith('http') || input.startsWith('git@')) ? input : `${platformBase}/${input}.git`; const name = input.includes('/') ? input.split('/').pop()?.replace(/\.git$/, '') || 'project' : 'project'; const target = `~/projects/${name}`; cloning = true; try { const r = await appRpc.request['git.clone']({ url, target }); if (!r?.ok) { cloning = false; return; } localPath = target; isGitRepo = true; } catch { cloning = false; return; } cloning = false; }
|
||||
else if (sourceType === 'template') { const target = sanitizePath(templateTargetDir); if (!target) return; const name = sanitize(projectName) || selectedTemplate; cloning = true; try { const r = await appRpc.request['project.createFromTemplate']({ templateId: selectedTemplate, targetDir: target, projectName: name }); if (!r?.ok) { cloning = false; return; } localPath = r.path; if (!projectName) projectName = name; } catch { cloning = false; return; } cloning = false; }
|
||||
if (!projectName && localPath) { const parts = localPath.replace(/\/+$/, '').split('/'); projectName = parts[parts.length - 1] || ''; }
|
||||
if (isGitRepo && localPath) { try { const r = await appRpc.request['git.branches']({ path: localPath }); if (r?.branches) { branches = r.branches; selectedBranch = r.current || ''; } } catch { /* ignore */ } }
|
||||
|
|
@ -122,7 +147,7 @@
|
|||
step = 1; sourceType = 'local'; localPath = ''; repoUrl = ''; cloneTarget = ''; githubRepo = ''; selectedTemplate = ''; templateTargetDir = '~/projects';
|
||||
remoteHost = ''; remoteUser = ''; remotePath = ''; remoteAuthMethod = 'agent'; remotePassword = ''; remoteKeyPath = '~/.ssh/id_ed25519'; remoteSshfs = false; remoteSshfsMountpoint = '';
|
||||
pathValid = 'idle'; isGitRepo = false; gitBranch = ''; gitProbeStatus = 'idle'; gitProbeBranches = []; githubInfo = null; githubLoading = false; githubProbeStatus = 'idle';
|
||||
cloning = false; projectName = ''; nameError = ''; selectedBranch = ''; branches = []; useWorktrees = false; selectedGroupId = groupId;
|
||||
cloning = false; githubPlatform = null; projectName = ''; nameError = ''; selectedBranch = ''; branches = []; useWorktrees = false; selectedGroupId = groupId;
|
||||
projectIcon = 'Terminal'; projectColor = 'var(--ctp-blue)'; shellChoice = 'bash'; provider = 'claude'; model = ''; permissionMode = 'default';
|
||||
systemPrompt = ''; autoStart = false; providerModels = []; modelsLoading = false; modelConfig = {};
|
||||
}
|
||||
|
|
@ -152,7 +177,7 @@
|
|||
</div>
|
||||
<div class="wz-body" style:display={step === 1 ? 'flex' : 'none'}>
|
||||
<h3 class="wz-step-title">Source</h3>
|
||||
<WizardStep1 bind:this={step1Ref} {sourceType} {localPath} {repoUrl} {cloneTarget} {githubRepo} {selectedTemplate} {remoteHost} {remoteUser} {remotePath} {remoteAuthMethod} {remotePassword} {remoteKeyPath} {remoteSshfs} {remoteSshfsMountpoint} {pathValid} {isGitRepo} {gitBranch} {gitProbeStatus} {gitProbeBranches} {githubInfo} {githubProbeStatus} {githubLoading} {cloning} {templates} {templateTargetDir} {templateOriginDir} onUpdate={handleUpdate} />
|
||||
<WizardStep1 bind:this={step1Ref} {sourceType} {localPath} {repoUrl} {cloneTarget} {githubRepo} {selectedTemplate} {remoteHost} {remoteUser} {remotePath} {remoteAuthMethod} {remotePassword} {remoteKeyPath} {remoteSshfs} {remoteSshfsMountpoint} {pathValid} {isGitRepo} {gitBranch} {gitProbeStatus} {gitProbeBranches} {githubInfo} {githubProbeStatus} {githubLoading} {githubPlatform} {cloning} {templates} {templateTargetDir} {templateOriginDir} onUpdate={handleUpdate} />
|
||||
<div class="wz-footer">
|
||||
<button class="wz-btn secondary" onclick={closeWizard}>Cancel</button>
|
||||
<div class="wz-footer-right">{#if cloning}<span class="wz-cloning">Cloning…</span>{/if}<button class="wz-btn primary" disabled={!step1Valid() || cloning} onclick={goToStep2}>Next</button></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue