11import React , { useCallback , useEffect } from "react" ;
22import { RUNTIME_MODE , type RuntimeMode } from "@/common/types/runtime" ;
33import { Select } from "../Select" ;
4- import { Loader2 , Wand2 } from "lucide-react" ;
4+ import { Loader2 , Wand2 , GitBranch } from "lucide-react" ;
55import { cn } from "@/common/lib/utils" ;
66import { Tooltip , TooltipTrigger , TooltipContent } from "../ui/tooltip" ;
77import { SSHIcon , WorktreeIcon , LocalIcon } from "../icons/RuntimeIcons" ;
88import { DocsLink } from "../DocsLink" ;
99import type { WorkspaceNameState } from "@/browser/hooks/useWorkspaceName" ;
1010
11+ export type BranchMode = "new" | "existing" ;
12+
1113interface CreationControlsProps {
1214 branches : string [ ] ;
15+ /** Remote-only branches (not in local branches) */
16+ remoteBranches : string [ ] ;
1317 /** Whether branches have finished loading (to distinguish loading vs non-git repo) */
1418 branchesLoaded : boolean ;
1519 trunkBranch : string ;
@@ -25,6 +29,12 @@ interface CreationControlsProps {
2529 projectName : string ;
2630 /** Workspace name/title generation state and actions */
2731 nameState : WorkspaceNameState ;
32+ /** Branch mode: "new" creates a new branch, "existing" uses an existing branch */
33+ branchMode : BranchMode ;
34+ onBranchModeChange : ( mode : BranchMode ) => void ;
35+ /** Selected existing branch (when branchMode is "existing") */
36+ selectedExistingBranch : string ;
37+ onSelectedExistingBranchChange : ( branch : string ) => void ;
2838}
2939
3040/** Runtime type button group with icons and colors */
@@ -166,6 +176,10 @@ export function CreationControls(props: CreationControlsProps) {
166176 }
167177 } , [ isNonGitRepo , runtimeMode , onRuntimeModeChange ] ) ;
168178
179+ // All existing branches (local + remote)
180+ const allExistingBranches = [ ...props . branches , ...props . remoteBranches ] ;
181+ const hasExistingBranches = allExistingBranches . length > 0 ;
182+
169183 const handleNameChange = useCallback (
170184 ( e : React . ChangeEvent < HTMLInputElement > ) => {
171185 nameState . setName ( e . target . value ) ;
@@ -187,75 +201,126 @@ export function CreationControls(props: CreationControlsProps) {
187201
188202 return (
189203 < div className = "mb-3 flex flex-col gap-4" >
204+ { /* Branch mode toggle - only show for git repos with existing branches */ }
205+ { hasExistingBranches && (
206+ < div className = "flex items-center gap-3" data-component = "BranchModeGroup" >
207+ < button
208+ type = "button"
209+ onClick = { ( ) => props . onBranchModeChange ( "new" ) }
210+ disabled = { props . disabled }
211+ className = { cn (
212+ "inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs font-medium transition-all duration-150" ,
213+ props . branchMode === "new"
214+ ? "bg-accent/20 text-accent-foreground border-accent/60"
215+ : "bg-transparent text-muted border-transparent hover:border-accent/40"
216+ ) }
217+ >
218+ < Wand2 size = { 12 } />
219+ New branch
220+ </ button >
221+ < button
222+ type = "button"
223+ onClick = { ( ) => props . onBranchModeChange ( "existing" ) }
224+ disabled = { props . disabled }
225+ className = { cn (
226+ "inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs font-medium transition-all duration-150" ,
227+ props . branchMode === "existing"
228+ ? "bg-accent/20 text-accent-foreground border-accent/60"
229+ : "bg-transparent text-muted border-transparent hover:border-accent/40"
230+ ) }
231+ >
232+ < GitBranch size = { 12 } />
233+ Existing branch
234+ </ button >
235+ </ div >
236+ ) }
237+
190238 { /* Project name / workspace name header row */ }
191239 < div className = "flex items-center" data-component = "WorkspaceNameGroup" >
192240 < h2 className = "text-foreground shrink-0 text-lg font-semibold" > { props . projectName } </ h2 >
193241 < span className = "text-muted-foreground mx-2 text-lg" > /</ span >
194242
195- { /* Name input with magic wand - uses grid overlay technique for auto-sizing */ }
196- < div className = "relative inline-grid items-center" >
197- { /* Hidden sizer span - determines width based on content, minimum is placeholder width */ }
198- < span className = "invisible col-start-1 row-start-1 pr-7 text-lg font-semibold whitespace-pre" >
199- { nameState . name || "workspace-name" }
200- </ span >
201- < Tooltip >
202- < TooltipTrigger asChild >
203- < input
204- id = "workspace-name"
205- type = "text"
206- size = { 1 }
207- value = { nameState . name }
208- onChange = { handleNameChange }
209- onFocus = { handleInputFocus }
210- placeholder = { nameState . isGenerating ? "Generating..." : "workspace-name" }
211- disabled = { props . disabled }
212- className = { cn (
213- "col-start-1 row-start-1 min-w-0 bg-transparent border-border-medium focus:border-accent h-7 w-full rounded-md border border-transparent text-lg font-semibold focus:border focus:bg-bg-dark focus:outline-none disabled:opacity-50" ,
214- nameState . autoGenerate ? "text-muted" : "text-foreground" ,
215- nameState . error && "border-red-500"
216- ) }
217- />
218- </ TooltipTrigger >
219- < TooltipContent align = "start" className = "max-w-64" >
220- A stable identifier used for git branches, worktree folders, and session directories.
221- </ TooltipContent >
222- </ Tooltip >
223- { /* Magic wand / loading indicator */ }
224- < div className = "absolute inset-y-0 right-0 flex items-center pr-2" >
225- { nameState . isGenerating ? (
226- < Loader2 className = "text-accent h-3.5 w-3.5 animate-spin" />
227- ) : (
228- < Tooltip >
229- < TooltipTrigger asChild >
230- < button
231- type = "button"
232- onClick = { handleWandClick }
233- disabled = { props . disabled }
234- className = "flex h-full items-center disabled:opacity-50"
235- aria-label = {
236- nameState . autoGenerate ? "Disable auto-naming" : "Enable auto-naming"
237- }
238- >
239- < Wand2
240- className = { cn (
241- "h-3.5 w-3.5 transition-colors" ,
242- nameState . autoGenerate
243- ? "text-accent"
244- : "text-muted-foreground opacity-50 hover:opacity-75"
245- ) }
246- />
247- </ button >
248- </ TooltipTrigger >
249- < TooltipContent align = "center" >
250- { nameState . autoGenerate ? "Auto-naming enabled" : "Click to enable auto-naming" }
251- </ TooltipContent >
252- </ Tooltip >
253- ) }
243+ { /* New branch mode: Name input with magic wand */ }
244+ { props . branchMode === "new" && (
245+ < div className = "relative inline-grid items-center" >
246+ { /* Hidden sizer span - determines width based on content, minimum is placeholder width */ }
247+ < span className = "invisible col-start-1 row-start-1 pr-7 text-lg font-semibold whitespace-pre" >
248+ { nameState . name || "workspace-name" }
249+ </ span >
250+ < Tooltip >
251+ < TooltipTrigger asChild >
252+ < input
253+ id = "workspace-name"
254+ type = "text"
255+ size = { 1 }
256+ value = { nameState . name }
257+ onChange = { handleNameChange }
258+ onFocus = { handleInputFocus }
259+ placeholder = { nameState . isGenerating ? "Generating..." : "workspace-name" }
260+ disabled = { props . disabled }
261+ className = { cn (
262+ "col-start-1 row-start-1 min-w-0 bg-transparent border-border-medium focus:border-accent h-7 w-full rounded-md border border-transparent text-lg font-semibold focus:border focus:bg-bg-dark focus:outline-none disabled:opacity-50" ,
263+ nameState . autoGenerate ? "text-muted" : "text-foreground" ,
264+ nameState . error && "border-red-500"
265+ ) }
266+ />
267+ </ TooltipTrigger >
268+ < TooltipContent align = "start" className = "max-w-64" >
269+ A stable identifier used for git branches, worktree folders, and session
270+ directories.
271+ </ TooltipContent >
272+ </ Tooltip >
273+ { /* Magic wand / loading indicator */ }
274+ < div className = "absolute inset-y-0 right-0 flex items-center pr-2" >
275+ { nameState . isGenerating ? (
276+ < Loader2 className = "text-accent h-3.5 w-3.5 animate-spin" />
277+ ) : (
278+ < Tooltip >
279+ < TooltipTrigger asChild >
280+ < button
281+ type = "button"
282+ onClick = { handleWandClick }
283+ disabled = { props . disabled }
284+ className = "flex h-full items-center disabled:opacity-50"
285+ aria-label = {
286+ nameState . autoGenerate ? "Disable auto-naming" : "Enable auto-naming"
287+ }
288+ >
289+ < Wand2
290+ className = { cn (
291+ "h-3.5 w-3.5 transition-colors" ,
292+ nameState . autoGenerate
293+ ? "text-accent"
294+ : "text-muted-foreground opacity-50 hover:opacity-75"
295+ ) }
296+ />
297+ </ button >
298+ </ TooltipTrigger >
299+ < TooltipContent align = "center" >
300+ { nameState . autoGenerate ? "Auto-naming enabled" : "Click to enable auto-naming" }
301+ </ TooltipContent >
302+ </ Tooltip >
303+ ) }
304+ </ div >
254305 </ div >
255- </ div >
306+ ) }
307+
308+ { /* Existing branch mode: Branch selector */ }
309+ { props . branchMode === "existing" && (
310+ < Select
311+ id = "existing-branch"
312+ value = { props . selectedExistingBranch }
313+ options = { allExistingBranches }
314+ onChange = { props . onSelectedExistingBranchChange }
315+ disabled = { props . disabled }
316+ className = "h-8 min-w-[200px] text-lg font-semibold"
317+ />
318+ ) }
256319
257320 { /* Error display */ }
258- { nameState . error && < span className = "text-xs text-red-500" > { nameState . error } </ span > }
321+ { nameState . error && props . branchMode === "new" && (
322+ < span className = "text-xs text-red-500" > { nameState . error } </ span >
323+ ) }
259324 </ div >
260325
261326 { /* Runtime type - button group */ }
0 commit comments