Skip to content

Commit 3ed2fa8

Browse files
authored
Add workspace deletion with confirmation dialog (#8)
- 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 426d775 commit 3ed2fa8

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

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: 19 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,22 @@ 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 menu = await Menu.new({ items: [deleteItem] });
85+
const window = getCurrentWindow();
86+
const position = new LogicalPosition(event.clientX, event.clientY);
87+
await menu.popup(position, window);
88+
}
89+
7290
const usagePercent = accountRateLimits?.primary?.usedPercent;
7391
const globalUsagePercent = accountRateLimits?.secondary?.usedPercent;
7492
const credits = accountRateLimits?.credits ?? null;
@@ -150,6 +168,7 @@ export function Sidebar({
150168
role="button"
151169
tabIndex={0}
152170
onClick={() => onSelectWorkspace(entry.id)}
171+
onContextMenu={(event) => showWorkspaceMenu(event, entry.id)}
153172
onKeyDown={(event) => {
154173
if (event.key === "Enter" || event.key === " ") {
155174
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)