Skip to content

feat(desktop): main terminal vs worktrees terminal#437

Merged
AviPeltz merged 7 commits intomainfrom
alleged-camel-6c7cb2
Dec 20, 2025
Merged

feat(desktop): main terminal vs worktrees terminal#437
AviPeltz merged 7 commits intomainfrom
alleged-camel-6c7cb2

Conversation

@AviPeltz
Copy link
Copy Markdown
Collaborator

@AviPeltz AviPeltz commented Dec 19, 2025

Description

Related Issues

Type of Change

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

Testing

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

Release Notes

  • New Features

    • Added branch workspace support, allowing users to create isolated workspaces for different Git branches
    • New branch switching interface with safety checks for uncommitted changes and untracked files
    • Automatic terminal refresh when switching between workspaces
    • Workspaces now automatically created for new projects on the current branch
  • Documentation

    • Added comprehensive design documentation for branch and worktree workspace architecture

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 19, 2025

Caution

Review failed

The head commit changed during the review from 13862e8 to b3eafd7.

Walkthrough

This PR introduces a branch workspace feature alongside existing worktree workspaces. Changes include database schema updates with a new type field and branch property, three new TRPC endpoints for creating and managing branch workspaces with safe Git operations, a new BranchSwitcher UI component, React Query hooks for branch workspace creation, and integration across renderer components with auto-creation of main workspaces per project.

Changes

