fix(app): better pending states for workspace operations

This commit is contained in:
Adam
2026-01-20 10:31:13 -06:00
parent 8f62d4a5e3
commit 7f862533d8

View File

@@ -122,6 +122,17 @@ export default function Layout(props: ParentProps) {
active: "" as string,
value: "",
})
const [busyWorkspaces, setBusyWorkspaces] = createSignal<Set<string>>(new Set())
const setBusy = (directory: string, value: boolean) => {
const key = workspaceKey(directory)
setBusyWorkspaces((prev) => {
const next = new Set(prev)
if (value) next.add(key)
else next.delete(key)
return next
})
}
const isBusy = (directory: string) => busyWorkspaces().has(workspaceKey(directory))
const editorRef = { current: undefined as HTMLInputElement | undefined }
const autoselecting = createMemo(() => {
@@ -995,6 +1006,8 @@ export default function Layout(props: ParentProps) {
if (!current) return
if (directory === current.worktree) return
setBusy(directory, true)
const result = await globalSDK.client.worktree
.remove({ directory: current.worktree, worktreeRemoveInput: { directory } })
.then((x) => x.data)
@@ -1006,6 +1019,8 @@ export default function Layout(props: ParentProps) {
return false
})
setBusy(directory, false)
if (!result) return
layout.projects.close(directory)
@@ -1021,12 +1036,7 @@ export default function Layout(props: ParentProps) {
if (!current) return
if (directory === current.worktree) return
const progress = showToast({
persistent: true,
title: "Resetting workspace",
description: "This may take a minute.",
})
const dismiss = () => toaster.dismiss(progress)
setBusy(directory, true)
const sessions = await globalSDK.client.session
.list({ directory })
@@ -1037,7 +1047,6 @@ export default function Layout(props: ParentProps) {
.reset({ directory: current.worktree, worktreeResetInput: { directory } })
.then((x) => x.data)
.catch((err) => {
dismiss()
showToast({
title: "Failed to reset workspace",
description: errorMessage(err),
@@ -1046,7 +1055,7 @@ export default function Layout(props: ParentProps) {
})
if (!result) {
dismiss()
setBusy(directory, false)
return
}
@@ -1066,7 +1075,8 @@ export default function Layout(props: ParentProps) {
)
await globalSDK.client.instance.dispose({ directory }).catch(() => undefined)
dismiss()
setBusy(directory, false)
const href = `/${base64Encode(directory)}/session`
navigate(href)
@@ -1590,6 +1600,7 @@ export default function Layout(props: ParentProps) {
const boot = createMemo(() => open() || active())
const loading = createMemo(() => open() && workspaceStore.status !== "complete" && sessions().length === 0)
const hasMore = createMemo(() => local() && workspaceStore.sessionTotal > workspaceStore.session.length)
const busy = createMemo(() => isBusy(props.directory))
const loadMore = async () => {
if (!local()) return
setWorkspaceStore("limit", (limit) => limit + 5)
@@ -1612,7 +1623,9 @@ export default function Layout(props: ParentProps) {
const header = () => (
<div class="flex items-center gap-1 min-w-0 flex-1">
<div class="flex items-center justify-center shrink-0 size-6">
<Icon name="branch" size="small" />
<Show when={busy()} fallback={<Icon name="branch" size="small" />}>
<Spinner class="size-[15px]" />
</Show>
</div>
<span class="text-14-medium text-text-base shrink-0">{local() ? "local" : "sandbox"} :</span>
<Show
@@ -1649,7 +1662,13 @@ export default function Layout(props: ParentProps) {
return (
// @ts-ignore
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
<div
use:sortable
classList={{
"opacity-30": sortable.isActiveDraggable,
"opacity-50 pointer-events-none": busy(),
}}
>
<Collapsible variant="ghost" open={open()} class="shrink-0" onOpenChange={openWrapper}>
<div class="px-2 py-1">
<div class="group/workspace relative">
@@ -1699,13 +1718,13 @@ export default function Layout(props: ParentProps) {
<DropdownMenu.ItemLabel>Rename</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item
disabled={local()}
disabled={local() || busy()}
onSelect={() => dialog.show(() => <DialogResetWorkspace directory={props.directory} />)}
>
<DropdownMenu.ItemLabel>Reset</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item
disabled={local()}
disabled={local() || busy()}
onSelect={() => dialog.show(() => <DialogDeleteWorkspace directory={props.directory} />)}
>
<DropdownMenu.ItemLabel>Delete</DropdownMenu.ItemLabel>