From d7fb51f427b9d224b796885687cb0418c0925490 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Sun, 24 May 2026 09:25:48 +0000 Subject: [PATCH] feat: add branch dropdown and new branch creation UI - Replace free-text branch input with dropdown of available branches - Add 'Create new branch...' option with name and base branch inputs - Load branches from API when repository is selected in clone mode - Pass newBranch parameter to createInstance API --- apps/web/src/pages/sessions.tsx | 101 +++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/apps/web/src/pages/sessions.tsx b/apps/web/src/pages/sessions.tsx index bf74bad..f2df26c 100644 --- a/apps/web/src/pages/sessions.tsx +++ b/apps/web/src/pages/sessions.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { listProjects } from "../api/projects"; import type { Project } from "../types"; -import { listRepositories, type GitRepository } from "../api/git_repositories"; +import { listRepositories, listRepositoryBranches, type GitRepository, type Branch } from "../api/git_repositories"; import { getUserSessions, type Session, @@ -43,6 +43,12 @@ export const SessionsPage = () => { const [branch, setBranch] = useState("main"); const [sshKeys, setSshKeys] = useState([]); + const [branches, setBranches] = useState([]); + const [isLoadingBranches, setIsLoadingBranches] = useState(false); + const [isCreatingNewBranch, setIsCreatingNewBranch] = useState(false); + const [newBranchName, setNewBranchName] = useState(""); + const [baseBranch, setBaseBranch] = useState(""); + const [dirtyDeleteSession, setDirtyDeleteSession] = useState(null); const [dirtyDeleteFiles, setDirtyDeleteFiles] = useState([]); @@ -116,6 +122,33 @@ export const SessionsPage = () => { void loadSshKeys(); }, []); + useEffect(() => { + const loadBranches = async () => { + if (!selectedRepo || !selectedProject || cloneMode !== "clone") { + setBranches([]); + setIsCreatingNewBranch(false); + setNewBranchName(""); + setBaseBranch(""); + return; + } + setIsLoadingBranches(true); + try { + const data = await listRepositoryBranches(selectedProject, selectedRepo); + setBranches(data.branches); + const defaultBranch = data.default_branch; + setBaseBranch(defaultBranch); + if (!branch || !data.branches.find((b) => b.name === branch)) { + setBranch(defaultBranch); + } + } catch { + setBranches([]); + } finally { + setIsLoadingBranches(false); + } + }; + void loadBranches(); + }, [selectedRepo, selectedProject, cloneMode]); + // Poll health every 30 seconds for active instances useEffect(() => { const checkHealth = async () => { @@ -213,7 +246,8 @@ export const SessionsPage = () => { selectedToolType, displayName || undefined, cloneMode, - cloneMode === "clone" ? branch : undefined + isCreatingNewBranch ? baseBranch : branch, + isCreatingNewBranch ? newBranchName : undefined ); // Auto-start the instance @@ -227,6 +261,10 @@ export const SessionsPage = () => { setDisplayName(""); setCloneMode("mount"); setBranch("main"); + setIsCreatingNewBranch(false); + setNewBranchName(""); + setBaseBranch(""); + setBranches([]); await loadSessions(); } catch (error) { setCreateStatus("error"); @@ -687,14 +725,61 @@ export const SessionsPage = () => { <> + {isCreatingNewBranch && ( + <> + + + + )} + {selectedRepo && (
{(() => {