diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/NewLayoutMain.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/NewLayoutMain.tsx
index 019ea6f6862..0a4894a038d 100644
--- a/apps/desktop/src/renderer/screens/main/components/NewLayout/NewLayoutMain.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/NewLayoutMain.tsx
@@ -7,6 +7,7 @@ import type React from "react";
import { useEffect, useRef, useState } from "react";
import type { ImperativePanelHandle } from "react-resizable-panels";
import type { Tab, Workspace, Worktree } from "shared/types";
+import { WorktreeProvider } from "../../../../contexts/WorktreeContext";
import { AppFrame } from "../AppFrame";
import { Background } from "../Background";
import TabContent from "../MainContent/TabContent";
@@ -16,10 +17,9 @@ import { PlanView } from "../PlanView";
import { Sidebar } from "../Sidebar";
import { DiffTab } from "../TabContent/components/DiffTab";
import { AddTaskModal } from "./AddTaskModal";
-import { TaskTabs, type WorktreeWithTask } from "./TaskTabs";
import type { TaskStatus } from "./StatusIndicator";
+import { TaskTabs, type WorktreeWithTask } from "./TaskTabs";
import { WorktreeTabView } from "./WorktreeTabView";
-import { WorktreeProvider } from "../../../../contexts/WorktreeContext";
// Type alias for task data used in UI
type UITask = {
@@ -241,12 +241,12 @@ function enrichWorktreesWithTasks(
isPending: true, // Mark as pending for UI
task: pending.taskData
? {
- id: pending.id,
- slug: pending.taskData.slug,
- title: pending.taskData.name,
- status: pending.taskData.status,
- description: pending.description || "",
- }
+ id: pending.id,
+ slug: pending.taskData.slug,
+ title: pending.taskData.name,
+ status: pending.taskData.status,
+ description: pending.description || "",
+ }
: undefined,
}),
);
@@ -745,9 +745,7 @@ export const NewLayoutMain: React.FC = () => {
} catch (error) {
console.error("Failed to create task/worktree:", error);
// Remove pending on error
- setPendingWorktrees((prev) =>
- prev.filter((wt) => wt.id !== pendingId),
- );
+ setPendingWorktrees((prev) => prev.filter((wt) => wt.id !== pendingId));
}
})();
};
@@ -976,7 +974,7 @@ export const NewLayoutMain: React.FC = () => {
/>
{/* Main content area - conditionally render based on mode */}
-
+
{mode === "plan" ? (
// Plan mode - show kanban board
{
{/* Main content panel */}
{loading ||
- error ||
- !currentWorkspace ||
- !selectedTab ||
- !selectedWorktree ? (
+ error ||
+ !currentWorkspace ||
+ !selectedTab ||
+ !selectedWorktree ? (
void;
- onExpandSidebar: () => void;
- isSidebarOpen: boolean;
- onAddTask: () => void;
- onCreatePR?: () => void;
- onMergePR?: () => void;
- worktrees: WorktreeWithTask[];
- selectedWorktreeId: string | null;
- onWorktreeSelect: (worktreeId: string) => void;
- mode?: "plan" | "edit";
- onModeChange?: (mode: "plan" | "edit") => void;
-}
-
-export const TaskTabs: React.FC = ({
- onCollapseSidebar,
- onExpandSidebar,
- isSidebarOpen,
- onAddTask,
- onCreatePR,
- onMergePR,
- worktrees,
- selectedWorktreeId,
- onWorktreeSelect,
- mode = "edit",
- onModeChange,
-}) => {
- const selectedWorktree = worktrees.find(wt => wt.id === selectedWorktreeId);
- const canCreatePR = selectedWorktree && !selectedWorktree.isPending;
- const hasPR = selectedWorktree && selectedWorktree.prUrl;
- return (
-
-
- {/* Sidebar collapse/expand toggle */}
-
- {isSidebarOpen ? (
-
-
-
-
-
- Collapse sidebar
-
-
- ) : (
-
-
-
-
-
- Expand sidebar
-
-
- )}
-
-
- {/* Plan/Edit mode toggle */}
- {onModeChange && (
-
-
-
-
-
-
- )}
-
- {/* Worktree tabs - each tab represents a worktree */}
- {worktrees.map((worktree) => {
- const hasTask = !!worktree.task;
- const task = worktree.task;
- const isPending = worktree.isPending;
- const displayTitle = hasTask && task
- ? task.slug
- : worktree.description || worktree.branch;
-
- const statusLabel = task
- ? task.status === "planning"
- ? "Planning"
- : task.status === "working"
- ? "Working"
- : task.status === "needs-feedback"
- ? "Needs Feedback"
- : "Ready to Merge"
- : "";
-
- return (
-
-
-
-
-
- {isPending ? (
-
- {/* Pending state */}
-
-
-
- Creating worktree...
-
-
-
- Setting up git worktree and initializing workspace
-
-
- ) : hasTask && task ? (
-
- {/* Task view */}
-
-
-
- [{task.slug}] {task.title}
-
-
- {task.description}
-
-
-
- {task.assignee && (
-
-
-
- )}
-
-
-
-
-
Status
-
-
- {statusLabel}
-
-
-
- {task.lastUpdated && (
-
- Updated
-
- {task.lastUpdated}
-
-
- )}
-
-
- Branch
-
- {worktree.branch}
-
-
-
-
- Tabs
-
- {worktree.tabs?.length || 0} open
-
-
-
-
- ) : (
-
- {/* Worktree-only view */}
-
-
- Worktree
-
-
- {displayTitle}
-
-
-
-
- Branch:
-
- {worktree.branch}
-
-
-
- Tabs:
-
- {worktree.tabs?.length || 0} open
-
-
-
-
- )}
-
-
- );
- })}
-
- {/* Add task/worktree button */}
-
-
-
-
-
- New task
-
-
-
-
- {/* Right side actions */}
-
- {hasPR && onMergePR ? (
-
-
-
-
-
- Merge pull request for {selectedWorktree?.branch}
-
-
- ) : onCreatePR ? (
-
-
-
-
-
-
- {canCreatePR
- ? `Create pull request for ${selectedWorktree?.branch}`
- : "Select a worktree to create a PR"}
-
-
-
- ) : null}
-
-
- );
-};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/AddTaskButton.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/AddTaskButton.tsx
new file mode 100644
index 00000000000..59adbbd2da7
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/AddTaskButton.tsx
@@ -0,0 +1,21 @@
+import { Button } from "@superset/ui/button";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { Plus } from "lucide-react";
+import type React from "react";
+
+interface AddTaskButtonProps {
+ onClick: () => void;
+}
+
+export const AddTaskButton: React.FC = ({ onClick }) => (
+
+
+
+
+
+ New task
+
+
+);
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/BasicWorktreeContent.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/BasicWorktreeContent.tsx
new file mode 100644
index 00000000000..59b8818223f
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/BasicWorktreeContent.tsx
@@ -0,0 +1,31 @@
+import type React from "react";
+import type { WorktreeWithTask } from "./types";
+
+interface BasicWorktreeContentProps {
+ worktree: WorktreeWithTask;
+}
+
+export const BasicWorktreeContent: React.FC = ({
+ worktree,
+}) => {
+ const displayTitle = worktree.description || worktree.branch;
+
+ return (
+
+
+ Worktree
+
{displayTitle}
+
+
+
+ Branch:
+ {worktree.branch}
+
+
+ Tabs:
+ {worktree.tabs?.length || 0} open
+
+
+
+ );
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/ModeToggle.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/ModeToggle.tsx
new file mode 100644
index 00000000000..11d7081f8e8
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/ModeToggle.tsx
@@ -0,0 +1,35 @@
+import type React from "react";
+
+interface ModeToggleProps {
+ mode: "plan" | "edit";
+ onChange: (mode: "plan" | "edit") => void;
+}
+
+export const ModeToggle: React.FC = ({ mode, onChange }) => (
+
+
+
+
+
+
+);
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PRActions.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PRActions.tsx
new file mode 100644
index 00000000000..24a5524101d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PRActions.tsx
@@ -0,0 +1,69 @@
+import { Button } from "@superset/ui/button";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { GitMerge, GitPullRequest } from "lucide-react";
+import type React from "react";
+
+interface PRActionsProps {
+ hasPR: boolean;
+ canCreatePR: boolean;
+ selectedBranch?: string;
+ onCreatePR?: () => void;
+ onMergePR?: () => void;
+}
+
+export const PRActions: React.FC = ({
+ hasPR,
+ canCreatePR,
+ selectedBranch,
+ onCreatePR,
+ onMergePR,
+}) => {
+ if (hasPR && onMergePR) {
+ return (
+
+
+
+
+
+ Merge pull request for {selectedBranch}
+
+
+ );
+ }
+
+ if (onCreatePR) {
+ return (
+
+
+
+
+
+
+ {canCreatePR
+ ? `Create pull request for ${selectedBranch}`
+ : "Select a worktree to create a PR"}
+
+
+
+ );
+ }
+
+ return null;
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PendingWorktreeContent.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PendingWorktreeContent.tsx
new file mode 100644
index 00000000000..ad67057c68c
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/PendingWorktreeContent.tsx
@@ -0,0 +1,14 @@
+import { Loader2 } from "lucide-react";
+import type React from "react";
+
+export const PendingWorktreeContent: React.FC = () => (
+
+
+
+
Creating worktree...
+
+
+ Setting up git worktree and initializing workspace
+
+
+);
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/SidebarToggle.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/SidebarToggle.tsx
new file mode 100644
index 00000000000..ea16de6216e
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/SidebarToggle.tsx
@@ -0,0 +1,42 @@
+import { Button } from "@superset/ui/button";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
+import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
+import type React from "react";
+
+interface SidebarToggleProps {
+ isOpen: boolean;
+ onCollapse: () => void;
+ onExpand: () => void;
+}
+
+export const SidebarToggle: React.FC = ({
+ isOpen,
+ onCollapse,
+ onExpand,
+}) => (
+
+ {isOpen ? (
+
+
+
+
+
+ Collapse sidebar
+
+
+ ) : (
+
+
+
+
+
+ Expand sidebar
+
+
+ )}
+
+);
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskTabs.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskTabs.tsx
new file mode 100644
index 00000000000..e34c0ef5666
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskTabs.tsx
@@ -0,0 +1,71 @@
+import type React from "react";
+import { AddTaskButton } from "./AddTaskButton";
+import { ModeToggle } from "./ModeToggle";
+import { PRActions } from "./PRActions";
+import { SidebarToggle } from "./SidebarToggle";
+import type { TaskTabsProps } from "./types";
+import { WorktreeTab } from "./WorktreeTab";
+
+export const TaskTabs: React.FC = ({
+ onCollapseSidebar,
+ onExpandSidebar,
+ isSidebarOpen,
+ onAddTask,
+ onCreatePR,
+ onMergePR,
+ worktrees,
+ selectedWorktreeId,
+ onWorktreeSelect,
+ mode = "edit",
+ onModeChange,
+}) => {
+ const selectedWorktree = worktrees.find((wt) => wt.id === selectedWorktreeId);
+ const canCreatePR = selectedWorktree && !selectedWorktree.isPending;
+ const hasPR = selectedWorktree && selectedWorktree.prUrl;
+
+ return (
+
+
+
+
+ {onModeChange &&
}
+
+
+ {worktrees.map((worktree) => (
+ onWorktreeSelect(worktree.id)}
+ />
+ ))}
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskWorktreeContent.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskWorktreeContent.tsx
new file mode 100644
index 00000000000..1cb4fdcd98a
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/TaskWorktreeContent.tsx
@@ -0,0 +1,70 @@
+import type React from "react";
+import { StatusIndicator } from "../StatusIndicator";
+import { TaskAssignee } from "../TaskAssignee";
+import type { WorktreeWithTask } from "./types";
+import { getStatusLabel } from "./utils";
+
+interface TaskWorktreeContentProps {
+ worktree: WorktreeWithTask;
+ task: NonNullable;
+}
+
+export const TaskWorktreeContent: React.FC = ({
+ worktree,
+ task,
+}) => {
+ const statusLabel = getStatusLabel(task.status);
+
+ return (
+
+
+
+
+ [{task.slug}] {task.title}
+
+
+ {task.description}
+
+
+
+ {task.assignee && (
+
+
+
+ )}
+
+
+
+
+
Status
+
+
+ {statusLabel}
+
+
+
+ {task.lastUpdated && (
+
+ Updated
+ {task.lastUpdated}
+
+ )}
+
+
+ Branch
+
+ {worktree.branch}
+
+
+
+
+ Tabs
+ {worktree.tabs?.length || 0} open
+
+
+
+ );
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTab.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTab.tsx
new file mode 100644
index 00000000000..ff6dedc2143
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTab.tsx
@@ -0,0 +1,48 @@
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@superset/ui/hover-card";
+import type React from "react";
+import { BasicWorktreeContent } from "./BasicWorktreeContent";
+import { PendingWorktreeContent } from "./PendingWorktreeContent";
+import { TaskWorktreeContent } from "./TaskWorktreeContent";
+import type { WorktreeWithTask } from "./types";
+import { WorktreeTabButton } from "./WorktreeTabButton";
+
+interface WorktreeTabProps {
+ worktree: WorktreeWithTask;
+ isSelected: boolean;
+ onSelect: () => void;
+}
+
+export const WorktreeTab: React.FC = ({
+ worktree,
+ isSelected,
+ onSelect,
+}) => {
+ const isPending = worktree.isPending;
+ const hasTask = !!worktree.task;
+ const task = worktree.task;
+
+ return (
+
+
+
+
+
+ {isPending ? (
+
+ ) : hasTask && task ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTabButton.tsx b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTabButton.tsx
new file mode 100644
index 00000000000..d0a8de72e46
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/WorktreeTabButton.tsx
@@ -0,0 +1,48 @@
+import { Loader2 } from "lucide-react";
+import type React from "react";
+import { StatusIndicator } from "../StatusIndicator";
+import type { WorktreeWithTask } from "./types";
+
+interface WorktreeTabButtonProps {
+ worktree: WorktreeWithTask;
+ isSelected: boolean;
+ onClick: () => void;
+}
+
+export const WorktreeTabButton: React.FC = ({
+ worktree,
+ isSelected,
+ onClick,
+}) => {
+ const hasTask = !!worktree.task;
+ const task = worktree.task;
+ const isPending = worktree.isPending;
+ const displayTitle =
+ hasTask && task ? task.slug : worktree.description || worktree.branch;
+
+ return (
+
+ );
+};
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/index.ts b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/index.ts
new file mode 100644
index 00000000000..584b9fe5a50
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/index.ts
@@ -0,0 +1,2 @@
+export { TaskTabs } from "./TaskTabs";
+export type { TaskTabsProps, WorktreeWithTask } from "./types";
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/types.ts b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/types.ts
new file mode 100644
index 00000000000..0564fff9e2d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/types.ts
@@ -0,0 +1,32 @@
+import type { Worktree } from "shared/types";
+import type { TaskStatus } from "../StatusIndicator";
+
+export interface WorktreeWithTask extends Worktree {
+ isPending?: boolean;
+ task?: {
+ id: string;
+ slug: string;
+ title: string;
+ status: TaskStatus;
+ description: string;
+ assignee?: {
+ name: string;
+ avatarUrl: string | null;
+ };
+ lastUpdated?: string;
+ };
+}
+
+export interface TaskTabsProps {
+ onCollapseSidebar: () => void;
+ onExpandSidebar: () => void;
+ isSidebarOpen: boolean;
+ onAddTask: () => void;
+ onCreatePR?: () => void;
+ onMergePR?: () => void;
+ worktrees: WorktreeWithTask[];
+ selectedWorktreeId: string | null;
+ onWorktreeSelect: (worktreeId: string) => void;
+ mode?: "plan" | "edit";
+ onModeChange?: (mode: "plan" | "edit") => void;
+}
diff --git a/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/utils.ts b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/utils.ts
new file mode 100644
index 00000000000..172ec95a594
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/NewLayout/TaskTabs/utils.ts
@@ -0,0 +1,15 @@
+import type { TaskStatus } from "../StatusIndicator";
+
+export const getStatusLabel = (status: TaskStatus): string => {
+ const labels: Record = {
+ planning: "Planning",
+ working: "Working",
+ "needs-feedback": "Needs Feedback",
+ "ready-to-merge": "Ready to Merge",
+ backlog: "Backlog",
+ todo: "Todo",
+ completed: "Completed",
+ canceled: "Canceled",
+ };
+ return labels[status] || "";
+};