@@ -1167,13 +1167,25 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
11671167 const [ newBranchName , setNewBranchName ] = useState ( '' ) ;
11681168 const [ branchToMerge , setBranchToMerge ] = useState ( '' ) ;
11691169 const [ selectedBranch , setSelectedBranch ] = useState < { name : string ; scope : 'local' | 'remote' } | null > ( null ) ;
1170+ const [ isCheckoutLoading , setIsCheckoutLoading ] = useState ( false ) ;
11701171 const [ branchFilter , setBranchFilter ] = useState ( '' ) ;
11711172 const [ debouncedBranchFilter , setDebouncedBranchFilter ] = useState ( '' ) ;
11721173 const branchItemRefs = useRef < { local : Map < string , HTMLDivElement > ; remote : Map < string , HTMLDivElement > } > ( {
11731174 local : new Map < string , HTMLDivElement > ( ) ,
11741175 remote : new Map < string , HTMLDivElement > ( ) ,
11751176 } ) ;
11761177
1178+ const normalizedSelectedBranchName = useMemo ( ( ) => {
1179+ if ( ! selectedBranch ) {
1180+ return '' ;
1181+ }
1182+ if ( selectedBranch . scope === 'remote' ) {
1183+ const segments = selectedBranch . name . split ( '/' ) . slice ( 1 ) ;
1184+ return segments . join ( '/' ) || selectedBranch . name ;
1185+ }
1186+ return selectedBranch . name ;
1187+ } , [ selectedBranch ] ) ;
1188+
11771189 // State for Releases Tab
11781190 const [ releases , setReleases ] = useState < ReleaseInfo [ ] | null > ( null ) ;
11791191 const [ releasesLoading , setReleasesLoading ] = useState ( false ) ;
@@ -1611,6 +1623,44 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
16111623 element ?. focus ( ) ;
16121624 } , [ ] ) ;
16131625
1626+ const handleCheckoutBranch = useCallback ( async ( ) => {
1627+ if ( ! repository || ! selectedBranch ) {
1628+ return ;
1629+ }
1630+
1631+ const currentBranch = branchInfo ?. current ;
1632+ const focusTarget = normalizedSelectedBranchName || selectedBranch . name ;
1633+ const checkoutLabel = selectedBranch . name ;
1634+
1635+ if ( currentBranch && normalizedSelectedBranchName && normalizedSelectedBranchName === currentBranch ) {
1636+ setToast ( { message : `Already on '${ currentBranch } '.` , type : 'info' } ) ;
1637+ return ;
1638+ }
1639+
1640+ setIsCheckoutLoading ( true ) ;
1641+ const checkoutTarget = selectedBranch . name ;
1642+
1643+ try {
1644+ const result = await window . electronAPI ?. checkoutBranch ( repository . localPath , checkoutTarget ) ;
1645+ if ( result ?. success ) {
1646+ setToast ( { message : `Checked out '${ checkoutLabel } '.` , type : 'success' } ) ;
1647+ await fetchBranches ( ) ;
1648+ await onRefreshState ( repository . id ) ;
1649+ setBranchToMerge ( '' ) ;
1650+ setSelectedBranch ( null ) ;
1651+ if ( focusTarget ) {
1652+ requestAnimationFrame ( ( ) => focusBranchItem ( 'local' , focusTarget ) ) ;
1653+ }
1654+ } else {
1655+ setToast ( { message : `Checkout failed: ${ result ?. error || 'Electron API not available.' } ` , type : 'error' } ) ;
1656+ }
1657+ } catch ( error : any ) {
1658+ setToast ( { message : `Checkout failed: ${ error . message } ` , type : 'error' } ) ;
1659+ } finally {
1660+ setIsCheckoutLoading ( false ) ;
1661+ }
1662+ } , [ repository , selectedBranch , branchInfo ?. current , normalizedSelectedBranchName , setToast , fetchBranches , onRefreshState , focusBranchItem ] ) ;
1663+
16141664 const handleBranchKeyDown = useCallback ( ( event : React . KeyboardEvent < HTMLDivElement > , branchName : string , scope : 'local' | 'remote' ) => {
16151665 if ( event . key === 'Enter' || event . key === ' ' ) {
16161666 event . preventDefault ( ) ;
@@ -1853,7 +1903,12 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
18531903 </ div >
18541904 </ div >
18551905 ) ;
1856- case 'branches' :
1906+ case 'branches' : {
1907+ const isCurrentSelection = Boolean ( normalizedSelectedBranchName && branchInfo ?. current && normalizedSelectedBranchName === branchInfo . current ) ;
1908+ const checkoutDisabled = ! selectedBranch || isCheckoutLoading || branchesLoading || isCurrentSelection ;
1909+ const selectionDescription = selectedBranch
1910+ ? `${ selectedBranch . scope === 'remote' ? 'Remote' : 'Local' } branch: ${ selectedBranch . name } `
1911+ : 'Select a branch to checkout.' ;
18571912 return (
18581913 < div className = "flex-1 flex flex-col overflow-hidden" >
18591914 < div className = "flex-1 flex flex-col overflow-hidden p-4 gap-4" >
@@ -1968,24 +2023,37 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19682023 </ ul >
19692024 </ div >
19702025 </ div >
1971- < div className = "grid grid-cols-1 md:grid-cols-2 gap-6 pt-4 mt-2 border-t border-gray-200 dark:border-gray-700 flex-shrink-0" >
1972- < div >
1973- < h4 className = "font-semibold mb-2" > Create New Branch</ h4 >
1974- < div className = "flex gap-2" >
1975- < input type = "text" value = { newBranchName } onChange = { e => setNewBranchName ( e . target . value ) } placeholder = "new-branch-name" className = { formInputStyle } />
1976- < button type = "button" onClick = { handleCreateBranch } className = "px-4 py-2 bg-green-600 text-white rounded-md text-sm font-medium hover:bg-green-700" > Create</ button >
1977- </ div >
2026+ < div className = "pt-4 mt-2 border-t border-gray-200 dark:border-gray-700 flex flex-col gap-4 flex-shrink-0" >
2027+ < div className = "flex flex-wrap items-center justify-between gap-3" >
2028+ < p className = "text-sm text-gray-600 dark:text-gray-300" > { selectionDescription } </ p >
2029+ < button
2030+ type = "button"
2031+ onClick = { handleCheckoutBranch }
2032+ disabled = { checkoutDisabled }
2033+ className = { `px-4 py-2 rounded-md text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 disabled:bg-gray-400 disabled:cursor-not-allowed ${ isCheckoutLoading ? 'cursor-wait' : '' } ` }
2034+ >
2035+ { isCheckoutLoading ? 'Checking out...' : 'Checkout' }
2036+ </ button >
19782037 </ div >
1979- < div >
1980- < h4 className = "font-semibold mb-2" > Merge Branch into Current</ h4 >
1981- < div className = "flex gap-2" >
1982- < select value = { branchToMerge || '' } onChange = { e => setBranchToMerge ( e . target . value ) } className = { formInputStyle } >
1983- < option value = "" disabled > Select a branch</ option >
1984- { ( branchInfo ?. local || [ ] ) . filter ( b => b !== branchInfo ?. current ) . map ( b => (
1985- < option key = { b } value = { b } > { b } </ option >
1986- ) ) }
1987- </ select >
1988- < button type = "button" onClick = { handleMergeBranch } className = "px-4 py-2 bg-indigo-600 text-white rounded-md text-sm font-medium hover:bg-indigo-700" > Merge</ button >
2038+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
2039+ < div >
2040+ < h4 className = "font-semibold mb-2" > Create New Branch</ h4 >
2041+ < div className = "flex gap-2" >
2042+ < input type = "text" value = { newBranchName } onChange = { e => setNewBranchName ( e . target . value ) } placeholder = "new-branch-name" className = { formInputStyle } />
2043+ < button type = "button" onClick = { handleCreateBranch } className = "px-4 py-2 bg-green-600 text-white rounded-md text-sm font-medium hover:bg-green-700" > Create</ button >
2044+ </ div >
2045+ </ div >
2046+ < div >
2047+ < h4 className = "font-semibold mb-2" > Merge Branch into Current</ h4 >
2048+ < div className = "flex gap-2" >
2049+ < select value = { branchToMerge || '' } onChange = { e => setBranchToMerge ( e . target . value ) } className = { formInputStyle } >
2050+ < option value = "" disabled > Select a branch</ option >
2051+ { ( branchInfo ?. local || [ ] ) . filter ( b => b !== branchInfo ?. current ) . map ( b => (
2052+ < option key = { b } value = { b } > { b } </ option >
2053+ ) ) }
2054+ </ select >
2055+ < button type = "button" onClick = { handleMergeBranch } className = "px-4 py-2 bg-indigo-600 text-white rounded-md text-sm font-medium hover:bg-indigo-700" > Merge</ button >
2056+ </ div >
19892057 </ div >
19902058 </ div >
19912059 </ div >
@@ -1994,6 +2062,7 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19942062 </ div >
19952063 </ div >
19962064 ) ;
2065+ }
19972066 case 'releases' :
19982067 if ( ! isGitHubRepo ) return < div className = "p-4 text-center text-gray-500" > Release management is only available for repositories hosted on GitHub.</ div > ;
19992068 if ( editingRelease ) {
0 commit comments