"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Background, BackgroundVariant, Controls, Panel, ReactFlow, ReactFlowProvider, useReactFlow, type Node, } from "@xyflow/react"; import { AlertTriangle, CheckCircle2, Focus, GitBranch, Layers3, LoaderCircle, RefreshCw, Search, Sparkles, Workflow, X, } from "lucide-react"; import { FlowCard } from "@/components/FlowCard"; import { Inspector } from "@/components/Inspector"; import { PhaseFrameNode } from "@/components/PhaseFrameNode"; import { fetchGraph, fetchRuns } from "@/lib/api"; import { buildCanvas, PHASE_LABELS, type CanvasData } from "@/lib/flow"; import type { FlowNodeRecord, Phase, RunGraph, RunSummary } from "@/lib/types"; const nodeTypes = { flowCard: FlowCard, phaseFrame: PhaseFrameNode }; const ALL_PHASES: Phase[] = ["phase-1", "phase-2", "phase-3", "delivery"]; function LoadingState() { return

正在装载 fake mission graph

连接本地 FastAPI :8788

; } function ErrorState({ message, retry }: { message: string; retry: () => void }) { return

可视化后端未就绪

{message}

; } function Canvas({ graph, selected, onSelect }: { graph: RunGraph; selected: FlowNodeRecord | null; onSelect: (node: FlowNodeRecord | null) => void; }) { const [query, setQuery] = useState(""); const [phases, setPhases] = useState>(new Set(ALL_PHASES)); const { fitView } = useReactFlow(); const canvas = useMemo(() => buildCanvas(graph, phases, query), [graph, phases, query]); const togglePhase = (phase: Phase) => setPhases((current) => { const next = new Set(current); if (next.has(phase)) next.delete(phase); else next.add(phase); return next.size ? next : new Set([phase]); }); const selectNode = useCallback((_: React.MouseEvent, node: Node) => { if (node.data.kind === "card") onSelect(node.data.item); }, [onSelect]); const matched = graph.nodes.filter((item) => { const node = canvas.nodes.find((candidate) => candidate.id === item.id); return node?.data.kind === "card" && !node.data.dimmed && !node.hidden; }).length; return <>
Mission Control智能创作构建系统
BUILD #{graph.run.script_build_id}{graph.run.title}
FAKE DATA {graph.run.status} {graph.run.node_count} nodes
({ ...node, selected: node.id === selected?.id }))} edges={canvas.edges} nodeTypes={nodeTypes} onNodeClick={selectNode} onPaneClick={() => onSelect(null)} onInit={(instance) => void instance.fitView({ padding: 0.06, duration: 0 })} minZoom={0.06} maxZoom={1.4} nodesConnectable={false} zoomOnDoubleClick={false} panOnScroll zoomOnPinch proOptions={{ hideAttribution: true }} > {query ? `${matched} / ${graph.nodes.length}` : `${graph.nodes.length} nodes · ${graph.edges.length} edges`} 控制流 Artifact Validation Publication
{selected ? onSelect(null)} /> : null} ; } function MissionControlInner() { const [runs, setRuns] = useState([]); const [graph, setGraph] = useState(null); const [selected, setSelected] = useState(null); const [error, setError] = useState(null); const [nonce, setNonce] = useState(0); useEffect(() => { const controller = new AbortController(); setError(null); Promise.all([fetchRuns(controller.signal), fetchGraph(10001, controller.signal)]) .then(([nextRuns, nextGraph]) => { setRuns(nextRuns); setGraph(nextGraph); }) .catch((reason: unknown) => { if (!controller.signal.aborted) setError(reason instanceof Error ? reason.message : "未知错误"); }); return () => controller.abort(); }, [nonce]); if (error) return setNonce((value) => value + 1)} />; if (!graph || !runs.length) return ; return
; } export function MissionControl() { return ; }