Cohort / File(s) Summary
Documentation
docs/BRANCH_WORKSPACE_UX.md
New UX design documentation detailing branch and worktree workspace semantics, UI flows, data structures, migration strategy, and backend procedures.
Database Schema & Migration
apps/desktop/src/main/lib/db/schemas.ts
apps/desktop/src/main/lib/db/index.ts
Extended Workspace interface with type: WorkspaceType and branch: string; made worktreeId optional. Added migrateWorkspaces function to populate new fields on startup for backward compatibility.
Git Utilities
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Added six new exports: listBranches, getCurrentBranch, checkBranchCheckoutSafety, checkoutBranch, safeCheckoutBranch, and CheckoutSafetyResult interface for safe branch operations with uncommitted changes detection.
Workspace Path Resolution
apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts
Added getWorkspacePath function that returns mainRepoPath for branch-type workspaces and worktree path for worktree-type workspaces.
Workspace Router Endpoints
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
Added three new endpoints (createBranchWorkspace, getBranches, switchBranchWorkspace); updated existing endpoints to include workspace type and branch fields; refactored deletion logic to conditionally skip worktree teardown for branch workspaces; integrated getWorkspacePath throughout.
Terminal Integration
apps/desktop/src/main/lib/terminal/manager.ts
apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
Added refreshPromptsForWorkspace method to TerminalManager; updated terminal router to use getWorkspacePath instead of getWorktreePath.
React Query Hooks
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
apps/desktop/src/renderer/react-query/workspaces/index.ts
New hook useCreateBranchWorkspace wrapping TRPC mutation with automatic query invalidation and tab creation on success; exported from barrel file.
Branch UI Component
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/*
New BranchSwitcher component with branch filtering, in-use indicators, safe checkout via mutation, and toast notifications.
Workspace Dropdown & Creation
apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
Replaced useCreateWorkspace with useCreateBranchWorkspace across creation flows; added auto-creation of main branch workspace per project in WorkspaceTabs.
Workspace Item & Group
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
Added branch-specific UI rendering and interactions; conditionally disable rename for branch workspaces; updated delete dialog to show non-destructive path for branches; added canRename and showHoverCard props to context menu.
Tabs Store
apps/desktop/src/renderer/stores/tabs/store.ts
Modified addTab to activate existing tab instead of creating duplicate when tab already exists for workspace.

Sequence Diagrams

sequenceDiagram
    actor User
    participant UI as UI Component
    participant TRPC as TRPC Router
    participant Git as Git Utils
    participant DB as Database
    participant Terminal as Terminal Manager
    
    User->>UI: Create Branch Workspace
    UI->>TRPC: createBranchWorkspace({ projectId })
    TRPC->>Git: getCurrentBranch(repoPath)
    Git-->>TRPC: current branch
    Note over TRPC: Resolve target branch<br/>(from input or current)
    TRPC->>Git: checkBranchCheckoutSafety(repoPath)
    Git-->>TRPC: CheckoutSafetyResult
    alt Unsafe
        TRPC-->>UI: Error
        UI-->>User: Toast: Safety check failed
    else Safe
        TRPC->>Git: safeCheckoutBranch(repoPath, branch)
        Git->>Git: Fetch, checkout, verify
        Git-->>TRPC: Success
        TRPC->>DB: Create/update workspace<br/>(type: "branch", branch)
        DB-->>TRPC: Workspace
        TRPC->>Terminal: refreshPromptsForWorkspace(id)
        Terminal-->>TRPC: Ack
        TRPC-->>UI: Workspace + wasExisting
        UI->>UI: Invalidate queries
        alt New workspace
            UI->>UI: Add tab
        end
        UI-->>User: Toast: Success
    end
Loading
sequenceDiagram
    actor User
    participant UI as BranchSwitcher UI
    participant TRPC as TRPC Router
    participant Git as Git Utils
    participant DB as Database
    
    User->>UI: Open branch dropdown
    UI->>TRPC: getBranches({ projectId })
    TRPC->>Git: listBranches(repoPath)
    Git-->>TRPC: { local, remote }
    TRPC->>DB: Query workspace branches
    DB-->>TRPC: Branches in use
    TRPC-->>UI: Branches + inUse mappings
    UI->>UI: Filter, sort, render
    
    User->>UI: Select branch
    UI->>TRPC: switchBranchWorkspace(workspaceId, branch)
    TRPC->>Git: checkBranchCheckoutSafety(repoPath)
    Git-->>TRPC: Result
    alt Unsafe
        TRPC-->>UI: Error
        UI-->>User: Toast: Cannot switch
    else Safe
        TRPC->>Git: safeCheckoutBranch(repoPath, branch)
        Git-->>TRPC: Success
        TRPC->>DB: Update workspace<br/>(branch, name, timestamps)
        DB-->>TRPC: Workspace
        TRPC->>TRPC: Invalidate queries
        TRPC-->>UI: Success
        UI->>UI: Close dropdown
        UI-->>User: Toast: Switched
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts: Three new endpoints with intertwined control flow for branch creation, retrieval, and switching; deletion logic branches on workspace type; multiple workspace property resolvals require careful validation.
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx: Branch-specific rendering paths, conditional disable of rename, interaction handler changes, and integration with BranchSwitcher require thorough testing of both workspace types.
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts: Six new exported functions with safety checks, error handling, and Git operation sequencing; each function has distinct error modes requiring validation.
  • apps/desktop/src/main/lib/db/index.ts: Migration logic must correctly populate type and branch fields for all existing workspaces without data loss.
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx: Auto-creation logic with state management (attemptedProjectsRef, isCreating) and effect dependencies; verify serialization and race condition handling.

Possibly related PRs

Poem

🐰 Hoppy branches, workspaces bloom,
Where checkout's safe, no more the gloom!
Tab-switching swift, with safety's care,
Git branches dance through workspace air!

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'feat(desktop): main terminal vs worktrees terminal' is vague and does not clearly convey the main purpose of the changes. The PR actually implements branch workspace functionality, workspace type system, branch switching, and terminal management, not a comparison between main and worktree terminals. Revise the title to accurately reflect the primary feature, such as 'feat(desktop): add branch workspace type support' or 'feat(desktop): implement branch workspaces with branch switching'.
Description check ⚠️ Warning The PR description contains only the template structure with no actual implementation details, related issues, testing information, or context about the changes. Complete the description by explaining the branch workspace feature, linking related issues, detailing testing performed, and providing context about why these changes were made.
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.

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.

@AviPeltz AviPeltz marked this pull request as ready for review December 20, 2025 00:34
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (1)

34-49: Inconsistent workspace creation path for existing projects.

The primary action for existing projects still uses createWorkspace (line 38), while the new project flow uses createBranchWorkspace (line 72). This creates an inconsistency where:

  • Quick action on existing project → creates worktree workspace
  • Opening new project → creates branch workspace

Based on the PR's branch workspace architecture, consider whether the primary action should also use createBranchWorkspace for consistency, or if this distinction is intentional.

apps/desktop/src/lib/trpc/routers/terminal/terminal.ts (1)

166-178: Inconsistent path resolution in getWorkspaceCwd.

While the createOrAttach procedure was updated to use getWorkspacePath (line 52-54), the getWorkspaceCwd procedure (lines 166-178) still uses worktree-specific logic and only returns worktree?.path. This means it won't work correctly for branch workspaces.

Consider updating to use getWorkspacePath for consistency:

🔎 Proposed fix
 		getWorkspaceCwd: publicProcedure
 			.input(z.string())
 			.query(async ({ input: workspaceId }) => {
 				const workspace = db.data.workspaces.find((w) => w.id === workspaceId);
 				if (!workspace) {
 					return undefined;
 				}
 
-				const worktree = db.data.worktrees.find(
-					(wt) => wt.id === workspace.worktreeId,
-				);
-				return worktree?.path;
+				return getWorkspacePath(workspace) ?? undefined;
 			}),
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (1)

230-289: Missing needsAttention indicator for branch workspaces.

The needsAttention badge (lines 282-287) is only rendered for worktree workspaces. Branch workspaces can also have terminal panes that need attention, but this indicator won't appear for them.

🔎 Proposed fix to include needsAttention for branch workspaces
 						) : isBranchWorkspace ? (
 							<div className="flex items-center gap-2 flex-1 min-w-0">
 								<Tooltip delayDuration={600}>
 									<TooltipTrigger asChild>
 										<div className="flex items-center gap-2 flex-1 min-w-0">
 											<div className="flex items-center justify-center size-5 rounded bg-primary/10 shrink-0">
 												<LuGitBranch className="size-3 text-primary" />
 											</div>
 											<span
 												className="text-sm font-medium whitespace-nowrap overflow-hidden flex-1 text-left"
 												style={{
 													maskImage:
 														"linear-gradient(to right, black calc(100% - 16px), transparent 100%)",
 													WebkitMaskImage:
 														"linear-gradient(to right, black calc(100% - 16px), transparent 100%)",
 												}}
 											>
 												{title}
 											</span>
+											{needsAttention && (
+												<span className="relative flex size-2 shrink-0">
+													<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75" />
+													<span className="relative inline-flex size-2 rounded-full bg-red-500" />
+												</span>
+											)}
 										</div>
 									</TooltipTrigger>
🧹 Nitpick comments (3)
apps/desktop/src/main/lib/db/index.ts (1)

41-41: Consider using structured logging.

Using console.log directly is acceptable for migration messages, but consider using a structured logger if one is available in the project for consistency with other logging patterns.

apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx (1)

141-142: Inconsistent user feedback for branch workspace creation.

This creates a branch workspace without toast feedback, while WorkspaceDropdown.tsx (lines 71-79) wraps the same operation in toast.promise() with loading/success/error states. This leads to inconsistent UX where the same operation provides feedback in one place but not another.

Consider adding toast feedback here for consistency:

🔎 Proposed fix for consistent feedback
-			// Create a main workspace on the current branch for the new project
-			await createBranchWorkspace.mutateAsync({ projectId: result.project.id });
+			// Create a main workspace on the current branch for the new project
+			await toast.promise(
+				createBranchWorkspace.mutateAsync({ projectId: result.project.id }),
+				{
+					loading: "Opening project...",
+					success: "Project opened",
+					error: (err) =>
+						err instanceof Error ? err.message : "Failed to open project",
+				},
+			);
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1)

470-479: Consider relaxing the untracked files check.

Git's default checkout only blocks if untracked files would be overwritten by files in the target branch. This check blocks on any untracked files, which could prevent legitimate branch switches when users have build artifacts, IDE files, or other harmless untracked content.

Consider using git checkout --dry-run or checking if the untracked files conflict with the target branch rather than blocking unconditionally.

🔎 Alternative approach using dry-run
-		// Block on untracked files as they could be overwritten
-		if (hasUntrackedFiles) {
-			return {
-				safe: false,
-				error:
-					"Cannot switch branches: you have untracked files that may be overwritten. Please commit, stash, or remove them first.",
-				hasUncommittedChanges: false,
-				hasUntrackedFiles: true,
-			};
-		}
+		// Note: We only check uncommitted changes here.
+		// Git checkout will fail naturally if untracked files conflict with the target branch.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c8fa8b and b587c6c.

📒 Files selected for processing (21)
  • apps/desktop/docs/BRANCH_WORKSPACE_UX.md (1 hunks)
  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts (3 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts (1 hunks)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (10 hunks)
  • apps/desktop/src/main/lib/db/index.ts (1 hunks)
  • apps/desktop/src/main/lib/db/schemas.ts (1 hunks)
  • apps/desktop/src/main/lib/terminal/manager.ts (1 hunks)
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx (3 hunks)
  • apps/desktop/src/renderer/react-query/workspaces/index.ts (1 hunks)
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (5 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx (2 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (4 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx (2 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (10 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (3 hunks)
  • apps/desktop/src/renderer/stores/tabs/store.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
apps/desktop/**/*.{ts,tsx,js,jsx}

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

