@@ -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" >
@@ -1953,24 +2008,37 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19532008 </ ul >
19542009 </ div >
19552010 </ div >
1956- < 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" >
1957- < div >
1958- < h4 className = "font-semibold mb-2" > Create New Branch</ h4 >
1959- < div className = "flex gap-2" >
1960- < input type = "text" value = { newBranchName } onChange = { e => setNewBranchName ( e . target . value ) } placeholder = "new-branch-name" className = { formInputStyle } />
1961- < 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 >
1962- </ div >
2011+ < div className = "pt-4 mt-2 border-t border-gray-200 dark:border-gray-700 flex flex-col gap-4 flex-shrink-0" >
2012+ < div className = "flex flex-wrap items-center justify-between gap-3" >
2013+ < p className = "text-sm text-gray-600 dark:text-gray-300" > { selectionDescription } </ p >
2014+ < button
2015+ type = "button"
2016+ onClick = { handleCheckoutBranch }
2017+ disabled = { checkoutDisabled }
2018+ 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' : '' } ` }
2019+ >
2020+ { isCheckoutLoading ? 'Checking out...' : 'Checkout' }
2021+ </ button >
19632022 </ div >
1964- < div >
1965- < h4 className = "font-semibold mb-2" > Merge Branch into Current</ h4 >
1966- < div className = "flex gap-2" >
1967- < select value = { branchToMerge || '' } onChange = { e => setBranchToMerge ( e . target . value ) } className = { formInputStyle } >
1968- < option value = "" disabled > Select a branch</ option >
1969- { ( branchInfo ?. local || [ ] ) . filter ( b => b !== branchInfo ?. current ) . map ( b => (
1970- < option key = { b } value = { b } > { b } </ option >
1971- ) ) }
1972- </ select >
1973- < 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 >
2023+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
2024+ < div >
2025+ < h4 className = "font-semibold mb-2" > Create New Branch</ h4 >
2026+ < div className = "flex gap-2" >
2027+ < input type = "text" value = { newBranchName } onChange = { e => setNewBranchName ( e . target . value ) } placeholder = "new-branch-name" className = { formInputStyle } />
2028+ < 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 >
2029+ </ div >
2030+ </ div >
2031+ < div >
2032+ < h4 className = "font-semibold mb-2" > Merge Branch into Current</ h4 >
2033+ < div className = "flex gap-2" >
2034+ < select value = { branchToMerge || '' } onChange = { e => setBranchToMerge ( e . target . value ) } className = { formInputStyle } >
2035+ < option value = "" disabled > Select a branch</ option >
2036+ { ( branchInfo ?. local || [ ] ) . filter ( b => b !== branchInfo ?. current ) . map ( b => (
2037+ < option key = { b } value = { b } > { b } </ option >
2038+ ) ) }
2039+ </ select >
2040+ < 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 >
2041+ </ div >
19742042 </ div >
19752043 </ div >
19762044 </ div >
@@ -1979,6 +2047,7 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19792047 </ div >
19802048 </ div >
19812049 ) ;
2050+ }
19822051 case 'releases' :
19832052 if ( ! isGitHubRepo ) return < div className = "p-4 text-center text-gray-500" > Release management is only available for repositories hosted on GitHub.</ div > ;
19842053 if ( editingRelease ) {
0 commit comments