From b8fc4e6642de15510091ca957dec508b04309bac Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Mon, 1 Jun 2026 17:48:27 +0200 Subject: [PATCH] feat: workspace creation with branch dropdown and auto-select - Fetch branches from selected repo via listRepositoryBranches API - Branch dropdown with default branch pre-selected - '+ Create new branch...' option reveals text input for custom branch - Auto-select first option when only one available: - Project: auto-selects when only 1 project - Repo: auto-selects when only 1 repo - Branch: auto-selects when only 1 branch, otherwise defaults to remote default - Falls back to free-text branch input if branch API fails - TypeScript + eslint clean --- apps/web/src/pages/workspaces.tsx | 104 +++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/apps/web/src/pages/workspaces.tsx b/apps/web/src/pages/workspaces.tsx index df2e261..84945e0 100644 --- a/apps/web/src/pages/workspaces.tsx +++ b/apps/web/src/pages/workspaces.tsx @@ -8,7 +8,7 @@ import { WorkspaceCard } from "../components/workspace-card"; import { StartToolModal } from "../components/start-tool-modal"; import { createInstance, startInstance } from "../api/sessions"; import { listProjects } from "../api/projects"; -import { listRepositories } from "../api/git_repositories"; +import { listRepositories, listRepositoryBranches } from "../api/git_repositories"; import { createWorkspaceTopLevel } from "../api/workspaces"; import type { Workspace } from "../types/workspace"; import type { ProjectWithRepos } from "../types"; @@ -152,10 +152,13 @@ function WorkspaceCreateInline({ }) { const [projects, setProjects] = useState([]); const [repos, setRepos] = useState([]); + const [branches, setBranches] = useState([]); const [selectedProject, setSelectedProject] = useState(""); const [selectedRepo, setSelectedRepo] = useState(""); + const [selectedBranch, setSelectedBranch] = useState(""); + const [newBranchName, setNewBranchName] = useState(""); + const [isNewBranch, setIsNewBranch] = useState(false); const [name, setName] = useState(""); - const [branch, setBranch] = useState("main"); const [loading, setLoading] = useState(false); const [fetching, setFetching] = useState(true); const [error, setError] = useState(null); @@ -198,6 +201,45 @@ function WorkspaceCreateInline({ void loadRepos(); }, [selectedProject]); + useEffect(() => { + if (!selectedProject || !selectedRepo) { + setBranches([]); + setSelectedBranch(""); + setIsNewBranch(false); + return; + } + const loadBranches = async () => { + try { + const data = await listRepositoryBranches(selectedProject, selectedRepo); + const branchNames = data.branches.map((b) => b.name); + setBranches(branchNames); + if (branchNames.length === 1) { + setSelectedBranch(branchNames[0]); + setIsNewBranch(false); + } else if (data.default_branch) { + setSelectedBranch(data.default_branch); + setIsNewBranch(false); + } + } catch { + // If branch fetch fails, fall back to free-text + setBranches([]); + setIsNewBranch(true); + } + }; + void loadBranches(); + }, [selectedProject, selectedRepo]); + + const handleBranchChange = (value: string) => { + if (value === "__new__") { + setIsNewBranch(true); + setSelectedBranch("__new__"); + setNewBranchName(""); + } else { + setIsNewBranch(false); + setSelectedBranch(value); + } + }; + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedRepo) { @@ -208,17 +250,24 @@ function WorkspaceCreateInline({ setError("Workspace name is required"); return; } + const branchName = isNewBranch ? newBranchName.trim() : selectedBranch; + if (!branchName) { + setError("Please select or enter a branch"); + return; + } setLoading(true); setError(null); try { await createWorkspaceTopLevel({ repo_id: selectedRepo, name: name.trim(), - branch: branch.trim() || "main", + branch: branchName, }); onCreated(); } catch (err) { - setError(err instanceof Error ? err.message : "Failed to create workspace"); + setError( + err instanceof Error ? err.message : "Failed to create workspace", + ); } finally { setLoading(false); } @@ -292,12 +341,47 @@ function WorkspaceCreateInline({ - setBranch(e.target.value)} - placeholder="main" - /> + {branches.length > 0 ? ( + <> + + {isNewBranch && ( + setNewBranchName(e.target.value)} + placeholder="new-branch-name" + required + style={{ marginTop: "0.5rem" }} + /> + )} + + ) : ( + { + setIsNewBranch(true); + setNewBranchName(e.target.value); + setSelectedBranch("__new__"); + }} + placeholder="main" + required + disabled={!selectedRepo} + /> + )} {error && (