apps/desktop/**/*.{ts,tsx,js,jsx}: For Electron interprocess communication, ALWAYS use trpc as defined in src/lib/trpc
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/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/main/lib/db/index.ts
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/terminal/manager.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid using any type in TypeScript - maintain type safety unless absolutely necessary

Files:

  • apps/desktop/src/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/main/lib/db/index.ts
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/terminal/manager.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome for formatting, linting, import organization, and safe fixes at the root level using bun run lint:fix

Files:

  • apps/desktop/src/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/main/lib/db/index.ts
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/terminal/manager.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in renderer process or shared code - use only in main process (src/main/)

Files:

  • apps/desktop/src/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
apps/desktop/src/{main,renderer,preload}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use type-safe IPC communication - define channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Files:

  • apps/desktop/src/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/main/lib/db/index.ts
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/terminal/manager.ts
  • apps/desktop/src/main/lib/db/schemas.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/{components,features}/**/[!.]*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Organize project structure with one folder per component: ComponentName/ComponentName.tsx with index.ts barrel export

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/{components,features}/**/*.{ts,tsx,test.ts,test.tsx,stories.tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use React + TailwindCSS v4 + shadcn/ui for UI development

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
**/{components,features}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/{components,features}/**/*.tsx: Nest components in parent's components/ folder if used only once, promote to highest shared parent's components/ if used 2+ times
Use one component per file - do not combine multiple components in a single file

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/DeleteWorkspaceDialog.tsx
  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/StartView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
apps/desktop/src/main/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Accept object parameters in IPC handlers - do not use positional parameters in ipcMain.handle()

Files:

  • apps/desktop/src/main/lib/db/index.ts
  • apps/desktop/src/main/lib/terminal/manager.ts
  • apps/desktop/src/main/lib/db/schemas.ts
🧠 Learnings (6)
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to packages/db/src/**/*.{ts,tsx} : Store database schema in packages/db/src/ and use Drizzle ORM for schema management

Applied to files:

  • apps/desktop/src/main/lib/db/index.ts
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to **/*.{tsx,css} : Use React + TailwindCSS v4 + shadcn/ui for UI development

Applied to files:

  • apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to apps/desktop/src/{main,renderer,preload}/**/*.{ts,tsx} : Use type-safe IPC communication - define channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceGroup.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to **/{components,features}/**/[!.]*.tsx : Organize project structure with one folder per component: ComponentName/ComponentName.tsx with index.ts barrel export

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
📚 Learning: 2025-12-18T17:26:38.664Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-18T17:26:38.664Z
Learning: Applies to apps/desktop/**/*.{ts,tsx,js,jsx} : For Electron interprocess communication, ALWAYS use trpc as defined in `src/lib/trpc`

Applied to files:

  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules in renderer process or shared code - use only in main process (src/main/)

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts
🧬 Code graph analysis (9)
apps/desktop/src/main/lib/db/index.ts (2)
apps/desktop/src/main/lib/db/schemas.ts (3)
  • Database (119-124)
  • Workspace (55-66)
  • defaultDatabase (126-131)
apps/desktop/src/main/lib/app-environment.ts (1)
  • DB_PATH (8-8)
apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx (1)
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)
  • useCreateBranchWorkspace (9-32)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItemContextMenu.tsx (2)
