From 4a73d51acd6cc2610fa962a424a6d7049520f560 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 10 Feb 2026 05:52:34 -0600 Subject: [PATCH] fix(app): workspace reset issues --- packages/opencode/src/worktree/index.ts | 40 ++++++++++++++++++++++++- packages/ui/src/components/toast.css | 10 ++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/worktree/index.ts b/packages/opencode/src/worktree/index.ts index 2e095136e..88c778cbb 100644 --- a/packages/opencode/src/worktree/index.ts +++ b/packages/opencode/src/worktree/index.ts @@ -219,6 +219,44 @@ export namespace Worktree { return [outputText(result.stderr), outputText(result.stdout)].filter(Boolean).join("\n") } + function failed(result: { stdout?: Uint8Array; stderr?: Uint8Array }) { + return [outputText(result.stderr), outputText(result.stdout)].filter(Boolean).flatMap((chunk) => + chunk + .split("\n") + .map((line) => line.trim()) + .flatMap((line) => { + const match = line.match(/^warning:\s+failed to remove\s+(.+):\s+/i) + if (!match) return [] + const value = match[1]?.trim().replace(/^['"]|['"]$/g, "") + if (!value) return [] + return [value] + }), + ) + } + + async function prune(root: string, entries: string[]) { + const base = await canonical(root) + await Promise.all( + entries.map(async (entry) => { + const target = await canonical(path.resolve(root, entry)) + if (target === base) return + if (!target.startsWith(`${base}${path.sep}`)) return + await fs.rm(target, { recursive: true, force: true }).catch(() => undefined) + }), + ) + } + + async function sweep(root: string) { + const first = await $`git clean -ffdx`.quiet().nothrow().cwd(root) + if (first.exitCode === 0) return first + + const entries = failed(first) + if (!entries.length) return first + + await prune(root, entries) + return $`git clean -ffdx`.quiet().nothrow().cwd(root) + } + async function canonical(input: string) { const abs = path.resolve(input) const real = await fs.realpath(abs).catch(() => abs) @@ -536,7 +574,7 @@ export namespace Worktree { throw new ResetFailedError({ message: errorText(resetToTarget) || "Failed to reset worktree to target" }) } - const clean = await $`git clean -fdx`.quiet().nothrow().cwd(worktreePath) + const clean = await sweep(worktreePath) if (clean.exitCode !== 0) { throw new ResetFailedError({ message: errorText(clean) || "Failed to clean worktree" }) } diff --git a/packages/ui/src/components/toast.css b/packages/ui/src/components/toast.css index ed7ba4a20..de547f9c7 100644 --- a/packages/ui/src/components/toast.css +++ b/packages/ui/src/components/toast.css @@ -7,7 +7,9 @@ flex-direction: column; gap: 8px; max-width: min(400px, calc(100vw - 64px)); + max-height: calc(100dvh - 96px); width: 100%; + overflow: hidden; pointer-events: none; [data-slot="toast-list"] { @@ -17,6 +19,8 @@ list-style: none; margin: 0; padding: 0; + max-height: 100%; + overflow-y: auto; } } @@ -26,6 +30,8 @@ align-items: flex-start; gap: 20px; padding: 16px 20px; + max-height: min(420px, calc(100dvh - 96px)); + overflow: hidden; pointer-events: auto; transition: all 150ms ease-out; @@ -91,8 +97,10 @@ display: flex; flex-direction: column; gap: 2px; + min-height: 0; min-width: 0; - overflow: hidden; + overflow-x: hidden; + overflow-y: auto; } [data-slot="toast-title"] {