From b9f39596362f06db45ad401a17069afa4dc5b61e Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 11:40:12 -0800 Subject: [PATCH 1/6] Restored the up/down arrow key navigation for the file changes list --- .../task-detail/components/ChangesPanel.tsx | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx index f8bf8f964..51714cd37 100644 --- a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx +++ b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx @@ -5,6 +5,8 @@ import { GitActionsBar } from "@features/task-detail/components/GitActionsBar"; import { useTaskData } from "@features/task-detail/hooks/useTaskData"; import { ArrowCounterClockwiseIcon, + CaretDownIcon, + CaretUpIcon, CodeIcon, CopyIcon, FilePlus, @@ -24,7 +26,8 @@ import { useExternalAppsStore } from "@stores/externalAppsStore"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { showMessageBox } from "@utils/dialog"; import { handleExternalAppAction } from "@utils/handleExternalAppAction"; -import { useState } from "react"; +import { useCallback, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; import { selectWorktreePath, useWorkspaceStore, @@ -350,6 +353,7 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) { const worktreePath = useWorkspaceStore(selectWorktreePath(taskId)); const repoPath = worktreePath ?? taskData.repoPath; const layout = usePanelLayoutStore((state) => state.getLayout(taskId)); + const openDiff = usePanelLayoutStore((state) => state.openDiff); const { data: changedFiles = [], isLoading } = useQuery({ queryKey: ["changed-files-head", repoPath], @@ -362,6 +366,40 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) { refetchOnWindowFocus: true, }); + const getActiveIndex = useCallback((): number => { + if (!layout) return -1; + return changedFiles.findIndex((file) => + isDiffTabActiveInTree(layout.panelTree, file.path, file.status), + ); + }, [layout, changedFiles]); + + const handleKeyNavigation = useCallback( + (direction: "up" | "down") => { + if (changedFiles.length === 0) return; + + const currentIndex = getActiveIndex(); + const startIndex = + currentIndex === -1 + ? direction === "down" + ? -1 + : changedFiles.length + : currentIndex; + const newIndex = + direction === "up" + ? Math.max(0, startIndex - 1) + : Math.min(changedFiles.length - 1, startIndex + 1); + + const file = changedFiles[newIndex]; + if (file) { + openDiff(taskId, file.path, file.status); + } + }, + [changedFiles, getActiveIndex, openDiff, taskId], + ); + + useHotkeys("up", () => handleKeyNavigation("up"), [handleKeyNavigation]); + useHotkeys("down", () => handleKeyNavigation("down"), [handleKeyNavigation]); + const isFileActive = (file: ChangedFile): boolean => { if (!layout) return false; return isDiffTabActiveInTree(layout.panelTree, file.path, file.status); @@ -404,6 +442,16 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) { isActive={isFileActive(file)} /> ))} + + + + / + + + + to switch files + + From 00f6dcc98570a0b9cffbf8fad7c254973947dbde Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 11:44:48 -0800 Subject: [PATCH 2/6] Make up/down in changes panel work with permissions listeners --- .../task-detail/components/ChangesPanel.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx index 51714cd37..91f0a7e96 100644 --- a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx +++ b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx @@ -28,6 +28,7 @@ import { showMessageBox } from "@utils/dialog"; import { handleExternalAppAction } from "@utils/handleExternalAppAction"; import { useCallback, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; +import { usePendingPermissionsForTask } from "@features/sessions/stores/sessionStore"; import { selectWorktreePath, useWorkspaceStore, @@ -354,6 +355,8 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) { const repoPath = worktreePath ?? taskData.repoPath; const layout = usePanelLayoutStore((state) => state.getLayout(taskId)); const openDiff = usePanelLayoutStore((state) => state.openDiff); + const pendingPermissions = usePendingPermissionsForTask(taskId); + const hasPendingPermissions = pendingPermissions.size > 0; const { data: changedFiles = [], isLoading } = useQuery({ queryKey: ["changed-files-head", repoPath], @@ -397,8 +400,18 @@ export function ChangesPanel({ taskId, task }: ChangesPanelProps) { [changedFiles, getActiveIndex, openDiff, taskId], ); - useHotkeys("up", () => handleKeyNavigation("up"), [handleKeyNavigation]); - useHotkeys("down", () => handleKeyNavigation("down"), [handleKeyNavigation]); + useHotkeys( + "up", + () => handleKeyNavigation("up"), + { enabled: !hasPendingPermissions }, + [handleKeyNavigation, hasPendingPermissions], + ); + useHotkeys( + "down", + () => handleKeyNavigation("down"), + { enabled: !hasPendingPermissions }, + [handleKeyNavigation, hasPendingPermissions], + ); const isFileActive = (file: ChangedFile): boolean => { if (!layout) return false; From 5f45cccf63fa5e007bf646fd9f207e97621979d0 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 11:44:55 -0800 Subject: [PATCH 3/6] Remove unused useTaskKeyboardNavigation --- .../renderer/constants/keyboard-shortcuts.ts | 22 +------ .../hooks/useTaskKeyboardNavigation.ts | 64 ------------------- 2 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 apps/array/src/renderer/features/task-list/hooks/useTaskKeyboardNavigation.ts diff --git a/apps/array/src/renderer/constants/keyboard-shortcuts.ts b/apps/array/src/renderer/constants/keyboard-shortcuts.ts index c92bb52f9..cb064430d 100644 --- a/apps/array/src/renderer/constants/keyboard-shortcuts.ts +++ b/apps/array/src/renderer/constants/keyboard-shortcuts.ts @@ -11,9 +11,6 @@ export const SHORTCUTS = { SWITCH_TAB: "mod+1,mod+2,mod+3,mod+4,mod+5,mod+6,mod+7,mod+8,mod+9", OPEN_IN_EDITOR: "mod+o", COPY_PATH: "mod+shift+c", - TASK_NAV_UP: "up", - TASK_NAV_DOWN: "down", - TASK_SELECT: "enter", TASK_REFRESH: "mod+r", BLUR: "escape", SUBMIT_BLUR: "mod+enter", @@ -23,8 +20,7 @@ export type ShortcutCategory = | "general" | "navigation" | "panels" - | "editor" - | "taskList"; + | "editor"; export interface KeyboardShortcut { id: string; @@ -117,20 +113,6 @@ export const KEYBOARD_SHORTCUTS: KeyboardShortcut[] = [ category: "panels", context: "Task detail", }, - { - id: "task-nav-up", - keys: SHORTCUTS.TASK_NAV_UP, - description: "Navigate up", - category: "taskList", - context: "Task list", - }, - { - id: "task-nav-down", - keys: SHORTCUTS.TASK_NAV_DOWN, - description: "Navigate down", - category: "taskList", - context: "Task list", - }, { id: "editor-bold", keys: "mod+b", @@ -166,7 +148,6 @@ export const CATEGORY_LABELS: Record = { navigation: "Navigation", panels: "Panels & Tabs", editor: "Editor", - taskList: "Task List", }; export function getShortcutsByCategory(): Record< @@ -178,7 +159,6 @@ export function getShortcutsByCategory(): Record< navigation: [], panels: [], editor: [], - taskList: [], }; for (const shortcut of KEYBOARD_SHORTCUTS) { grouped[shortcut.category].push(shortcut); diff --git a/apps/array/src/renderer/features/task-list/hooks/useTaskKeyboardNavigation.ts b/apps/array/src/renderer/features/task-list/hooks/useTaskKeyboardNavigation.ts deleted file mode 100644 index 848dc0e52..000000000 --- a/apps/array/src/renderer/features/task-list/hooks/useTaskKeyboardNavigation.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { SHORTCUTS } from "@renderer/constants/keyboard-shortcuts"; -import type { Task } from "@shared/types"; -import { useCallback } from "react"; -import { useHotkeys } from "react-hotkeys-hook"; - -export function useTaskKeyboardNavigation( - filteredTasks: Task[], - selectedIndex: number | null, - hoveredIndex: number | null, - contextMenuIndex: number | null, - setSelectedIndex: (index: number | null) => void, - setHoveredIndex: (index: number | null) => void, - onSelectTask: (task: Task) => void, - refetch: () => void, -) { - const handleKeyNavigation = useCallback( - (direction: "up" | "down") => { - setHoveredIndex(null); - const startIndex = selectedIndex ?? hoveredIndex ?? 0; - if (direction === "up") { - setSelectedIndex(Math.max(0, startIndex - 1)); - } else { - setSelectedIndex(Math.min(filteredTasks.length - 1, startIndex + 1)); - } - }, - [ - filteredTasks.length, - hoveredIndex, - selectedIndex, - setHoveredIndex, - setSelectedIndex, - ], - ); - - const handleSelectCurrent = useCallback(() => { - const index = selectedIndex ?? hoveredIndex; - if (index !== null && filteredTasks[index]) { - onSelectTask(filteredTasks[index]); - } - }, [filteredTasks, selectedIndex, hoveredIndex, onSelectTask]); - - useHotkeys( - SHORTCUTS.TASK_NAV_UP, - () => handleKeyNavigation("up"), - { enableOnFormTags: false, enabled: contextMenuIndex === null }, - [handleKeyNavigation, contextMenuIndex], - ); - - useHotkeys( - SHORTCUTS.TASK_NAV_DOWN, - () => handleKeyNavigation("down"), - { enableOnFormTags: false, enabled: contextMenuIndex === null }, - [handleKeyNavigation, contextMenuIndex], - ); - - useHotkeys( - SHORTCUTS.TASK_SELECT, - handleSelectCurrent, - { enableOnFormTags: false, enabled: contextMenuIndex === null }, - [handleSelectCurrent, contextMenuIndex], - ); - - useHotkeys(SHORTCUTS.TASK_REFRESH, () => refetch(), [refetch]); -} From 35e233bc30264e78f4ba928f3915626a873053ad Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 11:48:33 -0800 Subject: [PATCH 4/6] Update KeyboardShortcutsSheet.tsx --- apps/array/src/renderer/components/KeyboardShortcutsSheet.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/array/src/renderer/components/KeyboardShortcutsSheet.tsx b/apps/array/src/renderer/components/KeyboardShortcutsSheet.tsx index 55f5bee3e..bfa8a3cdb 100644 --- a/apps/array/src/renderer/components/KeyboardShortcutsSheet.tsx +++ b/apps/array/src/renderer/components/KeyboardShortcutsSheet.tsx @@ -23,7 +23,6 @@ export function KeyboardShortcutsSheet({ "general", "navigation", "panels", - "taskList", "editor", ]; From 3faa4cc9eb822d5bb604dfe3ffbafd098978b226 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 17:00:17 -0800 Subject: [PATCH 5/6] Update keyboard-shortcuts.ts --- apps/array/src/renderer/constants/keyboard-shortcuts.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/array/src/renderer/constants/keyboard-shortcuts.ts b/apps/array/src/renderer/constants/keyboard-shortcuts.ts index cb064430d..c5d915bef 100644 --- a/apps/array/src/renderer/constants/keyboard-shortcuts.ts +++ b/apps/array/src/renderer/constants/keyboard-shortcuts.ts @@ -16,11 +16,7 @@ export const SHORTCUTS = { SUBMIT_BLUR: "mod+enter", } as const; -export type ShortcutCategory = - | "general" - | "navigation" - | "panels" - | "editor"; +export type ShortcutCategory = "general" | "navigation" | "panels" | "editor"; export interface KeyboardShortcut { id: string; From 0b40a16bbd50b33b7c87a6344ed4b99f8a2b69b6 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 12 Jan 2026 17:00:29 -0800 Subject: [PATCH 6/6] Update ChangesPanel.tsx --- .../renderer/features/task-detail/components/ChangesPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx index 91f0a7e96..ffeb813b1 100644 --- a/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx +++ b/apps/array/src/renderer/features/task-detail/components/ChangesPanel.tsx @@ -1,6 +1,7 @@ import { FileIcon } from "@components/ui/FileIcon"; import { PanelMessage } from "@components/ui/PanelMessage"; import { isDiffTabActiveInTree, usePanelLayoutStore } from "@features/panels"; +import { usePendingPermissionsForTask } from "@features/sessions/stores/sessionStore"; import { GitActionsBar } from "@features/task-detail/components/GitActionsBar"; import { useTaskData } from "@features/task-detail/hooks/useTaskData"; import { @@ -28,7 +29,6 @@ import { showMessageBox } from "@utils/dialog"; import { handleExternalAppAction } from "@utils/handleExternalAppAction"; import { useCallback, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; -import { usePendingPermissionsForTask } from "@features/sessions/stores/sessionStore"; import { selectWorktreePath, useWorkspaceStore,