packages/ui/src/components/ui/context-menu.tsx (5)
  • ContextMenu (237-237)
  • ContextMenuTrigger (238-238)
  • ContextMenuContent (239-239)
  • ContextMenuItem (240-240)
  • ContextMenuSeparator (244-244)
packages/ui/src/components/ui/hover-card.tsx (2)
  • HoverCard (44-44)
  • HoverCardTrigger (44-44)
apps/desktop/src/lib/trpc/routers/terminal/terminal.ts (2)
apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts (1)
  • getWorkspacePath (17-32)
apps/desktop/src/lib/trpc/routers/terminal/utils/resolve-cwd.ts (1)
  • resolveCwd (14-54)
apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (2)
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)
  • useCreateBranchWorkspace (9-32)
apps/desktop/src/renderer/react-query/workspaces/index.ts (1)
  • useCreateBranchWorkspace (2-2)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (2)
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)
  • useCreateBranchWorkspace (9-32)
apps/desktop/src/renderer/react-query/workspaces/index.ts (1)
  • useCreateBranchWorkspace (2-2)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (2)
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)
  • useCreateBranchWorkspace (9-32)
apps/desktop/src/renderer/react-query/workspaces/index.ts (1)
  • useCreateBranchWorkspace (2-2)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (3)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (5)
  • getCurrentBranch (401-413)
  • safeCheckoutBranch (540-566)
  • listBranches (368-394)
  • worktreeExists (221-236)
  • removeWorktree (190-209)
apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts (1)
  • getWorkspacePath (17-32)
apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts (1)
  • runTeardown (37-75)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx (2)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx (1)
  • BranchSwitcher (22-190)
🔇 Additional comments (39)
apps/desktop/src/main/lib/terminal/manager.ts (2)

