11import { PanelMessage } from "@components/ui/PanelMessage" ;
22import { isDiffTabActiveInTree , usePanelLayoutStore } from "@features/panels" ;
33import { useTaskData } from "@features/task-detail/hooks/useTaskData" ;
4- import { ArrowCounterClockwiseIcon , FileIcon } from "@phosphor-icons/react" ;
4+ import {
5+ ArrowCounterClockwiseIcon ,
6+ CaretDownIcon ,
7+ CaretUpIcon ,
8+ FileIcon ,
9+ } from "@phosphor-icons/react" ;
510import { Badge , Box , Flex , IconButton , Text , Tooltip } from "@radix-ui/themes" ;
611import type { ChangedFile , GitFileStatus , Task } from "@shared/types" ;
712import { useQuery , useQueryClient } from "@tanstack/react-query" ;
813import { showMessageBox } from "@utils/dialog" ;
914import { handleExternalAppAction } from "@utils/handleExternalAppAction" ;
15+ import { useCallback } from "react" ;
16+ import { useHotkeys } from "react-hotkeys-hook" ;
1017import {
1118 selectWorktreePath ,
1219 useWorkspaceStore ,
@@ -245,6 +252,7 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) {
245252 const worktreePath = useWorkspaceStore ( selectWorktreePath ( taskId ) ) ;
246253 const repoPath = worktreePath ?? taskData . repoPath ;
247254 const layout = usePanelLayoutStore ( ( state ) => state . getLayout ( taskId ) ) ;
255+ const openDiff = usePanelLayoutStore ( ( state ) => state . openDiff ) ;
248256
249257 const { data : changedFiles = [ ] , isLoading } = useQuery ( {
250258 queryKey : [ "changed-files-head" , repoPath ] ,
@@ -253,6 +261,40 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) {
253261 refetchOnMount : "always" ,
254262 } ) ;
255263
264+ const getActiveIndex = useCallback ( ( ) : number => {
265+ if ( ! layout ) return - 1 ;
266+ return changedFiles . findIndex ( ( file ) =>
267+ isDiffTabActiveInTree ( layout . panelTree , file . path , file . status ) ,
268+ ) ;
269+ } , [ layout , changedFiles ] ) ;
270+
271+ const handleKeyNavigation = useCallback (
272+ ( direction : "up" | "down" ) => {
273+ if ( changedFiles . length === 0 ) return ;
274+
275+ const currentIndex = getActiveIndex ( ) ;
276+ const startIndex =
277+ currentIndex === - 1
278+ ? direction === "down"
279+ ? - 1
280+ : changedFiles . length
281+ : currentIndex ;
282+ const newIndex =
283+ direction === "up"
284+ ? Math . max ( 0 , startIndex - 1 )
285+ : Math . min ( changedFiles . length - 1 , startIndex + 1 ) ;
286+
287+ const file = changedFiles [ newIndex ] ;
288+ if ( file ) {
289+ openDiff ( taskId , file . path , file . status ) ;
290+ }
291+ } ,
292+ [ changedFiles , getActiveIndex , openDiff , taskId ] ,
293+ ) ;
294+
295+ useHotkeys ( "up" , ( ) => handleKeyNavigation ( "up" ) , [ handleKeyNavigation ] ) ;
296+ useHotkeys ( "down" , ( ) => handleKeyNavigation ( "down" ) , [ handleKeyNavigation ] ) ;
297+
256298 const isFileActive = ( file : ChangedFile ) : boolean => {
257299 if ( ! layout ) return false ;
258300 return isDiffTabActiveInTree ( layout . panelTree , file . path , file . status ) ;
@@ -282,6 +324,16 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) {
282324 isActive = { isFileActive ( file ) }
283325 />
284326 ) ) }
327+ < Flex align = "center" justify = "center" gap = "1" py = "2" >
328+ < CaretUpIcon size = { 12 } color = "var(--gray-10)" />
329+ < Text size = "1" className = "text-gray-10" >
330+ /
331+ </ Text >
332+ < CaretDownIcon size = { 12 } color = "var(--gray-10)" />
333+ < Text size = "1" className = "text-gray-10" ml = "1" >
334+ to switch files
335+ </ Text >
336+ </ Flex >
285337 </ Flex >
286338 </ Box >
287339 ) ;
0 commit comments