From dd2c3af77046e304101d4eba10befaa5c2592f8b Mon Sep 17 00:00:00 2001 From: Vrutin Tarunchandra Date: Mon, 12 Jan 2026 18:11:22 +0000 Subject: [PATCH] Add workspace deletion with confirmation dialog - Add removeWorkspace service function in tauri.ts - Add removeWorkspace hook function with confirmation dialog - Add workspace context menu with delete option in Sidebar - Wire up delete handler in App.tsx - Use Tauri ask dialog for confirmation with Delete/Cancel buttons --- src/App.tsx | 4 ++++ src/components/Sidebar.tsx | 19 ++++++++++++++++ src/hooks/useWorkspaces.ts | 45 ++++++++++++++++++++++++++++++++++++++ src/services/tauri.ts | 4 ++++ 4 files changed, 72 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index 2f428ea9e..796a7378b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -91,6 +91,7 @@ function MainApp() { connectWorkspace, markWorkspaceConnected, updateWorkspaceSettings, + removeWorkspace, hasLoaded, refreshWorkspaces, } = useWorkspaces({ onDebug: addDebugEntry }); @@ -355,6 +356,9 @@ function MainApp() { onDeleteThread={(workspaceId, threadId) => { removeThread(workspaceId, threadId); }} + onDeleteWorkspace={(workspaceId) => { + void removeWorkspace(workspaceId); + }} />
void; onSelectThread: (workspaceId: string, threadId: string) => void; onDeleteThread: (workspaceId: string, threadId: string) => void; + onDeleteWorkspace: (workspaceId: string) => void; }; export function Sidebar({ @@ -41,6 +42,7 @@ export function Sidebar({ onToggleWorkspaceCollapse, onSelectThread, onDeleteThread, + onDeleteWorkspace, }: SidebarProps) { const [expandedWorkspaces, setExpandedWorkspaces] = useState( new Set(), @@ -69,6 +71,22 @@ export function Sidebar({ await menu.popup(position, window); } + async function showWorkspaceMenu( + event: React.MouseEvent, + workspaceId: string, + ) { + event.preventDefault(); + event.stopPropagation(); + const deleteItem = await MenuItem.new({ + text: "Delete", + action: () => onDeleteWorkspace(workspaceId), + }); + const menu = await Menu.new({ items: [deleteItem] }); + const window = getCurrentWindow(); + const position = new LogicalPosition(event.clientX, event.clientY); + await menu.popup(position, window); + } + const usagePercent = accountRateLimits?.primary?.usedPercent; const globalUsagePercent = accountRateLimits?.secondary?.usedPercent; const credits = accountRateLimits?.credits ?? null; @@ -150,6 +168,7 @@ export function Sidebar({ role="button" tabIndex={0} onClick={() => onSelectWorkspace(entry.id)} + onContextMenu={(event) => showWorkspaceMenu(event, entry.id)} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); diff --git a/src/hooks/useWorkspaces.ts b/src/hooks/useWorkspaces.ts index a7e061621..460185da2 100644 --- a/src/hooks/useWorkspaces.ts +++ b/src/hooks/useWorkspaces.ts @@ -1,11 +1,13 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import type { DebugEntry } from "../types"; import type { WorkspaceInfo, WorkspaceSettings } from "../types"; +import { ask } from "@tauri-apps/plugin-dialog"; import { addWorkspace as addWorkspaceService, connectWorkspace as connectWorkspaceService, listWorkspaces, pickWorkspacePath, + removeWorkspace as removeWorkspaceService, updateWorkspaceSettings as updateWorkspaceSettingsService, } from "../services/tauri"; @@ -149,6 +151,48 @@ export function useWorkspaces(options: UseWorkspacesOptions = {}) { } } + async function removeWorkspace(workspaceId: string) { + const workspace = workspaces.find((entry) => entry.id === workspaceId); + const workspaceName = workspace?.name || "this workspace"; + + const confirmed = await ask( + `Are you sure you want to delete "${workspaceName}"?\n\nThis will remove the workspace from CodexMonitor.`, + { + title: "Delete Workspace", + kind: "warning", + okLabel: "Delete", + cancelLabel: "Cancel", + }, + ); + + if (!confirmed) { + return; + } + + onDebug?.({ + id: `${Date.now()}-client-remove-workspace`, + timestamp: Date.now(), + source: "client", + label: "workspace/remove", + payload: { workspaceId }, + }); + try { + await removeWorkspaceService(workspaceId); + setWorkspaces((prev) => prev.filter((entry) => entry.id !== workspaceId)); + setActiveWorkspaceId((prev) => (prev === workspaceId ? null : prev)); + await refreshWorkspaces(); + } catch (error) { + onDebug?.({ + id: `${Date.now()}-client-remove-workspace-error`, + timestamp: Date.now(), + source: "error", + label: "workspace/remove error", + payload: error instanceof Error ? error.message : String(error), + }); + throw error; + } + } + return { workspaces, activeWorkspace, @@ -158,6 +202,7 @@ export function useWorkspaces(options: UseWorkspacesOptions = {}) { connectWorkspace, markWorkspaceConnected, updateWorkspaceSettings, + removeWorkspace, hasLoaded, refreshWorkspaces, }; diff --git a/src/services/tauri.ts b/src/services/tauri.ts index de9146b28..ad279273f 100644 --- a/src/services/tauri.ts +++ b/src/services/tauri.ts @@ -29,6 +29,10 @@ export async function updateWorkspaceSettings( return invoke("update_workspace_settings", { id, settings }); } +export async function removeWorkspace(id: string): Promise { + return invoke("remove_workspace", { id }); +} + export async function connectWorkspace(id: string): Promise { return invoke("connect_workspace", { id }); }