diff --git a/supacode/Features/Canvas/Models/CanvasFocusRequest.swift b/supacode/Features/Canvas/Models/CanvasFocusRequest.swift index d52e57f3..092aacf2 100644 --- a/supacode/Features/Canvas/Models/CanvasFocusRequest.swift +++ b/supacode/Features/Canvas/Models/CanvasFocusRequest.swift @@ -26,6 +26,7 @@ struct CanvasCommandRequest: Equatable, Sendable { case organize case tile case selectAll + case navigate(CanvasNavigationDirection) } let id: Int diff --git a/supacode/Features/Canvas/Models/CanvasSpatialNavigation.swift b/supacode/Features/Canvas/Models/CanvasSpatialNavigation.swift index 685938fe..9c5ba371 100644 --- a/supacode/Features/Canvas/Models/CanvasSpatialNavigation.swift +++ b/supacode/Features/Canvas/Models/CanvasSpatialNavigation.swift @@ -1,6 +1,6 @@ import CoreGraphics -enum CanvasNavigationDirection { +enum CanvasNavigationDirection: Equatable, Sendable { case moveUp, moveDown, moveLeft, moveRight } diff --git a/supacode/Features/Canvas/Views/CanvasView+Focus.swift b/supacode/Features/Canvas/Views/CanvasView+Focus.swift index fe500216..a3a546b4 100644 --- a/supacode/Features/Canvas/Views/CanvasView+Focus.swift +++ b/supacode/Features/Canvas/Views/CanvasView+Focus.swift @@ -17,6 +17,8 @@ extension CanvasView { tileCardsWithFit() case .selectAll: selectAllCards() + case .navigate(let direction): + navigateCard(direction) } onCommandConsumed(request.id) } diff --git a/supacode/Features/Canvas/Views/CanvasView.swift b/supacode/Features/Canvas/Views/CanvasView.swift index f08145c2..1df7353d 100644 --- a/supacode/Features/Canvas/Views/CanvasView.swift +++ b/supacode/Features/Canvas/Views/CanvasView.swift @@ -97,22 +97,6 @@ struct CanvasView: View { for: AppShortcuts.CommandID.expandCanvasCard, in: resolvedKeybindings ) - let selectNextWorktreeShortcut = AppShortcuts.resolvedShortcut( - for: AppShortcuts.CommandID.selectNextWorktree, - in: resolvedKeybindings - ) - let selectPreviousWorktreeShortcut = AppShortcuts.resolvedShortcut( - for: AppShortcuts.CommandID.selectPreviousWorktree, - in: resolvedKeybindings - ) - let selectNextShelfBookShortcut = AppShortcuts.resolvedShortcut( - for: AppShortcuts.CommandID.selectNextShelfBook, - in: resolvedKeybindings - ) - let selectPreviousShelfBookShortcut = AppShortcuts.resolvedShortcut( - for: AppShortcuts.CommandID.selectPreviousShelfBook, - in: resolvedKeybindings - ) let _ = configReloadCounter CanvasScrollContainer( offset: $canvasOffset, @@ -256,38 +240,6 @@ struct CanvasView: View { toggleExpandFocusedCard() return .handled } - .onKeyPress( - selectPreviousWorktreeShortcut?.keyEquivalent ?? AppShortcuts.selectPreviousWorktree.keyEquivalent, - phases: .down - ) { keyPress in - guard let shortcut = selectPreviousWorktreeShortcut else { return .ignored } - guard keyPress.modifiers == shortcut.modifiers else { return .ignored } - return navigateCard(.moveUp) ? .handled : .ignored - } - .onKeyPress( - selectNextWorktreeShortcut?.keyEquivalent ?? AppShortcuts.selectNextWorktree.keyEquivalent, - phases: .down - ) { keyPress in - guard let shortcut = selectNextWorktreeShortcut else { return .ignored } - guard keyPress.modifiers == shortcut.modifiers else { return .ignored } - return navigateCard(.moveDown) ? .handled : .ignored - } - .onKeyPress( - selectPreviousShelfBookShortcut?.keyEquivalent ?? AppShortcuts.selectPreviousShelfBook.keyEquivalent, - phases: .down - ) { keyPress in - guard let shortcut = selectPreviousShelfBookShortcut else { return .ignored } - guard keyPress.modifiers == shortcut.modifiers else { return .ignored } - return navigateCard(.moveLeft) ? .handled : .ignored - } - .onKeyPress( - selectNextShelfBookShortcut?.keyEquivalent ?? AppShortcuts.selectNextShelfBook.keyEquivalent, - phases: .down - ) { keyPress in - guard let shortcut = selectNextShelfBookShortcut else { return .ignored } - guard keyPress.modifiers == shortcut.modifiers else { return .ignored } - return navigateCard(.moveRight) ? .handled : .ignored - } .onChange(of: expandedTabID) { _, newValue in onExpandedChange(newValue != nil) } diff --git a/supacode/Features/Repositories/Reducer/RepositoriesFeature+CoreReducer.swift b/supacode/Features/Repositories/Reducer/RepositoriesFeature+CoreReducer.swift index 28cd78ca..180f538f 100644 --- a/supacode/Features/Repositories/Reducer/RepositoriesFeature+CoreReducer.swift +++ b/supacode/Features/Repositories/Reducer/RepositoriesFeature+CoreReducer.swift @@ -426,10 +426,16 @@ extension RepositoriesFeature { } case .selectNextShelfBook: + if state.isShowingCanvas { + return .send(.requestCanvasCommand(.navigate(.moveRight))) + } guard let book = shelfBook(atOffset: 1, state: state) else { return .none } return shelfBookSelectionEffect(for: book) case .selectPreviousShelfBook: + if state.isShowingCanvas { + return .send(.requestCanvasCommand(.navigate(.moveLeft))) + } guard let book = shelfBook(atOffset: -1, state: state) else { return .none } return shelfBookSelectionEffect(for: book) @@ -621,8 +627,9 @@ extension RepositoriesFeature { } case .selectNextWorktree: - // Canvas handles directional navigation in CanvasView.onKeyPress. - guard !state.isShowingCanvas else { return .none } + if state.isShowingCanvas { + return .send(.requestCanvasCommand(.navigate(.moveDown))) + } // In Shelf, the vertical arrow pair maps to tab navigation // within the open book — horizontal (← / →) is already book // navigation, so the two axes match the Shelf layout. @@ -635,7 +642,9 @@ extension RepositoriesFeature { return .send(.selectWorktree(id, focusTerminal: true)) case .selectPreviousWorktree: - guard !state.isShowingCanvas else { return .none } + if state.isShowingCanvas { + return .send(.requestCanvasCommand(.navigate(.moveUp))) + } if state.isShelfActive, let worktree = state.selectedTerminalWorktree { return .run { _ in await terminalClient.send(.performBindingAction(worktree, action: "previous_tab"))