@@ -36,6 +36,11 @@ const COMBOBOX_LIMIT = 50;
3636// plain button the combobox's roving focus skips over.
3737const CREATE_BRANCH_ACTION = "__create_branch__" ;
3838
39+ // Sentinel for the "Use '<input>' as branch name" action in cloud mode.
40+ // Surfaced as the first list item so it's keyboard-reachable and
41+ // auto-highlighted while the (slow) remote search is still running.
42+ const USE_INPUT_BRANCH_ACTION = "__use_input_branch__" ;
43+
3944function LoadingRow ( { label } : { label : string } ) {
4045 return (
4146 < div className = "flex items-center gap-1 px-2 py-1.5 text-muted-foreground text-xs" >
@@ -136,8 +141,6 @@ export function BranchSelector({
136141
137142 const branches = isCloudMode ? ( cloudBranches ?? [ ] ) : localBranches ;
138143 const effectiveLoading = loading || ( isCloudMode && cloudBranchesLoading ) ;
139- const cloudStillLoading =
140- isCloudMode && cloudBranchesLoading && branches . length === 0 && ! open ;
141144 const branchListLoading = isCloudMode
142145 ? ! ! cloudBranchesLoading
143146 : localBranchesLoading ;
@@ -164,6 +167,40 @@ export function BranchSelector({
164167 } ) ,
165168 ) ;
166169
170+ // In local mode, surface in-progress git operations (rebase/merge/etc.) so the
171+ // user understands why there's no current branch and why we won't let them
172+ // checkout a different one — checkout would fail with a hard-to-read git error.
173+ const localBusy = ! isSelectionOnly && busyState ?. busy === true ;
174+ const busyOperationLabel =
175+ localBusy && busyState ?. busy
176+ ? BUSY_OPERATION_LABEL [ busyState . operation ]
177+ : null ;
178+
179+ const displayText = effectiveLoading
180+ ? "Loading..."
181+ : busyOperationLabel && ! displayedBranch
182+ ? busyOperationLabel
183+ : ( displayedBranch ?? "No branch" ) ;
184+
185+ const showSpinner =
186+ effectiveLoading || ( isCloudMode && open && cloudBranchesFetchingMore ) ;
187+
188+ const isDisabled = ! ! ( disabled || ! repoPath || localBusy ) ;
189+ const disabledReason =
190+ localBusy && busyOperationLabel
191+ ? `${ busyOperationLabel } in progress — finish or abort it to switch branches.`
192+ : null ;
193+ const inputValue = isCloudMode ? ( cloudSearchQuery ?? "" ) : searchQuery ;
194+ const trimmedInputValue = inputValue . trim ( ) ;
195+ const canUseInputBranch =
196+ ! isDisabled &&
197+ trimmedInputValue . length > 0 &&
198+ trimmedInputValue !== displayedBranch ;
199+ const showUseInputBranchAction =
200+ isCloudMode &&
201+ canUseInputBranch &&
202+ ! branches . some ( ( branch ) => branch === trimmedInputValue ) ;
203+
167204 const handleBranchChange = ( value : string | null ) => {
168205 if ( ! value ) return ;
169206 if ( value === CREATE_BRANCH_ACTION ) {
@@ -175,12 +212,15 @@ export function BranchSelector({
175212 ) ;
176213 return ;
177214 }
215+ const branchName =
216+ value === USE_INPUT_BRANCH_ACTION ? trimmedInputValue : value ;
217+ if ( ! branchName ) return ;
178218 if ( isSelectionOnly ) {
179- onBranchSelect ?.( value ) ;
180- } else if ( value !== currentBranch ) {
219+ onBranchSelect ?.( branchName ) ;
220+ } else if ( branchName !== currentBranch ) {
181221 checkoutMutation . mutate ( {
182222 directoryPath : repoPath as string ,
183- branchName : value ,
223+ branchName,
184224 } ) ;
185225 }
186226 if ( isCloudMode ) {
@@ -198,49 +238,20 @@ export function BranchSelector({
198238 }
199239 } ;
200240
201- // In local mode, surface in-progress git operations (rebase/merge/etc.) so the
202- // user understands why there's no current branch and why we won't let them
203- // checkout a different one — checkout would fail with a hard-to-read git error.
204- const localBusy = ! isSelectionOnly && busyState ?. busy === true ;
205- const busyOperationLabel =
206- localBusy && busyState ?. busy
207- ? BUSY_OPERATION_LABEL [ busyState . operation ]
208- : null ;
209-
210- const displayText = effectiveLoading
211- ? "Loading..."
212- : busyOperationLabel && ! displayedBranch
213- ? busyOperationLabel
214- : ( displayedBranch ?? "No branch" ) ;
215-
216- const showSpinner =
217- effectiveLoading || ( isCloudMode && open && cloudBranchesFetchingMore ) ;
218-
219- const isDisabled = ! ! (
220- disabled ||
221- ! repoPath ||
222- cloudStillLoading ||
223- localBusy
224- ) ;
225- const disabledReason =
226- localBusy && busyOperationLabel
227- ? `${ busyOperationLabel } in progress — finish or abort it to switch branches.`
228- : null ;
229- const inputValue = isCloudMode ? ( cloudSearchQuery ?? "" ) : searchQuery ;
230- const trimmedInputValue = inputValue . trim ( ) ;
231- const canUseInputBranch =
232- ! isDisabled &&
233- trimmedInputValue . length > 0 &&
234- trimmedInputValue !== displayedBranch ;
235-
236241 const handleUseInputBranch = ( ) => {
237242 if ( ! canUseInputBranch ) return ;
238243 handleBranchChange ( trimmedInputValue ) ;
239244 } ;
240245
246+ const comboboxItems = isCloudMode
247+ ? showUseInputBranchAction
248+ ? [ USE_INPUT_BRANCH_ACTION , ...branches ]
249+ : branches
250+ : [ ...branches , CREATE_BRANCH_ACTION ] ;
251+
241252 return (
242253 < Combobox
243- items = { isCloudMode ? branches : [ ... branches , CREATE_BRANCH_ACTION ] }
254+ items = { comboboxItems }
244255 limit = { COMBOBOX_LIMIT }
245256 autoHighlight
246257 value = { displayedBranch }
@@ -378,19 +389,35 @@ export function BranchSelector({
378389 ) }
379390
380391 < ComboboxList className = "max-h-[min(14rem,calc(var(--available-height,14rem)-5rem))]" >
381- { ( item : string ) =>
382- item === CREATE_BRANCH_ACTION ? (
383- < ComboboxListFooter key = "footer" >
392+ { ( item : string ) => {
393+ if ( item === CREATE_BRANCH_ACTION ) {
394+ return (
395+ < ComboboxListFooter key = "footer" >
396+ < ComboboxItem
397+ value = { CREATE_BRANCH_ACTION }
398+ title = "Create new branch"
399+ className = "text-accent-foreground"
400+ >
401+ < Plus size = { 11 } weight = "bold" />
402+ Create new branch
403+ </ ComboboxItem >
404+ </ ComboboxListFooter >
405+ ) ;
406+ }
407+ if ( item === USE_INPUT_BRANCH_ACTION ) {
408+ return (
384409 < ComboboxItem
385- value = { CREATE_BRANCH_ACTION }
386- title = "Create new branch"
410+ key = { USE_INPUT_BRANCH_ACTION }
411+ value = { USE_INPUT_BRANCH_ACTION }
412+ title = { `Use "${ trimmedInputValue } " as branch name` }
387413 className = "text-accent-foreground"
388414 >
389415 < Plus size = { 11 } weight = "bold" />
390- Create new branch
416+ Use " { trimmedInputValue } " as branch name
391417 </ ComboboxItem >
392- </ ComboboxListFooter >
393- ) : (
418+ ) ;
419+ }
420+ return (
394421 < ComboboxItem
395422 key = { item }
396423 value = { item }
@@ -399,8 +426,8 @@ export function BranchSelector({
399426 >
400427 { item }
401428 </ ComboboxItem >
402- )
403- }
429+ ) ;
430+ } }
404431 </ ComboboxList >
405432
406433 { isCloudMode && cloudBranchesHasMore ? (
0 commit comments