Skip to content

feat(desktop): show teardown logs in modal instead of error toast - #1277

Merged
saddlepaddle merged 1 commit into
mainfrom
teardown-failing-no-blokc
Feb 8, 2026
Merged

saddlepaddle merged 1 commit into
mainfrom
teardown-failing-no-blokc

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Feb 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • When a teardown script fails during workspace/worktree deletion, show a brief "Teardown failed" toast with a "View Logs" button instead of dumping raw terminal output into the toast
  • Clicking "View Logs" opens a modal with the full output rendered in a CodeBlock (language="log")
  • Return teardownResult.output from both delete and deleteWorktree tRPC procedures
  • Use setImmediate in teardown exit handler to let buffered stream data flush before reading combined output
  • Replace native <input type="checkbox"> with Checkbox + Label from @superset/ui

Test plan

  • Configure a project with a failing teardown command (e.g., exit 1 in .superset/config.json)
  • Delete a worktree workspace — error toast shows "Teardown failed" with "View Logs" button
  • Click "View Logs" — modal opens with full terminal output in a styled CodeBlock
  • Verify scrolling works for long output
  • Verify the modal can be closed and re-opened from the toast
  • Test worktree deletion from the WorkspacesListView
  • Verify successful deletions still show the normal success toast
  • Lint, typecheck, and tests pass

Summary by CodeRabbit

  • New Features

    • Added a Teardown Logs modal to view detailed logs when deletion fails.
    • Added a checkbox option in the deletion dialog to control deleting the local branch.
  • Bug Fixes

    • Improved deletion feedback: clearer success/error notifications and an actionable "View Logs" button when teardown produces logs.

@coderabbitai

coderabbitai Bot commented Feb 7, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Backend teardown result now carries optional diagnostic output; deletion paths propagate that output on failure. Frontend deletion dialogs were updated to surface teardown failures with a "View Logs" action. A new TeardownLogsDialog component provides a modal to view (ANSI-stripped) teardown logs and a global showTeardownLogs trigger.

Changes

Cohort / File(s) Summary
Backend: workspace delete procedure
apps/desktop/src/lib/trpc/routers/workspaces/procedures/delete.ts
Extended teardown result to include optional output?: string and propagate teardownResult.output in error responses for delete and deleteWorktree mutation paths.
Frontend: Sidebar delete dialog
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/.../DeleteWorkspaceDialog.tsx
Replaced toast.promise flow with async/await, added checkbox UI wiring, and shows teardown-specific error toast with "View Logs" action when output is present (uses showTeardownLogs).
Frontend: Row delete dialog
apps/desktop/src/renderer/screens/main/components/WorkspacesListView/.../DeleteWorktreeDialog.tsx
Switched to async/await deletion handler, shows success toast or teardown-failure toast with "View Logs" action when output present (uses showTeardownLogs).
UI: Teardown logs dialog component
apps/desktop/src/renderer/routes/_authenticated/components/TeardownLogsDialog/TeardownLogsDialog.tsx, .../index.ts
Added TeardownLogsDialog component and exported showTeardownLogs(logs: string) to open modal and display ANSI-stripped logs with copy support.
Layout: mount logs dialog
apps/desktop/src/renderer/routes/_authenticated/layout.tsx
Mounted <TeardownLogsDialog /> inside AuthenticatedLayout so showTeardownLogs can display the modal globally.
Minor: ref update
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/.../WorkspaceListItem.tsx
Changed ref assignment to use a callback ref for itemRef.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Frontend as Renderer (Dialog)
  participant TRPC as Backend (delete mutation)
  participant Logs as TeardownLogsDialog

  User->>Frontend: Click "Delete" (with show logs option)
  Frontend->>TRPC: call delete/deleteWorktree mutation
  TRPC-->>Frontend: response { success: true } OR { success: false, output?: string, error }
  alt success
    Frontend->>User: show success toast
  else teardown failed with output
    Frontend->>User: show error toast with "View Logs" action
    User->>Frontend: clicks "View Logs"
    Frontend->>Logs: showTeardownLogs(output) (ANSI stripped)
    Logs-->>User: modal with logs and copy button
  else teardown failed without output
    Frontend->>User: show generic error toast
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I dug a hole through delete and dread,
Logs in paw, ANSI dust to shed.
A modal opens, bright and neat,
Teardown whispers, clear and sweet.
Hop—view the logs and rest your head.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: showing teardown logs in a modal dialog instead of in an error toast.
Description check ✅ Passed The description is comprehensive and well-structured, covering the summary, test plan, and implementation details; however, it lacks explicit sections for Related Issues and Type of Change as specified in the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch teardown-failing-no-blokc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saddlepaddle
saddlepaddle force-pushed the teardown-failing-no-blokc branch 3 times, most recently from 26bcb1a to fd33c3c Compare February 7, 2026 06:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/desktop/src/renderer/components/TeardownLogsDialog/TeardownLogsDialog.tsx`:
- Around line 36-39: The module-level assignment to showLogsFn inside
TeardownLogsDialog is a render-side effect; move that assignment into a
useEffect so it runs after mount and is cleaned up on unmount. Specifically, in
TeardownLogsDialog use useEffect to set showLogsFn = (newLogs) => {
setLogs(newLogs); setIsOpen(true); } when the component mounts and reset
showLogsFn = undefined (or a no-op) in the effect cleanup to avoid stale
closures and state updates after unmount; also add useEffect to the React
import.

Comment on lines +36 to +39
showLogsFn = (newLogs) => {
setLogs(newLogs);
setIsOpen(true);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Side effect during render — move showLogsFn assignment into useEffect with cleanup.

Assigning a module-level mutable inside the render body runs on every render and is a side effect that violates React's expectation of pure render functions. More importantly, if TeardownLogsDialog ever unmounts, showLogsFn retains a stale reference to the old setLogs/setIsOpen, which will trigger React state-update-on-unmounted-component warnings.

Wrap in useEffect so the callback is registered after mount and cleared on unmount:

Proposed fix
 export function TeardownLogsDialog() {
 	const [logs, setLogs] = useState<string | null>(null);
 	const [isOpen, setIsOpen] = useState(false);

-	showLogsFn = (newLogs) => {
-		setLogs(newLogs);
-		setIsOpen(true);
-	};
+	useEffect(() => {
+		showLogsFn = (newLogs) => {
+			setLogs(newLogs);
+			setIsOpen(true);
+		};
+		return () => {
+			showLogsFn = null;
+		};
+	}, []);

You'll need to add useEffect to the React import.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
showLogsFn = (newLogs) => {
setLogs(newLogs);
setIsOpen(true);
};
export function TeardownLogsDialog() {
const [logs, setLogs] = useState<string | null>(null);
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
showLogsFn = (newLogs) => {
setLogs(newLogs);
setIsOpen(true);
};
return () => {
showLogsFn = null;
};
}, []);
🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/components/TeardownLogsDialog/TeardownLogsDialog.tsx`
around lines 36 - 39, The module-level assignment to showLogsFn inside
TeardownLogsDialog is a render-side effect; move that assignment into a
useEffect so it runs after mount and is cleaned up on unmount. Specifically, in
TeardownLogsDialog use useEffect to set showLogsFn = (newLogs) => {
setLogs(newLogs); setIsOpen(true); } when the component mounts and reset
showLogsFn = undefined (or a no-op) in the effect cleanup to avoid stale
closures and state updates after unmount; also add useEffect to the React
import.