363-381: Method is properly integrated with TRPC and behavior is intentional.

The refreshPromptsForWorkspace method is correctly called within the checkoutBranch mutation in the workspaces TRPC router, complying with the coding guideline to use TRPC for Electron interprocess communication. The newline write is intentional—it refreshes terminal prompts after a branch checkout to display the updated branch name. With proper error handling via try/catch, this implementation is sound.


363-381: This method is used internally within the main process and is not called from the renderer, so TRPC exposure is not required. The implementation is correct—it's properly called from a TRPC procedure (workspace branch checkout) as an internal utility to refresh terminal prompts after branch changes.

apps/desktop/src/renderer/react-query/workspaces/index.ts (1)

2-2: LGTM!

The barrel export follows the established pattern and properly exposes the new hook.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/index.ts (1)

1-1: LGTM!

The barrel export follows the coding guidelines for component structure (ComponentName/ComponentName.tsx with index.ts barrel export).

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

10-11: LGTM!

The Workspace interface correctly extends to include the new type and branch fields, aligning with the schema updates for branch workspace support.


78-79: LGTM!

The props are correctly passed to WorkspaceItem, enabling branch-aware rendering in the UI.

apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)

1-32: LGTM!

The hook implementation is well-structured:

  • Properly invalidates workspace queries on success
  • Conditionally creates tabs only for newly created workspaces (not existing ones)
  • Correctly chains user-provided onSuccess callbacks
  • Uses async/await for proper error handling

The logic integrates well with the idempotent addTab behavior in the tabs store.

apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts (1)

12-32: LGTM!

The getWorkspacePath function correctly handles both workspace types:

  • Branch workspaces resolve to the main repository path
  • Worktree workspaces resolve to the worktree path
  • Proper null handling for missing projects or worktrees

This provides a unified interface for path resolution across workspace types.

apps/desktop/src/main/lib/db/schemas.ts (1)

53-66: LGTM!

The schema updates properly model the dual nature of workspaces (worktree vs. branch):

  • WorkspaceType clearly defines the two variants
  • branch field is present for both types
  • worktreeId is appropriately optional (only needed for worktree type)
  • Clear comment documents when worktreeId is set

The breaking change (optional worktreeId) is handled by the migration logic in apps/desktop/src/main/lib/db/index.ts.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (3)

9-12: LGTM! Clean import and hook initialization.

The import of useCreateBranchWorkspace and hook initialization follows React best practices and aligns with the branch workspace architecture introduced in this PR.

Also applies to: 26-26


70-79: LGTM! Branch workspace creation for new projects.

The new project flow correctly uses createBranchWorkspace to create a main workspace on the current branch, with appropriate loading states and error handling.


106-110: LGTM! Proper disabled state handling.

The disabled state correctly includes createBranchWorkspace.isPending alongside the other pending states, preventing race conditions during workspace creation.

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

18-33: LGTM! Clean workspace type discrimination.

The addition of workspaceType prop with a sensible default and the isBranch flag provides clear type discrimination for different workspace behaviors.


117-157: LGTM! Branch workspace close dialog follows UX design.

The simplified dialog for branch workspaces correctly implements the "Close" (non-destructive) pattern as documented in BRANCH_WORKSPACE_UX.md. The messaging clearly communicates that the branch and commits remain in the repository.

apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModal.tsx (1)

22-25: LGTM! Proper hook integration.

The import and initialization of useCreateBranchWorkspace follows established patterns in the codebase.

Also applies to: 61-61

apps/desktop/docs/BRANCH_WORKSPACE_UX.md (1)

1-174: LGTM! Comprehensive design documentation.

This document provides excellent coverage of the branch workspace architecture, including:

  • Clear rationale for the one-branch-workspace-per-project rule
  • Terminology guidance (Close vs Delete)
  • Schema and API changes
  • Migration considerations

The documentation will be valuable for future developers working with this feature.

apps/desktop/src/lib/trpc/routers/terminal/terminal.ts (1)

8-8: LGTM! Proper workspace path abstraction.

The migration from getWorktreePath to getWorkspacePath correctly abstracts away the workspace type, allowing terminals to work with both branch and worktree workspaces through a unified interface.

Also applies to: 50-55

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

23-24: LGTM! Flexible context menu for different workspace types.

The addition of canRename and showHoverCard props with sensible defaults provides appropriate flexibility for branch vs worktree workspaces, while maintaining backward compatibility.

Also applies to: 33-34, 44-62


71-76: LGTM! Consistent rename gating.

The conditional rendering of the Rename menu item based on canRename is consistent across both rendering paths.

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (1)

4-7: LGTM! Proper hook integration.

