@@ -4,7 +4,7 @@ import { exists } from "@tauri-apps/plugin-fs";
44import { getCurrentWindow } from "@tauri-apps/api/window" ;
55import { APP_NAME } from "./constants" ;
66import { Header , TabBar , RepoTabBar , GitLogPanel , LeftSidebar , RepoStateBanner } from "./components/layout" ;
7- import type { PendingFilePreview } from "./components/layout/SidebarQuickDiff" ;
7+ import type { PendingFilePreview , BranchFileDiffInfo } from "./components/layout/SidebarQuickDiff" ;
88import { PendingTab } from "./components/tabs" ;
99import { MergeEditor } from "./components/shared/MergeEditor" ;
1010import type { FileStatus } from "./lib" ;
@@ -18,6 +18,7 @@ const TagTab = lazy(() => import("./components/tabs/TagTab").then(m => ({ defaul
1818const WorktreeTab = lazy ( ( ) => import ( "./components/tabs/WorktreeTab" ) . then ( m => ( { default : m . WorktreeTab } ) ) ) ;
1919const PluginTab = lazy ( ( ) => import ( "./components/tabs/PluginTab" ) . then ( m => ( { default : m . PluginTab } ) ) ) ;
2020const TimeLapsePanel = lazy ( ( ) => import ( "./components/layout/TimeLapsePanel" ) . then ( m => ( { default : m . TimeLapsePanel } ) ) ) ;
21+ const BranchDiffTab = lazy ( ( ) => import ( "./components/tabs/BranchDiffTab" ) . then ( m => ( { default : m . BranchDiffTab } ) ) ) ;
2122import {
2223 isGitRepository ,
2324 getRepositoryRoot ,
@@ -108,6 +109,10 @@ function App() {
108109 } | null > ( null ) ;
109110 const [ activeMergeFile , setActiveMergeFile ] = useState < FileStatus | null > ( null ) ;
110111 const [ conflictCount , setConflictCount ] = useState ( 0 ) ;
112+ const [ branchDiffOpen , setBranchDiffOpen ] = useState ( false ) ;
113+ const [ branchFileDiff , setBranchFileDiff ] = useState < BranchFileDiffInfo | null > ( null ) ;
114+ const [ branchDiffInitialBase , setBranchDiffInitialBase ] = useState < string | undefined > ( ) ;
115+ const [ branchDiffInitialCompare , setBranchDiffInitialCompare ] = useState < string | undefined > ( ) ;
111116
112117 // Set window title (visible in taskbar)
113118 useEffect ( ( ) => {
@@ -343,6 +348,10 @@ function App() {
343348 setLfsLockCount ( 0 ) ;
344349 setActiveMergeFile ( null ) ;
345350 setConflictCount ( 0 ) ;
351+ setBranchDiffOpen ( false ) ;
352+ setBranchFileDiff ( null ) ;
353+ setBranchDiffInitialBase ( undefined ) ;
354+ setBranchDiffInitialCompare ( undefined ) ;
346355 } , [ activeRepoId ] ) ;
347356
348357 // LFS 설치 여부 확인 (레포 변경 시)
@@ -471,6 +480,7 @@ function App() {
471480 refreshKey = { activeRepo . refreshKey }
472481 selectedDiff = { selectedDiff }
473482 pendingFilePreview = { pendingFilePreview }
483+ branchFileDiff = { branchFileDiff }
474484 onFileSelect = { setSelectedFilePath }
475485 onRefresh = { handleRefresh }
476486 highlightPath = { workspaceHighlight }
@@ -494,15 +504,29 @@ function App() {
494504 ) : (
495505 < >
496506 < TabBar
497- tabs = { BASE_INNER_TABS . map ( ( tab ) => {
498- if ( tab . id === "pending" && lfsLockCount > 0 )
499- return { ...tab , label : `Pending Changes [${ lfsLockCount } Locked]` } ;
500- if ( tab . id === "worktrees" && worktreeCount >= 2 )
501- return { ...tab , label : `Worktrees [+${ worktreeCount } ]` } ;
502- return tab ;
503- } ) }
507+ tabs = { [
508+ ...BASE_INNER_TABS . map ( ( tab ) => {
509+ if ( tab . id === "pending" && lfsLockCount > 0 )
510+ return { ...tab , label : `Pending Changes [${ lfsLockCount } Locked]` } ;
511+ if ( tab . id === "worktrees" && worktreeCount >= 2 )
512+ return { ...tab , label : `Worktrees [+${ worktreeCount } ]` } ;
513+ return tab ;
514+ } ) ,
515+ ...( branchDiffOpen
516+ ? [ { id : "branch-diff" , label : "⇄ Compare" , closeable : true } ]
517+ : [ ] ) ,
518+ ] }
504519 activeTab = { activeRepo . activeTab }
505520 onTabChange = { handleTabChange }
521+ onTabClose = { ( tabId ) => {
522+ if ( tabId === "branch-diff" ) {
523+ setBranchDiffOpen ( false ) ;
524+ setBranchFileDiff ( null ) ;
525+ if ( activeRepo . activeTab === "branch-diff" ) {
526+ handleTabChange ( "branches" ) ;
527+ }
528+ }
529+ } }
506530 />
507531 < div className = { activeRepo . activeTab === "pending" ? "contents" : "hidden" } >
508532 < PendingTab
@@ -551,6 +575,12 @@ function App() {
551575 } ) ( ) ;
552576 } }
553577 onShowInHistoryFile = { ( absolutePath ) => setSelectedFilePath ( absolutePath ) }
578+ onOpenCompare = { ( base , compare ) => {
579+ setBranchDiffInitialBase ( base || undefined ) ;
580+ setBranchDiffInitialCompare ( compare || undefined ) ;
581+ setBranchDiffOpen ( true ) ;
582+ handleTabChange ( "branch-diff" ) ;
583+ } }
554584 />
555585 ) }
556586 { activeRepo . activeTab === "branches" && (
@@ -559,6 +589,25 @@ function App() {
559589 refreshKey = { activeRepo . refreshKey }
560590 silentRefreshKey = { activeRepo . silentRefreshKey }
561591 onRefresh = { handleRefresh }
592+ onOpenCompare = { ( base , compare ) => {
593+ setBranchDiffInitialBase ( base || undefined ) ;
594+ setBranchDiffInitialCompare ( compare || undefined ) ;
595+ setBranchDiffOpen ( true ) ;
596+ handleTabChange ( "branch-diff" ) ;
597+ } }
598+ />
599+ ) }
600+ { activeRepo . activeTab === "branch-diff" && branchDiffOpen && (
601+ < BranchDiffTab
602+ repoPath = { activeRepo . repoPath }
603+ refreshKey = { activeRepo . refreshKey }
604+ initialBase = { branchDiffInitialBase }
605+ initialCompare = { branchDiffInitialCompare }
606+ onFileSelect = { ( diff , filename ) => {
607+ setSelectedDiff ( null ) ;
608+ setPendingFilePreview ( null ) ;
609+ setBranchFileDiff ( { diff, filename } ) ;
610+ } }
562611 />
563612 ) }
564613 { activeRepo . activeTab === "remotes" && (
0 commit comments