chore: checkpoint before DAG views UX overhaul

This commit is contained in:
zenchantlive 2026-02-22 20:43:59 -08:00
parent 5695125a75
commit a03def1ca1
125 changed files with 40711 additions and 581 deletions

View file

@ -27,47 +27,47 @@ export interface MissionCardProps {
}
const STATUS_CONFIG = {
planning: {
color: 'text-blue-400',
border: 'border-blue-500/30',
bg: 'bg-blue-500/5',
planning: {
color: 'text-blue-400',
border: 'border-blue-500/30',
bg: 'bg-blue-500/5',
label: 'PLANNING',
icon: Circle
},
active: {
color: 'text-emerald-400',
border: 'border-emerald-500/30',
bg: 'bg-emerald-500/5',
active: {
color: 'text-emerald-400',
border: 'border-emerald-500/30',
bg: 'bg-emerald-500/5',
label: 'ACTIVE',
icon: Activity
},
blocked: {
color: 'text-rose-400',
border: 'border-rose-500/30',
bg: 'bg-rose-500/5',
blocked: {
color: 'text-rose-400',
border: 'border-rose-500/30',
bg: 'bg-rose-500/5',
label: 'BLOCKED',
icon: AlertTriangle
},
completed: {
color: 'text-slate-400',
border: 'border-slate-500/30',
bg: 'bg-slate-500/5',
completed: {
color: 'text-slate-400',
border: 'border-slate-500/30',
bg: 'bg-slate-500/5',
label: 'COMPLETE',
icon: CheckCircle2
},
};
export function MissionCard({ id, projectRoot, title, description, status, stats, agents, onDeploy, onClick }: MissionCardProps) {
export function MissionCard({ id, projectRoot, title, description, status, agents, onDeploy, onClick }: MissionCardProps) {
const config = STATUS_CONFIG[status] || STATUS_CONFIG.planning;
const StatusIcon = config.icon;
const { topology, isLoading } = useSwarmTopology(projectRoot, id);
const isUnstaffed = agents.length === 0;
const isWorking = agents.some(a => a.status === 'working');
const showPulse = status === 'active' || isWorking;
return (
<Card
<Card
onClick={onClick}
className="group relative flex flex-col h-[320px] cursor-pointer overflow-hidden rounded-2xl border border-[var(--ui-border-soft)] bg-[var(--ui-bg-card)] hover:border-[var(--ui-accent-info)] hover:shadow-xl hover:shadow-black/20 transition-all duration-300"
>
@ -106,7 +106,7 @@ export function MissionCard({ id, projectRoot, title, description, status, stats
{/* GRAPH VISUALIZATION */}
<div className="px-5 py-2 flex-1 flex flex-col justify-end">
<SwarmGraph topology={topology} isLoading={isLoading} />
<SwarmGraph topology={topology} isLoading={isLoading} />
</div>
{/* FOOTER: SQUAD */}
@ -129,14 +129,14 @@ export function MissionCard({ id, projectRoot, title, description, status, stats
)}
</div>
<Button
size="sm"
<Button
size="sm"
variant="ghost"
onClick={(e) => { e.stopPropagation(); onDeploy(); }}
className={cn(
"h-7 px-3 text-[10px] font-bold uppercase tracking-wider border transition-all",
isUnstaffed
? "border-blue-500/20 text-blue-400 bg-blue-500/5 hover:bg-blue-500/10 hover:border-blue-500/40"
isUnstaffed
? "border-blue-500/20 text-blue-400 bg-blue-500/5 hover:bg-blue-500/10 hover:border-blue-500/40"
: "border-slate-700 text-slate-400 hover:text-white hover:bg-white/5 hover:border-slate-500"
)}
>

View file

@ -1,7 +1,6 @@
'use client';
import { useMemo } from 'react';
import { motion } from 'framer-motion';
import type { SwarmTopologyData } from '../../hooks/use-swarm-topology';
interface SwarmGraphProps {
@ -12,27 +11,26 @@ interface SwarmGraphProps {
export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
const nodes = useMemo(() => {
if (!topology) return [];
// Simple layout strategy: Clusters
// Done: Left side
// Active: Center
// Ready: Right
// Blocked: Bottom Right
const output: React.ReactNode[] = [];
const scale = 0.5;
// 1. Completed (Green Cluster)
topology.completed.forEach((item, i) => {
const col = i % 5;
const row = Math.floor(i / 5);
output.push(
<circle
<circle
key={`done-${item.id}`}
cx={20 + (col * 8)}
cy={20 + (row * 8)}
r={2.5}
fill="#34d399"
cx={20 + (col * 8)}
cy={20 + (row * 8)}
r={2.5}
fill="#34d399"
opacity={0.5}
/>
);
@ -40,32 +38,32 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
// 2. Active (Pulsing Center)
topology.active.forEach((item, i) => {
const cx = 140 + (i * 20);
const cy = 30 + (i % 2) * 10;
output.push(
<g key={`active-${item.id}`}>
<circle cx={cx} cy={cy} r={6} fill="#10b981" className="animate-pulse" />
<circle cx={cx} cy={cy} r={3} fill="#ecfdf5" />
</g>
);
const cx = 140 + (i * 20);
const cy = 30 + (i % 2) * 10;
output.push(
<g key={`active-${item.id}`}>
<circle cx={cx} cy={cy} r={6} fill="#10b981" className="animate-pulse" />
<circle cx={cx} cy={cy} r={3} fill="#ecfdf5" />
</g>
);
});
// 3. Ready (White Pipeline)
topology.ready.forEach((item, i) => {
const cx = 220 + (i * 10);
const cy = 30;
output.push(
<circle key={`ready-${item.id}`} cx={cx} cy={cy} r={3} fill="#94a3b8" />
);
const cx = 220 + (i * 10);
const cy = 30;
output.push(
<circle key={`ready-${item.id}`} cx={cx} cy={cy} r={3} fill="#94a3b8" />
);
});
// 4. Blocked (Red Hazard)
topology.blocked.forEach((item, i) => {
const cx = 220 + (i * 10);
const cy = 50;
output.push(
<circle key={`blocked-${item.id}`} cx={cx} cy={cy} r={3} fill="#f43f5e" />
);
const cx = 220 + (i * 10);
const cy = 50;
output.push(
<circle key={`blocked-${item.id}`} cx={cx} cy={cy} r={3} fill="#f43f5e" />
);
});
return output;
@ -80,11 +78,11 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
}
if (!topology || (topology.completed.length === 0 && topology.active.length === 0 && topology.ready.length === 0)) {
return (
<div className="h-16 w-full flex items-center justify-center bg-black/20 rounded-lg border border-dashed border-slate-800">
<span className="text-[10px] text-slate-600 font-mono">EMPTY SIGNAL</span>
</div>
);
return (
<div className="h-16 w-full flex items-center justify-center bg-black/20 rounded-lg border border-dashed border-slate-800">
<span className="text-[10px] text-slate-600 font-mono">EMPTY SIGNAL</span>
</div>
);
}
return (
@ -93,9 +91,9 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
{/* Connection Lines (Abstract) */}
<path d="M 60 30 L 130 30" stroke="#334155" strokeWidth="1" strokeDasharray="4 4" />
<path d="M 180 30 L 210 30" stroke="#334155" strokeWidth="1" strokeDasharray="4 4" />
{nodes}
{/* Labels */}
<text x="30" y="55" fontSize="8" fill="#475569" textAnchor="middle" fontFamily="monospace">DONE</text>
<text x="150" y="55" fontSize="8" fill="#10b981" textAnchor="middle" fontFamily="monospace" fontWeight="bold">ACTIVE</text>