From d69d4425fee5ed0ac7a12841eabc61ba4b024d21 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 19 Oct 2025 17:37:46 -0500 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A4=96=20Enhance=20Cmd+2=20to=20focus?= =?UTF-8?q?=20first=20hunk=20when=20already=20on=20Review=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When user presses Cmd+2 (or Ctrl+2 on Windows/Linux) while already viewing the Code Review tab, the keybind now focuses the panel and selects the first hunk for easier keyboard navigation. Implementation uses a simple prop trigger pattern: - RightSidebar maintains focusTrigger counter state - Incrementing the counter triggers ReviewPanel to focus first hunk - Declarative approach avoids forwardRef/useImperativeHandle complexity This makes keyboard-driven code review workflows smoother - users can quickly jump to the first hunk without needing to click or use j/k navigation from an arbitrary scroll position. --- src/components/RightSidebar.tsx | 13 +++++++++++-- .../RightSidebar/CodeReview/ReviewPanel.tsx | 13 +++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/RightSidebar.tsx b/src/components/RightSidebar.tsx index 0f4bc829a..95167e36b 100644 --- a/src/components/RightSidebar.tsx +++ b/src/components/RightSidebar.tsx @@ -178,6 +178,9 @@ const RightSidebarComponent: React.FC = ({ }) => { // Global tab preference (not per-workspace) const [selectedTab, setSelectedTab] = usePersistedState("right-sidebar-tab", "costs"); + + // Trigger for focusing first hunk in Review panel + const [focusTrigger, setFocusTrigger] = React.useState(0); // Notify parent (AIView) of tab changes so it can enable/disable resize functionality React.useEffect(() => { @@ -192,13 +195,18 @@ const RightSidebarComponent: React.FC = ({ setSelectedTab("costs"); } else if (matchesKeybind(e, KEYBINDS.REVIEW_TAB)) { e.preventDefault(); - setSelectedTab("review"); + // If already on Review tab, focus the first hunk + if (selectedTab === "review") { + setFocusTrigger((prev) => prev + 1); + } else { + setSelectedTab("review"); + } } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [setSelectedTab]); + }, [setSelectedTab, selectedTab]); const usage = useWorkspaceUsage(workspaceId); const [use1M] = use1MContext(); @@ -337,6 +345,7 @@ const RightSidebarComponent: React.FC = ({ workspaceId={workspaceId} workspacePath={workspacePath} onReviewNote={onReviewNote} + focusTrigger={focusTrigger} /> )} diff --git a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx index 01c62a578..d8734c40c 100644 --- a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx +++ b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx @@ -25,6 +25,8 @@ interface ReviewPanelProps { workspaceId: string; workspacePath: string; onReviewNote?: (note: string) => void; + /** Trigger to focus first hunk (increment to trigger) */ + focusTrigger?: number; } const PanelContainer = styled.div` @@ -274,7 +276,9 @@ export const ReviewPanel: React.FC = ({ workspaceId, workspacePath, onReviewNote, + focusTrigger, }) => { + const panelRef = useRef(null); const [hunks, setHunks] = useState([]); const [selectedHunkId, setSelectedHunkId] = useState(null); const [isLoadingHunks, setIsLoadingHunks] = useState(true); @@ -322,6 +326,14 @@ export const ReviewPanel: React.FC = ({ includeUncommitted: includeUncommitted, }); + // Focus first hunk when focusTrigger changes + useEffect(() => { + if (focusTrigger && focusTrigger > 0 && hunks.length > 0) { + panelRef.current?.focus(); + setSelectedHunkId(hunks[0].id); + } + }, [focusTrigger, hunks]); + // Load file tree - when workspace, diffBase, or refreshTrigger changes useEffect(() => { let cancelled = false; @@ -633,6 +645,7 @@ export const ReviewPanel: React.FC = ({ return ( setIsPanelFocused(true)} onBlur={() => setIsPanelFocused(false)} From 67218c1fb2132018bdfab9271c1ea6d63cbb38a6 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 19 Oct 2025 17:39:59 -0500 Subject: [PATCH 2/3] Fix formatting (trailing whitespace) --- src/components/RightSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/RightSidebar.tsx b/src/components/RightSidebar.tsx index 95167e36b..62102cfaf 100644 --- a/src/components/RightSidebar.tsx +++ b/src/components/RightSidebar.tsx @@ -178,7 +178,7 @@ const RightSidebarComponent: React.FC = ({ }) => { // Global tab preference (not per-workspace) const [selectedTab, setSelectedTab] = usePersistedState("right-sidebar-tab", "costs"); - + // Trigger for focusing first hunk in Review panel const [focusTrigger, setFocusTrigger] = React.useState(0); From f64f2892c0470a0fde9df012e4a0b1d4f79ba2bd Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 19 Oct 2025 17:41:53 -0500 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A4=96=20Adjust=20behavior:=20focus?= =?UTF-8?q?=20panel=20without=20changing=20hunk=20selection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed Cmd+2 behavior when already on Review tab to only focus the panel without modifying the selected hunk. This preserves user's position in the review and allows keyboard navigation to resume from where they left off. --- src/components/RightSidebar.tsx | 4 ++-- src/components/RightSidebar/CodeReview/ReviewPanel.tsx | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/RightSidebar.tsx b/src/components/RightSidebar.tsx index 62102cfaf..9be4770b4 100644 --- a/src/components/RightSidebar.tsx +++ b/src/components/RightSidebar.tsx @@ -179,7 +179,7 @@ const RightSidebarComponent: React.FC = ({ // Global tab preference (not per-workspace) const [selectedTab, setSelectedTab] = usePersistedState("right-sidebar-tab", "costs"); - // Trigger for focusing first hunk in Review panel + // Trigger for focusing Review panel (preserves hunk selection) const [focusTrigger, setFocusTrigger] = React.useState(0); // Notify parent (AIView) of tab changes so it can enable/disable resize functionality @@ -195,7 +195,7 @@ const RightSidebarComponent: React.FC = ({ setSelectedTab("costs"); } else if (matchesKeybind(e, KEYBINDS.REVIEW_TAB)) { e.preventDefault(); - // If already on Review tab, focus the first hunk + // If already on Review tab, focus the panel if (selectedTab === "review") { setFocusTrigger((prev) => prev + 1); } else { diff --git a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx index d8734c40c..88db02985 100644 --- a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx +++ b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx @@ -25,7 +25,7 @@ interface ReviewPanelProps { workspaceId: string; workspacePath: string; onReviewNote?: (note: string) => void; - /** Trigger to focus first hunk (increment to trigger) */ + /** Trigger to focus panel (increment to trigger) */ focusTrigger?: number; } @@ -326,13 +326,12 @@ export const ReviewPanel: React.FC = ({ includeUncommitted: includeUncommitted, }); - // Focus first hunk when focusTrigger changes + // Focus panel when focusTrigger changes (preserves current hunk selection) useEffect(() => { - if (focusTrigger && focusTrigger > 0 && hunks.length > 0) { + if (focusTrigger && focusTrigger > 0) { panelRef.current?.focus(); - setSelectedHunkId(hunks[0].id); } - }, [focusTrigger, hunks]); + }, [focusTrigger]); // Load file tree - when workspace, diffBase, or refreshTrigger changes useEffect(() => {