Skip to content

fix(desktop): no confirmation for empty workspace delete#266

Merged
AviPeltz merged 6 commits into
mainfrom
worried-silverfish-67c386
Dec 8, 2025
Merged

fix(desktop): no confirmation for empty workspace delete#266
AviPeltz merged 6 commits into
mainfrom
worried-silverfish-67c386

Conversation

@AviPeltz
Copy link
Copy Markdown
Collaborator

@AviPeltz AviPeltz commented Dec 6, 2025

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

  • New Features

    • Deletion dialog now warns if a workspace has uncommitted changes.
    • Deletion dialog now warns if a workspace has unpushed commits.
    • Workspaces with no active terminals, no uncommitted changes, and no unpushed commits can be deleted directly without confirmation.
  • Chores

    • Updated desktop app version to 0.0.9.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 6, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
website Ready Ready Preview Comment Dec 8, 2025 5:26am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 6, 2025

Walkthrough

Adds git-state checks for workspace deletion: two helpers (hasUncommittedChanges, hasUnpushedCommits), canDelete now returns hasChanges and hasUnpushedCommits and accepts skipGitChecks, backend runs parallel git checks, tests extended for git scenarios, and UI uses an initial git-status query plus a lightweight polling query and shows warnings for uncommitted/unpushed changes; WorkspaceItem can auto-delete when safe.

Changes

Cohort / File(s) Summary
Version Bump
apps/desktop/package.json
Version updated from 0.0.80.0.9
Git utilities
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Added exported helpers hasUncommittedChanges(worktreePath: string): Promise<boolean> and hasUnpushedCommits(worktreePath: string): Promise<boolean> that use simpleGit with fallback/error handling
Backend / canDelete
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
canDelete input extended to accept { id, skipGitChecks?: boolean }; runs/omits git checks based on flag; performs parallel checks and returns hasChanges: boolean and hasUnpushedCommits: boolean in all non-error response shapes
Tests
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.test.ts
Mocks extended (mockSimpleGitWithWorktreeList(..., options?)) to simulate isClean and unpushedCommitCount; new tests for uncommitted/unpushed cases, missing worktree, and skipGitChecks behavior
UI — Delete Dialog
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
Replaced single polling query with: (1) initial git-status query (no refetch) and (2) lightweight polling terminal-count query; merges results; exposes hasChanges and hasUnpushedCommits and renders warnings for both when true
UI — WorkspaceItem
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
New delete flow: canDelete query used on click with debouncing/refetch; auto-deletes when safe (no terminals, no changes, no warnings) using toast, otherwise opens DeleteWorkspaceDialog; retains other UI behaviors

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as WorkspaceItem / DeleteDialog
    participant TRPC as trpc.workspaces.canDelete
    participant Git as Git utils (simple-git)
    participant Storage as Workspace storage (delete)

    User->>UI: Click "Delete"
    UI->>UI: handleDeleteClick (debounce, show pending)
    UI->>TRPC: Query canDelete(id, skipGitChecks=false)
    TRPC->>Git: hasUncommittedChanges(worktreePath)
    TRPC->>Git: hasUnpushedCommits(worktreePath)
    Git-->>TRPC: return booleans
    TRPC-->>UI: { canDelete, activeTerminalCount, hasChanges, hasUnpushedCommits }
    alt canDelete && activeTerminalCount==0 && !hasChanges && !hasUnpushedCommits
        UI->>Storage: deleteWorkspace(id)
        Storage-->>UI: success
        UI->>User: show success toast
    else
        UI->>UI: open DeleteWorkspaceDialog (shows warnings)
        User->>UI: confirm
        UI->>Storage: deleteWorkspace(id)
        Storage-->>User: success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus:
    • workspaces.ts — ensure all canDelete return branches include the new fields and consistent behavior when skipGitChecks is used
    • utils/git.ts — correctness of upstream detection, rev-list usage, and error fallbacks
    • UI interaction split in DeleteWorkspaceDialog.tsx — merging two query sources and handling loading states
    • WorkspaceItem.tsx — debounce/refetch/delete flow and toast/error handling
    • Updated tests — validate mocks accurately reflect git behaviors

Possibly related PRs

Poem

