diff --git a/canvas/src/components/CommunicationOverlay.tsx b/canvas/src/components/CommunicationOverlay.tsx index 7b0c49c7..76e50157 100644 --- a/canvas/src/components/CommunicationOverlay.tsx +++ b/canvas/src/components/CommunicationOverlay.tsx @@ -143,14 +143,17 @@ export function CommunicationOverlay() {
+ {c.type === "a2a_send" ? "sent" : c.type === "a2a_receive" ? "received" : "task update"} {c.sourceName} + to {c.targetName}
+ {c.status} {age}
diff --git a/canvas/src/components/EmptyState.tsx b/canvas/src/components/EmptyState.tsx index 998d88c0..9f12f257 100644 --- a/canvas/src/components/EmptyState.tsx +++ b/canvas/src/components/EmptyState.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from "react"; import { api } from "@/lib/api"; import { useCanvasStore } from "@/store/canvas"; import { OrgTemplatesSection } from "./TemplatePalette"; +import { TIER_COLORS } from "@/lib/design-tokens"; interface Template { id: string; @@ -15,13 +16,6 @@ interface Template { skill_count: number; } -const TIER_COLORS: Record = { - 1: "text-zinc-400 border-zinc-700/60", - 2: "text-sky-400 border-sky-500/30", - 3: "text-violet-400 border-violet-500/30", - 4: "text-amber-400 border-amber-500/30", -}; - export function EmptyState() { const [templates, setTemplates] = useState([]); const [loading, setLoading] = useState(true); @@ -105,7 +99,13 @@ export function EmptyState() { {/* Template grid */} {loading ? ( -
Loading templates...
+
+ + Loading templates... +
) : templates.length > 0 ? (
{templates.map((t) => { @@ -115,7 +115,7 @@ export function EmptyState() { key={t.id} onClick={() => deploy(t)} disabled={!!deploying} - className="group rounded-xl border border-zinc-800/60 bg-zinc-900/50 px-3.5 py-3 hover:border-blue-500/40 hover:bg-zinc-900/80 transition-all disabled:opacity-50 text-left" + className="group rounded-xl border border-zinc-800/60 bg-zinc-900/50 px-3.5 py-3 hover:border-blue-500/40 hover:bg-zinc-900/80 transition-all disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-zinc-800/60 disabled:hover:bg-zinc-900/50 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/70" >
@@ -144,7 +144,7 @@ export function EmptyState() { diff --git a/canvas/src/components/SidePanel.tsx b/canvas/src/components/SidePanel.tsx index 6b0295fd..d9bef424 100644 --- a/canvas/src/components/SidePanel.tsx +++ b/canvas/src/components/SidePanel.tsx @@ -186,10 +186,10 @@ export function SidePanel() { aria-controls={`panel-${tab.id}`} tabIndex={panelTab === tab.id ? 0 : -1} onClick={() => setPanelTab(tab.id)} - className={`shrink-0 px-3 py-2.5 text-[10px] font-medium tracking-wide transition-all rounded-t-lg mx-0.5 focus:outline-none focus-visible:ring-1 focus-visible:ring-zinc-600 ${ + className={`shrink-0 px-3 py-2.5 text-[10px] font-medium tracking-wide transition-all rounded-t-lg mx-0.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/70 ${ panelTab === tab.id ? "text-zinc-100 bg-zinc-800/40 border-b-2 border-blue-500" - : "text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/20" + : "text-zinc-500 hover:text-zinc-200 hover:bg-zinc-800/40" }`} > diff --git a/canvas/src/components/TemplatePalette.tsx b/canvas/src/components/TemplatePalette.tsx index e94f6363..032d92a9 100644 --- a/canvas/src/components/TemplatePalette.tsx +++ b/canvas/src/components/TemplatePalette.tsx @@ -5,6 +5,7 @@ import { api } from "@/lib/api"; import { checkDeploySecrets, type PreflightResult } from "@/lib/deploy-preflight"; import { MissingKeysModal } from "./MissingKeysModal"; import { ConfirmDialog } from "./ConfirmDialog"; +import { TIER_CONFIG as TIER_LABELS_SHARED } from "@/lib/design-tokens"; interface Template { id: string; @@ -90,7 +91,11 @@ export function OrgTemplatesSection() {
{loading && ( -
+
+ Loading…
)} @@ -141,12 +146,7 @@ export function OrgTemplatesSection() { ); } -const TIER_LABELS: Record = { - 1: { label: "T1", color: "text-zinc-400 bg-zinc-800/60" }, - 2: { label: "T2", color: "text-sky-400 bg-sky-950/40" }, - 3: { label: "T3", color: "text-violet-400 bg-violet-950/40" }, - 4: { label: "T4", color: "text-amber-400 bg-amber-950/40" }, -}; +const TIER_LABELS = TIER_LABELS_SHARED; function ImportAgentButton({ onImported }: { onImported: () => void }) { const [importing, setImporting] = useState(false); @@ -354,7 +354,11 @@ export function TemplatePalette() {
{loading && ( -
+
+ Loading…
)} @@ -380,7 +384,7 @@ export function TemplatePalette() { key={t.id} onClick={() => handleDeploy(t)} disabled={isDeploying} - className="w-full text-left bg-zinc-800/40 hover:bg-zinc-800/70 border border-zinc-700/40 hover:border-zinc-600/50 rounded-xl p-3 transition-all disabled:opacity-50 group" + className="w-full text-left bg-zinc-800/40 hover:bg-zinc-800/70 border border-zinc-700/40 hover:border-zinc-600/50 rounded-xl p-3 transition-all disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-zinc-800/40 disabled:hover:border-zinc-700/40 group focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/70" >
diff --git a/canvas/src/components/WorkspaceNode.tsx b/canvas/src/components/WorkspaceNode.tsx index 5d4a6d39..ad469de6 100644 --- a/canvas/src/components/WorkspaceNode.tsx +++ b/canvas/src/components/WorkspaceNode.tsx @@ -5,6 +5,7 @@ import { Handle, Position, type NodeProps, type Node } from "@xyflow/react"; import { useCanvasStore, type WorkspaceNodeData } from "@/store/canvas"; import { showToast } from "@/components/Toaster"; import { Tooltip } from "@/components/Tooltip"; +import { STATUS_CONFIG, TIER_CONFIG } from "@/lib/design-tokens"; import { useShallow } from "zustand/react/shallow"; /** Stable selector: returns children, grandchild flag, and descendant count for a node */ @@ -27,15 +28,6 @@ function useHierarchyInfo(parentId: string) { return { children, hasGrandchildren, descendantCount }; } -const STATUS_CONFIG: Record = { - online: { dot: "bg-emerald-400", glow: "shadow-emerald-400/50", label: "Online", bar: "from-emerald-500/20 to-transparent" }, - offline: { dot: "bg-zinc-500", glow: "", label: "Offline", bar: "from-zinc-600/10 to-transparent" }, - paused: { dot: "bg-indigo-400", glow: "", label: "Paused", bar: "from-indigo-500/10 to-transparent" }, - degraded: { dot: "bg-amber-400", glow: "shadow-amber-400/50", label: "Degraded", bar: "from-amber-500/20 to-transparent" }, - failed: { dot: "bg-red-400", glow: "shadow-red-400/50", label: "Failed", bar: "from-red-500/20 to-transparent" }, - provisioning: { dot: "bg-sky-400 motion-safe:animate-pulse", glow: "shadow-sky-400/50", label: "Starting", bar: "from-sky-500/20 to-transparent" }, -}; - /** Eject/extract arrow icon — visually distinct from delete ✕ */ function EjectIcon() { return ( @@ -46,13 +38,6 @@ function EjectIcon() { ); } -const TIER_CONFIG: Record = { - 1: { label: "T1", color: "text-zinc-500 bg-zinc-800/80" }, - 2: { label: "T2", color: "text-sky-400 bg-sky-950/50" }, - 3: { label: "T3", color: "text-violet-400 bg-violet-950/50" }, - 4: { label: "T4", color: "text-amber-400 bg-amber-950/50" }, -}; - export function WorkspaceNode({ id, data }: NodeProps>) { const statusCfg = STATUS_CONFIG[data.status] || STATUS_CONFIG.offline; const tierCfg = TIER_CONFIG[data.tier] || { label: `T${data.tier}`, color: "text-zinc-500 bg-zinc-800" }; @@ -123,6 +108,7 @@ export function WorkspaceNode({ id, data }: NodeProps>) : "bg-zinc-900/90 border border-zinc-700/80 hover:border-zinc-500/60 shadow-lg shadow-black/30 hover:shadow-xl hover:shadow-black/40" } backdrop-blur-sm + focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/70 focus-visible:ring-offset-1 focus-visible:ring-offset-zinc-950 `} > {/* Status gradient bar at top */} diff --git a/canvas/src/lib/design-tokens.ts b/canvas/src/lib/design-tokens.ts new file mode 100644 index 00000000..f50e9d43 --- /dev/null +++ b/canvas/src/lib/design-tokens.ts @@ -0,0 +1,22 @@ +export const STATUS_CONFIG: Record = { + online: { dot: "bg-emerald-400", glow: "shadow-emerald-400/50", label: "Online", bar: "from-emerald-500/20 to-transparent" }, + offline: { dot: "bg-zinc-500", glow: "", label: "Offline", bar: "from-zinc-600/10 to-transparent" }, + paused: { dot: "bg-indigo-400", glow: "", label: "Paused", bar: "from-indigo-500/10 to-transparent" }, + degraded: { dot: "bg-amber-400", glow: "shadow-amber-400/50", label: "Degraded", bar: "from-amber-500/20 to-transparent" }, + failed: { dot: "bg-red-400", glow: "shadow-red-400/50", label: "Failed", bar: "from-red-500/20 to-transparent" }, + provisioning: { dot: "bg-sky-400 motion-safe:animate-pulse", glow: "shadow-sky-400/50", label: "Starting", bar: "from-sky-500/20 to-transparent" }, +}; + +export const TIER_CONFIG: Record = { + 1: { label: "T1", color: "text-zinc-500 bg-zinc-800/80" }, + 2: { label: "T2", color: "text-sky-400 bg-sky-950/50" }, + 3: { label: "T3", color: "text-violet-400 bg-violet-950/50" }, + 4: { label: "T4", color: "text-amber-400 bg-amber-950/50" }, +}; + +export const TIER_COLORS: Record = { + 1: "text-zinc-400 border-zinc-700/60", + 2: "text-sky-400 border-sky-500/30", + 3: "text-violet-400 border-violet-500/30", + 4: "text-amber-400 border-amber-500/30", +};