mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-01 22:48:16 +00:00
fix(app): better pending states for workspace operations
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user