From 099ebaf8002238b26fec99fb11fc026a9bd3efbe Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Sat, 17 Jan 2026 20:00:44 +0100 Subject: [PATCH] feat(ui): add Open in Terminal action to worktree dropdown Add "Open in Terminal" option to the worktree actions dropdown menu. This opens the system terminal in the worktree directory. Changes: - Add openInTerminal method to http-api-client - Add Terminal icon and menu item to worktree-actions-dropdown - Add onOpenInTerminal prop to WorktreeTab component - Add handleOpenInTerminal handler to use-worktree-actions hook - Wire up handler in worktree-panel for both mobile and desktop views Extracted from PR #558. --- .../components/worktree-actions-dropdown.tsx | 7 +++++++ .../components/worktree-tab.tsx | 3 +++ .../hooks/use-worktree-actions.ts | 19 +++++++++++++++++++ .../worktree-panel/worktree-panel.tsx | 4 ++++ apps/ui/src/lib/http-api-client.ts | 2 ++ 5 files changed, 35 insertions(+) diff --git a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx index 459e2ce8..c8f33fc0 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx +++ b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx @@ -26,6 +26,7 @@ import { RefreshCw, Copy, ScrollText, + Terminal, } from 'lucide-react'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; @@ -51,6 +52,7 @@ interface WorktreeActionsDropdownProps { onPull: (worktree: WorktreeInfo) => void; onPush: (worktree: WorktreeInfo) => void; onOpenInEditor: (worktree: WorktreeInfo, editorCommand?: string) => void; + onOpenInTerminal: (worktree: WorktreeInfo) => void; onCommit: (worktree: WorktreeInfo) => void; onCreatePR: (worktree: WorktreeInfo) => void; onAddressPRComments: (worktree: WorktreeInfo, prInfo: PRInfo) => void; @@ -81,6 +83,7 @@ export function WorktreeActionsDropdown({ onPull, onPush, onOpenInEditor, + onOpenInTerminal, onCommit, onCreatePR, onAddressPRComments, @@ -303,6 +306,10 @@ export function WorktreeActionsDropdown({ )} + onOpenInTerminal(worktree)} className="text-xs"> + + Open in Terminal + {!worktree.isMain && hasInitScript && ( onRunInitScript(worktree)} className="text-xs"> diff --git a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx index 212e6d89..359a8f99 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx +++ b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx @@ -38,6 +38,7 @@ interface WorktreeTabProps { onPull: (worktree: WorktreeInfo) => void; onPush: (worktree: WorktreeInfo) => void; onOpenInEditor: (worktree: WorktreeInfo, editorCommand?: string) => void; + onOpenInTerminal: (worktree: WorktreeInfo) => void; onCommit: (worktree: WorktreeInfo) => void; onCreatePR: (worktree: WorktreeInfo) => void; onAddressPRComments: (worktree: WorktreeInfo, prInfo: PRInfo) => void; @@ -82,6 +83,7 @@ export function WorktreeTab({ onPull, onPush, onOpenInEditor, + onOpenInTerminal, onCommit, onCreatePR, onAddressPRComments, @@ -343,6 +345,7 @@ export function WorktreeTab({ onPull={onPull} onPush={onPush} onOpenInEditor={onOpenInEditor} + onOpenInTerminal={onOpenInTerminal} onCommit={onCommit} onCreatePR={onCreatePR} onAddressPRComments={onAddressPRComments} diff --git a/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktree-actions.ts b/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktree-actions.ts index f1f245dc..d3b1db85 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktree-actions.ts +++ b/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktree-actions.ts @@ -143,6 +143,24 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre } }, []); + const handleOpenInTerminal = useCallback(async (worktree: WorktreeInfo) => { + try { + const api = getElectronAPI(); + if (!api?.worktree?.openInTerminal) { + logger.warn('Open in terminal API not available'); + return; + } + const result = await api.worktree.openInTerminal(worktree.path); + if (result.success && result.result) { + toast.success(result.result.message); + } else if (result.error) { + toast.error(result.error); + } + } catch (error) { + logger.error('Open in terminal failed:', error); + } + }, []); + return { isPulling, isPushing, @@ -153,5 +171,6 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre handlePull, handlePush, handleOpenInEditor, + handleOpenInTerminal, }; } diff --git a/apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx b/apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx index fbd54d73..dd8c9376 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx +++ b/apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx @@ -80,6 +80,7 @@ export function WorktreePanel({ handlePull, handlePush, handleOpenInEditor, + handleOpenInTerminal, } = useWorktreeActions({ fetchWorktrees, fetchBranches, @@ -246,6 +247,7 @@ export function WorktreePanel({ onPull={handlePull} onPush={handlePush} onOpenInEditor={handleOpenInEditor} + onOpenInTerminal={handleOpenInTerminal} onCommit={onCommit} onCreatePR={onCreatePR} onAddressPRComments={onAddressPRComments} @@ -333,6 +335,7 @@ export function WorktreePanel({ onPull={handlePull} onPush={handlePush} onOpenInEditor={handleOpenInEditor} + onOpenInTerminal={handleOpenInTerminal} onCommit={onCommit} onCreatePR={onCreatePR} onAddressPRComments={onAddressPRComments} @@ -391,6 +394,7 @@ export function WorktreePanel({ onPull={handlePull} onPush={handlePush} onOpenInEditor={handleOpenInEditor} + onOpenInTerminal={handleOpenInTerminal} onCommit={onCommit} onCreatePR={onCreatePR} onAddressPRComments={onAddressPRComments} diff --git a/apps/ui/src/lib/http-api-client.ts b/apps/ui/src/lib/http-api-client.ts index 2943f3e2..f1d02e9a 100644 --- a/apps/ui/src/lib/http-api-client.ts +++ b/apps/ui/src/lib/http-api-client.ts @@ -1805,6 +1805,8 @@ export class HttpApiClient implements ElectronAPI { this.post('/api/worktree/switch-branch', { worktreePath, branchName }), openInEditor: (worktreePath: string, editorCommand?: string) => this.post('/api/worktree/open-in-editor', { worktreePath, editorCommand }), + openInTerminal: (worktreePath: string) => + this.post('/api/worktree/open-in-terminal', { worktreePath }), getDefaultEditor: () => this.get('/api/worktree/default-editor'), getAvailableEditors: () => this.get('/api/worktree/available-editors'), refreshEditors: () => this.post('/api/worktree/refresh-editors', {}),