Skip to content

Commit 702be8a

Browse files
committed
Add hotkeys to switch between files in the changes panel
1 parent f9b316c commit 702be8a

File tree

1 file changed

+53
-1
lines changed

1 file changed

+53
-1
lines changed

apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { PanelMessage } from "@components/ui/PanelMessage";
22
import { isDiffTabActiveInTree, usePanelLayoutStore } from "@features/panels";
33
import { 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";
510
import { Badge, Box, Flex, IconButton, Text, Tooltip } from "@radix-ui/themes";
611
import type { ChangedFile, GitFileStatus, Task } from "@shared/types";
712
import { useQuery, useQueryClient } from "@tanstack/react-query";
813
import { showMessageBox } from "@utils/dialog";
914
import { handleExternalAppAction } from "@utils/handleExternalAppAction";
15+
import { useCallback } from "react";
16+
import { useHotkeys } from "react-hotkeys-hook";
1017
import {
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

Comments
 (0)