Skip to content

Commit fa11733

Browse files
committed
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
1 parent 4796e22 commit fa11733

4 files changed

Lines changed: 78 additions & 0 deletions

File tree

src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ function MainApp() {
9191
connectWorkspace,
9292
markWorkspaceConnected,
9393
updateWorkspaceSettings,
94+
removeWorkspace,
9495
hasLoaded,
9596
refreshWorkspaces,
9697
} = useWorkspaces({ onDebug: addDebugEntry });
@@ -355,6 +356,9 @@ function MainApp() {
355356
onDeleteThread={(workspaceId, threadId) => {
356357
removeThread(workspaceId, threadId);
357358
}}
359+
onDeleteWorkspace={(workspaceId) => {
360+
void removeWorkspace(workspaceId);
361+
}}
358362
/>
359363
<div
360364
className="sidebar-resizer"

src/components/Sidebar.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type SidebarProps = {
2323
onToggleWorkspaceCollapse: (workspaceId: string, collapsed: boolean) => void;
2424
onSelectThread: (workspaceId: string, threadId: string) => void;
2525
onDeleteThread: (workspaceId: string, threadId: string) => void;
26+
onDeleteWorkspace: (workspaceId: string) => void;
2627
};
2728

2829
export function Sidebar({
@@ -41,6 +42,7 @@ export function Sidebar({
4142
onToggleWorkspaceCollapse,
4243
onSelectThread,
4344
onDeleteThread,
45+
onDeleteWorkspace,
4446
}: SidebarProps) {
4547
const [expandedWorkspaces, setExpandedWorkspaces] = useState(
4648
new Set<string>(),
@@ -69,6 +71,28 @@ export function Sidebar({
6971
await menu.popup(position, window);
7072
}
7173

74+
async function showWorkspaceMenu(
75+
event: React.MouseEvent,
76+
workspaceId: string,
77+
) {
78+
event.preventDefault();
79+
event.stopPropagation();
80+
const deleteItem = await MenuItem.new({
81+
text: "Delete",
82+
action: () => onDeleteWorkspace(workspaceId),
83+
});
84+
const copyItem = await MenuItem.new({
85+
text: "Copy ID",
86+
action: async () => {
87+
await navigator.clipboard.writeText(workspaceId);
88+
},
89+
});
90+
const menu = await Menu.new({ items: [copyItem, deleteItem] });
91+
const window = getCurrentWindow();
92+
const position = new LogicalPosition(event.clientX, event.clientY);
93+
await menu.popup(position, window);
94+
}
95+
7296
const usagePercent = accountRateLimits?.primary?.usedPercent;
7397
const globalUsagePercent = accountRateLimits?.secondary?.usedPercent;
7498
const credits = accountRateLimits?.credits ?? null;
@@ -150,6 +174,7 @@ export function Sidebar({
150174
role="button"
151175
tabIndex={0}
152176
onClick={() => onSelectWorkspace(entry.id)}
177+
onContextMenu={(event) => showWorkspaceMenu(event, entry.id)}
153178
onKeyDown={(event) => {
154179
if (event.key === "Enter" || event.key === " ") {
155180
event.preventDefault();

src/hooks/useWorkspaces.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { useCallback, useEffect, useMemo, useState } from "react";
22
import type { DebugEntry } from "../types";
33
import type { WorkspaceInfo, WorkspaceSettings } from "../types";
4+
import { ask } from "@tauri-apps/plugin-dialog";
45
import {
56
addWorkspace as addWorkspaceService,
67
connectWorkspace as connectWorkspaceService,
78
listWorkspaces,
89
pickWorkspacePath,
10+
removeWorkspace as removeWorkspaceService,
911
updateWorkspaceSettings as updateWorkspaceSettingsService,
1012
} from "../services/tauri";
1113

@@ -149,6 +151,48 @@ export function useWorkspaces(options: UseWorkspacesOptions = {}) {
149151
}
150152
}
151153

154+
async function removeWorkspace(workspaceId: string) {
155+
const workspace = workspaces.find((entry) => entry.id === workspaceId);
156+
const workspaceName = workspace?.name || "this workspace";
157+
158+
const confirmed = await ask(
159+
`Are you sure you want to delete "${workspaceName}"?\n\nThis will remove the workspace from CodexMonitor.`,
160+
{
161+
title: "Delete Workspace",
162+
kind: "warning",
163+
okLabel: "Delete",
164+
cancelLabel: "Cancel",
165+
},
166+
);
167+
168+
if (!confirmed) {
169+
return;
170+
}
171+
172+
onDebug?.({
173+
id: `${Date.now()}-client-remove-workspace`,
174+
timestamp: Date.now(),
175+
source: "client",
176+
label: "workspace/remove",
177+
payload: { workspaceId },
178+
});
179+
try {
180+
await removeWorkspaceService(workspaceId);
181+
setWorkspaces((prev) => prev.filter((entry) => entry.id !== workspaceId));
182+
setActiveWorkspaceId((prev) => (prev === workspaceId ? null : prev));
183+
await refreshWorkspaces();
184+
} catch (error) {
185+
onDebug?.({
186+
id: `${Date.now()}-client-remove-workspace-error`,
187+
timestamp: Date.now(),
188+
source: "error",
189+
label: "workspace/remove error",
190+
payload: error instanceof Error ? error.message : String(error),
191+
});
192+
throw error;
193+
}
194+
}
195+
152196
return {
153197
workspaces,
154198
activeWorkspace,
@@ -158,6 +202,7 @@ export function useWorkspaces(options: UseWorkspacesOptions = {}) {
158202
connectWorkspace,
159203
markWorkspaceConnected,
160204
updateWorkspaceSettings,
205+
removeWorkspace,
161206
hasLoaded,
162207
refreshWorkspaces,
163208
};

src/services/tauri.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export async function updateWorkspaceSettings(
2929
return invoke<WorkspaceInfo>("update_workspace_settings", { id, settings });
3030
}
3131

32+
export async function removeWorkspace(id: string): Promise<void> {
33+
return invoke("remove_workspace", { id });
34+
}
35+
3236
export async function connectWorkspace(id: string): Promise<void> {
3337
return invoke("connect_workspace", { id });
3438
}

0 commit comments

Comments
 (0)