Files
multiAgentTry/frontend/src/components/BarrierSyncCard.tsx

280 lines
9.2 KiB
TypeScript
Raw Normal View History

import { Shield, Zap } from "lucide-react";
type NodeState = "ready" | "waiting" | "pending";
interface BarrierNode {
id: string;
name: string;
gradient: string;
state: NodeState;
waitingFor?: string;
}
const nodes: BarrierNode[] = [
{
id: "n1",
name: "CLA",
gradient: "linear-gradient(135deg,#8b5cf6,#6366f1)",
state: "ready",
},
{
id: "n2",
name: "KIM",
gradient: "linear-gradient(135deg,#f59e0b,#d97706)",
state: "waiting",
waitingFor: "架构设计完成",
},
{
id: "n3",
name: "OPC",
gradient: "linear-gradient(135deg,#10b981,#059669)",
state: "ready",
},
{
id: "n4",
name: "USR",
gradient: "linear-gradient(135deg,#f59e0b,#b45309)",
state: "pending",
},
];
const stateColors: Record<NodeState, string> = {
ready: "#00ff9d",
waiting: "#ff9500",
pending: "#374151",
};
const stateLabels: Record<NodeState, string> = {
ready: "READY",
waiting: "WAIT",
pending: "IDLE",
};
const syncPoints = [
{ name: "INIT", completed: true },
{ name: "REVIEW", completed: true },
{ name: "DESIGN", completed: false, active: true },
{ name: "IMPL", completed: false },
{ name: "DEPLOY", completed: false },
];
export function BarrierSyncCard() {
const readyCount = nodes.filter(n => n.state === "ready").length;
return (
<div className="glass-card" style={{ padding: 20, height: "100%", display: "flex", flexDirection: "column" }}>
{/* Header */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14, flexShrink: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<div
style={{
width: 28,
height: 28,
borderRadius: 8,
background: "rgba(0,240,255,0.1)",
border: "1px solid rgba(0,240,255,0.2)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Shield size={14} color="#00f0ff" />
</div>
<span className="card-title"></span>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<span className="font-mono-code" style={{ fontSize: 11, color: "#ff9500" }}>
{readyCount}/{nodes.length}
</span>
<div
style={{
padding: "3px 10px",
background: "rgba(255,149,0,0.1)",
border: "1px solid rgba(255,149,0,0.3)",
borderRadius: 6,
}}
>
<span className="font-mono-code" style={{ fontSize: 10, color: "#ff9500" }}>
<span className="animate-pulse-fast"></span>
</span>
</div>
</div>
</div>
<div style={{ display: "flex", gap: 20, flex: 1, minHeight: 0 }}>
{/* Left: Agent nodes */}
<div style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 200 }}>
{nodes.map(node => (
<div
key={node.id}
style={{
display: "flex",
alignItems: "center",
gap: 10,
padding: "8px 12px",
background: "rgba(0,0,0,0.3)",
border: `1px solid ${stateColors[node.state]}30`,
borderRadius: 10,
transition: "all 0.3s ease",
}}
>
<div
className="agent-avatar"
style={{ background: node.gradient, width: 32, height: 32, fontSize: 10 }}
>
{node.name}
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div className="font-mono-code" style={{ fontSize: 11, color: "#9ca3af" }}>
{node.name === "CLA" ? "Claude Code" : node.name === "KIM" ? "Kimi CLI" : node.name === "OPC" ? "OpenCode" : "Tech Lead"}
</div>
{node.waitingFor && (
<div className="font-mono-code" style={{ fontSize: 9, color: "#ff9500", marginTop: 1 }}>
: {node.waitingFor}
</div>
)}
</div>
<div
className="barrier-node"
style={{
width: 32,
height: 32,
borderColor: stateColors[node.state],
color: stateColors[node.state],
background: `${stateColors[node.state]}10`,
fontSize: 8,
}}
title={stateLabels[node.state]}
>
{node.state === "ready" ? (
<svg width="12" height="12" viewBox="0 0 12 12">
<path d="M2 6L5 9L10 3" stroke="#00ff9d" strokeWidth="1.5" strokeLinecap="round" fill="none" />
</svg>
) : node.state === "waiting" ? (
<Zap size={12} />
) : (
<span style={{ fontSize: 8 }}></span>
)}
</div>
</div>
))}
</div>
{/* Right: Sync points */}
<div style={{ flex: 1, minWidth: 0 }}>
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280", marginBottom: 10 }}>
</div>
{/* Progress line */}
<div style={{ position: "relative", marginBottom: 16 }}>
<div
style={{
height: 3,
background: "#111827",
borderRadius: 2,
position: "relative",
overflow: "visible",
}}
>
{/* Completed portion */}
<div
style={{
position: "absolute",
left: 0,
top: 0,
height: "100%",
width: "42%",
background: "linear-gradient(90deg,#00f0ff,#00ff9d)",
borderRadius: 2,
}}
/>
{/* Active flowing portion */}
<div
style={{
position: "absolute",
left: "42%",
top: 0,
height: "100%",
width: "16%",
background: "linear-gradient(90deg,#ff9500,rgba(255,149,0,0.3))",
borderRadius: 2,
overflow: "hidden",
}}
>
<div
style={{
position: "absolute",
inset: 0,
background: "linear-gradient(90deg,transparent,rgba(255,255,255,0.3),transparent)",
animation: "line-flow 1.5s linear infinite",
}}
/>
</div>
{/* Dots */}
{syncPoints.map((sp, i) => {
const left = `${(i / (syncPoints.length - 1)) * 100}%`;
return (
<div
key={sp.name}
style={{
position: "absolute",
top: "50%",
left,
transform: "translate(-50%,-50%)",
width: 12,
height: 12,
borderRadius: "50%",
border: `2px solid ${sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151"}`,
background: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#111827",
zIndex: 2,
boxShadow: sp.active ? "0 0 8px rgba(255,149,0,0.6)" : sp.completed ? "0 0 6px rgba(0,255,157,0.4)" : "none",
}}
className={sp.active ? "animate-scale-pulse" : ""}
/>
);
})}
</div>
{/* Labels */}
<div style={{ display: "flex", justifyContent: "space-between", marginTop: 12 }}>
{syncPoints.map(sp => (
<div
key={sp.name}
className="font-mono-code"
style={{
fontSize: 9,
color: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151",
textAlign: "center",
flex: 1,
}}
>
{sp.name}
</div>
))}
</div>
</div>
{/* Status panel */}
<div
style={{
padding: "10px 12px",
background: "rgba(255,149,0,0.05)",
border: "1px solid rgba(255,149,0,0.15)",
borderRadius: 10,
}}
>
<div className="font-mono-code" style={{ fontSize: 10, color: "#ff9500", marginBottom: 4 }}>
DESIGN
</div>
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280" }}>
Agent wait_for_meeting("design_review") · {readyCount}/{nodes.length}
</div>
</div>
</div>
</div>
</div>
);
}