fix(frontend): improve import picker feedback

This commit is contained in:
Aditi Chauhan 2026-07-01 14:20:14 +05:30
parent b649d59b06
commit 877ac7840f
3 changed files with 72 additions and 45 deletions

View File

@ -844,10 +844,26 @@ async function isGitRepo(repoPath: string): Promise<boolean> {
}
}
async function resolveDefaultBranch(repoPath: string): Promise<string> {
try {
const ref = await gitOutput(repoPath, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
if (ref) return ref.replace(/^origin\//, "");
} catch {
// Fall back to the checked-out branch when origin/HEAD is unavailable.
}
try {
const branch = await gitOutput(repoPath, ["branch", "--show-current"]);
if (branch) return branch;
} catch {
// Detached or unreadable HEAD is represented below.
}
return "HEAD";
}
async function scanGitRepo(repoPath: string, rootPath: string): Promise<GitRepoScanResult | null> {
if (!(await isGitRepo(repoPath))) return null;
const [branchResult, remoteResult] = await Promise.allSettled([
gitOutput(repoPath, ["branch", "--show-current"]),
resolveDefaultBranch(repoPath),
gitOutput(repoPath, ["remote", "get-url", "origin"]),
]);
const relativePath = repoPath === rootPath ? "." : path.relative(rootPath, repoPath);

View File

@ -1,5 +1,5 @@
import * as Dialog from "@radix-ui/react-dialog";
import { ArrowLeft, CheckCircle2, Folder, FolderPlus, GitBranch, Package, RefreshCw, X, XCircle } from "lucide-react";
import { ArrowLeft, CheckCircle2, Folder, FolderPlus, Package, RefreshCw, X, XCircle } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import type React from "react";
import { aoBridge } from "../lib/bridge";
@ -37,9 +37,10 @@ function ImportCard({ active = false, children, onClick, "aria-label": ariaLabel
aria-label={ariaLabel}
onClick={onClick}
className={cn(
"flex min-h-[184px] flex-col rounded-lg border border-border bg-background/70 p-4 text-left transition-colors",
"hover:border-border-strong hover:bg-surface",
active && "border-accent-dim bg-accent-weak/20",
"group flex min-h-[184px] cursor-pointer flex-col rounded-lg border border-border bg-background/70 p-4 text-left transition",
"hover:-translate-y-0.5 hover:border-border-strong hover:bg-surface hover:shadow-sm",
"active:translate-y-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70",
active && "border-accent-dim bg-accent-weak/20 ring-1 ring-accent/40",
)}
>
{children}
@ -156,49 +157,56 @@ export function ImportProjectDialog({ error, isImporting, onOpenChange, onSubmit
</button>
</Dialog.Close>
</div>
<div className="grid gap-3 p-5 sm:grid-cols-2">
<ImportCard aria-label="Workspace" onClick={() => selectMode("workspace")}>
<div className="mb-5 rounded-md border border-border bg-surface p-3">
<div className="flex items-center gap-2 text-[12px] text-muted-foreground">
<Folder className="size-4 text-accent" aria-hidden="true" />
<span className="font-medium text-foreground">workspace</span>
<div className="grid min-h-[330px] place-items-center px-5 py-8">
<div className="grid w-full max-w-[620px] gap-3 sm:grid-cols-2">
<ImportCard aria-label="Workspace" onClick={() => selectMode("workspace")}>
<div className="mb-5 grid min-h-[118px] place-items-center rounded-md border border-border bg-surface px-3 py-3 transition-colors group-hover:border-border-strong">
<div className="w-[min(100%,206px)]">
<div className="mx-auto flex w-fit items-center gap-2 text-[12px] text-muted-foreground">
<Folder className="size-4 text-accent" aria-hidden="true" />
<span className="font-medium text-foreground">workspace</span>
</div>
<div className="mt-2 grid gap-1.5">
{["web-app", "api-server", "shared-libs"].map((name) => (
<span
key={name}
className="flex h-8 items-center rounded-md border border-border bg-background px-3 font-mono text-[11px] text-muted-foreground"
>
<span className="mr-2 size-1.5 rounded-full bg-success" aria-hidden="true" />
{name}
</span>
))}
</div>
</div>
</div>
<div className="mt-3 grid gap-1.5 pl-5">
{["web-app", "api-server", "shared-libs"].map((name) => (
<span
key={name}
className="rounded border border-border bg-background px-2 py-1 font-mono text-[11px] text-muted-foreground"
>
{name}
</span>
))}
<div className="mt-auto">
<div className="text-[14px] font-semibold text-foreground">Workspace</div>
<p className="mt-1 text-[12px] leading-5 text-muted-foreground">
Several Git repos that live under one parent folder
</p>
<div className="mt-3 font-mono text-[10px] uppercase tracking-[0.08em] text-passive">
Multiple repositories
</div>
</div>
</div>
<div className="mt-auto">
<div className="text-[14px] font-semibold text-foreground">Workspace</div>
<p className="mt-1 text-[12px] leading-5 text-muted-foreground">
Several Git repos that live under one parent folder
</p>
<div className="mt-3 font-mono text-[10px] uppercase tracking-[0.08em] text-passive">
Multiple repositories
</ImportCard>
<ImportCard aria-label="Project" onClick={() => selectMode("project")}>
<div className="mb-5 grid min-h-[118px] place-items-center rounded-md border border-border bg-surface px-3 py-3 transition-colors group-hover:border-border-strong">
<div className="flex h-11 w-[min(100%,160px)] items-center justify-center gap-2 rounded-md border border-border bg-background px-3 font-mono text-[11px] text-muted-foreground">
<span className="size-1.5 rounded-full bg-success" aria-hidden="true" />
<span className="font-medium text-foreground">web-app</span>
<span aria-hidden="true">·</span>
<span>main</span>
</div>
</div>
</div>
</ImportCard>
<ImportCard aria-label="Project" onClick={() => selectMode("project")}>
<div className="mb-5 rounded-md border border-border bg-surface p-3">
<div className="inline-flex items-center gap-2 rounded-md border border-border bg-background px-2.5 py-1.5 font-mono text-[11px] text-muted-foreground">
<GitBranch className="size-3.5 text-accent" aria-hidden="true" />
web-app · main
<div className="mt-auto">
<div className="text-[14px] font-semibold text-foreground">Project</div>
<p className="mt-1 text-[12px] leading-5 text-muted-foreground">A single Git repository</p>
<div className="mt-3 font-mono text-[10px] uppercase tracking-[0.08em] text-passive">
One repository
</div>
</div>
</div>
<div className="mt-auto">
<div className="text-[14px] font-semibold text-foreground">Project</div>
<p className="mt-1 text-[12px] leading-5 text-muted-foreground">A single Git repository</p>
<div className="mt-3 font-mono text-[10px] uppercase tracking-[0.08em] text-passive">
One repository
</div>
</div>
</ImportCard>
</ImportCard>
</div>
</div>
</div>
) : (
@ -286,7 +294,9 @@ export function ImportProjectDialog({ error, isImporting, onOpenChange, onSubmit
<div className="mt-0.5 truncate text-[11px] text-passive">{displayPath(repo.path)}</div>
</div>
<div className="max-w-[220px] shrink-0 truncate text-right font-mono text-[11px] text-muted-foreground">
{repo.hasRemote ? `${repo.branch} ${remoteHost(repo.remote)}` : "No remote configured"}
{repo.hasRemote
? `default ${repo.branch} · ${remoteHost(repo.remote)}`
: "No remote configured"}
</div>
</div>
))}

View File

@ -165,6 +165,7 @@ describe("Sidebar", () => {
await user.click(screen.getByRole("button", { name: "Project" }));
await user.click(screen.getByRole("button", { name: /Choose a project folder/ }));
expect((await screen.findAllByText("/repo/new-project")).length).toBeGreaterThan(0);
expect(screen.getByText("default main · github.com")).toBeInTheDocument();
await chooseOption(screen.getByRole("combobox", { name: "Worker agent" }), "codex");
await chooseOption(screen.getByRole("combobox", { name: "Orchestrator agent" }), "claude-code");
await user.click(screen.getByRole("button", { name: "Import project" }));