feat: Add compact mode to TaskGraph, use in pipeline tab
TaskGraph now accepts a `compact` prop that hides the second line (category badge, priority) and uses tighter vertical padding. Blocked-by count moves inline on the first line as "N deps". Pipeline phase cards pass compact; the plan tab's phase detail keeps the full two-line layout.
This commit is contained in:
@@ -25,6 +25,8 @@ interface TaskGraphProps {
|
||||
agentNameMap: Map<string, string>;
|
||||
onClickTask: (taskId: string) => void;
|
||||
onDeleteTask?: (taskId: string) => void;
|
||||
/** Hide category badge and priority — use in compact contexts like the pipeline */
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -51,6 +53,7 @@ export function TaskGraph({
|
||||
agentNameMap,
|
||||
onClickTask,
|
||||
onDeleteTask,
|
||||
compact = false,
|
||||
}: TaskGraphProps) {
|
||||
// Flatten task dep edges to DependencyEdge format for reuse of groupPhasesByDependencyLevel
|
||||
const columns = useMemo(() => {
|
||||
@@ -112,6 +115,7 @@ export function TaskGraph({
|
||||
? () => onDeleteTask(task.id)
|
||||
: undefined
|
||||
}
|
||||
compact={compact}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -127,6 +131,7 @@ export function TaskGraph({
|
||||
? () => onDeleteTask(col.phases[0].id)
|
||||
: undefined
|
||||
}
|
||||
compact={compact}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -146,12 +151,14 @@ function TaskNode({
|
||||
agentName,
|
||||
onClick,
|
||||
onDelete,
|
||||
compact = false,
|
||||
}: {
|
||||
task: SerializedTask;
|
||||
blockedByCount: number;
|
||||
agentName?: string;
|
||||
onClick: () => void;
|
||||
onDelete?: () => void;
|
||||
compact?: boolean;
|
||||
}) {
|
||||
const variant = mapEntityStatus(task.status);
|
||||
const catConfig = getCategoryConfig(task.category);
|
||||
@@ -163,10 +170,14 @@ function TaskNode({
|
||||
? "text-status-warning-fg"
|
||||
: null;
|
||||
|
||||
const hasSecondLine =
|
||||
!compact && (true /* category badge always shows */ || priorityColor || agentName || blockedByCount > 0);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group flex w-full cursor-pointer gap-2 rounded-md px-2 py-1.5 text-left transition-all hover:bg-accent/50",
|
||||
"group flex w-full cursor-pointer gap-2 rounded-md px-2 text-left transition-all hover:bg-accent/50",
|
||||
compact ? "py-1" : "py-1.5",
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
@@ -179,13 +190,18 @@ function TaskNode({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Two-line content */}
|
||||
{/* Content */}
|
||||
<div className="min-w-0 flex-1">
|
||||
{/* Line 1: name + status + delete */}
|
||||
{/* Line 1: name + status + blocked count (compact) + delete */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="min-w-0 flex-1 truncate text-[13px]">
|
||||
{task.name}
|
||||
</span>
|
||||
{compact && blockedByCount > 0 && (
|
||||
<span className="shrink-0 text-[10px] text-muted-foreground">
|
||||
{blockedByCount} dep{blockedByCount === 1 ? "" : "s"}
|
||||
</span>
|
||||
)}
|
||||
<StatusBadge
|
||||
status={task.status}
|
||||
className="shrink-0 text-[9px]"
|
||||
@@ -206,7 +222,8 @@ function TaskNode({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Line 2: category + priority + agent + blocked count */}
|
||||
{/* Line 2: category + priority + agent + blocked count (full mode only) */}
|
||||
{hasSecondLine && (
|
||||
<div className="mt-0.5 flex items-center gap-1.5">
|
||||
<Badge variant={catConfig.variant} size="xs">
|
||||
{catConfig.label}
|
||||
@@ -227,6 +244,7 @@ function TaskNode({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -141,6 +141,7 @@ export function PipelinePhaseGroup({ phase, tasks, taskDepsRaw, isBlocked, detai
|
||||
blockedByCountMap={blockedByCountMap}
|
||||
agentNameMap={emptyAgentMap}
|
||||
onClickTask={setSelectedTaskId}
|
||||
compact
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user