🐇 In a burrow of branches and stems, I hop with delight,
I sniff for unpushed leaves and changes that bite,
If safe, I nudge—quick delete with a cheer,
If not, I warn gently so work stays near,
Hoppity code, keep the repo bright! 🌿

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete, containing only template placeholders with no actual details about changes, rationale, testing, or related issues. Add a meaningful description of changes, testing approach, related issues, and implementation rationale. Replace template placeholders with actual content.
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: allowing direct deletion of empty workspaces without requiring a confirmation dialog.
✨ 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 worried-silverfish-67c386

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.

…c386

# Conflicts:
#	apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 8, 2025

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@AviPeltz AviPeltz marked this pull request as ready for review December 8, 2025 05:05
@AviPeltz AviPeltz changed the title no confirmation for empty workspace Desktop(fix): no confirmation for empty workspace delete Dec 8, 2025
@AviPeltz AviPeltz changed the title Desktop(fix): no confirmation for empty workspace delete fix(desktop): no confirmation for empty workspace delete Dec 8, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)

373-379: Consider adding error handling for consistency.

hasUnpushedCommits gracefully handles errors by returning false, but hasUncommittedChanges will propagate exceptions if git.status() fails (e.g., corrupted repo, missing .git). This could cause canDelete to fail entirely.

 export async function hasUncommittedChanges(
 	worktreePath: string,
 ): Promise<boolean> {
-	const git = simpleGit(worktreePath);
-	const status = await git.status();
-	return !status.isClean();
+	try {
+		const git = simpleGit(worktreePath);
+		const status = await git.status();
+		return !status.isClean();
+	} catch {
+		// If status check fails, assume no uncommitted changes to allow deletion
+		return false;
+	}
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e12c00 and bd3a334.

📒 Files selected for processing (5)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.test.ts (3 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (5 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.test.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
🧰 Additional context used
📓 Path-based instructions (5)
apps/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: Please use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them
Avoid any type in TypeScript unless absolutely necessary

Files:

  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
**/{components,pages}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/{components,pages}/**/*.tsx: Use one folder per component with structure ComponentName/ComponentName.tsx plus index.ts for barrel export
Create one component per file - do not use multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
apps/desktop/src/{renderer,shared,preload}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in renderer process code or shared code in the desktop app

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
🧬 Code graph analysis (2)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)
  • hasUnpushedCommits (386-417)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (2)
  • hasUncommittedChanges (373-379)
  • hasUnpushedCommits (386-417)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (7)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)

386-417: LGTM!

The implementation correctly handles the upstream tracking case and gracefully falls back for branches without configured upstreams. The fallback to false on errors is appropriate for the deletion flow (fails open, allowing deletion when git state cannot be determined).

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx (2)

30-58: Clever optimization with split queries.

The approach of using an initial expensive git-status query with staleTime: Number.POSITIVE_INFINITY and a separate cheap polling query for terminal count is a good pattern for balancing responsiveness with performance.

One minor observation: when gitStatusData is undefined (still loading), canDeleteData falls back to terminalCountData, which has hasChanges: false and hasUnpushedCommits: false. This means during the brief window before git status loads, the UI could show no warnings even if there are changes. However, since isLoadingGitStatus gates the UI with "Checking workspace status...", this is handled correctly.


116-125: LGTM!

The warnings are clear, consistent with the existing warning styling, and correctly handle the case where both uncommitted changes and unpushed commits exist simultaneously.

apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (4)

16-20: LGTM!

The new git utility imports are correctly added and follow the existing import structure.


292-329: LGTM!

The skipGitChecks option is a clean optimization for the polling use case. The early return path correctly skips expensive git operations while still returning terminal count, and the response shape remains consistent with hasChanges: false and hasUnpushedCommits: false defaults.


358-372: Good use of parallel execution.

Running both git checks concurrently with Promise.all is efficient. One minor note: if hasUncommittedChanges throws (it lacks error handling unlike hasUnpushedCommits), the error will be caught at line 373 and return canDelete: false with the error message, which is acceptable behavior.


385-394: Consistent response shape across all return paths.

All branches of canDelete now return hasChanges and hasUnpushedCommits, ensuring the frontend always receives a predictable response shape regardless of workspace state.

@AviPeltz AviPeltz merged commit 81b5157 into main Dec 8, 2025
8 checks passed
@Kitenite Kitenite deleted the worried-silverfish-67c386 branch December 9, 2025 00:47
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