From e16d49b52a393aa9a762edf57f93434a4bd7844e Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Thu, 5 Feb 2026 00:33:04 +0000
Subject: Replace custom DAG editor with React Flow library

- Install @xyflow/react for smooth node-based UI
- Create custom DefinitionNode and ContractNode components
- Use React Flow's built-in drag-and-drop, edge drawing, pan/zoom
- Removes laggy custom implementation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---
 .../frontend/src/components/chains/ChainEditor.tsx | 1696 +++++++-------------
 1 file changed, 538 insertions(+), 1158 deletions(-)

(limited to 'makima/frontend/src/components/chains/ChainEditor.tsx')

diff --git a/makima/frontend/src/components/chains/ChainEditor.tsx b/makima/frontend/src/components/chains/ChainEditor.tsx
index a4c8a39..5b77170 100644
--- a/makima/frontend/src/components/chains/ChainEditor.tsx
+++ b/makima/frontend/src/components/chains/ChainEditor.tsx
@@ -1,8 +1,26 @@
-import { useState, useCallback, useMemo, useRef, useEffect } from "react";
+import { useState, useCallback, useEffect, useMemo } from "react";
+import {
+  ReactFlow,
+  Node,
+  Edge,
+  Controls,
+  Background,
+  useNodesState,
+  useEdgesState,
+  addEdge,
+  Connection,
+  NodeProps,
+  Handle,
+  Position,
+  BackgroundVariant,
+  NodeChange,
+  MarkerType,
+} from "@xyflow/react";
+import "@xyflow/react/dist/style.css";
+
 import type {
   ChainWithContracts,
   ChainGraphResponse,
-  ChainContractDetail,
   ChainContractDefinition,
   ChainDefinitionGraphResponse,
   AddContractDefinitionRequest,
@@ -33,10 +51,172 @@ interface ChainEditorProps {
   onContractClick: (contractId: string) => void;
 }
 
-// Node dimensions
-const NODE_WIDTH = 180;
+// Node dimensions for layout
+const NODE_WIDTH = 200;
 const NODE_HEIGHT = 80;
-const CANVAS_PADDING = 40;
+const GRID_SPACING_X = 280;
+const GRID_SPACING_Y = 120;
+
+// Custom node component for definitions
+function DefinitionNode({ data, selected }: NodeProps) {
+  const isCheckpoint = data.contractType === "checkpoint";
+  const status = data.isInstantiated ? data.contractStatus || "pending" : "pending";
+  const colors = getStatusColor(status, isCheckpoint);
+
+  return (
+    <div
+      className={`rounded-lg border-2 overflow-hidden ${
+        isCheckpoint ? "bg-[#0f0a1e]" : "bg-[#0a1628]"
+      } ${selected ? "ring-2 ring-offset-2 ring-offset-[#050d18]" : ""} ${
+        selected ? (isCheckpoint ? "ring-[#a78bfa]" : "ring-[#75aafc]") : ""
+      }`}
+      style={{
+        width: NODE_WIDTH,
+        height: NODE_HEIGHT,
+        borderColor: selected
+          ? isCheckpoint
+            ? "#a78bfa"
+            : "#75aafc"
+          : colors.border,
+        borderStyle: data.isInstantiated ? "solid" : "dashed",
+      }}
+    >
+      {/* Top handle for incoming edges */}
+      <Handle
+        type="target"
+        position={Position.Top}
+        className="!bg-[#75aafc] !w-3 !h-3 !border-2 !border-[#0a1628]"
+      />
+
+      {/* Status indicator bar */}
+      <div className="h-1.5" style={{ backgroundColor: colors.bg }} />
+
+      {/* Content */}
+      <div className="p-2">
+        <div className="flex items-center justify-between mb-1">
+          <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">
+            {data.name}
+          </span>
+          {isCheckpoint ? (
+            <CheckIcon className="w-4 h-4 text-[#a78bfa] opacity-70 flex-shrink-0 ml-1" />
+          ) : (
+            <ChainIcon className="w-4 h-4 text-[#75aafc] opacity-50 flex-shrink-0 ml-1" />
+          )}
+        </div>
+        <div className="flex items-center justify-between">
+          <span
+            className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
+            style={{
+              color: colors.bg,
+              backgroundColor: `${colors.bg}20`,
+            }}
+          >
+            {data.isInstantiated ? status : isCheckpoint ? "checkpoint" : "definition"}
+          </span>
+          <span className="font-mono text-[10px] text-[#8b949e]">
+            {data.contractType}
+          </span>
+        </div>
+      </div>
+
+      {/* Bottom handle for outgoing edges */}
+      <Handle
+        type="source"
+        position={Position.Bottom}
+        className="!bg-[#f59e0b] !w-3 !h-3 !border-2 !border-[#0a1628]"
+      />
+    </div>
+  );
+}
+
+// Custom node for contracts (active chains)
+function ContractNode({ data, selected }: NodeProps) {
+  const colors = getStatusColor(data.status);
+
+  return (
+    <div
+      className={`rounded-lg border-2 bg-[#0a1628] overflow-hidden ${
+        selected ? "ring-2 ring-[#75aafc] ring-offset-2 ring-offset-[#050d18]" : ""
+      }`}
+      style={{
+        width: NODE_WIDTH,
+        height: NODE_HEIGHT,
+        borderColor: selected ? "#75aafc" : colors.border,
+      }}
+    >
+      <Handle
+        type="target"
+        position={Position.Top}
+        className="!bg-[#75aafc] !w-3 !h-3 !border-2 !border-[#0a1628]"
+      />
+
+      <div className="h-1.5" style={{ backgroundColor: colors.bg }} />
+
+      <div className="p-2">
+        <div className="flex items-center justify-between mb-1">
+          <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">
+            {data.name}
+          </span>
+          <ChainIcon className="w-4 h-4 text-[#75aafc] opacity-50 flex-shrink-0 ml-1" />
+        </div>
+        <div className="flex items-center justify-between">
+          <span
+            className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
+            style={{
+              color: colors.bg,
+              backgroundColor: `${colors.bg}20`,
+            }}
+          >
+            {data.status}
+          </span>
+          {data.phase && (
+            <span className="font-mono text-[10px] text-[#8b949e]">{data.phase}</span>
+          )}
+        </div>
+      </div>
+
+      <Handle
+        type="source"
+        position={Position.Bottom}
+        className="!bg-[#f59e0b] !w-3 !h-3 !border-2 !border-[#0a1628]"
+      />
+    </div>
+  );
+}
+
+const nodeTypes = {
+  definition: DefinitionNode,
+  contract: ContractNode,
+};
+
+function getStatusColor(status: string, isCheckpoint = false) {
+  if (isCheckpoint) {
+    switch (status) {
+      case "active":
+        return { bg: "#a78bfa", border: "#8b5cf6", text: "#5b21b6" };
+      case "completed":
+        return { bg: "#818cf8", border: "#6366f1", text: "#3730a3" };
+      case "pending":
+        return { bg: "#c4b5fd", border: "#a78bfa", text: "#6d28d9" };
+      case "failed":
+        return { bg: "#ef4444", border: "#dc2626", text: "#991b1b" };
+      default:
+        return { bg: "#a78bfa", border: "#8b5cf6", text: "#5b21b6" };
+    }
+  }
+  switch (status) {
+    case "active":
+      return { bg: "#4ade80", border: "#22c55e", text: "#166534" };
+    case "completed":
+      return { bg: "#60a5fa", border: "#3b82f6", text: "#1e40af" };
+    case "pending":
+      return { bg: "#f59e0b", border: "#d97706", text: "#92400e" };
+    case "blocked":
+      return { bg: "#ef4444", border: "#dc2626", text: "#991b1b" };
+    default:
+      return { bg: "#6b7280", border: "#4b5563", text: "#374151" };
+  }
+}
 
 export function ChainEditor({
   chain,
@@ -46,34 +226,19 @@ export function ChainEditor({
   onRefresh,
   onContractClick,
 }: ChainEditorProps) {
-  const canvasRef = useRef<HTMLDivElement>(null);
-  const [selectedNode, setSelectedNode] = useState<string | null>(null);
-  const [hoveredNode, setHoveredNode] = useState<string | null>(null);
   const [definitions, setDefinitions] = useState<ChainContractDefinition[]>([]);
   const [definitionGraph, setDefinitionGraph] = useState<ChainDefinitionGraphResponse | null>(null);
   const [showAddDefinition, setShowAddDefinition] = useState(false);
   const [isStarting, setIsStarting] = useState(false);
   const [isStopping, setIsStopping] = useState(false);
   const [error, setError] = useState<string | null>(null);
+  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
 
-  // Drag state
-  const [draggedNode, setDraggedNode] = useState<string | null>(null);
-  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
-  const [localPositions, setLocalPositions] = useState<Map<string, { x: number; y: number }>>(new Map());
-
-  // Edge drawing state
-  const [edgeDrawing, setEdgeDrawing] = useState<{
-    fromNode: string;
-    toPoint: { x: number; y: number };
-  } | null>(null);
-
-  // Context menu state (nodeId is null for canvas context menu)
-  const [contextMenu, setContextMenu] = useState<{
-    x: number;
-    y: number;
-    nodeId: string | null;
-    canvasPosition?: { gridX: number; gridY: number };
-  } | null>(null);
+  const [nodes, setNodes, onNodesChange] = useNodesState([]);
+  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
+
+  const showDefinitions = chain.status === "pending" || chain.status === "archived";
+  const canEdit = chain.status === "pending";
 
   // Load definitions when chain changes
   useEffect(() => {
@@ -92,152 +257,167 @@ export function ChainEditor({
     loadDefinitions();
   }, [chain.id]);
 
-  // Determine which view to show: definitions (pending chain) or contracts (active/completed)
-  const showDefinitions = chain.status === "pending" || chain.status === "archived";
-  const currentGraph = showDefinitions ? definitionGraph : graph;
-
-  // Use positions from graph nodes, with local overrides for dragging
-  const nodePositions = useMemo(() => {
-    const positions = new Map<string, { x: number; y: number }>();
-
-    if (showDefinitions && definitionGraph?.nodes) {
-      for (const node of definitionGraph.nodes) {
-        // Use local position if available (during drag), otherwise use server position
-        const localPos = localPositions.get(node.id);
-        if (localPos) {
-          positions.set(node.id, localPos);
-        } else {
-          positions.set(node.id, {
-            x: CANVAS_PADDING + (node.x || 0) * (NODE_WIDTH + 60),
-            y: CANVAS_PADDING + (node.y || 0) * (NODE_HEIGHT + 40),
-          });
+  // Convert definitions/contracts to React Flow nodes and edges
+  useEffect(() => {
+    if (showDefinitions && definitionGraph) {
+      const flowNodes: Node[] = definitionGraph.nodes.map((node) => ({
+        id: node.id,
+        type: "definition",
+        position: {
+          x: (node.x || 0) * GRID_SPACING_X,
+          y: (node.y || 0) * GRID_SPACING_Y,
+        },
+        data: {
+          name: node.name,
+          contractType: node.contractType,
+          isInstantiated: node.isInstantiated,
+          contractStatus: node.contractStatus,
+        },
+        draggable: canEdit,
+      }));
+
+      const flowEdges: Edge[] = definitionGraph.edges.map((edge, index) => ({
+        id: `${edge.from}-${edge.to}-${index}`,
+        source: edge.from,
+        target: edge.to,
+        type: "smoothstep",
+        animated: false,
+        style: { stroke: "#3f6fb3", strokeWidth: 2 },
+        markerEnd: { type: MarkerType.ArrowClosed, color: "#75aafc" },
+      }));
+
+      setNodes(flowNodes);
+      setEdges(flowEdges);
+    } else if (!showDefinitions && graph) {
+      const flowNodes: Node[] = graph.nodes.map((node) => ({
+        id: node.contractId,
+        type: "contract",
+        position: {
+          x: (node.x || 0) * GRID_SPACING_X,
+          y: (node.y || 0) * GRID_SPACING_Y,
+        },
+        data: {
+          name: node.name,
+          status: node.status,
+          phase: node.phase,
+        },
+        draggable: false,
+      }));
+
+      const flowEdges: Edge[] = graph.edges.map((edge, index) => ({
+        id: `${edge.from}-${edge.to}-${index}`,
+        source: edge.from,
+        target: edge.to,
+        type: "smoothstep",
+        animated: edge.from === selectedNodeId || edge.to === selectedNodeId,
+        style: { stroke: "#3f6fb3", strokeWidth: 2 },
+        markerEnd: { type: MarkerType.ArrowClosed, color: "#75aafc" },
+      }));
+
+      setNodes(flowNodes);
+      setEdges(flowEdges);
+    }
+  }, [showDefinitions, definitionGraph, graph, canEdit, selectedNodeId, setNodes, setEdges]);
+
+  // Handle node position changes (drag end)
+  const handleNodesChange = useCallback(
+    async (changes: NodeChange[]) => {
+      onNodesChange(changes);
+
+      // Save position changes to backend
+      for (const change of changes) {
+        if (change.type === "position" && change.dragging === false && change.position) {
+          const gridX = Math.round(change.position.x / GRID_SPACING_X);
+          const gridY = Math.round(change.position.y / GRID_SPACING_Y);
+
+          try {
+            await updateChainDefinition(chain.id, change.id, {
+              editorX: gridX,
+              editorY: gridY,
+            });
+          } catch (err) {
+            console.error("Failed to save position:", err);
+          }
         }
       }
-    } else if (!showDefinitions && graph?.nodes) {
-      for (const node of graph.nodes) {
-        positions.set(node.contractId, {
-          x: CANVAS_PADDING + (node.x || 0) * (NODE_WIDTH + 60),
-          y: CANVAS_PADDING + (node.y || 0) * (NODE_HEIGHT + 40),
-        });
-      }
-    }
+    },
+    [chain.id, onNodesChange]
+  );
 
-    return positions;
-  }, [showDefinitions, definitionGraph?.nodes, graph?.nodes, localPositions]);
+  // Handle new edge connections
+  const handleConnect = useCallback(
+    async (connection: Connection) => {
+      if (!connection.source || !connection.target) return;
 
-  // Canvas dimensions
-  const canvasDimensions = useMemo(() => {
-    if (nodePositions.size === 0) {
-      return { width: 600, height: 400 };
-    }
+      // Find the definitions
+      const sourceDef = definitions.find((d) => d.id === connection.source);
+      const targetDef = definitions.find((d) => d.id === connection.target);
 
-    let maxX = 0;
-    let maxY = 0;
-    for (const pos of nodePositions.values()) {
-      maxX = Math.max(maxX, pos.x + NODE_WIDTH);
-      maxY = Math.max(maxY, pos.y + NODE_HEIGHT);
-    }
+      if (!sourceDef || !targetDef) return;
 
-    return {
-      width: Math.max(600, maxX + CANVAS_PADDING),
-      height: Math.max(400, maxY + CANVAS_PADDING),
-    };
-  }, [nodePositions]);
+      // Add dependency: target depends on source
+      const currentDeps = targetDef.dependsOnNames || [];
+      if (!currentDeps.includes(sourceDef.name)) {
+        try {
+          await updateChainDefinition(chain.id, connection.target, {
+            dependsOn: [...currentDeps, sourceDef.name],
+          });
 
-  const handleNodeClick = useCallback((nodeId: string) => {
-    setSelectedNode(nodeId);
-  }, []);
+          // Refresh
+          const [defs, defGraph] = await Promise.all([
+            listChainDefinitions(chain.id),
+            getChainDefinitionGraph(chain.id),
+          ]);
+          setDefinitions(defs);
+          setDefinitionGraph(defGraph);
+        } catch (err) {
+          console.error("Failed to create dependency:", err);
+          setError(err instanceof Error ? err.message : "Failed to create dependency");
+        }
+      }
+    },
+    [chain.id, definitions]
+  );
 
-  const handleNodeDoubleClick = useCallback(
-    (nodeId: string) => {
-      // For definitions, we can't open a contract yet
-      if (showDefinitions) return;
-      onContractClick(nodeId);
+  // Handle node selection
+  const handleNodeClick = useCallback(
+    (_: React.MouseEvent, node: Node) => {
+      setSelectedNodeId(node.id);
     },
-    [onContractClick, showDefinitions]
+    []
   );
 
-  const getStatusColor = (status: string, isCheckpoint = false) => {
-    // Checkpoint contracts use purple/violet colors
-    if (isCheckpoint) {
-      switch (status) {
-        case "active":
-          return { bg: "#a78bfa", border: "#8b5cf6", text: "#5b21b6" };
-        case "completed":
-          return { bg: "#818cf8", border: "#6366f1", text: "#3730a3" };
-        case "pending":
-          return { bg: "#c4b5fd", border: "#a78bfa", text: "#6d28d9" };
-        case "failed":
-          return { bg: "#ef4444", border: "#dc2626", text: "#991b1b" };
-        default:
-          return { bg: "#a78bfa", border: "#8b5cf6", text: "#5b21b6" };
+  // Handle node double-click (open contract)
+  const handleNodeDoubleClick = useCallback(
+    (_: React.MouseEvent, node: Node) => {
+      if (!showDefinitions) {
+        onContractClick(node.id);
       }
-    }
-    switch (status) {
-      case "active":
-        return { bg: "#4ade80", border: "#22c55e", text: "#166534" };
-      case "completed":
-        return { bg: "#60a5fa", border: "#3b82f6", text: "#1e40af" };
-      case "pending":
-        return { bg: "#f59e0b", border: "#d97706", text: "#92400e" };
-      case "blocked":
-        return { bg: "#ef4444", border: "#dc2626", text: "#991b1b" };
-      default:
-        return { bg: "#6b7280", border: "#4b5563", text: "#374151" };
-    }
-  };
-
-  // Find selected item (definition or contract)
-  const selectedDefinition = showDefinitions && selectedNode
-    ? definitions.find((d) => d.id === selectedNode)
-    : null;
-  const selectedContract = !showDefinitions && selectedNode
-    ? chain.contracts.find((c) => c.contractId === selectedNode)
-    : null;
-
-  const handleStartChain = useCallback(async () => {
-    setIsStarting(true);
-    setError(null);
-    try {
-      await startChain(chain.id);
-      onRefresh();
-    } catch (err) {
-      setError(err instanceof Error ? err.message : "Failed to start chain");
-    } finally {
-      setIsStarting(false);
-    }
-  }, [chain.id, onRefresh]);
+    },
+    [showDefinitions, onContractClick]
+  );
 
-  const handleStopChain = useCallback(async () => {
-    if (!confirm("Are you sure you want to stop this chain?")) return;
-    setIsStopping(true);
-    setError(null);
-    try {
-      await stopChain(chain.id);
-      onRefresh();
-    } catch (err) {
-      setError(err instanceof Error ? err.message : "Failed to stop chain");
-    } finally {
-      setIsStopping(false);
-    }
-  }, [chain.id, onRefresh]);
+  // Handle pane click (deselect)
+  const handlePaneClick = useCallback(() => {
+    setSelectedNodeId(null);
+  }, []);
 
-  // Position for new definition (set from canvas right-click)
-  const [newDefinitionPosition, setNewDefinitionPosition] = useState<{ x: number; y: number } | null>(null);
+  // Find selected definition
+  const selectedDefinition = showDefinitions && selectedNodeId
+    ? definitions.find((d) => d.id === selectedNodeId)
+    : null;
 
-  // Find free space on the grid for new definitions
+  // Find free position for new definition
   const findFreePosition = useCallback(() => {
     if (!definitionGraph?.nodes || definitionGraph.nodes.length === 0) {
       return { x: 0, y: 0 };
     }
 
-    // Get all occupied positions
     const occupied = new Set<string>();
     for (const node of definitionGraph.nodes) {
       occupied.add(`${node.x},${node.y}`);
     }
 
-    // Find first free position by scanning row by row
     for (let y = 0; y < 10; y++) {
       for (let x = 0; x < 10; x++) {
         if (!occupied.has(`${x},${y}`)) {
@@ -246,243 +426,102 @@ export function ChainEditor({
       }
     }
 
-    // Fallback: place at the end of the last row
     const maxY = Math.max(...definitionGraph.nodes.map((n) => n.y || 0));
     return { x: 0, y: maxY + 1 };
   }, [definitionGraph?.nodes]);
 
-  const handleAddDefinition = useCallback(async (req: AddContractDefinitionRequest) => {
+  const handleAddDefinition = useCallback(
+    async (req: AddContractDefinitionRequest) => {
+      try {
+        const position = findFreePosition();
+        const reqWithPosition = { ...req, editorX: position.x, editorY: position.y };
+        await createChainDefinition(chain.id, reqWithPosition);
+
+        const [defs, defGraph] = await Promise.all([
+          listChainDefinitions(chain.id),
+          getChainDefinitionGraph(chain.id),
+        ]);
+        setDefinitions(defs);
+        setDefinitionGraph(defGraph);
+        setShowAddDefinition(false);
+      } catch (err) {
+        console.error("Failed to add definition:", err);
+        setError(err instanceof Error ? err.message : "Failed to add definition");
+      }
+    },
+    [chain.id, findFreePosition]
+  );
+
+  const handleDeleteDefinition = useCallback(
+    async (definitionId: string) => {
+      if (!confirm("Are you sure you want to delete this definition?")) return;
+      try {
+        await deleteChainDefinition(chain.id, definitionId);
+
+        const [defs, defGraph] = await Promise.all([
+          listChainDefinitions(chain.id),
+          getChainDefinitionGraph(chain.id),
+        ]);
+        setDefinitions(defs);
+        setDefinitionGraph(defGraph);
+        setSelectedNodeId(null);
+      } catch (err) {
+        console.error("Failed to delete definition:", err);
+        setError(err instanceof Error ? err.message : "Failed to delete definition");
+      }
+    },
+    [chain.id]
+  );
+
+  const handleStartChain = useCallback(async () => {
+    setIsStarting(true);
+    setError(null);
     try {
-      // Use specified position or find free space
-      const position = newDefinitionPosition || findFreePosition();
-      const reqWithPosition = { ...req, editorX: position.x, editorY: position.y };
-      await createChainDefinition(chain.id, reqWithPosition);
-      // Reload definitions
-      const [defs, defGraph] = await Promise.all([
-        listChainDefinitions(chain.id),
-        getChainDefinitionGraph(chain.id),
-      ]);
-      setDefinitions(defs);
-      setDefinitionGraph(defGraph);
-      setShowAddDefinition(false);
-      setNewDefinitionPosition(null);
+      await startChain(chain.id);
+      onRefresh();
     } catch (err) {
-      console.error("Failed to add definition:", err);
-      setError(err instanceof Error ? err.message : "Failed to add definition");
+      setError(err instanceof Error ? err.message : "Failed to start chain");
+    } finally {
+      setIsStarting(false);
     }
-  }, [chain.id, newDefinitionPosition, findFreePosition]);
+  }, [chain.id, onRefresh]);
 
-  const handleDeleteDefinition = useCallback(async (definitionId: string) => {
-    if (!confirm("Are you sure you want to delete this definition?")) return;
+  const handleStopChain = useCallback(async () => {
+    if (!confirm("Are you sure you want to stop this chain?")) return;
+    setIsStopping(true);
+    setError(null);
     try {
-      await deleteChainDefinition(chain.id, definitionId);
-      // Reload definitions
-      const [defs, defGraph] = await Promise.all([
-        listChainDefinitions(chain.id),
-        getChainDefinitionGraph(chain.id),
-      ]);
-      setDefinitions(defs);
-      setDefinitionGraph(defGraph);
-      setSelectedNode(null);
+      await stopChain(chain.id);
+      onRefresh();
     } catch (err) {
-      console.error("Failed to delete definition:", err);
-      setError(err instanceof Error ? err.message : "Failed to delete definition");
+      setError(err instanceof Error ? err.message : "Failed to stop chain");
+    } finally {
+      setIsStopping(false);
     }
-  }, [chain.id]);
-
-  // Refresh definitions helper
-  const refreshDefinitions = useCallback(async () => {
-    const [defs, defGraph] = await Promise.all([
-      listChainDefinitions(chain.id),
-      getChainDefinitionGraph(chain.id),
-    ]);
-    setDefinitions(defs);
-    setDefinitionGraph(defGraph);
-    setLocalPositions(new Map()); // Clear local positions
-  }, [chain.id]);
-
-  // Context menu handlers
-  const handleContextMenu = useCallback((e: React.MouseEvent, nodeId: string) => {
-    e.preventDefault();
-    e.stopPropagation();
-    setContextMenu({ x: e.clientX, y: e.clientY, nodeId });
-  }, []);
-
-  // Canvas context menu (for creating new definitions)
-  const handleCanvasContextMenu = useCallback((e: React.MouseEvent) => {
-    if (!showDefinitions || chain.status !== "pending") return;
-    e.preventDefault();
-
-    const canvasRect = canvasRef.current?.getBoundingClientRect();
-    if (!canvasRect) return;
-
-    // Calculate grid position from click
-    const clickX = e.clientX - canvasRect.left;
-    const clickY = e.clientY - canvasRect.top;
-    const gridX = Math.round((clickX - CANVAS_PADDING) / (NODE_WIDTH + 60));
-    const gridY = Math.round((clickY - CANVAS_PADDING) / (NODE_HEIGHT + 40));
-
-    setContextMenu({
-      x: e.clientX,
-      y: e.clientY,
-      nodeId: null,
-      canvasPosition: { gridX: Math.max(0, gridX), gridY: Math.max(0, gridY) },
-    });
-  }, [showDefinitions, chain.status]);
-
-  const closeContextMenu = useCallback(() => {
-    setContextMenu(null);
-  }, []);
+  }, [chain.id, onRefresh]);
 
-  // Handle creating definition at canvas position
-  const handleCreateAtPosition = useCallback((gridX: number, gridY: number) => {
-    setNewDefinitionPosition({ x: gridX, y: gridY });
-    setShowAddDefinition(true);
-    closeContextMenu();
-  }, [closeContextMenu]);
-
-  // Handle canvas click to close context menu
-  const handleCanvasClick = useCallback(() => {
-    if (contextMenu) {
-      closeContextMenu();
-    }
-  }, [contextMenu, closeContextMenu]);
-
-  // Drag handlers
-  const handleDragStart = useCallback((e: React.MouseEvent, nodeId: string) => {
-    if (!showDefinitions || chain.status !== "pending") return;
-    e.preventDefault();
-    const pos = nodePositions.get(nodeId);
-    if (!pos) return;
-
-    setDraggedNode(nodeId);
-    setDragOffset({
-      x: e.clientX - pos.x,
-      y: e.clientY - pos.y,
-    });
-  }, [showDefinitions, chain.status, nodePositions]);
-
-  const handleDragMove = useCallback((e: React.MouseEvent) => {
-    if (!draggedNode) return;
-
-    const canvasRect = canvasRef.current?.getBoundingClientRect();
-    if (!canvasRect) return;
-
-    const newX = Math.max(CANVAS_PADDING, e.clientX - dragOffset.x);
-    const newY = Math.max(CANVAS_PADDING, e.clientY - dragOffset.y);
-
-    setLocalPositions(prev => {
-      const newMap = new Map(prev);
-      newMap.set(draggedNode, { x: newX, y: newY });
-      return newMap;
-    });
-  }, [draggedNode, dragOffset]);
-
-  const handleDragEnd = useCallback(async () => {
-    if (!draggedNode) return;
-
-    const localPos = localPositions.get(draggedNode);
-    if (localPos) {
-      // Convert pixel position back to grid coordinates
-      const gridX = Math.round((localPos.x - CANVAS_PADDING) / (NODE_WIDTH + 60));
-      const gridY = Math.round((localPos.y - CANVAS_PADDING) / (NODE_HEIGHT + 40));
+  const handleRemoveDependency = useCallback(
+    async (nodeId: string, depName: string) => {
+      const def = definitions.find((d) => d.id === nodeId);
+      if (!def) return;
 
+      const newDeps = (def.dependsOnNames || []).filter((d) => d !== depName);
       try {
-        await updateChainDefinition(chain.id, draggedNode, {
-          editorX: gridX,
-          editorY: gridY,
-        });
-        await refreshDefinitions();
-      } catch (err) {
-        console.error("Failed to update position:", err);
-        setError(err instanceof Error ? err.message : "Failed to update position");
-      }
-    }
+        await updateChainDefinition(chain.id, nodeId, { dependsOn: newDeps });
 
-    setDraggedNode(null);
-  }, [draggedNode, localPositions, chain.id, refreshDefinitions]);
-
-  // Edge drawing handlers
-  const handleEdgeDrawStart = useCallback((e: React.MouseEvent, nodeId: string) => {
-    if (!showDefinitions || chain.status !== "pending") return;
-    e.preventDefault();
-    e.stopPropagation();
-
-    const canvasRect = canvasRef.current?.getBoundingClientRect();
-    if (!canvasRect) return;
-
-    const pos = nodePositions.get(nodeId);
-    if (!pos) return;
-
-    setEdgeDrawing({
-      fromNode: nodeId,
-      toPoint: {
-        x: pos.x + NODE_WIDTH / 2,
-        y: pos.y + NODE_HEIGHT,
-      },
-    });
-  }, [showDefinitions, chain.status, nodePositions]);
-
-  const handleEdgeDrawMove = useCallback((e: React.MouseEvent) => {
-    if (!edgeDrawing) return;
-
-    const canvasRect = canvasRef.current?.getBoundingClientRect();
-    if (!canvasRect) return;
-
-    setEdgeDrawing(prev => prev ? {
-      ...prev,
-      toPoint: {
-        x: e.clientX - canvasRect.left,
-        y: e.clientY - canvasRect.top,
-      },
-    } : null);
-  }, [edgeDrawing]);
-
-  const handleEdgeDrawEnd = useCallback(async (targetNodeId: string | null) => {
-    if (!edgeDrawing) return;
-
-    if (targetNodeId && targetNodeId !== edgeDrawing.fromNode) {
-      // Find the target definition and its current dependencies
-      const fromDef = definitions.find(d => d.id === edgeDrawing.fromNode);
-      const toDef = definitions.find(d => d.id === targetNodeId);
-
-      if (fromDef && toDef) {
-        // Add dependency: targetNodeId depends on fromNode
-        const currentDeps = toDef.dependsOnNames || [];
-        if (!currentDeps.includes(fromDef.name)) {
-          try {
-            await updateChainDefinition(chain.id, targetNodeId, {
-              dependsOn: [...currentDeps, fromDef.name],
-            });
-            await refreshDefinitions();
-          } catch (err) {
-            console.error("Failed to create dependency:", err);
-            setError(err instanceof Error ? err.message : "Failed to create dependency");
-          }
-        }
+        const [defs, defGraph] = await Promise.all([
+          listChainDefinitions(chain.id),
+          getChainDefinitionGraph(chain.id),
+        ]);
+        setDefinitions(defs);
+        setDefinitionGraph(defGraph);
+      } catch (err) {
+        console.error("Failed to remove dependency:", err);
+        setError(err instanceof Error ? err.message : "Failed to remove dependency");
       }
-    }
-
-    setEdgeDrawing(null);
-  }, [edgeDrawing, definitions, chain.id, refreshDefinitions]);
-
-  // Handle removing a dependency via context menu
-  const handleRemoveDependency = useCallback(async (nodeId: string, depName: string) => {
-    const def = definitions.find(d => d.id === nodeId);
-    if (!def) return;
-
-    const newDeps = (def.dependsOnNames || []).filter(d => d !== depName);
-    try {
-      await updateChainDefinition(chain.id, nodeId, {
-        dependsOn: newDeps,
-      });
-      await refreshDefinitions();
-      closeContextMenu();
-    } catch (err) {
-      console.error("Failed to remove dependency:", err);
-      setError(err instanceof Error ? err.message : "Failed to remove dependency");
-    }
-  }, [definitions, chain.id, refreshDefinitions, closeContextMenu]);
+    },
+    [chain.id, definitions]
+  );
 
   return (
     <div className="panel h-full flex flex-col">
@@ -494,7 +533,7 @@ export function ChainEditor({
               onClick={onBack}
               className="font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
             >
-              Back
+              ← Back
             </button>
             <div>
               <h2 className="font-mono text-sm text-[#dbe7ff]">{chain.name}</h2>
@@ -511,7 +550,6 @@ export function ChainEditor({
             >
               {chain.status}
             </span>
-            {/* Chain control buttons */}
             {chain.status === "pending" && definitions.length > 0 && (
               <button
                 onClick={handleStartChain}
@@ -547,13 +585,13 @@ export function ChainEditor({
 
       {/* Main content */}
       <div className="flex-1 flex min-h-0">
-        {/* DAG Canvas */}
-        <div className="flex-1 overflow-auto bg-[#050d18]">
+        {/* React Flow Canvas */}
+        <div className="flex-1 bg-[#050d18]">
           {loading ? (
             <div className="flex items-center justify-center h-full">
               <p className="font-mono text-xs text-[#8b949e]">Loading graph...</p>
             </div>
-          ) : !currentGraph || currentGraph.nodes.length === 0 ? (
+          ) : nodes.length === 0 ? (
             <div className="flex items-center justify-center h-full">
               <div className="text-center">
                 <p className="font-mono text-sm text-[#8b949e] mb-2">
@@ -566,7 +604,7 @@ export function ChainEditor({
                     ? "Add contract definitions to build your chain"
                     : "Start the chain to create contracts from definitions"}
                 </p>
-                {showDefinitions && (
+                {showDefinitions && canEdit && (
                   <button
                     onClick={() => setShowAddDefinition(true)}
                     className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors"
@@ -577,293 +615,39 @@ export function ChainEditor({
               </div>
             </div>
           ) : (
-            <div
-              ref={canvasRef}
-              className="relative"
-              style={{
-                width: canvasDimensions.width,
-                height: canvasDimensions.height,
-                minWidth: "100%",
-                minHeight: "100%",
-                cursor: draggedNode ? "grabbing" : edgeDrawing ? "crosshair" : "default",
-              }}
-              onClick={handleCanvasClick}
-              onContextMenu={handleCanvasContextMenu}
-              onMouseMove={(e) => {
-                if (draggedNode) handleDragMove(e);
-                if (edgeDrawing) handleEdgeDrawMove(e);
-              }}
-              onMouseUp={() => {
-                if (draggedNode) handleDragEnd();
-                if (edgeDrawing) handleEdgeDrawEnd(null);
-              }}
-              onMouseLeave={() => {
-                if (draggedNode) handleDragEnd();
-                if (edgeDrawing) setEdgeDrawing(null);
+            <ReactFlow
+              nodes={nodes}
+              edges={edges}
+              onNodesChange={handleNodesChange}
+              onEdgesChange={onEdgesChange}
+              onConnect={canEdit ? handleConnect : undefined}
+              onNodeClick={handleNodeClick}
+              onNodeDoubleClick={handleNodeDoubleClick}
+              onPaneClick={handlePaneClick}
+              nodeTypes={nodeTypes}
+              fitView
+              fitViewOptions={{ padding: 0.2 }}
+              minZoom={0.5}
+              maxZoom={2}
+              defaultEdgeOptions={{
+                type: "smoothstep",
+                style: { stroke: "#3f6fb3", strokeWidth: 2 },
+                markerEnd: { type: MarkerType.ArrowClosed, color: "#75aafc" },
               }}
+              connectionLineStyle={{ stroke: "#f59e0b", strokeWidth: 2 }}
+              proOptions={{ hideAttribution: true }}
             >
-              {/* SVG layer for edges */}
-              <svg
-                className="absolute inset-0 pointer-events-none"
-                style={{
-                  width: canvasDimensions.width,
-                  height: canvasDimensions.height,
-                }}
-              >
-                <defs>
-                  <marker
-                    id="arrowhead"
-                    markerWidth="10"
-                    markerHeight="7"
-                    refX="9"
-                    refY="3.5"
-                    orient="auto"
-                  >
-                    <polygon
-                      points="0 0, 10 3.5, 0 7"
-                      fill="#75aafc"
-                      opacity="0.6"
-                    />
-                  </marker>
-                  <marker
-                    id="arrowhead-preview"
-                    markerWidth="10"
-                    markerHeight="7"
-                    refX="9"
-                    refY="3.5"
-                    orient="auto"
-                  >
-                    <polygon
-                      points="0 0, 10 3.5, 0 7"
-                      fill="#f59e0b"
-                      opacity="0.8"
-                    />
-                  </marker>
-                </defs>
-                {currentGraph.edges.map((edge, index) => {
-                  const fromPos = nodePositions.get(edge.from);
-                  const toPos = nodePositions.get(edge.to);
-                  if (!fromPos || !toPos) return null;
-
-                  // Calculate edge path (from bottom of source to top of target)
-                  const startX = fromPos.x + NODE_WIDTH / 2;
-                  const startY = fromPos.y + NODE_HEIGHT;
-                  const endX = toPos.x + NODE_WIDTH / 2;
-                  const endY = toPos.y;
-
-                  // Bezier control points for smooth curves
-                  const midY = (startY + endY) / 2;
-
-                  const isHighlighted =
-                    hoveredNode === edge.from || hoveredNode === edge.to;
-
-                  return (
-                    <path
-                      key={`${edge.from}-${edge.to}-${index}`}
-                      d={`M ${startX} ${startY} C ${startX} ${midY}, ${endX} ${midY}, ${endX} ${endY}`}
-                      fill="none"
-                      stroke={isHighlighted ? "#75aafc" : "#3f6fb3"}
-                      strokeWidth={isHighlighted ? 2 : 1.5}
-                      strokeDasharray={isHighlighted ? "none" : "4 2"}
-                      markerEnd="url(#arrowhead)"
-                      opacity={isHighlighted ? 1 : 0.6}
-                    />
-                  );
-                })}
-                {/* Edge drawing preview line */}
-                {edgeDrawing && (() => {
-                  const fromPos = nodePositions.get(edgeDrawing.fromNode);
-                  if (!fromPos) return null;
-                  const startX = fromPos.x + NODE_WIDTH / 2;
-                  const startY = fromPos.y + NODE_HEIGHT;
-                  const endX = edgeDrawing.toPoint.x;
-                  const endY = edgeDrawing.toPoint.y;
-                  const midY = (startY + endY) / 2;
-                  return (
-                    <path
-                      d={`M ${startX} ${startY} C ${startX} ${midY}, ${endX} ${midY}, ${endX} ${endY}`}
-                      fill="none"
-                      stroke="#f59e0b"
-                      strokeWidth={2}
-                      strokeDasharray="6 3"
-                      markerEnd="url(#arrowhead-preview)"
-                      opacity={0.8}
-                    />
-                  );
-                })()}
-              </svg>
-
-              {/* Node layer */}
-              {showDefinitions && definitionGraph
-                ? definitionGraph.nodes.map((node) => {
-                    const pos = nodePositions.get(node.id);
-                    if (!pos) return null;
-
-                    const isCheckpoint = node.contractType === "checkpoint";
-                    const status = node.isInstantiated
-                      ? node.contractStatus || "pending"
-                      : "pending";
-                    const colors = getStatusColor(status, isCheckpoint);
-                    const isSelected = selectedNode === node.id;
-                    const isHovered = hoveredNode === node.id;
-
-                    const isDragging = draggedNode === node.id;
-                    const canEdit = chain.status === "pending";
-
-                    return (
-                      <div
-                        key={node.id}
-                        onClick={() => handleNodeClick(node.id)}
-                        onContextMenu={(e) => canEdit && handleContextMenu(e, node.id)}
-                        onMouseEnter={() => !draggedNode && setHoveredNode(node.id)}
-                        onMouseLeave={() => !draggedNode && setHoveredNode(null)}
-                        onMouseUp={() => edgeDrawing && handleEdgeDrawEnd(node.id)}
-                        className={`absolute ${
-                          isDragging ? "z-50 shadow-lg" : "transition-all duration-150"
-                        } ${
-                          isSelected
-                            ? isCheckpoint
-                              ? "ring-2 ring-[#a78bfa] ring-offset-2 ring-offset-[#050d18]"
-                              : "ring-2 ring-[#75aafc] ring-offset-2 ring-offset-[#050d18]"
-                            : ""
-                        }`}
-                        style={{
-                          left: pos.x,
-                          top: pos.y,
-                          width: NODE_WIDTH,
-                          height: NODE_HEIGHT,
-                          transform: isHovered && !isDragging ? "scale(1.02)" : "scale(1)",
-                          cursor: isDragging ? "grabbing" : canEdit ? "grab" : "pointer",
-                        }}
-                      >
-                        {/* Drag handle (entire node) */}
-                        <div
-                          className={`w-full h-full rounded-lg border-2 overflow-hidden ${
-                            isCheckpoint ? "bg-[#0f0a1e]" : "bg-[#0a1628]"
-                          }`}
-                          style={{
-                            borderColor: isSelected
-                              ? isCheckpoint
-                                ? "#a78bfa"
-                                : "#75aafc"
-                              : colors.border,
-                            borderStyle: node.isInstantiated ? "solid" : "dashed",
-                          }}
-                          onMouseDown={(e) => canEdit && handleDragStart(e, node.id)}
-                        >
-                          {/* Status indicator bar */}
-                          <div
-                            className="h-1.5"
-                            style={{ backgroundColor: colors.bg }}
-                          />
-                          {/* Content */}
-                          <div className="p-2">
-                            <div className="flex items-center justify-between mb-1">
-                              <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">
-                                {node.name}
-                              </span>
-                              {isCheckpoint ? (
-                                <CheckIcon className="w-4 h-4 text-[#a78bfa] opacity-70 flex-shrink-0 ml-1" />
-                              ) : (
-                                <ChainIcon className="w-4 h-4 text-[#75aafc] opacity-50 flex-shrink-0 ml-1" />
-                              )}
-                            </div>
-                            <div className="flex items-center justify-between">
-                              <span
-                                className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
-                                style={{
-                                  color: colors.bg,
-                                  backgroundColor: `${colors.bg}20`,
-                                }}
-                              >
-                                {node.isInstantiated ? status : isCheckpoint ? "checkpoint" : "definition"}
-                              </span>
-                              <span className="font-mono text-[10px] text-[#8b949e]">
-                                {node.contractType}
-                              </span>
-                            </div>
-                          </div>
-                        </div>
-                        {/* Edge drawing handle (connector dot at bottom) */}
-                        {canEdit && (isHovered || isSelected) && !isDragging && (
-                          <div
-                            className="absolute left-1/2 -translate-x-1/2 -bottom-2 w-4 h-4 rounded-full bg-[#f59e0b] border-2 border-[#0a1628] cursor-crosshair hover:scale-125 transition-transform"
-                            onMouseDown={(e) => handleEdgeDrawStart(e, node.id)}
-                            title="Drag to create dependency"
-                          />
-                        )}
-                      </div>
-                    );
-                  })
-                : graph?.nodes.map((node) => {
-                    const pos = nodePositions.get(node.contractId);
-                    if (!pos) return null;
-
-                    const colors = getStatusColor(node.status);
-                    const isSelected = selectedNode === node.contractId;
-                    const isHovered = hoveredNode === node.contractId;
-
-                    return (
-                      <div
-                        key={node.contractId}
-                        onClick={() => handleNodeClick(node.contractId)}
-                        onDoubleClick={() => handleNodeDoubleClick(node.contractId)}
-                        onMouseEnter={() => setHoveredNode(node.contractId)}
-                        onMouseLeave={() => setHoveredNode(null)}
-                        className={`absolute cursor-pointer transition-all duration-150 ${
-                          isSelected
-                            ? "ring-2 ring-[#75aafc] ring-offset-2 ring-offset-[#050d18]"
-                            : ""
-                        }`}
-                        style={{
-                          left: pos.x,
-                          top: pos.y,
-                          width: NODE_WIDTH,
-                          height: NODE_HEIGHT,
-                          transform: isHovered ? "scale(1.02)" : "scale(1)",
-                        }}
-                      >
-                        <div
-                          className="w-full h-full rounded-lg border-2 bg-[#0a1628] overflow-hidden"
-                          style={{
-                            borderColor: isSelected ? "#75aafc" : colors.border,
-                          }}
-                        >
-                          {/* Status indicator bar */}
-                          <div
-                            className="h-1.5"
-                            style={{ backgroundColor: colors.bg }}
-                          />
-                          {/* Content */}
-                          <div className="p-2">
-                            <div className="flex items-center justify-between mb-1">
-                              <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">
-                                {node.name}
-                              </span>
-                              <ChainIcon className="w-4 h-4 text-[#75aafc] opacity-50 flex-shrink-0 ml-1" />
-                            </div>
-                            <div className="flex items-center justify-between">
-                              <span
-                                className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
-                                style={{
-                                  color: colors.bg,
-                                  backgroundColor: `${colors.bg}20`,
-                                }}
-                              >
-                                {node.status}
-                              </span>
-                              {node.phase && (
-                                <span className="font-mono text-[10px] text-[#8b949e]">
-                                  {node.phase}
-                                </span>
-                              )}
-                            </div>
-                          </div>
-                        </div>
-                      </div>
-                    );
-                  })}
-            </div>
+              <Background
+                variant={BackgroundVariant.Dots}
+                gap={20}
+                size={1}
+                color="#1a2744"
+              />
+              <Controls
+                className="!bg-[#0a1628] !border-[rgba(117,170,252,0.3)] !rounded-lg"
+                showInteractive={false}
+              />
+            </ReactFlow>
           )}
         </div>
 
@@ -871,17 +655,9 @@ export function ChainEditor({
         {selectedDefinition && (
           <DefinitionDetailPanel
             definition={selectedDefinition}
-            onClose={() => setSelectedNode(null)}
+            onClose={() => setSelectedNodeId(null)}
             onDelete={handleDeleteDefinition}
-          />
-        )}
-        {selectedContract && (
-          <ContractDetailPanel
-            contract={selectedContract}
-            allContracts={chain.contracts}
-            onClose={() => setSelectedNode(null)}
-            onSelectContract={setSelectedNode}
-            onOpenContract={onContractClick}
+            onRemoveDependency={handleRemoveDependency}
           />
         )}
       </div>
@@ -892,18 +668,18 @@ export function ChainEditor({
           {showDefinitions ? (
             <>
               <span>{definitions.length} definitions</span>
-              {chain.status === "pending" && (
+              {canEdit && (
                 <>
                   <span className="text-[#556677]">|</span>
                   <span className="text-[#556677]">Drag nodes to reposition</span>
                   <span className="text-[#556677]">|</span>
-                  <span className="text-[#556677]">Drag from <span className="text-[#f59e0b]">●</span> to link</span>
-                  <span className="text-[#556677]">|</span>
-                  <span className="text-[#556677]">Right-click for options</span>
+                  <span className="text-[#556677]">
+                    Drag from <span className="text-[#f59e0b]">●</span> to link
+                  </span>
                 </>
               )}
               <span className="flex-1" />
-              {chain.status === "pending" && (
+              {canEdit && (
                 <button
                   onClick={() => setShowAddDefinition(true)}
                   className="text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
@@ -916,7 +692,8 @@ export function ChainEditor({
             <>
               <span>{chain.contracts.length} contracts</span>
               <span>
-                {chain.contracts.filter((c) => c.contractStatus === "completed").length} completed
+                {chain.contracts.filter((c) => c.contractStatus === "completed").length}{" "}
+                completed
               </span>
               <span>
                 {chain.contracts.filter((c) => c.contractStatus === "active").length} active
@@ -936,202 +713,36 @@ export function ChainEditor({
           onCancel={() => setShowAddDefinition(false)}
         />
       )}
-
-      {/* Context Menu */}
-      {contextMenu && (
-        <ContextMenu
-          x={contextMenu.x}
-          y={contextMenu.y}
-          nodeId={contextMenu.nodeId}
-          canvasPosition={contextMenu.canvasPosition}
-          definition={contextMenu.nodeId ? definitions.find((d) => d.id === contextMenu.nodeId) : undefined}
-          allDefinitions={definitions}
-          onClose={closeContextMenu}
-          onDelete={handleDeleteDefinition}
-          onRemoveDependency={handleRemoveDependency}
-          onCreateAtPosition={handleCreateAtPosition}
-        />
-      )}
-    </div>
-  );
-}
-
-// Context Menu Component
-interface ContextMenuProps {
-  x: number;
-  y: number;
-  nodeId: string | null;
-  canvasPosition?: { gridX: number; gridY: number };
-  definition?: ChainContractDefinition;
-  allDefinitions: ChainContractDefinition[];
-  onClose: () => void;
-  onDelete: (id: string) => void;
-  onRemoveDependency: (nodeId: string, depName: string) => void;
-  onCreateAtPosition: (gridX: number, gridY: number) => void;
-}
-
-function ContextMenu({
-  x,
-  y,
-  nodeId,
-  canvasPosition,
-  definition,
-  allDefinitions: _allDefinitions,
-  onClose,
-  onDelete,
-  onRemoveDependency,
-  onCreateAtPosition,
-}: ContextMenuProps) {
-  const menuRef = useRef<HTMLDivElement>(null);
-
-  // Close on click outside
-  useEffect(() => {
-    const handleClickOutside = (e: MouseEvent) => {
-      if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
-        onClose();
-      }
-    };
-    document.addEventListener("mousedown", handleClickOutside);
-    return () => document.removeEventListener("mousedown", handleClickOutside);
-  }, [onClose]);
-
-  // Close on escape
-  useEffect(() => {
-    const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === "Escape") onClose();
-    };
-    document.addEventListener("keydown", handleKeyDown);
-    return () => document.removeEventListener("keydown", handleKeyDown);
-  }, [onClose]);
-
-  // Canvas context menu (no node selected)
-  if (!nodeId && canvasPosition) {
-    return (
-      <div
-        ref={menuRef}
-        className="fixed z-50 min-w-40 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] shadow-xl rounded-lg overflow-hidden"
-        style={{ left: x, top: y }}
-      >
-        <div className="py-1">
-          <button
-            onClick={() => onCreateAtPosition(canvasPosition.gridX, canvasPosition.gridY)}
-            className="w-full px-3 py-2 text-left font-mono text-xs text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.1)] transition-colors flex items-center gap-2"
-          >
-            <PlusIcon className="w-3 h-3" />
-            Create Definition Here
-          </button>
-        </div>
-      </div>
-    );
-  }
-
-  // Node context menu requires both nodeId and definition
-  if (!nodeId || !definition) return null;
-
-  const dependencies = definition.dependsOnNames || [];
-
-  return (
-    <div
-      ref={menuRef}
-      className="fixed z-50 min-w-48 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] shadow-xl rounded-lg overflow-hidden"
-      style={{ left: x, top: y }}
-    >
-      {/* Header */}
-      <div className="px-3 py-2 border-b border-[rgba(117,170,252,0.2)] bg-[#0f1c32]">
-        <span className="font-mono text-xs text-[#dbe7ff]">{definition.name}</span>
-      </div>
-
-      {/* Actions */}
-      <div className="py-1">
-        <button
-          onClick={() => {
-            onDelete(nodeId);
-            onClose();
-          }}
-          className="w-full px-3 py-2 text-left font-mono text-xs text-red-400 hover:bg-red-400/10 transition-colors flex items-center gap-2"
-        >
-          <TrashIcon className="w-3 h-3" />
-          Delete Definition
-        </button>
-      </div>
-
-      {/* Dependencies section */}
-      {dependencies.length > 0 && (
-        <>
-          <div className="border-t border-[rgba(117,170,252,0.2)]" />
-          <div className="px-3 py-2">
-            <span className="font-mono text-[10px] text-[#8b949e] uppercase">Dependencies</span>
-          </div>
-          <div className="pb-1">
-            {dependencies.map((depName) => (
-              <div
-                key={depName}
-                className="px-3 py-1.5 flex items-center justify-between hover:bg-[rgba(117,170,252,0.1)] group"
-              >
-                <span className="font-mono text-xs text-[#9bc3ff]">{depName}</span>
-                <button
-                  onClick={() => onRemoveDependency(nodeId, depName)}
-                  className="font-mono text-[10px] text-red-400 opacity-0 group-hover:opacity-100 transition-opacity hover:underline"
-                >
-                  Remove
-                </button>
-              </div>
-            ))}
-          </div>
-        </>
-      )}
-
-      {/* Hint */}
-      <div className="border-t border-[rgba(117,170,252,0.2)] px-3 py-2">
-        <span className="font-mono text-[10px] text-[#556677]">
-          Drag from connector to create dependency
-        </span>
-      </div>
     </div>
   );
 }
 
-// Trash Icon
-function TrashIcon({ className }: { className?: string }) {
-  return (
-    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
-      <path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
-    </svg>
-  );
-}
-
-// Plus Icon
-function PlusIcon({ className }: { className?: string }) {
-  return (
-    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
-      <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
-    </svg>
-  );
-}
-
+// Detail panel for definitions
 interface DefinitionDetailPanelProps {
   definition: ChainContractDefinition;
   onClose: () => void;
   onDelete: (id: string) => void;
+  onRemoveDependency: (nodeId: string, depName: string) => void;
 }
 
 function DefinitionDetailPanel({
   definition,
   onClose,
   onDelete,
+  onRemoveDependency,
 }: DefinitionDetailPanelProps) {
+  const dependencies = definition.dependsOnNames || [];
+
   return (
     <div className="w-72 border-l border-[rgba(117,170,252,0.2)] bg-[#0a1628] overflow-y-auto">
       <div className="p-3 border-b border-[rgba(117,170,252,0.2)]">
         <div className="flex items-center justify-between mb-2">
-          <h3 className="font-mono text-xs text-[#75aafc] uppercase">
-            Definition Details
-          </h3>
+          <h3 className="font-mono text-xs text-[#75aafc] uppercase">Definition Details</h3>
           <button
             onClick={onClose}
             className="font-mono text-xs text-[#8b949e] hover:text-[#dbe7ff]"
           >
-            Close
+            ✕
           </button>
         </div>
       </div>
@@ -1151,65 +762,45 @@ function DefinitionDetailPanel({
             <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
               Description
             </label>
-            <p className="font-mono text-xs text-[#dbe7ff]">{definition.description}</p>
+            <p className="font-mono text-xs text-[#9bc3ff]">{definition.description}</p>
           </div>
         )}
 
-        {/* Contract Type */}
+        {/* Type */}
         <div>
           <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Contract Type
+            Type
           </label>
-          <span className="font-mono text-xs text-[#dbe7ff]">{definition.contractType}</span>
-        </div>
-
-        {/* Initial Phase */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Initial Phase
-          </label>
-          <span className="font-mono text-xs text-[#dbe7ff]">
-            {definition.initialPhase || "plan"}
-          </span>
+          <p className="font-mono text-xs text-[#9bc3ff]">{definition.contractType}</p>
         </div>
 
         {/* Dependencies */}
-        {definition.dependsOnNames && definition.dependsOnNames.length > 0 && (
+        {dependencies.length > 0 && (
           <div>
             <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-              Depends On
+              Dependencies
             </label>
             <div className="space-y-1">
-              {definition.dependsOnNames.map((depName) => (
-                <span
-                  key={depName}
-                  className="block font-mono text-xs text-[#9bc3ff]"
+              {dependencies.map((dep) => (
+                <div
+                  key={dep}
+                  className="flex items-center justify-between bg-[rgba(117,170,252,0.1)] px-2 py-1 rounded"
                 >
-                  {depName}
-                </span>
-              ))}
-            </div>
-          </div>
-        )}
-
-        {/* Tasks */}
-        {definition.tasks && definition.tasks.length > 0 && (
-          <div>
-            <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-              Tasks ({definition.tasks.length})
-            </label>
-            <div className="space-y-1">
-              {definition.tasks.map((task, i) => (
-                <div key={i} className="font-mono text-xs text-[#dbe7ff]">
-                  {task.name}
+                  <span className="font-mono text-xs text-[#9bc3ff]">{dep}</span>
+                  <button
+                    onClick={() => onRemoveDependency(definition.id, dep)}
+                    className="font-mono text-[10px] text-red-400 hover:text-red-300"
+                  >
+                    ✕
+                  </button>
                 </div>
               ))}
             </div>
           </div>
         )}
 
-        {/* Actions */}
-        <div className="pt-2 border-t border-[rgba(117,170,252,0.2)]">
+        {/* Delete button */}
+        <div className="pt-4 border-t border-[rgba(117,170,252,0.2)]">
           <button
             onClick={() => onDelete(definition.id)}
             className="w-full px-3 py-2 font-mono text-xs text-red-400 border border-red-400/30 hover:bg-red-400/10 transition-colors"
@@ -1222,150 +813,19 @@ function DefinitionDetailPanel({
   );
 }
 
-interface ContractDetailPanelProps {
-  contract: ChainContractDetail;
-  allContracts: ChainContractDetail[];
-  onClose: () => void;
-  onSelectContract: (contractId: string) => void;
-  onOpenContract: (contractId: string) => void;
-}
-
-function ContractDetailPanel({
-  contract,
-  allContracts,
-  onClose,
-  onSelectContract,
-  onOpenContract,
-}: ContractDetailPanelProps) {
-  return (
-    <div className="w-72 border-l border-[rgba(117,170,252,0.2)] bg-[#0a1628] overflow-y-auto">
-      <div className="p-3 border-b border-[rgba(117,170,252,0.2)]">
-        <div className="flex items-center justify-between mb-2">
-          <h3 className="font-mono text-xs text-[#75aafc] uppercase">
-            Contract Details
-          </h3>
-          <button
-            onClick={onClose}
-            className="font-mono text-xs text-[#8b949e] hover:text-[#dbe7ff]"
-          >
-            Close
-          </button>
-        </div>
-      </div>
-
-      <div className="p-3 space-y-4">
-        {/* Name */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Name
-          </label>
-          <p className="font-mono text-sm text-[#dbe7ff]">
-            {contract.contractName}
-          </p>
-        </div>
-
-        {/* Status */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Status
-          </label>
-          <span
-            className={`font-mono text-xs uppercase ${
-              statusColors[contract.contractStatus] || "text-[#555]"
-            }`}
-          >
-            {contract.contractStatus}
-          </span>
-        </div>
-
-        {/* Phase */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Phase
-          </label>
-          <span className="font-mono text-sm text-[#dbe7ff]">
-            {contract.contractPhase}
-          </span>
-        </div>
-
-        {/* Dependencies */}
-        {contract.dependsOn && contract.dependsOn.length > 0 && (
-          <div>
-            <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-              Depends On
-            </label>
-            <div className="space-y-1">
-              {contract.dependsOn.map((depId) => {
-                const dep = allContracts.find((c) => c.contractId === depId);
-                return (
-                  <button
-                    key={depId}
-                    onClick={() => onSelectContract(depId)}
-                    className="block w-full text-left font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] truncate"
-                  >
-                    {dep?.contractName || depId}
-                  </button>
-                );
-              })}
-            </div>
-          </div>
-        )}
-
-        {/* Order Index */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Order Index
-          </label>
-          <p className="font-mono text-xs text-[#dbe7ff]">
-            {contract.orderIndex}
-          </p>
-        </div>
-
-        {/* Created */}
-        <div>
-          <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-            Created
-          </label>
-          <p className="font-mono text-xs text-[#dbe7ff]">
-            {new Date(contract.createdAt).toLocaleString()}
-          </p>
-        </div>
-
-        {/* Actions */}
-        <div className="pt-2 border-t border-[rgba(117,170,252,0.2)]">
-          <button
-            onClick={() => onOpenContract(contract.contractId)}
-            className="w-full px-3 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors"
-          >
-            Open Contract
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
-
+// Add Definition Modal
 interface AddDefinitionModalProps {
   existingNames: string[];
   onSubmit: (req: AddContractDefinitionRequest) => void;
   onCancel: () => void;
 }
 
-function AddDefinitionModal({
-  existingNames,
-  onSubmit,
-  onCancel,
-}: AddDefinitionModalProps) {
+function AddDefinitionModal({ existingNames, onSubmit, onCancel }: AddDefinitionModalProps) {
   const [name, setName] = useState("");
   const [description, setDescription] = useState("");
   const [contractType, setContractType] = useState("simple");
   const [initialPhase, setInitialPhase] = useState("plan");
   const [dependsOn, setDependsOn] = useState<string[]>([]);
-  // Checkpoint validation options
-  const [checkDeliverables, setCheckDeliverables] = useState(true);
-  const [runTests, setRunTests] = useState(false);
-  const [checkContent, setCheckContent] = useState("");
-  const [onFailure, setOnFailure] = useState<"block" | "retry" | "warn">("block");
 
   const isCheckpoint = contractType === "checkpoint";
 
@@ -1375,18 +835,9 @@ function AddDefinitionModal({
       name: name.trim(),
       description: description.trim() || undefined,
       contractType,
-      initialPhase: isCheckpoint ? "execute" : initialPhase, // Checkpoints always start in execute
+      initialPhase: isCheckpoint ? "execute" : initialPhase,
       dependsOn: dependsOn.length > 0 ? dependsOn : undefined,
     };
-    // Add validation config for checkpoint contracts
-    if (isCheckpoint) {
-      req.validation = {
-        checkDeliverables,
-        runTests,
-        checkContent: checkContent.trim() || undefined,
-        onFailure,
-      };
-    }
     onSubmit(req);
   };
 
@@ -1406,202 +857,131 @@ function AddDefinitionModal({
         <div className="space-y-4">
           {/* Name */}
           <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
-              Name
+            <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
+              Name *
             </label>
             <input
               type="text"
               value={name}
               onChange={(e) => setName(e.target.value)}
+              className="w-full px-3 py-2 bg-[#050d18] border border-[rgba(117,170,252,0.3)] font-mono text-sm text-[#dbe7ff] focus:border-[#75aafc] focus:outline-none"
               placeholder="e.g., Research Phase"
-              className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
-              autoFocus
             />
           </div>
 
           {/* Description */}
           <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
-              Description (optional)
+            <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
+              Description
             </label>
             <textarea
               value={description}
               onChange={(e) => setDescription(e.target.value)}
-              placeholder="What does this contract accomplish?"
-              rows={2}
-              className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
+              className="w-full px-3 py-2 bg-[#050d18] border border-[rgba(117,170,252,0.3)] font-mono text-sm text-[#dbe7ff] focus:border-[#75aafc] focus:outline-none resize-none h-20"
+              placeholder="Optional description..."
             />
           </div>
 
-          {/* Contract Type */}
+          {/* Type */}
           <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
+            <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
               Contract Type
             </label>
             <select
               value={contractType}
               onChange={(e) => setContractType(e.target.value)}
-              className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
+              className="w-full px-3 py-2 bg-[#050d18] border border-[rgba(117,170,252,0.3)] font-mono text-sm text-[#dbe7ff] focus:border-[#75aafc] focus:outline-none"
             >
               <option value="simple">Simple</option>
-              <option value="specification">Specification</option>
-              <option value="execute">Execute</option>
-              <option value="checkpoint">Checkpoint (Validation)</option>
+              <option value="checkpoint">Checkpoint (validation)</option>
             </select>
-            {isCheckpoint && (
-              <p className="mt-1 font-mono text-[10px] text-[#a78bfa]">
-                Checkpoint contracts validate outputs before allowing downstream contracts to proceed.
-              </p>
-            )}
           </div>
 
-          {/* Initial Phase - hidden for checkpoints */}
+          {/* Initial Phase (not for checkpoints) */}
           {!isCheckpoint && (
             <div>
-              <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
+              <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
                 Initial Phase
               </label>
               <select
                 value={initialPhase}
                 onChange={(e) => setInitialPhase(e.target.value)}
-                className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
+                className="w-full px-3 py-2 bg-[#050d18] border border-[rgba(117,170,252,0.3)] font-mono text-sm text-[#dbe7ff] focus:border-[#75aafc] focus:outline-none"
               >
                 <option value="plan">Plan</option>
                 <option value="execute">Execute</option>
-                <option value="review">Review</option>
               </select>
             </div>
           )}
 
-          {/* Checkpoint Validation Options */}
-          {isCheckpoint && (
-            <div className="p-3 bg-[#0f0a1e] border border-[#6366f1]/30 space-y-3">
-              <h4 className="font-mono text-xs text-[#a78bfa] uppercase">Validation Options</h4>
-
-              <label className="flex items-center gap-2 cursor-pointer">
-                <input
-                  type="checkbox"
-                  checked={checkDeliverables}
-                  onChange={(e) => setCheckDeliverables(e.target.checked)}
-                  className="accent-[#a78bfa]"
-                />
-                <span className="font-mono text-xs text-[#dbe7ff]">Check required deliverables exist</span>
-              </label>
-
-              <label className="flex items-center gap-2 cursor-pointer">
-                <input
-                  type="checkbox"
-                  checked={runTests}
-                  onChange={(e) => setRunTests(e.target.checked)}
-                  className="accent-[#a78bfa]"
-                />
-                <span className="font-mono text-xs text-[#dbe7ff]">Run test suite</span>
-              </label>
-
-              <div>
-                <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-                  Custom Validation Instructions (optional)
-                </label>
-                <textarea
-                  value={checkContent}
-                  onChange={(e) => setCheckContent(e.target.value)}
-                  placeholder="Additional criteria for Claude to check..."
-                  rows={3}
-                  className="w-full px-2 py-1.5 bg-[#0d1b2d] border border-[#6366f1]/50 text-[#dbe7ff] font-mono text-xs focus:outline-none focus:border-[#a78bfa] resize-none"
-                />
-              </div>
-
-              <div>
-                <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
-                  On Failure
-                </label>
-                <select
-                  value={onFailure}
-                  onChange={(e) => setOnFailure(e.target.value as "block" | "retry" | "warn")}
-                  className="w-full px-2 py-1.5 bg-[#0d1b2d] border border-[#6366f1]/50 text-[#dbe7ff] font-mono text-xs focus:outline-none focus:border-[#a78bfa]"
-                >
-                  <option value="block">Block - Stop chain until fixed</option>
-                  <option value="retry">Retry - Retry upstream contracts</option>
-                  <option value="warn">Warn - Log warning but continue</option>
-                </select>
-              </div>
-            </div>
-          )}
-
           {/* Dependencies */}
           {existingNames.length > 0 && (
             <div>
-              <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
+              <label className="block font-mono text-[10px] text-[#8b949e] uppercase mb-1">
                 Depends On
               </label>
-              <div className="space-y-1 max-h-32 overflow-y-auto">
+              <div className="flex flex-wrap gap-2">
                 {existingNames.map((depName) => (
-                  <label key={depName} className="flex items-center gap-2 cursor-pointer">
-                    <input
-                      type="checkbox"
-                      checked={dependsOn.includes(depName)}
-                      onChange={() => toggleDependency(depName)}
-                      className="accent-[#75aafc]"
-                    />
-                    <span className="font-mono text-xs text-[#dbe7ff]">{depName}</span>
-                  </label>
+                  <button
+                    key={depName}
+                    type="button"
+                    onClick={() => toggleDependency(depName)}
+                    className={`px-2 py-1 font-mono text-xs border transition-colors ${
+                      dependsOn.includes(depName)
+                        ? "bg-[#75aafc]/20 border-[#75aafc] text-[#75aafc]"
+                        : "border-[rgba(117,170,252,0.3)] text-[#8b949e] hover:border-[#75aafc]"
+                    }`}
+                  >
+                    {depName}
+                  </button>
                 ))}
               </div>
             </div>
           )}
+        </div>
 
-          {/* Actions */}
-          <div className="flex gap-2 justify-end pt-2">
-            <button
-              onClick={onCancel}
-              className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
-            >
-              Cancel
-            </button>
-            <button
-              onClick={handleSubmit}
-              disabled={!name.trim()}
-              className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
-            >
-              Add Definition
-            </button>
-          </div>
+        {/* Actions */}
+        <div className="flex justify-end gap-2 mt-6">
+          <button
+            onClick={onCancel}
+            className="px-4 py-2 font-mono text-xs text-[#8b949e] border border-[rgba(117,170,252,0.3)] hover:border-[#75aafc] transition-colors"
+          >
+            Cancel
+          </button>
+          <button
+            onClick={handleSubmit}
+            disabled={!name.trim()}
+            className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50"
+          >
+            Add Definition
+          </button>
         </div>
       </div>
     </div>
   );
 }
 
+// Icons
 function ChainIcon({ className }: { className?: string }) {
   return (
-    <svg
-      className={className}
-      viewBox="0 0 24 24"
-      fill="none"
-      stroke="currentColor"
-      strokeWidth="2"
-      strokeLinecap="round"
-      strokeLinejoin="round"
-    >
-      <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
-      <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
+    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+      <path
+        strokeLinecap="round"
+        strokeLinejoin="round"
+        d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
+      />
     </svg>
   );
 }
 
 function CheckIcon({ className }: { className?: string }) {
   return (
-    <svg
-      className={className}
-      viewBox="0 0 24 24"
-      fill="none"
-      stroke="currentColor"
-      strokeWidth="2"
-      strokeLinecap="round"
-      strokeLinejoin="round"
-    >
-      <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
-      <polyline points="22 4 12 14.01 9 11.01" />
+    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+      <path
+        strokeLinecap="round"
+        strokeLinejoin="round"
+        d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
+      />
     </svg>
   );
 }
-- 
cgit v1.2.3