The imports and hook initialization follow established patterns in the codebase.

Also applies to: 26-26

apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/BranchSwitcher/BranchSwitcher.tsx (5)

16-26: LGTM! Clean component interface.

The props interface is well-defined and the component follows React best practices.


30-43: LGTM! Efficient data fetching pattern.

The query is correctly gated by enabled: isOpen to avoid unnecessary fetches, and workspace invalidation on success ensures UI consistency.


45-74: LGTM! Well-structured branch prioritization.

The sorting logic correctly implements the priority scheme from the UX documentation:

  1. Default branches (main/master/develop)
  2. Branches in use by worktrees
  3. Alphabetical ordering

Proper use of memoization prevents unnecessary re-computation.


76-96: LGTM! Clean search and switch logic.

The search filtering is case-insensitive and properly memoized. The switch handler includes a short-circuit for the current branch and provides clear user feedback via toast notifications.


98-189: LGTM! Comprehensive branch switcher UI.

The rendering logic provides:

  • Proper loading and empty states
  • Clear visual differentiation (in-use vs regular branches)
  • Contextual labels ("worktree", "default")
  • Current branch indication with checkmark
  • Pagination with 50-item limit and overflow message
  • Proper event handling with stopPropagation

The implementation aligns well with the UX documentation.

apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (1)

8-8: LGTM! Clean migration to branch workspace paradigm.

The refactor from useCreateWorkspace to useCreateBranchWorkspace is consistent throughout the component. The updated error message "Failed to open workspace" is more accurate since createBranchWorkspace may activate an existing workspace rather than always creating a new one.

Also applies to: 18-18, 50-51, 61-66, 77-77

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

361-394: LGTM! Well-structured branch listing utility.

Clean implementation with proper error handling for offline scenarios. The remote branch normalization (stripping origin/ and filtering out symbolic refs like origin/HEAD -> origin/main) is correct.


396-413: LGTM! Correctly handles detached HEAD state.

Returning null for detached HEAD ("HEAD" literal) is the right behavior for callers that need to know if they're on a named branch.


501-531: LGTM! Good handling of remote-only branches.

The function correctly creates a local tracking branch when checking out a branch that only exists on remote. The fallthrough to let git provide its natural error message for non-existent branches is a clean approach.


533-566: LGTM! Solid implementation with post-checkout verification.

The early return optimization when already on the target branch, combined with the post-checkout verification, makes this a robust utility. The verification step is good defensive programming to catch edge cases where checkout might silently fail or land on a different branch.

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

76-96: LGTM! Appropriate non-destructive delete flow for branch workspaces.

The logic correctly differentiates branch workspaces from worktrees: branch workspaces only show a confirmation dialog when there are active terminals (since closing is non-destructive), while worktree deletion continues to check for uncommitted changes, unpushed commits, etc.


306-319: LGTM! Appropriate UX copy for close vs delete semantics.

The aria-labels and tooltip text correctly distinguish "Close workspace" (non-destructive for branches) from "Delete workspace" (destructive for worktrees).

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

116-117: LGTM! Existing create mutation extended with type and branch fields.

The addition of type: "worktree" and branch fields ensures backward compatibility while supporting the new workspace type system.


265-296: LGTM! Clean branch listing query with worktree usage tracking.

The inUse array correctly identifies branches that are currently checked out in worktrees, which is valuable for the UI to warn users or disable selection.


298-348: LGTM! Well-implemented branch switching with terminal refresh.

The flow correctly:

  1. Uses safeCheckoutBranch for safety checks before switching
  2. Refreshes terminal prompts so they display the new branch
  3. Updates workspace metadata atomically

378-416: LGTM! Unified path resolution via getWorkspacePath.

The switch to getWorkspacePath(workspace) correctly abstracts the path resolution for both workspace types, returning the main repo path for branch workspaces and the worktree path for worktree workspaces.


516-527: LGTM! Appropriate short-circuit for branch workspaces.

Branch workspaces don't require git status checks since closing them is non-destructive (no worktree removal, no branch deletion). The early return with canDelete: true is correct.


629-671: LGTM! Correct conditional worktree removal.

The guard workspace.type === "worktree" && workspace.worktreeId correctly skips worktree teardown/removal for branch workspaces while still performing terminal cleanup and database removal for both workspace types.


957-968: LGTM! Consistent workspace creation in openWorktree.

Adding type: "worktree" and branch fields ensures consistency with the updated workspace schema across all creation paths.

Comment thread apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts Outdated
Comment thread apps/desktop/src/main/lib/db/index.ts
Comment thread apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx Outdated
Comment thread apps/desktop/src/renderer/stores/tabs/store.ts
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: 1