@saddlepaddle
saddlepaddle force-pushed the teardown-failing-no-blokc branch from fd33c3c to e036118 Compare February 7, 2026 06:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/desktop/src/renderer/routes/_authenticated/components/TeardownLogsDialog/TeardownLogsDialog.tsx`:
- Around line 13-18: Replace the custom ANSI_REGEX and local stripAnsi function
with the existing strip-ansi library: remove the ANSI_REGEX constant and the
stripAnsi(text: string) implementation, import the library (e.g. import
stripAnsi from 'strip-ansi') at the top of the file, and update any local calls
to use the imported stripAnsi function; also remove the biome-ignore comment
that was tied to the custom regex. This keeps usage consistent with other
modules like commandBuffer.ts and avoids duplicating regex logic.

Comment on lines +13 to +18
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching ANSI escape sequences
const ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]/g;

function stripAnsi(text: string): string {
return text.replace(ANSI_REGEX, "");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if strip-ansi is available in the project dependencies
find . -name "package.json" -type f -exec grep -l "strip-ansi" {} \;

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Check if strip-ansi is a direct dependency in apps/desktop/package.json
grep -A 2 -B 2 "strip-ansi" ./apps/desktop/package.json

# Search for any existing usage of strip-ansi in the codebase
rg "from\s+['\"]strip-ansi" --type ts --type tsx --type js --type jsx
rg "import.*strip-ansi"

Repository: superset-sh/superset

Length of output: 393


Use strip-ansi library instead of custom regex for ANSI code removal.

The library is already in the dependency tree (strip-ansi@^7.1.2) and used elsewhere in the codebase (commandBuffer.ts). Replace the custom regex with the established library for consistency and maintainability.

🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/routes/_authenticated/components/TeardownLogsDialog/TeardownLogsDialog.tsx`
around lines 13 - 18, Replace the custom ANSI_REGEX and local stripAnsi function
with the existing strip-ansi library: remove the ANSI_REGEX constant and the
stripAnsi(text: string) implementation, import the library (e.g. import
stripAnsi from 'strip-ansi') at the top of the file, and update any local calls
to use the imported stripAnsi function; also remove the biome-ignore comment
that was tied to the custom regex. This keeps usage consistent with other
modules like commandBuffer.ts and avoids duplicating regex logic.

When a teardown script fails during workspace/worktree deletion, show a
brief "Teardown failed" toast with a "View Logs" button that opens a
modal displaying the full terminal output in a CodeBlock, instead of
dumping the raw output into the toast message.

- Return teardown output from tRPC delete procedures
- Add TeardownLogsDialog using imperative Alerter pattern
- Use CodeBlock with language="log" for styled log display
- Replace native checkbox with Checkbox + Label components
- Fix stream flushing with setImmediate in teardown exit handler
@saddlepaddle
saddlepaddle force-pushed the teardown-failing-no-blokc branch from e036118 to fb16dff Compare February 7, 2026 07:05
@saddlepaddle
saddlepaddle merged commit 6af8d1d into main Feb 8, 2026
6 checks passed
@github-actions

github-actions Bot commented Feb 8, 2026

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

@Kitenite
Kitenite deleted the teardown-failing-no-blokc branch February 9, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant