@pixerate/editor 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/canvas/layout.ts","../../src/agent/planner.ts","../../src/agent/tools.ts"],"sourcesContent":["import dagre from '@dagrejs/dagre';\nimport type {\n\tCanvasEdge,\n\tCanvasLayoutOptions,\n\tCanvasNode,\n\tIncrementalLayoutOptions,\n\tPlaceBesideOptions,\n\tRect,\n\tSide,\n\tXYPosition\n} from './types.js';\n\nexport const DEFAULT_NODE_WIDTH = 200;\nexport const DEFAULT_NODE_HEIGHT = 60;\nexport const DEFAULT_PLACEMENT_GAP = 48;\n\n/**\n * Calculates upper-left flow coordinate for an element to be centered in the viewport.\n */\nexport function getCenteredNodePosition(\n\twidth = 0,\n\theight = 0,\n\tscreenToFlowPosition: (position: XYPosition) => XYPosition,\n\tscreenDimensions?: { width: number; height: number }\n): XYPosition {\n\tconst rect =\n\t\tscreenDimensions ??\n\t\t(typeof document !== 'undefined'\n\t\t\t? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }\n\t\t\t: { width: 1920, height: 1080 });\n\n\tconst flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });\n\treturn {\n\t\tx: flowPos.x - width / 2,\n\t\ty: flowPos.y - height / 2\n\t};\n}\n\n/**\n * Updates positions of nodes so their collective bounding box is centered in the current viewport.\n */\nexport function centerNodes<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\tscreenToFlowPosition: (position: XYPosition) => XYPosition,\n\tgetNodesBounds: (nodes: TNode[]) => Rect,\n\tscreenDimensions?: { width: number; height: number }\n): TNode[] {\n\tif (nodes.length === 0) {\n\t\treturn [];\n\t}\n\n\tconst bounds = getNodesBounds(nodes);\n\tconst boundingBoxOrigin = { x: bounds.x, y: bounds.y };\n\n\tconst centeredPosition = getCenteredNodePosition(\n\t\tbounds.width,\n\t\tbounds.height,\n\t\tscreenToFlowPosition,\n\t\tscreenDimensions\n\t);\n\n\tconst deltaX = centeredPosition.x - boundingBoxOrigin.x;\n\tconst deltaY = centeredPosition.y - boundingBoxOrigin.y;\n\n\treturn nodes.map((node) => ({\n\t\t...node,\n\t\tposition: {\n\t\t\tx: node.position.x + deltaX,\n\t\t\ty: node.position.y + deltaY\n\t\t}\n\t}));\n}\n\n/**\n * Automatically calculates directed hierarchical layout of nodes and edges using Dagre.\n */\nexport function getLayoutedNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\tgetNodesBounds?: (nodes: TNode[]) => Rect,\n\toptions: CanvasLayoutOptions = {}\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\n\tconst {\n\t\tdirection = 'LR',\n\t\tranksep = 100,\n\t\tnodesep = 25,\n\t\tdefaultNodeWidth = DEFAULT_NODE_WIDTH,\n\t\tdefaultNodeHeight = DEFAULT_NODE_HEIGHT\n\t} = options;\n\n\tconst dagreGraph = new dagre.graphlib.Graph();\n\tdagreGraph.setDefaultEdgeLabel(() => ({}));\n\tdagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });\n\n\tconst nodeIds = new Set(nodes.map((node) => node.id));\n\n\tnodes.forEach((node) => {\n\t\tconst nodeWidth = node.measured?.width ?? (node as any).width ?? defaultNodeWidth;\n\t\tconst nodeHeight = node.measured?.height ?? (node as any).height ?? defaultNodeHeight;\n\t\tdagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });\n\t});\n\n\tedges.forEach((edge) => {\n\t\tif (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {\n\t\t\tdagreGraph.setEdge(edge.source, edge.target);\n\t\t}\n\t});\n\n\tdagre.layout(dagreGraph);\n\n\treturn nodes.map((node) => {\n\t\tconst nodeWithPosition = dagreGraph.node(node.id);\n\t\tconst nodeWidth = node.measured?.width ?? (node as any).width ?? defaultNodeWidth;\n\t\tconst nodeHeight = node.measured?.height ?? (node as any).height ?? defaultNodeHeight;\n\n\t\treturn {\n\t\t\t...node,\n\t\t\tposition: {\n\t\t\t\tx: nodeWithPosition.x - nodeWidth / 2,\n\t\t\t\ty: nodeWithPosition.y - nodeHeight / 2\n\t\t\t}\n\t\t};\n\t});\n}\n\n/**\n * Heuristic text-based node size estimator when DOM measurement is not available.\n */\nexport function estimateNodeSize(\n\tnode: CanvasNode,\n\tdefaultWidth = DEFAULT_NODE_WIDTH,\n\tdefaultHeight = DEFAULT_NODE_HEIGHT\n): { width: number; height: number } {\n\tconst label = String(node.data?.label ?? node.data?.title ?? node.id ?? '');\n\tconst lines = label.split(/\\n|<br\\s*\\/?>/);\n\tconst longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, '').length), 1);\n\tconst width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));\n\tconst height = Math.max(defaultHeight, lines.length * 20 + 24);\n\treturn { width, height };\n}\n\n/**\n * Computes bounding rectangle of an array of rectangles.\n */\nexport function boundsOf(rects: Rect[]): Rect | null {\n\tif (!rects.length) return null;\n\tconst minX = Math.min(...rects.map((r) => r.x));\n\tconst minY = Math.min(...rects.map((r) => r.y));\n\tconst maxX = Math.max(...rects.map((r) => r.x + r.width));\n\tconst maxY = Math.max(...rects.map((r) => r.y + r.height));\n\treturn { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * Center coordinate of a rectangle.\n */\nexport function centre(r: Rect): XYPosition {\n\treturn { x: r.x + r.width / 2, y: r.y + r.height / 2 };\n}\n\n/**\n * Determines whether two rectangles overlap, including an optional padding distance.\n */\nexport function overlaps(a: Rect, b: Rect, pad = 16): boolean {\n\treturn (\n\t\ta.x < b.x + b.width + pad &&\n\t\ta.x + a.width + pad > b.x &&\n\t\ta.y < b.y + b.height + pad &&\n\t\ta.y + a.height + pad > b.y\n\t);\n}\n\n/**\n * Maps directed layout flow to its natural forward growth side.\n */\nexport function flowSide(direction: 'LR' | 'TB' | 'RL' | 'BT' = 'LR'): Side {\n\tswitch (direction) {\n\t\tcase 'LR':\n\t\t\treturn 'right';\n\t\tcase 'RL':\n\t\t\treturn 'left';\n\t\tcase 'BT':\n\t\t\treturn 'above';\n\t\tcase 'TB':\n\t\tdefault:\n\t\t\treturn 'below';\n\t}\n}\n\n/**\n * Incremental layout blending:\n * Keeps established/manual node positions intact. Runs Dagre on the entire graph,\n * computes the centroid translation offset between auto and stored positions of existing nodes,\n * and places unpositioned/new nodes at (auto + offset).\n */\nexport function ensureLayout<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\toptions: IncrementalLayoutOptions = {}\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\n\tconst stored = options.storedPositions ?? {};\n\tconst missing = nodes.filter((n) => !stored[n.id]);\n\n\t// Case 1: Every node has an established coordinate -> preserve verbatim\n\tif (missing.length === 0) {\n\t\treturn nodes.map((n) => ({\n\t\t\t...n,\n\t\t\tposition: { ...stored[n.id]! }\n\t\t}));\n\t}\n\n\t// Calculate full dagre auto-layout\n\tconst autoLayouted = getLayoutedNodes(nodes, edges, undefined, options);\n\tconst autoMap = new Map<string, XYPosition>(\n\t\tautoLayouted.map((n) => [n.id, n.position])\n\t);\n\n\t// Case 2: No nodes have stored coordinates -> return auto layout directly\n\tconst both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));\n\tif (both.length === 0) {\n\t\treturn autoLayouted;\n\t}\n\n\t// Case 3: Blend! Align on centroid translation offset of overlapping nodes\n\tlet dx = 0;\n\tlet dy = 0;\n\tfor (const n of both) {\n\t\tconst s = stored[n.id]!;\n\t\tconst a = autoMap.get(n.id)!;\n\t\tdx += s.x - a.x;\n\t\tdy += s.y - a.y;\n\t}\n\tdx /= both.length;\n\tdy /= both.length;\n\n\treturn nodes.map((node) => {\n\t\tif (stored[node.id]) {\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tposition: { ...stored[node.id]! }\n\t\t\t};\n\t\t}\n\t\tconst autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };\n\t\treturn {\n\t\t\t...node,\n\t\t\tposition: {\n\t\t\t\tx: Math.round(autoPos.x + dx),\n\t\t\t\ty: Math.round(autoPos.y + dy)\n\t\t\t}\n\t\t};\n\t});\n}\n\n/**\n * Finds a free top-left coordinate for a node of `size` adjacent to `nearNodeId` (or the cluster),\n * sliding along the perpendicular axis past any intersecting bounding boxes to guarantee non-overlapping placement.\n */\nexport function placeBeside(\n\trects: Record<string, Rect>,\n\tsize: { width: number; height: number },\n\tside: Side,\n\tnearNodeId?: string,\n\toptions: PlaceBesideOptions = {}\n): XYPosition {\n\tconst {\n\t\tgap = DEFAULT_PLACEMENT_GAP,\n\t\tignore = [],\n\t\tmaxAttempts = 32\n\t} = options;\n\n\tconst skip = new Set([nearNodeId, ...ignore].filter(Boolean));\n\tconst obstacles = Object.entries(rects)\n\t\t.filter(([id]) => !skip.has(id))\n\t\t.map(([, r]) => r);\n\n\tconst anchor =\n\t\t(nearNodeId && rects[nearNodeId]) ||\n\t\tboundsOf(Object.values(rects)) ||\n\t\t{ x: 0, y: 0, width: 0, height: 0 };\n\n\tconst anchorCenter = centre(anchor);\n\n\tlet baseX: number;\n\tlet baseY: number;\n\n\tswitch (side) {\n\t\tcase 'right':\n\t\t\tbaseX = anchor.x + anchor.width + gap;\n\t\t\tbaseY = anchorCenter.y - size.height / 2;\n\t\t\tbreak;\n\t\tcase 'left':\n\t\t\tbaseX = anchor.x - gap - size.width;\n\t\t\tbaseY = anchorCenter.y - size.height / 2;\n\t\t\tbreak;\n\t\tcase 'above':\n\t\t\tbaseX = anchorCenter.x - size.width / 2;\n\t\t\tbaseY = anchor.y - gap - size.height;\n\t\t\tbreak;\n\t\tcase 'below':\n\t\tdefault:\n\t\t\tbaseX = anchorCenter.x - size.width / 2;\n\t\t\tbaseY = anchor.y + anchor.height + gap;\n\t\t\tbreak;\n\t}\n\n\t// Slide perpendicular to growth direction, alternating positive and negative steps (+1, -1, +2, -2...)\n\tconst isHorizontal = side === 'right' || side === 'left';\n\tconst step = (isHorizontal ? size.height : size.width) + 20;\n\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tconst offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);\n\t\tconst candidateBox: Rect = {\n\t\t\tx: isHorizontal ? baseX : baseX + offset,\n\t\t\ty: isHorizontal ? baseY + offset : baseY,\n\t\t\twidth: size.width,\n\t\t\theight: size.height\n\t\t};\n\n\t\tif (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {\n\t\t\treturn {\n\t\t\t\tx: Math.round(candidateBox.x),\n\t\t\t\ty: Math.round(candidateBox.y)\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tx: Math.round(baseX),\n\t\ty: Math.round(baseY)\n\t};\n}\n","import { ensureLayout, getLayoutedNodes, placeBeside } from '../canvas/layout.js';\nimport type { CanvasEdge, CanvasNode, Rect } from '../canvas/types.js';\nimport type { AgentCommand, AgentStep, EditorSnapshot } from './types.js';\n\n/**\n * Plans a sequence of atomic execution steps from a high-level agent command.\n */\nexport function planAgentCommand<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tcommand: AgentCommand<TNode, TEdge>,\n\tsnapshot: EditorSnapshot<TNode, TEdge>,\n\toptions: {\n\t\tdefaultNodeSize?: { width: number; height: number };\n\t\tidGenerator?: () => string;\n\t} = {}\n): AgentStep<TNode, TEdge>[] {\n\tconst defaultNodeSize = options.defaultNodeSize ?? { width: 200, height: 60 };\n\tconst idGen = options.idGenerator ?? (() => `node-${Math.random().toString(36).substring(2, 9)}`);\n\tconst edgeIdGen = () => `edge-${Math.random().toString(36).substring(2, 9)}`;\n\n\tconst nodes = snapshot.canvas?.nodes ?? [];\n\tconst edges = snapshot.canvas?.edges ?? [];\n\n\tswitch (command.type) {\n\t\tcase 'canvas:add_node': {\n\t\t\tlet pos = command.node.position;\n\n\t\t\tif (!pos) {\n\t\t\t\tif (command.nearNodeId) {\n\t\t\t\t\t// Use collision-aware directional placement\n\t\t\t\t\tconst rects: Record<string, Rect> = {};\n\t\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\t\trects[n.id] = {\n\t\t\t\t\t\t\tx: n.position.x,\n\t\t\t\t\t\t\ty: n.position.y,\n\t\t\t\t\t\t\twidth: n.measured?.width ?? (n as any).width ?? defaultNodeSize.width,\n\t\t\t\t\t\t\theight: n.measured?.height ?? (n as any).height ?? defaultNodeSize.height\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tpos = placeBeside(\n\t\t\t\t\t\trects,\n\t\t\t\t\t\tdefaultNodeSize,\n\t\t\t\t\t\tcommand.side ?? 'right',\n\t\t\t\t\t\tcommand.nearNodeId\n\t\t\t\t\t);\n\t\t\t\t} else if (nodes.length > 0) {\n\t\t\t\t\t// Default beside the last node\n\t\t\t\t\tconst lastNode = nodes[nodes.length - 1];\n\t\t\t\t\tconst rects: Record<string, Rect> = {};\n\t\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\t\trects[n.id] = {\n\t\t\t\t\t\t\tx: n.position.x,\n\t\t\t\t\t\t\ty: n.position.y,\n\t\t\t\t\t\t\twidth: n.measured?.width ?? (n as any).width ?? defaultNodeSize.width,\n\t\t\t\t\t\t\theight: n.measured?.height ?? (n as any).height ?? defaultNodeSize.height\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tpos = placeBeside(rects, defaultNodeSize, 'right', lastNode.id);\n\t\t\t\t} else {\n\t\t\t\t\tpos = { x: 100, y: 100 };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst fullNode: TNode = {\n\t\t\t\t...command.node,\n\t\t\t\tid: command.node.id || idGen(),\n\t\t\t\tposition: pos\n\t\t\t} as TNode;\n\n\t\t\tconst steps: AgentStep<TNode, TEdge>[] = [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_add_node',\n\t\t\t\t\tnode: fullNode\n\t\t\t\t}\n\t\t\t];\n\n\t\t\t// If attached to nearNode, optionally automatically connect\n\t\t\tif (command.nearNodeId) {\n\t\t\t\tsteps.push({\n\t\t\t\t\ttype: 'step:canvas_add_edge',\n\t\t\t\t\tedge: {\n\t\t\t\t\t\tid: edgeIdGen(),\n\t\t\t\t\t\tsource: command.nearNodeId,\n\t\t\t\t\t\ttarget: fullNode.id\n\t\t\t\t\t} as TEdge\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn steps;\n\t\t}\n\n\t\tcase 'canvas:remove_nodes': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_remove_nodes',\n\t\t\t\t\tnodeIds: command.nodeIds\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:connect': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_add_edge',\n\t\t\t\t\tedge: {\n\t\t\t\t\t\tid: edgeIdGen(),\n\t\t\t\t\t\tsource: command.source,\n\t\t\t\t\t\ttarget: command.target,\n\t\t\t\t\t\tlabel: command.label\n\t\t\t\t\t} as TEdge\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:update_node': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_update_node',\n\t\t\t\t\tid: command.id,\n\t\t\t\t\tdata: command.data\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:layout': {\n\t\t\tconst dir = command.direction ?? 'LR';\n\t\t\tlet layouted: TNode[];\n\n\t\t\tif (command.incremental) {\n\t\t\t\tconst stored: Record<string, { x: number; y: number }> = {};\n\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\tif (n.position && (n.position.x !== 0 || n.position.y !== 0)) {\n\t\t\t\t\t\tstored[n.id] = { ...n.position };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlayouted = ensureLayout(nodes, edges, {\n\t\t\t\t\tdirection: dir,\n\t\t\t\t\tstoredPositions: stored\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tlayouted = getLayoutedNodes(nodes, edges, undefined, { direction: dir });\n\t\t\t}\n\n\t\t\tconst positions: Record<string, { x: number; y: number }> = {};\n\t\t\tfor (const node of layouted) {\n\t\t\t\tpositions[node.id] = { ...node.position };\n\t\t\t}\n\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_move_nodes',\n\t\t\t\t\tpositions\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:select': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_select',\n\t\t\t\t\tnodeIds: command.nodeIds\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:highlight': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_highlight',\n\t\t\t\t\tnodeIds: command.nodeIds,\n\t\t\t\t\tcolor: command.color ?? '#3b82f6',\n\t\t\t\t\tdurationMs: command.durationMs ?? 2000\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:insert_text': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_insert',\n\t\t\t\t\ttext: command.text,\n\t\t\t\t\tat: command.at\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:replace_range': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_replace',\n\t\t\t\t\tfrom: command.from,\n\t\t\t\t\tto: command.to,\n\t\t\t\t\ttext: command.text\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:set_variable': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_set_variable',\n\t\t\t\t\tname: command.name,\n\t\t\t\t\tvalue: command.value\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:select': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_select',\n\t\t\t\t\tfrom: command.from,\n\t\t\t\t\tto: command.to\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tdefault: {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n","/**\n * Standard MCP Tool Definitions for @pixerate/editor.\n * Compatible with the Model Context Protocol (MCP) tool specification schema.\n */\n\nexport const MCP_TOOL_GET_EDITOR_STATE = {\n\tname: 'get_editor_state',\n\tdescription:\n\t\t'Retrieves the current state of the document, variables, selection, and canvas graph (nodes and edges).',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tincludeDocument: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Whether to include the prompt document text and variables (default: true).'\n\t\t\t},\n\t\t\tincludeCanvas: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Whether to include the canvas nodes and edges (default: true).'\n\t\t\t}\n\t\t}\n\t}\n} as const;\n\nexport const MCP_TOOL_EDIT_CANVAS = {\n\tname: 'edit_canvas',\n\tdescription:\n\t\t'Applies high-level structural operations to the visual canvas (adding nodes, connecting edges, organizing layout, or moving elements).',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\toperation: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: [\n\t\t\t\t\t'add_node',\n\t\t\t\t\t'remove_nodes',\n\t\t\t\t\t'connect',\n\t\t\t\t\t'update_node',\n\t\t\t\t\t'layout',\n\t\t\t\t\t'select',\n\t\t\t\t\t'highlight'\n\t\t\t\t],\n\t\t\t\tdescription: 'The canvas operation to execute.'\n\t\t\t},\n\t\t\tnode: {\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: 'Node definition when operation is add_node. Can omit position to auto-place.'\n\t\t\t},\n\t\t\tnearNodeId: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Anchor node ID to position beside using collision-aware placement.'\n\t\t\t},\n\t\t\tside: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['top', 'right', 'bottom', 'left'],\n\t\t\t\tdescription: 'Side of the anchor node to place the new node beside.'\n\t\t\t},\n\t\t\tnodeIds: {\n\t\t\t\ttype: 'array',\n\t\t\t\titems: { type: 'string' },\n\t\t\t\tdescription: 'Array of node IDs for remove_nodes, select, or highlight.'\n\t\t\t},\n\t\t\tsource: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Source node ID for connect operation.'\n\t\t\t},\n\t\t\ttarget: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Target node ID for connect operation.'\n\t\t\t},\n\t\t\tlabel: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Optional edge label for connect operation.'\n\t\t\t},\n\t\t\tdirection: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['LR', 'TB', 'RL', 'BT'],\n\t\t\t\tdescription: 'Layout direction (default LR).'\n\t\t\t},\n\t\t\tincremental: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Preserve existing node coordinates and blend layout (default true).'\n\t\t\t}\n\t\t},\n\t\trequired: ['operation']\n\t}\n} as const;\n\nexport const MCP_TOOL_EDIT_PROMPT = {\n\tname: 'edit_prompt',\n\tdescription:\n\t\t'Applies edits to the prompt template document, including inserting text, replacing ranges, and setting variables.',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\toperation: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['insert_text', 'replace_range', 'set_variable', 'select'],\n\t\t\t\tdescription: 'The prompt operation to execute.'\n\t\t\t},\n\t\t\ttext: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Text to insert or replace.'\n\t\t\t},\n\t\t\tat: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Position to insert text at.'\n\t\t\t},\n\t\t\tfrom: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Start offset for replace_range or select.'\n\t\t\t},\n\t\t\tto: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'End offset for replace_range or select.'\n\t\t\t},\n\t\t\tname: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Variable name for set_variable.'\n\t\t\t},\n\t\t\tvalue: {\n\t\t\t\tdescription: 'Variable value for set_variable.'\n\t\t\t}\n\t\t},\n\t\trequired: ['operation']\n\t}\n} as const;\n\nexport const EDITOR_MCP_TOOLS = [\n\tMCP_TOOL_GET_EDITOR_STATE,\n\tMCP_TOOL_EDIT_CANVAS,\n\tMCP_TOOL_EDIT_PROMPT\n] as const;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/canvas/layout.ts","../../src/agent/planner.ts","../../src/agent/tools.ts"],"sourcesContent":["import dagre from '@dagrejs/dagre';\nimport type {\n\tCanvasEdge,\n\tCanvasLayoutOptions,\n\tCanvasNode,\n\tIncrementalLayoutOptions,\n\tPlaceBesideOptions,\n\tRect,\n\tSide,\n\tXYPosition\n} from './types.js';\n\nexport const DEFAULT_NODE_WIDTH = 200;\nexport const DEFAULT_NODE_HEIGHT = 60;\nexport const DEFAULT_PLACEMENT_GAP = 48;\n\n/**\n * Calculates upper-left flow coordinate for an element to be centered in the viewport.\n */\nexport function getCenteredNodePosition(\n\twidth = 0,\n\theight = 0,\n\tscreenToFlowPosition: (position: XYPosition) => XYPosition,\n\tscreenDimensions?: { width: number; height: number }\n): XYPosition {\n\tconst rect =\n\t\tscreenDimensions ??\n\t\t(typeof document !== 'undefined'\n\t\t\t? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }\n\t\t\t: { width: 1920, height: 1080 });\n\n\tconst flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });\n\treturn {\n\t\tx: flowPos.x - width / 2,\n\t\ty: flowPos.y - height / 2\n\t};\n}\n\n/**\n * Updates positions of nodes so their collective bounding box is centered in the current viewport.\n */\nexport function centerNodes<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\tscreenToFlowPosition: (position: XYPosition) => XYPosition,\n\tgetNodesBounds: (nodes: TNode[]) => Rect,\n\tscreenDimensions?: { width: number; height: number }\n): TNode[] {\n\tif (nodes.length === 0) {\n\t\treturn [];\n\t}\n\n\tconst bounds = getNodesBounds(nodes);\n\tconst boundingBoxOrigin = { x: bounds.x, y: bounds.y };\n\n\tconst centeredPosition = getCenteredNodePosition(\n\t\tbounds.width,\n\t\tbounds.height,\n\t\tscreenToFlowPosition,\n\t\tscreenDimensions\n\t);\n\n\tconst deltaX = centeredPosition.x - boundingBoxOrigin.x;\n\tconst deltaY = centeredPosition.y - boundingBoxOrigin.y;\n\n\treturn nodes.map((node) => ({\n\t\t...node,\n\t\tposition: {\n\t\t\tx: node.position.x + deltaX,\n\t\t\ty: node.position.y + deltaY\n\t\t}\n\t}));\n}\n\n/**\n * Automatically calculates directed hierarchical layout of nodes and edges using Dagre.\n */\nexport function getLayoutedNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\tgetNodesBounds?: (nodes: TNode[]) => Rect,\n\toptions: CanvasLayoutOptions = {}\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\n\tconst {\n\t\tdirection = 'LR',\n\t\tranksep = 100,\n\t\tnodesep = 25,\n\t\tdefaultNodeWidth = DEFAULT_NODE_WIDTH,\n\t\tdefaultNodeHeight = DEFAULT_NODE_HEIGHT\n\t} = options;\n\n\tconst dagreGraph = new dagre.graphlib.Graph();\n\tdagreGraph.setDefaultEdgeLabel(() => ({}));\n\tdagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });\n\n\tconst nodeIds = new Set(nodes.map((node) => node.id));\n\n\tnodes.forEach((node) => {\n\t\tconst nodeWidth = node.measured?.width ?? (node as any).width ?? defaultNodeWidth;\n\t\tconst nodeHeight = node.measured?.height ?? (node as any).height ?? defaultNodeHeight;\n\t\tdagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });\n\t});\n\n\tedges.forEach((edge) => {\n\t\tif (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {\n\t\t\tdagreGraph.setEdge(edge.source, edge.target);\n\t\t}\n\t});\n\n\tdagre.layout(dagreGraph);\n\n\treturn nodes.map((node) => {\n\t\tconst nodeWithPosition = dagreGraph.node(node.id);\n\t\tconst nodeWidth = node.measured?.width ?? (node as any).width ?? defaultNodeWidth;\n\t\tconst nodeHeight = node.measured?.height ?? (node as any).height ?? defaultNodeHeight;\n\n\t\treturn {\n\t\t\t...node,\n\t\t\tposition: {\n\t\t\t\tx: nodeWithPosition.x - nodeWidth / 2,\n\t\t\t\ty: nodeWithPosition.y - nodeHeight / 2\n\t\t\t}\n\t\t};\n\t});\n}\n\n/**\n * Heuristic text-based node size estimator when DOM measurement is not available.\n */\nexport function estimateNodeSize(\n\tnode: CanvasNode,\n\tdefaultWidth = DEFAULT_NODE_WIDTH,\n\tdefaultHeight = DEFAULT_NODE_HEIGHT\n): { width: number; height: number } {\n\tconst label = String(node.data?.label ?? node.data?.title ?? node.id ?? '');\n\tconst lines = label.split(/\\n|<br\\s*\\/?>/);\n\tconst longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, '').length), 1);\n\tconst width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));\n\tconst height = Math.max(defaultHeight, lines.length * 20 + 24);\n\treturn { width, height };\n}\n\n/**\n * Computes bounding rectangle of an array of rectangles.\n */\nexport function boundsOf(rects: Rect[]): Rect | null {\n\tif (!rects.length) return null;\n\tconst minX = Math.min(...rects.map((r) => r.x));\n\tconst minY = Math.min(...rects.map((r) => r.y));\n\tconst maxX = Math.max(...rects.map((r) => r.x + r.width));\n\tconst maxY = Math.max(...rects.map((r) => r.y + r.height));\n\treturn { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * Center coordinate of a rectangle.\n */\nexport function centre(r: Rect): XYPosition {\n\treturn { x: r.x + r.width / 2, y: r.y + r.height / 2 };\n}\n\n/**\n * Determines whether two rectangles overlap, including an optional padding distance.\n */\nexport function overlaps(a: Rect, b: Rect, pad = 16): boolean {\n\treturn (\n\t\ta.x < b.x + b.width + pad &&\n\t\ta.x + a.width + pad > b.x &&\n\t\ta.y < b.y + b.height + pad &&\n\t\ta.y + a.height + pad > b.y\n\t);\n}\n\n/**\n * Maps directed layout flow to its natural forward growth side.\n */\nexport function flowSide(direction: 'LR' | 'TB' | 'RL' | 'BT' = 'LR'): Side {\n\tswitch (direction) {\n\t\tcase 'LR':\n\t\t\treturn 'right';\n\t\tcase 'RL':\n\t\t\treturn 'left';\n\t\tcase 'BT':\n\t\t\treturn 'above';\n\t\tcase 'TB':\n\t\tdefault:\n\t\t\treturn 'below';\n\t}\n}\n\n/**\n * Incremental layout blending:\n * Keeps established/manual node positions intact. Runs Dagre on the entire graph,\n * computes the centroid translation offset between auto and stored positions of existing nodes,\n * and places unpositioned/new nodes at (auto + offset).\n */\nexport function ensureLayout<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\toptions: IncrementalLayoutOptions = {}\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\n\tconst stored = options.storedPositions ?? {};\n\tconst missing = nodes.filter((n) => !stored[n.id]);\n\n\t// Case 1: Every node has an established coordinate -> preserve verbatim\n\tif (missing.length === 0) {\n\t\treturn nodes.map((n) => ({\n\t\t\t...n,\n\t\t\tposition: { ...stored[n.id]! }\n\t\t}));\n\t}\n\n\t// Calculate full dagre auto-layout\n\tconst autoLayouted = getLayoutedNodes(nodes, edges, undefined, options);\n\tconst autoMap = new Map<string, XYPosition>(\n\t\tautoLayouted.map((n) => [n.id, n.position])\n\t);\n\n\t// Case 2: No nodes have stored coordinates -> return auto layout directly\n\tconst both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));\n\tif (both.length === 0) {\n\t\treturn autoLayouted;\n\t}\n\n\t// Case 3: Blend! Align on centroid translation offset of overlapping nodes\n\tlet dx = 0;\n\tlet dy = 0;\n\tfor (const n of both) {\n\t\tconst s = stored[n.id]!;\n\t\tconst a = autoMap.get(n.id)!;\n\t\tdx += s.x - a.x;\n\t\tdy += s.y - a.y;\n\t}\n\tdx /= both.length;\n\tdy /= both.length;\n\n\treturn nodes.map((node) => {\n\t\tif (stored[node.id]) {\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tposition: { ...stored[node.id]! }\n\t\t\t};\n\t\t}\n\t\tconst autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };\n\t\treturn {\n\t\t\t...node,\n\t\t\tposition: {\n\t\t\t\tx: Math.round(autoPos.x + dx),\n\t\t\t\ty: Math.round(autoPos.y + dy)\n\t\t\t}\n\t\t};\n\t});\n}\n\n/**\n * Finds a free top-left coordinate for a node of `size` adjacent to `nearNodeId` (or the cluster),\n * sliding along the perpendicular axis past any intersecting bounding boxes to guarantee non-overlapping placement.\n */\nexport function placeBeside(\n\trects: Record<string, Rect>,\n\tsize: { width: number; height: number },\n\tside: Side,\n\tnearNodeId?: string,\n\toptions: PlaceBesideOptions = {}\n): XYPosition {\n\tconst {\n\t\tgap = DEFAULT_PLACEMENT_GAP,\n\t\tignore = [],\n\t\tmaxAttempts = 32\n\t} = options;\n\n\tconst skip = new Set([nearNodeId, ...ignore].filter(Boolean));\n\tconst obstacles = Object.entries(rects)\n\t\t.filter(([id]) => !skip.has(id))\n\t\t.map(([, r]) => r);\n\n\tconst anchor =\n\t\t(nearNodeId && rects[nearNodeId]) ||\n\t\tboundsOf(Object.values(rects)) ||\n\t\t{ x: 0, y: 0, width: 0, height: 0 };\n\n\tconst anchorCenter = centre(anchor);\n\n\tlet baseX: number;\n\tlet baseY: number;\n\n\tswitch (side) {\n\t\tcase 'right':\n\t\t\tbaseX = anchor.x + anchor.width + gap;\n\t\t\tbaseY = anchorCenter.y - size.height / 2;\n\t\t\tbreak;\n\t\tcase 'left':\n\t\t\tbaseX = anchor.x - gap - size.width;\n\t\t\tbaseY = anchorCenter.y - size.height / 2;\n\t\t\tbreak;\n\t\tcase 'above':\n\t\t\tbaseX = anchorCenter.x - size.width / 2;\n\t\t\tbaseY = anchor.y - gap - size.height;\n\t\t\tbreak;\n\t\tcase 'below':\n\t\tdefault:\n\t\t\tbaseX = anchorCenter.x - size.width / 2;\n\t\t\tbaseY = anchor.y + anchor.height + gap;\n\t\t\tbreak;\n\t}\n\n\t// Slide perpendicular to growth direction, alternating positive and negative steps (+1, -1, +2, -2...)\n\tconst isHorizontal = side === 'right' || side === 'left';\n\tconst step = (isHorizontal ? size.height : size.width) + 20;\n\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tconst offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);\n\t\tconst candidateBox: Rect = {\n\t\t\tx: isHorizontal ? baseX : baseX + offset,\n\t\t\ty: isHorizontal ? baseY + offset : baseY,\n\t\t\twidth: size.width,\n\t\t\theight: size.height\n\t\t};\n\n\t\tif (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {\n\t\t\treturn {\n\t\t\t\tx: Math.round(candidateBox.x),\n\t\t\t\ty: Math.round(candidateBox.y)\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tx: Math.round(baseX),\n\t\ty: Math.round(baseY)\n\t};\n}\n","import { ensureLayout, getLayoutedNodes, placeBeside } from '../canvas/layout.js';\nimport type { CanvasEdge, CanvasNode, Rect } from '../canvas/types.js';\nimport type { AgentCommand, AgentStep, EditorSnapshot } from './types.js';\n\n/**\n * Plans a sequence of atomic execution steps from a high-level agent command.\n */\nexport function planAgentCommand<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tcommand: AgentCommand<TNode, TEdge>,\n\tsnapshot: EditorSnapshot<TNode, TEdge>,\n\toptions: {\n\t\tdefaultNodeSize?: { width: number; height: number };\n\t\tidGenerator?: () => string;\n\t} = {}\n): AgentStep<TNode, TEdge>[] {\n\tconst defaultNodeSize = options.defaultNodeSize ?? { width: 200, height: 60 };\n\tconst idGen = options.idGenerator ?? (() => `node-${Math.random().toString(36).substring(2, 9)}`);\n\tconst edgeIdGen = () => `edge-${Math.random().toString(36).substring(2, 9)}`;\n\n\tconst nodes = snapshot.canvas?.nodes ?? [];\n\tconst edges = snapshot.canvas?.edges ?? [];\n\n\tswitch (command.type) {\n\t\tcase 'canvas:add_node': {\n\t\t\tlet pos = command.node.position;\n\n\t\t\tif (!pos) {\n\t\t\t\tif (command.nearNodeId) {\n\t\t\t\t\t// Use collision-aware directional placement\n\t\t\t\t\tconst rects: Record<string, Rect> = {};\n\t\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\t\trects[n.id] = {\n\t\t\t\t\t\t\tx: n.position.x,\n\t\t\t\t\t\t\ty: n.position.y,\n\t\t\t\t\t\t\twidth: n.measured?.width ?? (n as any).width ?? defaultNodeSize.width,\n\t\t\t\t\t\t\theight: n.measured?.height ?? (n as any).height ?? defaultNodeSize.height\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tpos = placeBeside(\n\t\t\t\t\t\trects,\n\t\t\t\t\t\tdefaultNodeSize,\n\t\t\t\t\t\tcommand.side ?? 'right',\n\t\t\t\t\t\tcommand.nearNodeId\n\t\t\t\t\t);\n\t\t\t\t} else if (nodes.length > 0) {\n\t\t\t\t\t// Default beside the last node\n\t\t\t\t\tconst lastNode = nodes[nodes.length - 1];\n\t\t\t\t\tconst rects: Record<string, Rect> = {};\n\t\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\t\trects[n.id] = {\n\t\t\t\t\t\t\tx: n.position.x,\n\t\t\t\t\t\t\ty: n.position.y,\n\t\t\t\t\t\t\twidth: n.measured?.width ?? (n as any).width ?? defaultNodeSize.width,\n\t\t\t\t\t\t\theight: n.measured?.height ?? (n as any).height ?? defaultNodeSize.height\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tpos = placeBeside(rects, defaultNodeSize, 'right', lastNode.id);\n\t\t\t\t} else {\n\t\t\t\t\tpos = { x: 100, y: 100 };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst fullNode: TNode = {\n\t\t\t\t...command.node,\n\t\t\t\tid: command.node.id || idGen(),\n\t\t\t\tposition: pos\n\t\t\t} as TNode;\n\n\t\t\tconst steps: AgentStep<TNode, TEdge>[] = [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_add_node',\n\t\t\t\t\tnode: fullNode\n\t\t\t\t}\n\t\t\t];\n\n\t\t\t// If attached to nearNode, optionally automatically connect\n\t\t\tif (command.nearNodeId) {\n\t\t\t\tsteps.push({\n\t\t\t\t\ttype: 'step:canvas_add_edge',\n\t\t\t\t\tedge: {\n\t\t\t\t\t\tid: edgeIdGen(),\n\t\t\t\t\t\tsource: command.nearNodeId,\n\t\t\t\t\t\ttarget: fullNode.id\n\t\t\t\t\t} as TEdge\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn steps;\n\t\t}\n\n\t\tcase 'canvas:remove_nodes': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_remove_nodes',\n\t\t\t\t\tnodeIds: command.nodeIds\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:connect': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_add_edge',\n\t\t\t\t\tedge: {\n\t\t\t\t\t\tid: edgeIdGen(),\n\t\t\t\t\t\tsource: command.source,\n\t\t\t\t\t\ttarget: command.target,\n\t\t\t\t\t\tlabel: command.label\n\t\t\t\t\t} as TEdge\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:update_node': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_update_node',\n\t\t\t\t\tid: command.id,\n\t\t\t\t\tdata: command.data\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:layout': {\n\t\t\tconst dir = command.direction ?? 'LR';\n\t\t\tlet layouted: TNode[];\n\n\t\t\tif (command.incremental) {\n\t\t\t\tconst stored: Record<string, { x: number; y: number }> = {};\n\t\t\t\tfor (const n of nodes) {\n\t\t\t\t\tif (n.position && (n.position.x !== 0 || n.position.y !== 0)) {\n\t\t\t\t\t\tstored[n.id] = { ...n.position };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlayouted = ensureLayout(nodes, edges, {\n\t\t\t\t\tdirection: dir,\n\t\t\t\t\tstoredPositions: stored\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tlayouted = getLayoutedNodes(nodes, edges, undefined, { direction: dir });\n\t\t\t}\n\n\t\t\tconst positions: Record<string, { x: number; y: number }> = {};\n\t\t\tfor (const node of layouted) {\n\t\t\t\tpositions[node.id] = { ...node.position };\n\t\t\t}\n\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_move_nodes',\n\t\t\t\t\tpositions\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:select': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_select',\n\t\t\t\t\tnodeIds: command.nodeIds\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'canvas:highlight': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:canvas_highlight',\n\t\t\t\t\tnodeIds: command.nodeIds,\n\t\t\t\t\tcolor: command.color ?? '#3b82f6',\n\t\t\t\t\tdurationMs: command.durationMs ?? 2000\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:insert_text': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_insert',\n\t\t\t\t\ttext: command.text,\n\t\t\t\t\tat: command.at\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:replace_range': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_replace',\n\t\t\t\t\tfrom: command.from,\n\t\t\t\t\tto: command.to,\n\t\t\t\t\ttext: command.text\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:set_variable': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_set_variable',\n\t\t\t\t\tname: command.name,\n\t\t\t\t\tvalue: command.value\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'prompt:select': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:prompt_select',\n\t\t\t\t\tfrom: command.from,\n\t\t\t\t\tto: command.to\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'choreography:say': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:say',\n\t\t\t\t\ttext: command.text,\n\t\t\t\t\tatNodeId: command.atNodeId,\n\t\t\t\t\tposition: command.position\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tcase 'choreography:wait': {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'step:wait',\n\t\t\t\t\tms: command.ms\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\n\t\tdefault: {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n","/**\n * Standard MCP Tool Definitions for @pixerate/editor.\n * Compatible with the Model Context Protocol (MCP) tool specification schema.\n */\n\nexport const MCP_TOOL_GET_EDITOR_STATE = {\n\tname: 'get_editor_state',\n\tdescription:\n\t\t'Retrieves the current state of the document, variables, selection, and canvas graph (nodes and edges).',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tincludeDocument: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Whether to include the prompt document text and variables (default: true).'\n\t\t\t},\n\t\t\tincludeCanvas: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Whether to include the canvas nodes and edges (default: true).'\n\t\t\t}\n\t\t}\n\t}\n} as const;\n\nexport const MCP_TOOL_EDIT_CANVAS = {\n\tname: 'edit_canvas',\n\tdescription:\n\t\t'Applies high-level structural operations to the visual canvas (adding nodes, connecting edges, organizing layout, or moving elements).',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\toperation: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: [\n\t\t\t\t\t'add_node',\n\t\t\t\t\t'remove_nodes',\n\t\t\t\t\t'connect',\n\t\t\t\t\t'update_node',\n\t\t\t\t\t'layout',\n\t\t\t\t\t'select',\n\t\t\t\t\t'highlight'\n\t\t\t\t],\n\t\t\t\tdescription: 'The canvas operation to execute.'\n\t\t\t},\n\t\t\tnode: {\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: 'Node definition when operation is add_node. Can omit position to auto-place.'\n\t\t\t},\n\t\t\tnearNodeId: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Anchor node ID to position beside using collision-aware placement.'\n\t\t\t},\n\t\t\tside: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['top', 'right', 'bottom', 'left'],\n\t\t\t\tdescription: 'Side of the anchor node to place the new node beside.'\n\t\t\t},\n\t\t\tnodeIds: {\n\t\t\t\ttype: 'array',\n\t\t\t\titems: { type: 'string' },\n\t\t\t\tdescription: 'Array of node IDs for remove_nodes, select, or highlight.'\n\t\t\t},\n\t\t\tsource: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Source node ID for connect operation.'\n\t\t\t},\n\t\t\ttarget: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Target node ID for connect operation.'\n\t\t\t},\n\t\t\tlabel: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Optional edge label for connect operation.'\n\t\t\t},\n\t\t\tdirection: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['LR', 'TB', 'RL', 'BT'],\n\t\t\t\tdescription: 'Layout direction (default LR).'\n\t\t\t},\n\t\t\tincremental: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdescription: 'Preserve existing node coordinates and blend layout (default true).'\n\t\t\t}\n\t\t},\n\t\trequired: ['operation']\n\t}\n} as const;\n\nexport const MCP_TOOL_EDIT_PROMPT = {\n\tname: 'edit_prompt',\n\tdescription:\n\t\t'Applies edits to the prompt template document, including inserting text, replacing ranges, and setting variables.',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\toperation: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['insert_text', 'replace_range', 'set_variable', 'select'],\n\t\t\t\tdescription: 'The prompt operation to execute.'\n\t\t\t},\n\t\t\ttext: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Text to insert or replace.'\n\t\t\t},\n\t\t\tat: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Position to insert text at.'\n\t\t\t},\n\t\t\tfrom: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Start offset for replace_range or select.'\n\t\t\t},\n\t\t\tto: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'End offset for replace_range or select.'\n\t\t\t},\n\t\t\tname: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Variable name for set_variable.'\n\t\t\t},\n\t\t\tvalue: {\n\t\t\t\tdescription: 'Variable value for set_variable.'\n\t\t\t}\n\t\t},\n\t\trequired: ['operation']\n\t}\n} as const;\n\nexport const MCP_TOOL_VIEW_EDITOR = {\n\tname: 'view_editor',\n\tdescription:\n\t\t'Captures a visual image snapshot (JPEG or PNG) of the editor canvas or viewport for multimodal evaluation. Active agent cursor indicators are automatically excluded from the snapshot.',\n\tinputSchema: {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tscope: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['viewport', 'canvas'],\n\t\t\t\tdescription: 'Whether to capture only the visible viewport or the full canvas bounding box (default: viewport).'\n\t\t\t},\n\t\t\tformat: {\n\t\t\t\ttype: 'string',\n\t\t\t\tenum: ['jpeg', 'png'],\n\t\t\t\tdescription: 'Image format of the base64 snapshot (default: jpeg).'\n\t\t\t},\n\t\t\tmaxEdge: {\n\t\t\t\ttype: 'integer',\n\t\t\t\tdescription: 'Maximum width or height dimension in pixels (default: 1024).'\n\t\t\t}\n\t\t}\n\t}\n} as const;\n\n/**\n * CSS selectors for ephemeral UI overlays (such as agent cursors, presence tags, and transient bubbles)\n * that should be excluded during canvas image export and visual snapshot generation.\n */\nexport const EXPORT_EXCLUDED_SELECTORS = [\n\t'.pixerate-presence',\n\t'.agent-presence',\n\t'.ursula-presence'\n] as const;\n\nexport const EDITOR_MCP_TOOLS = [\n\tMCP_TOOL_GET_EDITOR_STATE,\n\tMCP_TOOL_EDIT_CANVAS,\n\tMCP_TOOL_EDIT_PROMPT,\n\tMCP_TOOL_VIEW_EDITOR\n] as const;\n\n"],"mappings":"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","names":[]}
@@ -33,17 +33,22 @@ __export(canvas_exports, {
33
33
  DEFAULT_NODE_HEIGHT: () => DEFAULT_NODE_HEIGHT,
34
34
  DEFAULT_NODE_WIDTH: () => DEFAULT_NODE_WIDTH,
35
35
  DEFAULT_PLACEMENT_GAP: () => DEFAULT_PLACEMENT_GAP,
36
+ ManualClock: () => ManualClock,
36
37
  StreamingLayoutManager: () => StreamingLayoutManager,
38
+ blendLayout: () => blendLayout,
37
39
  boundsOf: () => boundsOf,
38
40
  calculateDagreReflowDisplacement: () => calculateDagreReflowDisplacement,
39
41
  calculateDirectionalDisplacement: () => calculateDirectionalDisplacement,
42
+ canvasHeight: () => canvasHeight,
40
43
  centerNodes: () => centerNodes,
41
44
  centre: () => centre,
42
45
  computeNodesBoundingBox: () => computeNodesBoundingBox,
46
+ connectedOnly: () => connectedOnly,
43
47
  createBezierTrajectory: () => createBezierTrajectory,
44
48
  createFanOutTrajectory: () => createFanOutTrajectory,
45
49
  defaultEasing: () => defaultEasing,
46
50
  ensureLayout: () => ensureLayout,
51
+ estimateCanvasHeight: () => estimateCanvasHeight,
47
52
  estimateNodeSize: () => estimateNodeSize,
48
53
  flowSide: () => flowSide,
49
54
  getCenteredNodePosition: () => getCenteredNodePosition,
@@ -51,11 +56,14 @@ __export(canvas_exports, {
51
56
  isEventFromTextInput: () => isEventFromTextInput,
52
57
  isValidCanvasNode: () => isValidCanvasNode,
53
58
  isValidUrl: () => isValidUrl,
59
+ layoutBounds: () => layoutBounds,
54
60
  linearPositionTrajectory: () => linearPositionTrajectory,
55
61
  linearTrajectory: () => linearTrajectory,
56
62
  overlaps: () => overlaps,
63
+ pageHidden: () => pageHidden,
57
64
  parseCanvasClipboardData: () => parseCanvasClipboardData,
58
65
  placeBeside: () => placeBeside,
66
+ realClock: () => realClock,
59
67
  remapPastedNodes: () => remapPastedNodes,
60
68
  repositionNodesTo: () => repositionNodesTo,
61
69
  runMultiNodeTransition: () => runMultiNodeTransition,
@@ -269,6 +277,79 @@ function placeBeside(rects, size, side, nearNodeId, options = {}) {
269
277
  };
270
278
  }
271
279
 
280
+ // src/canvas/clock.ts
281
+ var pageHidden = () => typeof document !== "undefined" && document.hidden;
282
+ var realClock = {
283
+ live: true,
284
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
285
+ frame: () => new Promise((resolve) => {
286
+ if (typeof requestAnimationFrame === "function" && !pageHidden()) {
287
+ requestAnimationFrame(() => resolve());
288
+ } else {
289
+ setTimeout(resolve, 16);
290
+ }
291
+ }),
292
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)))
293
+ };
294
+ var settle = () => new Promise((resolve) => setTimeout(resolve, 0));
295
+ var ManualClock = class {
296
+ live = false;
297
+ time;
298
+ frames = [];
299
+ sleepers = [];
300
+ seq = 0;
301
+ constructor(start = 0) {
302
+ this.time = start;
303
+ }
304
+ now() {
305
+ return this.time;
306
+ }
307
+ frame() {
308
+ return new Promise((resolve) => this.frames.push(resolve));
309
+ }
310
+ sleep(ms) {
311
+ return new Promise(
312
+ (resolve) => this.sleepers.push({
313
+ at: this.time + Math.max(0, ms),
314
+ seq: this.seq++,
315
+ resolve
316
+ })
317
+ );
318
+ }
319
+ /** Whether any frames or sleep timers are currently pending. */
320
+ get pending() {
321
+ return this.frames.length > 0 || this.sleepers.length > 0;
322
+ }
323
+ /** Advances time by `ms`, resolving due sleep timers in order, followed by queued frames. */
324
+ async advance(ms) {
325
+ const end = this.time + Math.max(0, ms);
326
+ await settle();
327
+ while (true) {
328
+ let next;
329
+ for (const s of this.sleepers) {
330
+ if (s.at <= end && (!next || s.at < next.at || s.at === next.at && s.seq < next.seq)) {
331
+ next = s;
332
+ }
333
+ }
334
+ if (!next) break;
335
+ this.sleepers.splice(this.sleepers.indexOf(next), 1);
336
+ this.time = Math.max(this.time, next.at);
337
+ next.resolve();
338
+ await settle();
339
+ }
340
+ this.time = end;
341
+ const dueFrames = this.frames.splice(0, this.frames.length);
342
+ for (const resolveFrame of dueFrames) {
343
+ resolveFrame();
344
+ }
345
+ await settle();
346
+ }
347
+ /** Convenience method to advance by 1 frame at a given fps (default: 60fps = 16.67ms). */
348
+ async tick(fps = 60) {
349
+ await this.advance(Math.round(1e3 / fps));
350
+ }
351
+ };
352
+
272
353
  // src/canvas/trajectory.ts
273
354
  var defaultEasing = (t) => --t * t * t + 1;
274
355
  var linearTrajectory = (origin, target, progress) => {
@@ -336,9 +417,15 @@ function runMultiNodeTransition(transitions, options = {}) {
336
417
  return () => {
337
418
  };
338
419
  }
339
- const { defaultDuration = 400, defaultEasing: easingFunc = defaultEasing, onUpdate, onComplete } = options;
420
+ const {
421
+ defaultDuration = 400,
422
+ defaultEasing: easingFunc = defaultEasing,
423
+ clock = realClock,
424
+ onUpdate,
425
+ onComplete
426
+ } = options;
340
427
  let isCancelled = false;
341
- const startTime = Date.now();
428
+ const startTime = clock.now();
342
429
  const items = transitions.map((tr, i) => ({
343
430
  ...tr,
344
431
  index: tr.index ?? i,
@@ -350,7 +437,7 @@ function runMultiNodeTransition(transitions, options = {}) {
350
437
  }));
351
438
  function frame() {
352
439
  if (isCancelled) return;
353
- const now = Date.now();
440
+ const now = clock.now();
354
441
  let allCompleted = true;
355
442
  for (let i = 0; i < items.length; i++) {
356
443
  const item = items[i];
@@ -382,9 +469,7 @@ function runMultiNodeTransition(transitions, options = {}) {
382
469
  }
383
470
  onUpdate?.();
384
471
  if (!allCompleted) {
385
- if (typeof requestAnimationFrame !== "undefined") {
386
- requestAnimationFrame(frame);
387
- }
472
+ clock.frame().then(frame);
388
473
  } else {
389
474
  onComplete?.();
390
475
  }
@@ -662,10 +747,76 @@ function repositionNodesTo(nodes, targetOrigin) {
662
747
  }));
663
748
  }
664
749
 
750
+ // src/canvas/view.ts
751
+ function clampHeight(contentHeight, options = {}) {
752
+ const { min = 320, max = 560, padding = 48 } = options;
753
+ return Math.round(Math.min(max, Math.max(min, contentHeight + padding)));
754
+ }
755
+ function layoutBounds(nodes) {
756
+ if (nodes.length === 0) return null;
757
+ let minX = Infinity;
758
+ let minY = Infinity;
759
+ let maxX = -Infinity;
760
+ let maxY = -Infinity;
761
+ for (const node of nodes) {
762
+ const x = node.position.x;
763
+ const y = node.position.y;
764
+ const width = node.width ?? 180;
765
+ const height = node.height ?? 80;
766
+ minX = Math.min(minX, x);
767
+ minY = Math.min(minY, y);
768
+ maxX = Math.max(maxX, x + width);
769
+ maxY = Math.max(maxY, y + height);
770
+ }
771
+ if (minX === Infinity) return null;
772
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
773
+ }
774
+ function canvasHeight(nodes, options) {
775
+ return clampHeight(layoutBounds(nodes)?.height ?? 0, options);
776
+ }
777
+ var DEFAULT_RANK_HEIGHT = 80 + 72;
778
+ var NODES_PER_RANK = 2;
779
+ function estimateCanvasHeight(nodes, edges, options) {
780
+ const ranks = Math.max(1, Math.ceil(nodes.length / NODES_PER_RANK));
781
+ return clampHeight(ranks * DEFAULT_RANK_HEIGHT, options);
782
+ }
783
+ function mix(a, b, t) {
784
+ return a + (b - a) * t;
785
+ }
786
+ function blendLayout(from, to, t) {
787
+ if (t >= 1) return to;
788
+ const fromMap = new Map(from.map((n) => [n.id, n]));
789
+ return to.map((target) => {
790
+ const origin = fromMap.get(target.id);
791
+ if (!origin) return target;
792
+ return {
793
+ ...target,
794
+ position: {
795
+ x: mix(origin.position.x, target.position.x, t),
796
+ y: mix(origin.position.y, target.position.y, t)
797
+ },
798
+ width: origin.width !== void 0 && target.width !== void 0 ? mix(origin.width, target.width, t) : target.width,
799
+ height: origin.height !== void 0 && target.height !== void 0 ? mix(origin.height, target.height, t) : target.height
800
+ };
801
+ });
802
+ }
803
+ function connectedOnly(nodes, edges) {
804
+ if (edges.length === 0) return nodes;
805
+ const linkedNodeIds = /* @__PURE__ */ new Set();
806
+ for (const edge of edges) {
807
+ linkedNodeIds.add(edge.source);
808
+ linkedNodeIds.add(edge.target);
809
+ }
810
+ const filtered = nodes.filter((node) => linkedNodeIds.has(node.id));
811
+ return filtered.length > 0 ? filtered : nodes;
812
+ }
813
+
665
814
  // src/canvas/streaming.ts
666
815
  var StreamingLayoutManager = class {
667
816
  throttleMs;
668
817
  glideDurationMs;
818
+ connectedOnly;
819
+ clock;
669
820
  layoutOptions;
670
821
  onLayoutUpdated;
671
822
  pendingTimer = null;
@@ -674,6 +825,8 @@ var StreamingLayoutManager = class {
674
825
  constructor(options = {}) {
675
826
  this.throttleMs = options.throttleMs ?? 200;
676
827
  this.glideDurationMs = options.glideDurationMs ?? 300;
828
+ this.connectedOnly = options.connectedOnly ?? false;
829
+ this.clock = options.clock ?? realClock;
677
830
  this.layoutOptions = options.layoutOptions ?? { direction: "LR" };
678
831
  this.onLayoutUpdated = options.onLayoutUpdated;
679
832
  }
@@ -701,7 +854,8 @@ var StreamingLayoutManager = class {
701
854
  this.recalculateAndGlide(nodes, edges, currentPositions);
702
855
  }
703
856
  recalculateAndGlide(nodes, edges, currentPositions) {
704
- if (nodes.length === 0) return;
857
+ const targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;
858
+ if (targetNodes.length === 0) return;
705
859
  if (this.activeTransitionCancel) {
706
860
  this.activeTransitionCancel();
707
861
  this.activeTransitionCancel = null;
@@ -710,7 +864,7 @@ var StreamingLayoutManager = class {
710
864
  for (const [id, pos] of currentPositions.entries()) {
711
865
  stored[id] = pos;
712
866
  }
713
- const layouted = ensureLayout(nodes, edges, {
867
+ const layouted = ensureLayout(targetNodes, edges, {
714
868
  ...this.layoutOptions,
715
869
  storedPositions: stored
716
870
  });
@@ -734,6 +888,7 @@ var StreamingLayoutManager = class {
734
888
  this.targetPositions = nextPositions;
735
889
  if (transitions.length > 0) {
736
890
  this.activeTransitionCancel = runMultiNodeTransition(transitions, {
891
+ clock: this.clock,
737
892
  onUpdate: () => {
738
893
  const liveMap = new Map(currentPositions);
739
894
  for (const trans of transitions) {
@@ -766,17 +921,22 @@ var StreamingLayoutManager = class {
766
921
  DEFAULT_NODE_HEIGHT,
767
922
  DEFAULT_NODE_WIDTH,
768
923
  DEFAULT_PLACEMENT_GAP,
924
+ ManualClock,
769
925
  StreamingLayoutManager,
926
+ blendLayout,
770
927
  boundsOf,
771
928
  calculateDagreReflowDisplacement,
772
929
  calculateDirectionalDisplacement,
930
+ canvasHeight,
773
931
  centerNodes,
774
932
  centre,
775
933
  computeNodesBoundingBox,
934
+ connectedOnly,
776
935
  createBezierTrajectory,
777
936
  createFanOutTrajectory,
778
937
  defaultEasing,
779
938
  ensureLayout,
939
+ estimateCanvasHeight,
780
940
  estimateNodeSize,
781
941
  flowSide,
782
942
  getCenteredNodePosition,
@@ -784,11 +944,14 @@ var StreamingLayoutManager = class {
784
944
  isEventFromTextInput,
785
945
  isValidCanvasNode,
786
946
  isValidUrl,
947
+ layoutBounds,
787
948
  linearPositionTrajectory,
788
949
  linearTrajectory,
789
950
  overlaps,
951
+ pageHidden,
790
952
  parseCanvasClipboardData,
791
953
  placeBeside,
954
+ realClock,
792
955
  remapPastedNodes,
793
956
  repositionNodesTo,
794
957
  runMultiNodeTransition,