fix: close confirm dialog immediately and gray out removing worktrees

Dismiss the confirmation dialog as soon as the user confirms instead
of waiting for the API call. Show the item grayed out with
pointer-events disabled while deletion is in progress. Auto-select
the previous (or next) worktree when the selected one is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
centdix
2026-02-21 02:35:07 +00:00
parent 6299e7a36a
commit 65a8789dfd
2 changed files with 21 additions and 11 deletions
+17 -9
View File
@@ -17,9 +17,9 @@
let worktrees = $state<WorktreeInfo[]>([]);
let selectedBranch = $state<string | null>(null);
let removeBranch = $state<string | null>(null);
let removingBranches = $state<Set<string>>(new Set());
let showCreateDialog = $state(false);
let creating = $state(false);
let removing = $state(false);
let createProfile = $state<Profile>("agent-only");
let visibleWorktrees = $derived(
@@ -63,17 +63,26 @@
}
async function handleRemove() {
if (!removeBranch) return;
removing = true;
const branch = removeBranch;
if (!branch) return;
removeBranch = null;
// Select neighbor before starting the async removal
if (selectedBranch === branch) {
const idx = visibleWorktrees.findIndex((w) => w.branch === branch);
const neighbor = visibleWorktrees[idx - 1] ?? visibleWorktrees[idx + 1];
const isNeighborMain = neighbor && (neighbor.path === "(here)" || neighbor.branch === "main");
selectedBranch = neighbor && !isNeighborMain ? neighbor.branch : null;
}
removingBranches = new Set([...removingBranches, branch]);
try {
await api.removeWorktree(removeBranch);
if (selectedBranch === removeBranch) selectedBranch = null;
removeBranch = null;
await api.removeWorktree(branch);
await refresh();
} catch (err) {
alert(`Failed to remove: ${err instanceof Error ? err.message : err}`);
} finally {
removing = false;
removingBranches = new Set([...removingBranches].filter((b) => b !== branch));
}
}
@@ -95,7 +104,7 @@
title="New Worktree"
><span class="text-lg leading-none">+</span> New</button>
</div>
<WorktreeList worktrees={visibleWorktrees} selected={selectedBranch} onselect={(b) => (selectedBranch = b)} onremove={(b) => (removeBranch = b)} />
<WorktreeList worktrees={visibleWorktrees} selected={selectedBranch} removing={removingBranches} onselect={(b) => (selectedBranch = b)} onremove={(b) => (removeBranch = b)} />
</aside>
<main class="flex-1 min-w-0 flex flex-col overflow-hidden">
@@ -164,7 +173,6 @@
{#if removeBranch}
<ConfirmDialog
message={`Remove worktree "${removeBranch}"? This action cannot be undone.`}
loading={removing}
onconfirm={handleRemove}
oncancel={() => (removeBranch = null)}
/>
@@ -1,9 +1,10 @@
<script lang="ts">
import type { WorktreeInfo } from "./types";
let { worktrees, selected, onselect, onremove }: {
let { worktrees, selected, removing, onselect, onremove }: {
worktrees: WorktreeInfo[];
selected: string | null;
removing: Set<string>;
onselect: (branch: string) => void;
onremove: (branch: string) => void;
} = $props();
@@ -20,7 +21,8 @@
{#each worktrees as wt (wt.branch)}
{@const isMain = wt.path === "(here)" || wt.branch === "main"}
{@const isActive = wt.branch === selected}
<li class="mb-0.5 group relative">
{@const isRemoving = removing.has(wt.branch)}
<li class="mb-0.5 group relative {isRemoving ? 'opacity-40 pointer-events-none' : ''}">
<button
type="button"
class="w-full py-2.5 px-3 rounded-md border cursor-pointer flex flex-col gap-1 text-left text-inherit text-sm bg-transparent hover:bg-hover {isActive ? 'bg-active border-accent' : 'border-transparent'}"