Fix sidebar project removal

Remove projects through a daemon RPC so the sidebar can delete the project
record after archiving its active workspaces. Reopening a removed directory
recreates the missing project record so local projects keep their derived
basename, and History skips orphaned archived workspace rows.

Fixes #1583.
This commit is contained in:
Mohamed Boudra
2026-06-19 00:46:40 +07:00
parent f876b80f7a
commit 9a09ccb3d6
20 changed files with 736 additions and 29 deletions
@@ -69,6 +69,7 @@ import {
type SidebarWorkspaceEntry,
} from "@/hooks/use-sidebar-workspaces-list";
import { useSidebarOrderStore } from "@/stores/sidebar-order-store";
import { useSessionStore } from "@/stores/session-store";
import { useShowShortcutBadges } from "@/hooks/use-show-shortcut-badges";
import { ContextMenuTrigger, useContextMenu } from "@/components/ui/context-menu";
import {
@@ -113,8 +114,6 @@ import { buildSidebarProjectRowModel } from "@/utils/sidebar-project-row-model";
import { redirectIfArchivingActiveWorkspace } from "@/utils/sidebar-workspace-archive-redirect";
import { openExternalUrl } from "@/utils/open-external-url";
import { requireWorkspaceDirectory, resolveWorkspaceDirectory } from "@/utils/workspace-directory";
import { archiveWorkspacesOptimistically } from "@/workspace/workspace-archive";
import { selectProjectWorkspacesToArchive } from "@/workspace/project-workspace-archive";
import { useWorkspaceArchive } from "@/workspace/use-workspace-archive";
import {
isWeb as platformIsWeb,
@@ -1785,6 +1784,7 @@ function ProjectBlock({
displayName,
iconDataUri,
serverId,
canRemoveProject,
selectionEnabled,
showShortcutBadges,
shortcutIndexByWorkspaceKey,
@@ -1805,6 +1805,7 @@ function ProjectBlock({
displayName: string;
iconDataUri: string | null;
serverId: string | null;
canRemoveProject: boolean;
selectionEnabled: boolean;
showShortcutBadges: boolean;
shortcutIndexByWorkspaceKey: Map<string, number>;
@@ -1923,26 +1924,24 @@ function ProjectBlock({
toast.error(t("sidebar.project.toasts.hostDisconnected"));
return;
}
if (!canRemoveProject) {
toast.error(t("sidebar.project.toasts.updateHostToRemove"));
return;
}
setIsRemovingProject(true);
void selectProjectWorkspacesToArchive(project.workspaces)
.then((workspaces) => archiveWorkspacesOptimistically({ client, workspaces }))
.then((failures) => {
if (failures.length > 0) {
toast.error(t("sidebar.project.toasts.removeFailed"));
}
setIsRemovingProject(false);
return;
})
void client
.removeProject(project.projectKey)
.catch((error) => {
toast.error(
error instanceof Error ? error.message : t("sidebar.project.toasts.removeFailed"),
);
})
.finally(() => {
setIsRemovingProject(false);
return;
});
})();
}, [isRemovingProject, serverId, displayName, t, toast, project.workspaces]);
}, [isRemovingProject, serverId, displayName, t, toast, project.projectKey, canRemoveProject]);
const handleToggleCollapsed = useCallback(() => {
onToggleCollapsed(project.projectKey);
@@ -2000,6 +1999,7 @@ function areProjectBlockPropsEqual(previous: ProjectBlockProps, next: ProjectBlo
previous.displayName === next.displayName &&
previous.iconDataUri === next.iconDataUri &&
previous.serverId === next.serverId &&
previous.canRemoveProject === next.canRemoveProject &&
previous.selectionEnabled === next.selectionEnabled &&
previous.showShortcutBadges === next.showShortcutBadges &&
previous.shortcutIndexByWorkspaceKey === next.shortcutIndexByWorkspaceKey &&
@@ -2145,6 +2145,9 @@ function ProjectModeList({
const setProjectOrder = useSidebarOrderStore((state) => state.setProjectOrder);
const getWorkspaceOrder = useSidebarOrderStore((state) => state.getWorkspaceOrder);
const setWorkspaceOrder = useSidebarOrderStore((state) => state.setWorkspaceOrder);
const canRemoveProject = useSessionStore((state) =>
serverId ? state.sessions[serverId]?.serverInfo?.features?.projectRemove === true : false,
);
const isWorkspaceRoute = useMemo(
() => Boolean(pathname && parseHostWorkspaceRouteFromPathname(pathname)),
@@ -2309,6 +2312,7 @@ function ProjectModeList({
displayName={item.projectName}
iconDataUri={projectIconByProjectKey.get(item.projectKey) ?? null}
serverId={serverId}
canRemoveProject={canRemoveProject}
selectionEnabled={selectionEnabled}
showShortcutBadges={showShortcutBadges}
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
@@ -2335,6 +2339,7 @@ function ProjectModeList({
onToggleProjectCollapsed,
parentGestureRef,
projectIconByProjectKey,
canRemoveProject,
selectionEnabled,
serverId,
shortcutIndexByWorkspaceKey,