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>;
|
agentNameMap: Map<string, string>;
|
||||||
onClickTask: (taskId: string) => void;
|
onClickTask: (taskId: string) => void;
|
||||||
onDeleteTask?: (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,
|
agentNameMap,
|
||||||
onClickTask,
|
onClickTask,
|
||||||
onDeleteTask,
|
onDeleteTask,
|
||||||
|
compact = false,
|
||||||
}: TaskGraphProps) {
|
}: TaskGraphProps) {
|
||||||
// Flatten task dep edges to DependencyEdge format for reuse of groupPhasesByDependencyLevel
|
// Flatten task dep edges to DependencyEdge format for reuse of groupPhasesByDependencyLevel
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
@@ -112,6 +115,7 @@ export function TaskGraph({
|
|||||||
? () => onDeleteTask(task.id)
|
? () => onDeleteTask(task.id)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
compact={compact}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -127,6 +131,7 @@ export function TaskGraph({
|
|||||||
? () => onDeleteTask(col.phases[0].id)
|
? () => onDeleteTask(col.phases[0].id)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
compact={compact}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -146,12 +151,14 @@ function TaskNode({
|
|||||||
agentName,
|
agentName,
|
||||||
onClick,
|
onClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
compact = false,
|
||||||
}: {
|
}: {
|
||||||
task: SerializedTask;
|
task: SerializedTask;
|
||||||
blockedByCount: number;
|
blockedByCount: number;
|
||||||
agentName?: string;
|
agentName?: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
|
compact?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const variant = mapEntityStatus(task.status);
|
const variant = mapEntityStatus(task.status);
|
||||||
const catConfig = getCategoryConfig(task.category);
|
const catConfig = getCategoryConfig(task.category);
|
||||||
@@ -163,10 +170,14 @@ function TaskNode({
|
|||||||
? "text-status-warning-fg"
|
? "text-status-warning-fg"
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const hasSecondLine =
|
||||||
|
!compact && (true /* category badge always shows */ || priorityColor || agentName || blockedByCount > 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
@@ -179,13 +190,18 @@ function TaskNode({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Two-line content */}
|
{/* Content */}
|
||||||
<div className="min-w-0 flex-1">
|
<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">
|
<div className="flex items-center gap-2">
|
||||||
<span className="min-w-0 flex-1 truncate text-[13px]">
|
<span className="min-w-0 flex-1 truncate text-[13px]">
|
||||||
{task.name}
|
{task.name}
|
||||||
</span>
|
</span>
|
||||||
|
{compact && blockedByCount > 0 && (
|
||||||
|
<span className="shrink-0 text-[10px] text-muted-foreground">
|
||||||
|
{blockedByCount} dep{blockedByCount === 1 ? "" : "s"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<StatusBadge
|
<StatusBadge
|
||||||
status={task.status}
|
status={task.status}
|
||||||
className="shrink-0 text-[9px]"
|
className="shrink-0 text-[9px]"
|
||||||
@@ -206,27 +222,29 @@ function TaskNode({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Line 2: category + priority + agent + blocked count */}
|
{/* Line 2: category + priority + agent + blocked count (full mode only) */}
|
||||||
<div className="mt-0.5 flex items-center gap-1.5">
|
{hasSecondLine && (
|
||||||
<Badge variant={catConfig.variant} size="xs">
|
<div className="mt-0.5 flex items-center gap-1.5">
|
||||||
{catConfig.label}
|
<Badge variant={catConfig.variant} size="xs">
|
||||||
</Badge>
|
{catConfig.label}
|
||||||
{priorityColor && (
|
</Badge>
|
||||||
<span className={cn("text-[10px] font-medium capitalize", priorityColor)}>
|
{priorityColor && (
|
||||||
{task.priority}
|
<span className={cn("text-[10px] font-medium capitalize", priorityColor)}>
|
||||||
</span>
|
{task.priority}
|
||||||
)}
|
</span>
|
||||||
{agentName && (
|
)}
|
||||||
<span className="truncate text-[10px] text-muted-foreground">
|
{agentName && (
|
||||||
{agentName}
|
<span className="truncate text-[10px] text-muted-foreground">
|
||||||
</span>
|
{agentName}
|
||||||
)}
|
</span>
|
||||||
{blockedByCount > 0 && (
|
)}
|
||||||
<span className="text-[10px] text-muted-foreground">
|
{blockedByCount > 0 && (
|
||||||
blocked by {blockedByCount}
|
<span className="text-[10px] text-muted-foreground">
|
||||||
</span>
|
blocked by {blockedByCount}
|
||||||
)}
|
</span>
|
||||||
</div>
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export function PipelinePhaseGroup({ phase, tasks, taskDepsRaw, isBlocked, detai
|
|||||||
blockedByCountMap={blockedByCountMap}
|
blockedByCountMap={blockedByCountMap}
|
||||||
agentNameMap={emptyAgentMap}
|
agentNameMap={emptyAgentMap}
|
||||||
onClickTask={setSelectedTaskId}
|
onClickTask={setSelectedTaskId}
|
||||||
|
compact
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user