♻️ Duplicate comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (1)

449-449: Same empty string fallback concern as line 414.

Similar to line 414, this empty string fallback could hide path resolution failures. Consider handling null explicitly or filtering out cases where the path cannot be determined.

🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (1)

46-77: Consider refactoring to zustand to align with project guidelines.

The effect logic is correct and the dependency array properly uses createBranchWorkspace.mutate (addressing the past review concern). However, per the coding guidelines and learnings, effects should be avoided unless absolutely necessary, with zustand preferred for state management.

This background auto-creation logic could be refactored into a zustand store action that:

  • Tracks attempted projects in the store
  • Executes after groups data loads
  • Maintains the serialization logic

This would eliminate the effect and centralize the state management.

Based on learnings: The project prefers zustand for state management and avoiding effects when possible.

The current implementation works correctly, but if you'd like to align more closely with the project's architecture preferences, I can help design a zustand-based approach. Would you like me to propose a refactor?

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b587c6c and 277f179.

📒 Files selected for processing (4)
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (10 hunks)
  • apps/desktop/src/main/lib/db/index.ts (1 hunks)
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (3 hunks)
  • apps/desktop/src/renderer/stores/tabs/store.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
apps/desktop/**/*.{ts,tsx,js,jsx}

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

apps/desktop/**/*.{ts,tsx,js,jsx}: For Electron interprocess communication, ALWAYS use trpc as defined in src/lib/trpc
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/renderer/stores/tabs/store.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/db/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid using any type in TypeScript - maintain type safety unless absolutely necessary

Files:

  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/db/index.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome for formatting, linting, import organization, and safe fixes at the root level using bun run lint:fix

Files:

  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/db/index.ts
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules in renderer process or shared code - use only in main process (src/main/)

Files:

  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
apps/desktop/src/{main,renderer,preload}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use type-safe IPC communication - define channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Files:

  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
  • apps/desktop/src/main/lib/db/index.ts
