From 29a5f567b523d65b6abfd9e4a12be9d23f331ec8 Mon Sep 17 00:00:00 2001 From: Oliver Browne Date: Thu, 2 Apr 2026 17:07:58 +0300 Subject: [PATCH] signals inbox keyboard name --- .../components/GlobalEventHandlers.tsx | 2 + .../renderer/constants/keyboard-shortcuts.ts | 7 ++ .../inbox/components/InboxSignalsTab.tsx | 73 ++++++++++++++++++- .../features/inbox/components/ReportCard.tsx | 1 + .../sidebar/components/items/HomeItem.tsx | 51 ++++++++----- 5 files changed, 113 insertions(+), 21 deletions(-) diff --git a/apps/code/src/renderer/components/GlobalEventHandlers.tsx b/apps/code/src/renderer/components/GlobalEventHandlers.tsx index de2aa27c1..67a51367f 100644 --- a/apps/code/src/renderer/components/GlobalEventHandlers.tsx +++ b/apps/code/src/renderer/components/GlobalEventHandlers.tsx @@ -35,6 +35,7 @@ export function GlobalEventHandlers({ (state) => state.navigateToTaskInput, ); const navigateToTask = useNavigationStore((state) => state.navigateToTask); + const navigateToInbox = useNavigationStore((state) => state.navigateToInbox); const navigateToFolderSettings = useNavigationStore( (state) => state.navigateToFolderSettings, ); @@ -165,6 +166,7 @@ export function GlobalEventHandlers({ useHotkeys(SHORTCUTS.TOGGLE_LEFT_SIDEBAR, toggleLeftSidebar, globalOptions); useHotkeys(SHORTCUTS.TOGGLE_RIGHT_SIDEBAR, toggleRightSidebar, globalOptions); useHotkeys(SHORTCUTS.SHORTCUTS_SHEET, onToggleShortcutsSheet, globalOptions); + useHotkeys(SHORTCUTS.INBOX, navigateToInbox, globalOptions); useHotkeys(SHORTCUTS.PREV_TASK, handlePrevTask, globalOptions, [ handlePrevTask, ]); diff --git a/apps/code/src/renderer/constants/keyboard-shortcuts.ts b/apps/code/src/renderer/constants/keyboard-shortcuts.ts index dba95a10b..beda81fe7 100644 --- a/apps/code/src/renderer/constants/keyboard-shortcuts.ts +++ b/apps/code/src/renderer/constants/keyboard-shortcuts.ts @@ -18,6 +18,7 @@ export const SHORTCUTS = { COPY_PATH: "mod+shift+c", TOGGLE_FOCUS: "mod+r", PASTE_AS_FILE: "mod+shift+v", + INBOX: "mod+shift+i", BLUR: "escape", SUBMIT_BLUR: "mod+enter", } as const; @@ -66,6 +67,12 @@ export const KEYBOARD_SHORTCUTS: KeyboardShortcut[] = [ description: "Show keyboard shortcuts", category: "general", }, + { + id: "inbox", + keys: SHORTCUTS.INBOX, + description: "Open inbox", + category: "navigation", + }, { id: "switch-task", keys: "mod+0-9", diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index 72e82d1a7..0098750b3 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -28,7 +28,12 @@ import { CircleNotchIcon, ClockIcon, Cloud as CloudIcon, + CommandIcon, GithubLogoIcon, + KanbanIcon, + KeyReturnIcon, + TicketIcon, + VideoIcon, WarningIcon, XIcon, } from "@phosphor-icons/react"; @@ -377,6 +382,58 @@ export function InboxSignalsTab() { [reports, selectedReportId], ); + // ── Arrow-key navigation between reports ────────────────────────────── + const reportsRef = useRef(reports); + reportsRef.current = reports; + const selectedReportIdRef = useRef(selectedReportId); + selectedReportIdRef.current = selectedReportId; + + const leftPaneRef = useRef(null); + + // Auto-focus the list pane when the inbox mounts so arrow keys work immediately + useEffect(() => { + leftPaneRef.current?.focus(); + }, []); + + const navigateReport = useCallback((direction: 1 | -1) => { + const list = reportsRef.current; + if (list.length === 0) return; + const currentId = selectedReportIdRef.current; + const currentIndex = currentId + ? list.findIndex((r) => r.id === currentId) + : -1; + const nextIndex = + currentIndex === -1 + ? 0 + : Math.max(0, Math.min(list.length - 1, currentIndex + direction)); + const nextId = list[nextIndex].id; + setSelectedReportId(nextId); + // Move focus back to the list container so the previously clicked card + // loses its focus outline + leftPaneRef.current?.focus(); + leftPaneRef.current + ?.querySelector(`[data-report-id="${nextId}"]`) + ?.scrollIntoView({ block: "nearest" }); + }, []); + + const handleCreateTaskRef = useRef<() => void>(() => {}); + + const handleListKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "ArrowDown") { + e.preventDefault(); + navigateReport(1); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + navigateReport(-1); + } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + handleCreateTaskRef.current(); + } + }, + [navigateReport], + ); + const artefactsQuery = useInboxReportArtefacts(selectedReport?.id ?? "", { enabled: !!selectedReport, }); @@ -448,6 +505,7 @@ export function InboxSignalsTab() { }); navigateToTaskInput(); }; + handleCreateTaskRef.current = handleCreateTask; const handleOpenCloudConfirm = useCallback(() => { openCloudConfirm(repositories[0] ?? null); @@ -582,8 +640,11 @@ export function InboxSignalsTab() { } className="text-[12px]" > - - {isRunningCloudTask ? "Running..." : "Run cloud"} + Pick up task + + + + )} @@ -882,7 +943,13 @@ export function InboxSignalsTab() { className="scroll-area-constrain-width" style={{ height: "100%" }} > - + } - label="Inbox" - isActive={isActive} - onClick={onClick} - endContent={ - signalCount && signalCount > 0 ? ( - - {formatSignalCount(signalCount)} - - ) : undefined - } - /> + +
+ + } + label="Inbox" + isActive={isActive} + onClick={onClick} + endContent={ + signalCount && signalCount > 0 ? ( + + {formatSignalCount(signalCount)} + + ) : undefined + } + /> +
+
); }