@@ -1932,6 +1932,8 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19321932 const [ branchFilter , setBranchFilter ] = useState ( '' ) ;
19331933 const [ debouncedBranchFilter , setDebouncedBranchFilter ] = useState ( '' ) ;
19341934 const [ isDeletingBranches , setIsDeletingBranches ] = useState ( false ) ;
1935+ const [ isPruningRemoteBranches , setIsPruningRemoteBranches ] = useState ( false ) ;
1936+ const [ isCleaningLocalBranches , setIsCleaningLocalBranches ] = useState ( false ) ;
19351937 const branchItemRefs = useRef < { local : Map < string , HTMLDivElement > ; remote : Map < string , HTMLDivElement > } > ( {
19361938 local : new Map < string , HTMLDivElement > ( ) ,
19371939 remote : new Map < string , HTMLDivElement > ( ) ,
@@ -1968,6 +1970,7 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
19681970
19691971 const formInputStyle = "block w-full bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-1.5 px-3 text-gray-900 dark:text-white focus:outline-none focus:ring-blue-500 focus:border-blue-500" ;
19701972 const formLabelStyle = "block text-sm font-medium text-gray-700 dark:text-gray-300" ;
1973+ const branchActionButtonStyle = 'inline-flex items-center justify-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md border transition focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-60 disabled:cursor-not-allowed' ;
19711974
19721975 const isGitRepo = formData . vcs === VcsType . Git ;
19731976 const isSvnRepo = formData . vcs === VcsType . Svn ;
@@ -2573,6 +2576,58 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
25732576 } ) ;
25742577 } , [ repository , selectedBranches , branchInfo ?. current , confirmAction , setToast , fetchBranches , onRefreshState , isGitRepo ] ) ;
25752578
2579+ const handlePruneRemoteBranches = useCallback ( async ( ) => {
2580+ if ( ! repository ) {
2581+ return ;
2582+ }
2583+ if ( ! isGitRepo ) {
2584+ setToast ( { message : 'Remote pruning is only supported for Git repositories.' , type : 'info' } ) ;
2585+ return ;
2586+ }
2587+
2588+ setIsPruningRemoteBranches ( true ) ;
2589+ try {
2590+ const result = await window . electronAPI ?. pruneRemoteBranches ( repository . localPath ) ;
2591+ if ( result ?. success ) {
2592+ setToast ( { message : result ?. message ?? 'Pruned stale remote branches.' , type : 'success' } ) ;
2593+ await fetchBranches ( ) ;
2594+ await onRefreshState ( repository . id ) ;
2595+ } else {
2596+ setToast ( { message : `Error: ${ result ?. error || 'Electron API not available.' } ` , type : 'error' } ) ;
2597+ }
2598+ } catch ( error : any ) {
2599+ setToast ( { message : `Error: ${ error ?. message || 'Failed to prune remote branches.' } ` , type : 'error' } ) ;
2600+ } finally {
2601+ setIsPruningRemoteBranches ( false ) ;
2602+ }
2603+ } , [ repository , isGitRepo , setToast , fetchBranches , onRefreshState ] ) ;
2604+
2605+ const handleCleanupLocalBranches = useCallback ( async ( ) => {
2606+ if ( ! repository ) {
2607+ return ;
2608+ }
2609+ if ( ! isGitRepo ) {
2610+ setToast ( { message : 'Local branch cleanup is only supported for Git repositories.' , type : 'info' } ) ;
2611+ return ;
2612+ }
2613+
2614+ setIsCleaningLocalBranches ( true ) ;
2615+ try {
2616+ const result = await window . electronAPI ?. cleanupLocalBranches ( repository . localPath ) ;
2617+ if ( result ?. success ) {
2618+ setToast ( { message : result ?. message ?? 'Removed merged or stale local branches.' , type : 'success' } ) ;
2619+ await fetchBranches ( ) ;
2620+ await onRefreshState ( repository . id ) ;
2621+ } else {
2622+ setToast ( { message : `Error: ${ result ?. error || 'Electron API not available.' } ` , type : 'error' } ) ;
2623+ }
2624+ } catch ( error : any ) {
2625+ setToast ( { message : `Error: ${ error ?. message || 'Failed to clean up local branches.' } ` , type : 'error' } ) ;
2626+ } finally {
2627+ setIsCleaningLocalBranches ( false ) ;
2628+ }
2629+ } , [ repository , isGitRepo , setToast , fetchBranches , onRefreshState ] ) ;
2630+
25762631 const handleMergeBranch = async ( ) => {
25772632 if ( ! repository || ! branchToMerge ) return ;
25782633 if ( ! isGitRepo ) {
@@ -3017,29 +3072,51 @@ const RepoEditView: React.FC<RepoEditViewProps> = ({ onSave, onCancel, repositor
30173072 < p className = "text-sm" >
30183073 Current branch: < span className = "font-bold font-mono text-blue-600 dark:text-blue-400" > { branchInfo ?. current } </ span >
30193074 </ p >
3020- < div className = "max-w-md flex flex-col sm:flex-row sm:items-center gap-2" >
3075+ < div className = "max-w-2xl flex flex-col sm:flex-row sm:items-center gap-2" >
30213076 < input
30223077 type = "text"
30233078 value = { branchFilter }
30243079 onChange = { event => setBranchFilter ( event . target . value ) }
30253080 placeholder = "Filter branches"
3026- className = { `${ formInputStyle } flex-1` }
3081+ className = { `${ formInputStyle } flex-1 min-w-[12rem] ` }
30273082 />
3028- < button
3029- type = "button"
3030- onClick = { fetchBranches }
3031- disabled = { branchesLoading }
3032- className = "inline-flex items-center justify-center gap-2 px-3 py-1.5 text-sm font-medium text-blue-600 border border-blue-600 rounded-md transition focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500 hover:bg-blue-50 disabled:opacity-60 disabled:cursor-not-allowed"
3033- >
3034- { branchesLoading ? (
3083+ < div className = "flex flex-wrap items-center gap-2" >
3084+ < button
3085+ type = "button"
3086+ onClick = { fetchBranches }
3087+ disabled = { branchesLoading || isPruningRemoteBranches || isCleaningLocalBranches }
3088+ className = { `${ branchActionButtonStyle } text-blue-600 border-blue-600 hover:bg-blue-50 focus-visible:ring-blue-500` }
3089+ >
3090+ { branchesLoading ? (
3091+ < >
3092+ < ArrowPathIcon className = "h-4 w-4 animate-spin" />
3093+ Refreshing...
3094+ </ >
3095+ ) : (
3096+ 'Refresh'
3097+ ) }
3098+ </ button >
3099+ { isGitRepo && (
30353100 < >
3036- < ArrowPathIcon className = "h-4 w-4 animate-spin" />
3037- Refreshing...
3101+ < button
3102+ type = "button"
3103+ onClick = { handlePruneRemoteBranches }
3104+ disabled = { branchesLoading || isPruningRemoteBranches || isCleaningLocalBranches }
3105+ className = { `${ branchActionButtonStyle } text-amber-600 border-amber-600 hover:bg-amber-50 focus-visible:ring-amber-500` }
3106+ >
3107+ { isPruningRemoteBranches ? 'Pruning…' : 'Prune Remotes' }
3108+ </ button >
3109+ < button
3110+ type = "button"
3111+ onClick = { handleCleanupLocalBranches }
3112+ disabled = { branchesLoading || isPruningRemoteBranches || isCleaningLocalBranches }
3113+ className = { `${ branchActionButtonStyle } text-emerald-600 border-emerald-600 hover:bg-emerald-50 focus-visible:ring-emerald-500` }
3114+ >
3115+ { isCleaningLocalBranches ? 'Cleaning…' : 'Clean Local Branches' }
3116+ </ button >
30383117 </ >
3039- ) : (
3040- 'Refresh'
30413118 ) }
3042- </ button >
3119+ </ div >
30433120 </ div >
30443121 < p className = "text-xs text-gray-500 dark:text-gray-400" > Tip: Use Shift or Ctrl/Cmd-click to select multiple branches.</ p >
30453122 < div className = "flex-1 grid grid-cols-1 md:grid-cols-2 gap-6 overflow-hidden" >
0 commit comments