**/{components,features}/**/[!.]*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Organize project structure with one folder per component: ComponentName/ComponentName.tsx with index.ts barrel export

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
**/{components,features}/**/*.{ts,tsx,test.ts,test.tsx,stories.tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use React + TailwindCSS v4 + shadcn/ui for UI development

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
**/{components,features}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

**/{components,features}/**/*.tsx: Nest components in parent's components/ folder if used only once, promote to highest shared parent's components/ if used 2+ times
Use one component per file - do not combine multiple components in a single file

Files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
apps/desktop/src/main/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Accept object parameters in IPC handlers - do not use positional parameters in ipcMain.handle()

Files:

  • apps/desktop/src/main/lib/db/index.ts
🧠 Learnings (2)
📚 Learning: 2025-12-18T17:26:38.664Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-18T17:26:38.664Z
Learning: Applies to apps/desktop/**/*.{ts,tsx,js,jsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx
📚 Learning: 2025-12-18T23:19:10.405Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.405Z
Learning: Applies to packages/db/src/**/*.{ts,tsx} : Store database schema in packages/db/src/ and use Drizzle ORM for schema management

Applied to files:

  • apps/desktop/src/main/lib/db/index.ts
🧬 Code graph analysis (3)
apps/desktop/src/renderer/stores/tabs/store.ts (2)
apps/desktop/src/renderer/stores/tabs/utils.ts (1)
  • getFirstPaneId (204-209)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
  • set (162-172)
apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (3)
apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts (5)
  • getCurrentBranch (401-413)
  • safeCheckoutBranch (540-566)
  • listBranches (368-394)
  • worktreeExists (221-236)
  • removeWorktree (190-209)
apps/desktop/src/lib/trpc/routers/workspaces/utils/worktree.ts (1)
  • getWorkspacePath (17-32)
apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts (1)
  • runTeardown (37-75)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (2)
apps/desktop/src/renderer/react-query/workspaces/useCreateBranchWorkspace.ts (1)
  • useCreateBranchWorkspace (9-32)
apps/desktop/src/renderer/react-query/workspaces/index.ts (1)
  • useCreateBranchWorkspace (2-2)
⏰ 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 (11)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/index.tsx (3)

4-7: LGTM! Clean import structure.

The import addition follows the project's alias patterns and maintains type safety.


26-26: LGTM! Proper hook initialization.

The mutation hook is correctly initialized and follows the established patterns in the codebase.


39-42: LGTM! Appropriate use of ref and state.

Using a ref for attemptedProjectsRef avoids unnecessary re-renders while isCreating state correctly triggers effect re-evaluation for serialization.

apps/desktop/src/renderer/stores/tabs/store.ts (1)

85-107: Previous concern addressed - idempotency logic now ensures valid paneId.

The addition of the idempotency check correctly handles the case where a tab already exists for the workspace. The use of getFirstPaneId(existingTab.layout) as a fallback (lines 91-93) ensures that a valid paneId is always returned, resolving the concern raised in the previous review about potentially undefined paneId values. Additionally, the code now explicitly sets focusedPaneIds for the existing tab, ensuring consistent state.

apps/desktop/src/main/lib/db/index.ts (2)

10-56: Previous concern addressed - migration now handles all edge cases.

The migration logic correctly addresses the previous review concern by ensuring that ws.branch is always set to a valid string value. When the associated worktree is not found (lines 35-40) or when there's no worktreeId (lines 43-46), the code now falls back to "unknown" and logs appropriate warnings. This ensures the schema requirement that branch is a required string field is always satisfied.


65-66: LGTM - migration properly integrated.

The migration is correctly hooked into the initialization flow, ensuring existing data is migrated when the database is first loaded.

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

159-262: Previous race condition concern addressed with atomic check.

The existence check for branch workspaces is now performed atomically inside the db.update callback (lines 215-227), which addresses the race condition concern from the previous review. If an existing branch workspace is found, it's activated without creating a duplicate; otherwise, a new one is created. The wasExisting flag in the response (line 260) provides useful information to callers about whether this was a new creation or an activation.


264-295: LGTM - getBranches provides useful branch metadata.

The getBranches procedure correctly lists local and remote branches with an inUse set indicating which branches are currently associated with worktrees. The optional fetch parameter allows callers to control whether to refresh remote refs, avoiding UI stalls during normal operation.


516-526: LGTM - correct handling for non-destructive branch workspaces.

Branch workspaces are correctly identified as non-destructive and bypass the expensive git checks. Since branch workspaces operate on the main repository path and don't involve worktree removal, the early return with canDelete: true is appropriate.


628-670: LGTM - branch workspaces correctly skip worktree operations.

The deletion logic properly distinguishes between branch and worktree workspace types. Branch workspaces skip the worktree-specific teardown and removal operations (lines 629-670), which is correct since they don't have associated worktrees to clean up. The guard on line 629 ensures that only worktree-type workspaces with a worktreeId proceed with worktree removal.


298-347: No action needed. The terminalManager.refreshPromptsForWorkspace method exists in apps/desktop/src/main/lib/terminal/manager.ts (line 367) and is properly imported in the workspaces file. The implementation correctly iterates through active terminal sessions for the given workspace and sends a newline character to refresh their prompts. The method call on line 322 is correct.

groupsMap.get(workspace.projectId)?.workspaces.push({
...workspace,
worktreePath: getWorktreePath(workspace.worktreeId) ?? "",
worktreePath: getWorkspacePath(workspace) ?? "",
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

Empty string fallback could hide path resolution failures.

When getWorkspacePath(workspace) returns null, this code defaults to an empty string. According to the relevant code snippets, getWorkspacePath returns null when the path "is not determinable." Using an empty string as a fallback could hide these failure cases and cause issues for downstream consumers expecting a valid filesystem path.

Consider one of the following approaches:

Option 1: Filter out workspaces with undeterminable paths
 for (const workspace of workspaces) {
   if (groupsMap.has(workspace.projectId)) {
+    const worktreePath = getWorkspacePath(workspace);
+    if (!worktreePath) {
+      console.warn(`Could not determine path for workspace ${workspace.id}`);
+      continue;
+    }
     groupsMap.get(workspace.projectId)?.workspaces.push({
       ...workspace,
-      worktreePath: getWorkspacePath(workspace) ?? "",
+      worktreePath,
     });
   }
 }
Option 2: Make worktreePath optional in the response type
 worktreePath: string;
+worktreePath: string | null;
 // ... in response type
 
 // Then in the mapping:
-worktreePath: getWorkspacePath(workspace) ?? "",
+worktreePath: getWorkspacePath(workspace),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts around line 414,
the code uses getWorkspacePath(workspace) ?? "" which masks path resolution
failures by supplying an empty string; either (A) filter out workspaces where
getWorkspacePath(workspace) returns null before mapping so only entries with
determinable paths are returned, or (B) make worktreePath optional in the
response DTO/Type and pass undefined/null when getWorkspacePath returns null;
implement one approach and update the response type and all callers/tests
accordingly to handle the new behavior (remove the empty-string fallback and
ensure downstream consumers correctly treat missing paths).

@AviPeltz AviPeltz merged commit b2ecbc3 into main Dec 20, 2025
5 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

Service Status
Neon Database (Neon)

Thank you for your contribution! 🎉


Preview resources have been processed for cleanup

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