From 430ad54d77e4bb1709debccf4b9a0ae87ed31439 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 4 Mar 2026 15:28:55 +0700 Subject: [PATCH] refactor: use confirmDialog and unique draft agent IDs --- .../src/components/sidebar-workspace-list.tsx | 56 ++++++++----------- .../app/src/hooks/use-agent-autocomplete.ts | 14 ++--- .../keyboard-shortcut-routing.test.ts | 24 +++++++- .../src/keyboard/keyboard-shortcut-routing.ts | 4 +- .../src/screens/agent/draft-agent-screen.tsx | 8 +-- 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/packages/app/src/components/sidebar-workspace-list.tsx b/packages/app/src/components/sidebar-workspace-list.tsx index 2243472c2..6054b8939 100644 --- a/packages/app/src/components/sidebar-workspace-list.tsx +++ b/packages/app/src/components/sidebar-workspace-list.tsx @@ -5,7 +5,6 @@ import { Image, Platform, ActivityIndicator, - Alert, StatusBar, ScrollView, type GestureResponderEvent, @@ -53,6 +52,7 @@ import { useToast } from '@/contexts/toast-context' import { useCheckoutGitActionsStore } from '@/stores/checkout-git-actions-store' import { buildSidebarShortcutModel } from '@/utils/sidebar-shortcuts' import { hasVisibleOrderChanged, mergeWithRemainder } from '@/utils/sidebar-reorder' +import { confirmDialog } from '@/utils/confirm-dialog' const PASEO_WORKTREE_PATH_MARKER = '/.paseo/worktrees' @@ -459,7 +459,6 @@ function WorkspaceRowInner({ dragHandleProps, menuController, }: WorkspaceRowInnerProps) { - const { theme } = useUnistyles() const createdAtLabel = resolveWorkspaceCreatedAtLabel(workspace) const interaction = useLongPressDragInteraction({ drag, @@ -547,12 +546,6 @@ function WorkspaceRowInner({ return ( {content} - {isArchiving ? ( - - - Archiving - - ) : null} ) } @@ -593,31 +586,28 @@ function WorkspaceRowWithMenu({ return } - Alert.alert( - 'Archive worktree?', - `Archive "${workspace.name}"?\n\nThis removes the worktree from the sidebar.`, - [ - { - text: 'Cancel', - style: 'cancel', - }, - { - text: 'Archive', - style: 'destructive', - onPress: () => { - void archiveWorktree({ - serverId: workspace.serverId, - cwd: workspace.workspaceId, - worktreePath: workspace.workspaceId, - }).catch((error) => { - const message = error instanceof Error ? error.message : 'Failed to archive worktree' - toast.error(message) - }) - }, - }, - ], - { cancelable: true } - ) + void (async () => { + const confirmed = await confirmDialog({ + title: 'Archive worktree?', + message: `Archive "${workspace.name}"?\n\nThis removes the worktree from the sidebar.`, + confirmLabel: 'Archive', + cancelLabel: 'Cancel', + destructive: true, + }) + + if (!confirmed) { + return + } + + void archiveWorktree({ + serverId: workspace.serverId, + cwd: workspace.workspaceId, + worktreePath: workspace.workspaceId, + }).catch((error) => { + const message = error instanceof Error ? error.message : 'Failed to archive worktree' + toast.error(message) + }) + })() }, [archiveWorktree, isArchiving, toast, workspace.name, workspace.serverId, workspace.workspaceId]) return ( diff --git a/packages/app/src/hooks/use-agent-autocomplete.ts b/packages/app/src/hooks/use-agent-autocomplete.ts index 521eb8b11..eac27074c 100644 --- a/packages/app/src/hooks/use-agent-autocomplete.ts +++ b/packages/app/src/hooks/use-agent-autocomplete.ts @@ -124,19 +124,19 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut [draftConfig] ) - const isRealAgent = Boolean(agentId) && !agentId.startsWith('__') - const queryDraftConfig = isRealAgent ? undefined : normalizedDraftConfig - const canLoadCommands = Boolean(serverId) && (isRealAgent || !!queryDraftConfig) + const isDraftContext = normalizedDraftConfig !== undefined + const queryDraftConfig = isDraftContext ? normalizedDraftConfig : undefined + const canLoadCommands = Boolean(serverId) && (Boolean(agentId) || isDraftContext) const agentCwd = useSessionStore( (state) => state.sessions[serverId]?.agents?.get(agentId)?.cwd ?? '' ) const autocompleteCwd = useMemo(() => { - if (isRealAgent) { - return agentCwd.trim() + if (isDraftContext) { + return queryDraftConfig?.cwd ?? '' } - return queryDraftConfig?.cwd ?? '' - }, [agentCwd, isRealAgent, queryDraftConfig]) + return agentCwd.trim() + }, [agentCwd, isDraftContext, queryDraftConfig]) const { client, isConnected } = useHostRuntimeSession(serverId) diff --git a/packages/app/src/keyboard/keyboard-shortcut-routing.test.ts b/packages/app/src/keyboard/keyboard-shortcut-routing.test.ts index eb5afc291..c11a7d246 100644 --- a/packages/app/src/keyboard/keyboard-shortcut-routing.test.ts +++ b/packages/app/src/keyboard/keyboard-shortcut-routing.test.ts @@ -1,8 +1,30 @@ import { describe, expect, it } from "vitest"; -import { canToggleFileExplorerShortcut } from "./keyboard-shortcut-routing"; +import { + canToggleFileExplorerShortcut, + resolveSelectedOrRouteAgentKey, +} from "./keyboard-shortcut-routing"; describe("keyboard-shortcut-routing", () => { + describe("resolveSelectedOrRouteAgentKey", () => { + it("returns selected agent key when provided", () => { + const key = resolveSelectedOrRouteAgentKey({ + selectedAgentId: "server-1:agent-1", + pathname: "/h/server-1/workspace/workspace-1/tab/draft_123", + }); + + expect(key).toBe("server-1:agent-1"); + }); + + it("maps workspace draft tabs to the draft tab id key", () => { + const key = resolveSelectedOrRouteAgentKey({ + pathname: "/h/server-1/workspace/workspace-1/tab/draft_123", + }); + + expect(key).toBe("server-1:draft_123"); + }); + }); + describe("canToggleFileExplorerShortcut", () => { const toggleFileExplorer = () => undefined; diff --git a/packages/app/src/keyboard/keyboard-shortcut-routing.ts b/packages/app/src/keyboard/keyboard-shortcut-routing.ts index 0bd99183e..7cf2ad691 100644 --- a/packages/app/src/keyboard/keyboard-shortcut-routing.ts +++ b/packages/app/src/keyboard/keyboard-shortcut-routing.ts @@ -4,8 +4,6 @@ import { parseHostWorkspaceTabRouteFromPathname, } from "@/utils/host-routes"; -const DRAFT_AGENT_ID = "__new_agent__"; - export function resolveSelectedOrRouteAgentKey(input: { selectedAgentId?: string; pathname: string; @@ -22,7 +20,7 @@ export function resolveSelectedOrRouteAgentKey(input: { return agentId ? `${workspaceTabRoute.serverId}:${agentId}` : null; } if (tabId.startsWith("draft_")) { - return `${workspaceTabRoute.serverId}:${DRAFT_AGENT_ID}`; + return `${workspaceTabRoute.serverId}:${tabId}`; } } diff --git a/packages/app/src/screens/agent/draft-agent-screen.tsx b/packages/app/src/screens/agent/draft-agent-screen.tsx index b4eb65082..b59e43fe6 100644 --- a/packages/app/src/screens/agent/draft-agent-screen.tsx +++ b/packages/app/src/screens/agent/draft-agent-screen.tsx @@ -53,7 +53,6 @@ import { useTauriDragHandlers } from '@/utils/tauri-window' import { useKeyboardShiftStyle } from '@/hooks/use-keyboard-shift-style' import { normalizeAgentSnapshot } from '@/utils/agent-snapshots' -const DRAFT_AGENT_ID = '__new_agent__' const EMPTY_PENDING_PERMISSIONS = new Map() const EMPTY_STREAM_ITEMS: StreamItem[] = [] const MAX_INITIAL_AGENT_TITLE_CHARS = 60 @@ -252,6 +251,7 @@ function DraftAgentScreenContent({ const tauriDragHandlers = useTauriDragHandlers() const isExplorerOpen = isMobile ? mobileView === 'file-explorer' : desktopFileExplorerOpen const draftIdRef = useRef(generateDraftId()) + const draftAgentIdRef = useRef(generateDraftId()) const [worktreeMode, setWorktreeMode] = useState<'none' | 'create' | 'attach'>('none') const [baseBranch, setBaseBranch] = useState('') @@ -866,7 +866,7 @@ function DraftAgentScreenContent({ return { serverId, - id: DRAFT_AGENT_ID, + id: draftAgentIdRef.current, provider, status: 'running', createdAt: now, @@ -1160,7 +1160,7 @@ function DraftAgentScreenContent({ {machine.tag === 'creating' && draftAgent && selectedServerId ? (