@pixerate/editor 0.14.3 → 0.15.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.
Files changed (71) hide show
  1. package/dist/agent/index.cjs +28 -220
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts +1 -1
  4. package/dist/agent/index.d.ts +1 -1
  5. package/dist/agent/index.js +5 -154
  6. package/dist/agent/index.js.map +1 -1
  7. package/dist/canvas/index.cjs +113 -377
  8. package/dist/canvas/index.cjs.map +1 -1
  9. package/dist/canvas/index.d.cts +2 -2
  10. package/dist/canvas/index.d.ts +2 -2
  11. package/dist/canvas/index.js +18 -208
  12. package/dist/canvas/index.js.map +1 -1
  13. package/dist/chunk-33UVEDOS.cjs +233 -0
  14. package/dist/chunk-33UVEDOS.cjs.map +1 -0
  15. package/dist/chunk-7FWEWE6T.js +1888 -0
  16. package/dist/chunk-7FWEWE6T.js.map +1 -0
  17. package/dist/chunk-BYCOFZAA.js +514 -0
  18. package/dist/chunk-BYCOFZAA.js.map +1 -0
  19. package/dist/chunk-CYKQLDYH.cjs +1290 -0
  20. package/dist/chunk-CYKQLDYH.cjs.map +1 -0
  21. package/dist/chunk-GLSIZKOX.js +17 -0
  22. package/dist/chunk-GLSIZKOX.js.map +1 -0
  23. package/dist/chunk-GWHJVZPI.js +233 -0
  24. package/dist/chunk-GWHJVZPI.js.map +1 -0
  25. package/dist/chunk-LDES3EOS.cjs +1888 -0
  26. package/dist/chunk-LDES3EOS.cjs.map +1 -0
  27. package/dist/chunk-MPPXH7U5.cjs +336 -0
  28. package/dist/chunk-MPPXH7U5.cjs.map +1 -0
  29. package/dist/chunk-NNJRJJSC.js +1290 -0
  30. package/dist/chunk-NNJRJJSC.js.map +1 -0
  31. package/dist/chunk-O7LOPQMH.js +336 -0
  32. package/dist/chunk-O7LOPQMH.js.map +1 -0
  33. package/dist/chunk-SEDR75VH.cjs +17 -0
  34. package/dist/chunk-SEDR75VH.cjs.map +1 -0
  35. package/dist/chunk-SHWP2A43.cjs +514 -0
  36. package/dist/chunk-SHWP2A43.cjs.map +1 -0
  37. package/dist/chunk-VHZCXHS4.js +222 -0
  38. package/dist/chunk-VHZCXHS4.js.map +1 -0
  39. package/dist/chunk-WADCJ7A7.cjs +222 -0
  40. package/dist/chunk-WADCJ7A7.cjs.map +1 -0
  41. package/dist/extensions/index.cjs +25 -1920
  42. package/dist/extensions/index.cjs.map +1 -1
  43. package/dist/extensions/index.js +22 -1868
  44. package/dist/extensions/index.js.map +1 -1
  45. package/dist/grammar/index.cjs +44 -387
  46. package/dist/grammar/index.cjs.map +1 -1
  47. package/dist/grammar/index.js +23 -321
  48. package/dist/grammar/index.js.map +1 -1
  49. package/dist/history/index.cjs +6 -257
  50. package/dist/history/index.cjs.map +1 -1
  51. package/dist/history/index.js +4 -227
  52. package/dist/history/index.js.map +1 -1
  53. package/dist/image-editor/index.cjs +63 -106
  54. package/dist/image-editor/index.cjs.map +1 -1
  55. package/dist/index.cjs +324 -7232
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +0 -4
  58. package/dist/index.d.ts +0 -4
  59. package/dist/index.js +258 -7007
  60. package/dist/index.js.map +1 -1
  61. package/dist/serializers/index.cjs +28 -558
  62. package/dist/serializers/index.cjs.map +1 -1
  63. package/dist/serializers/index.js +16 -496
  64. package/dist/serializers/index.js.map +1 -1
  65. package/dist/spreadsheet/index.cjs +21 -1319
  66. package/dist/spreadsheet/index.cjs.map +1 -1
  67. package/dist/spreadsheet/index.js +14 -1274
  68. package/dist/spreadsheet/index.js.map +1 -1
  69. package/dist/{types-lGtRobRV.d.cts → types-DC8b2TsA.d.cts} +1 -1
  70. package/dist/{types-lGtRobRV.d.ts → types-DC8b2TsA.d.ts} +1 -1
  71. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/canvas/layout.ts","../../src/canvas/clock.ts","../../src/canvas/trajectory.ts","../../src/canvas/displacement.ts","../../src/canvas/clipboard.ts","../../src/canvas/view.ts","../../src/canvas/streaming.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","/**\n * The time source that canvas animations and agent performances play on.\n *\n * Defaults to `realClock` (requestAnimationFrame and setTimeout).\n * A `ManualClock` moves only when told to via `advance(ms)` or `tick(fps)`,\n * enabling deterministic frame-by-frame stepping for testing, video generation,\n * or headless server environments.\n */\n\nexport interface Clock {\n\t/** Current time in milliseconds. */\n\tnow(): number;\n\t/** Resolves on the next animation frame. */\n\tframe(): Promise<void>;\n\t/** Resolves after `ms` milliseconds of this clock's time. */\n\tsleep(ms: number): Promise<void>;\n\t/**\n\t * Whether the clock follows wall time. Live clocks stop or degrade when the\n\t * tab is in the background (browsers pause animation frames), so transitions\n\t * can commit immediately rather than leaving operations hanging.\n\t */\n\treadonly live: boolean;\n}\n\n/** Whether the browser tab is hidden (where RAF pauses). */\nexport const pageHidden = (): boolean =>\n\ttypeof document !== 'undefined' && document.hidden;\n\n/** Real-time clock using requestAnimationFrame and setTimeout. */\nexport const realClock: Clock = {\n\tlive: true,\n\tnow: () => (typeof performance !== 'undefined' ? performance.now() : Date.now()),\n\tframe: () =>\n\t\tnew Promise<void>((resolve) => {\n\t\t\tif (typeof requestAnimationFrame === 'function' && !pageHidden()) {\n\t\t\t\trequestAnimationFrame(() => resolve());\n\t\t\t} else {\n\t\t\t\tsetTimeout(resolve, 16);\n\t\t\t}\n\t\t}),\n\tsleep: (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, Math.max(0, ms)))\n};\n\nconst settle = () => new Promise<void>((resolve) => setTimeout(resolve, 0));\n\n/**\n * A deterministic clock that only moves forward when `advance` or `tick` is called.\n * Allows step-by-step frame testing and video rendering without waiting for real time.\n */\nexport class ManualClock implements Clock {\n\treadonly live = false;\n\tprivate time: number;\n\tprivate frames: (() => void)[] = [];\n\tprivate sleepers: { at: number; seq: number; resolve: () => void }[] = [];\n\tprivate seq = 0;\n\n\tconstructor(start = 0) {\n\t\tthis.time = start;\n\t}\n\n\tpublic now(): number {\n\t\treturn this.time;\n\t}\n\n\tpublic frame(): Promise<void> {\n\t\treturn new Promise<void>((resolve) => this.frames.push(resolve));\n\t}\n\n\tpublic sleep(ms: number): Promise<void> {\n\t\treturn new Promise<void>((resolve) =>\n\t\t\tthis.sleepers.push({\n\t\t\t\tat: this.time + Math.max(0, ms),\n\t\t\t\tseq: this.seq++,\n\t\t\t\tresolve\n\t\t\t})\n\t\t);\n\t}\n\n\t/** Whether any frames or sleep timers are currently pending. */\n\tpublic get pending(): boolean {\n\t\treturn this.frames.length > 0 || this.sleepers.length > 0;\n\t}\n\n\t/** Advances time by `ms`, resolving due sleep timers in order, followed by queued frames. */\n\tpublic async advance(ms: number): Promise<void> {\n\t\tconst end = this.time + Math.max(0, ms);\n\t\tawait settle();\n\n\t\twhile (true) {\n\t\t\tlet next: (typeof this.sleepers)[number] | undefined;\n\t\t\tfor (const s of this.sleepers) {\n\t\t\t\tif (s.at <= end && (!next || s.at < next.at || (s.at === next.at && s.seq < next.seq))) {\n\t\t\t\t\tnext = s;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!next) break;\n\n\t\t\tthis.sleepers.splice(this.sleepers.indexOf(next), 1);\n\t\t\tthis.time = Math.max(this.time, next.at);\n\t\t\tnext.resolve();\n\t\t\tawait settle();\n\t\t}\n\n\t\tthis.time = end;\n\t\tconst dueFrames = this.frames.splice(0, this.frames.length);\n\t\tfor (const resolveFrame of dueFrames) {\n\t\t\tresolveFrame();\n\t\t}\n\t\tawait settle();\n\t}\n\n\t/** Convenience method to advance by 1 frame at a given fps (default: 60fps = 16.67ms). */\n\tpublic async tick(fps = 60): Promise<void> {\n\t\tawait this.advance(Math.round(1000 / fps));\n\t}\n}\n","import type {\n\tCanvasNode,\n\tFanOutTrajectoryOptions,\n\tNodeTransition,\n\tTrajectoryFunction,\n\tTrajectoryPoint,\n\tXYPosition\n} from './types.js';\n\n/**\n * Standard cubic ease-out function (pure math, framework-agnostic).\n */\nexport const defaultEasing = (t: number): number => --t * t * t + 1;\n\n/**\n * Standard linear trajectory: straight line interpolation with linear scale & opacity progression.\n */\nexport const linearTrajectory: TrajectoryFunction = (\n\torigin: XYPosition,\n\ttarget: XYPosition,\n\tprogress: number\n): TrajectoryPoint => {\n\treturn {\n\t\tposition: {\n\t\t\tx: origin.x + (target.x - origin.x) * progress,\n\t\t\ty: origin.y + (target.y - origin.y) * progress\n\t\t},\n\t\tscale: progress,\n\t\topacity: Math.min(1, progress * 1.5)\n\t};\n};\n\n/**\n * Linear position trajectory: pure position interpolation without altering scale or opacity.\n */\nexport const linearPositionTrajectory: TrajectoryFunction = (\n\torigin: XYPosition,\n\ttarget: XYPosition,\n\tprogress: number\n): TrajectoryPoint => {\n\treturn {\n\t\tposition: {\n\t\t\tx: origin.x + (target.x - origin.x) * progress,\n\t\t\ty: origin.y + (target.y - origin.y) * progress\n\t\t}\n\t};\n};\n\n/**\n * Creates a fan-out trajectory where nodes arc outward from origin to target.\n */\nexport function createFanOutTrajectory(options: FanOutTrajectoryOptions = {}): TrajectoryFunction {\n\tconst {\n\t\tcurvature = 0.4,\n\t\tstartScale = 0,\n\t\tendScale = 1,\n\t\tstartOpacity = 0,\n\t\tendOpacity = 1\n\t} = options;\n\n\treturn (\n\t\torigin: XYPosition,\n\t\ttarget: XYPosition,\n\t\tprogress: number,\n\t\tindex: number,\n\t\ttotal: number\n\t): TrajectoryPoint => {\n\t\tconst dx = target.x - origin.x;\n\t\tconst dy = target.y - origin.y;\n\n\t\t// Normalized index from -1 (top) to +1 (bottom)\n\t\tconst normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;\n\n\t\t// Perpendicular control point offset to create smooth fan arc\n\t\tconst midX = origin.x + dx * 0.4;\n\t\tconst midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;\n\n\t\t// Quadratic Bezier interpolation: B(t) = (1-t)^2 * P0 + 2(1-t)t * P1 + t^2 * P2\n\t\tconst t = progress;\n\t\tconst oneMinusT = 1 - t;\n\n\t\tconst x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;\n\t\tconst y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;\n\n\t\tconst scale = startScale + (endScale - startScale) * progress;\n\t\tconst opacity = startOpacity + (endOpacity - startOpacity) * progress;\n\n\t\treturn {\n\t\t\tposition: { x, y },\n\t\t\tscale,\n\t\t\topacity\n\t\t};\n\t};\n}\n\n/**\n * Creates a custom quadratic Bezier trajectory with a specified control point resolver.\n */\nexport function createBezierTrajectory(\n\tgetControlPoint: (origin: XYPosition, target: XYPosition) => XYPosition\n): TrajectoryFunction {\n\treturn (origin: XYPosition, target: XYPosition, progress: number): TrajectoryPoint => {\n\t\tconst cp = getControlPoint(origin, target);\n\t\tconst t = progress;\n\t\tconst oneMinusT = 1 - t;\n\n\t\tconst x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;\n\t\tconst y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;\n\n\t\treturn {\n\t\t\tposition: { x, y },\n\t\t\tscale: progress,\n\t\t\topacity: progress\n\t\t};\n\t};\n}\n\nimport { realClock, type Clock } from './clock.js';\n\n/**\n * Coordinates and animates multiple node transitions simultaneously with zero per-frame array allocations.\n */\nexport function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(\n\ttransitions: NodeTransition<TNode>[],\n\toptions: {\n\t\tdefaultDuration?: number;\n\t\tdefaultEasing?: (t: number) => number;\n\t\tclock?: Clock;\n\t\tonUpdate?: () => void;\n\t\tonComplete?: () => void;\n\t} = {}\n): () => void {\n\tif (transitions.length === 0) {\n\t\toptions.onComplete?.();\n\t\treturn () => {};\n\t}\n\n\tconst {\n\t\tdefaultDuration = 400,\n\t\tdefaultEasing: easingFunc = defaultEasing,\n\t\tclock = realClock,\n\t\tonUpdate,\n\t\tonComplete\n\t} = options;\n\n\tlet isCancelled = false;\n\tconst startTime = clock.now();\n\n\tconst items = transitions.map((tr, i) => ({\n\t\t...tr,\n\t\tindex: tr.index ?? i,\n\t\ttotal: tr.total ?? transitions.length,\n\t\tduration: tr.duration ?? defaultDuration,\n\t\teasing: tr.easing ?? easingFunc,\n\t\tdelay: tr.delay ?? 0,\n\t\ttrajectory: tr.trajectory ?? linearTrajectory\n\t}));\n\n\tfunction frame() {\n\t\tif (isCancelled) return;\n\n\t\tconst now = clock.now();\n\t\tlet allCompleted = true;\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst item = items[i];\n\t\t\tconst elapsed = now - (startTime + item.delay);\n\n\t\t\tif (elapsed < 0) {\n\t\t\t\tallCompleted = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Guard zero duration and division by zero (referenced in GOTCHAS.md)\n\t\t\tconst rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);\n\t\t\tconst progress = item.duration <= 0 ? 1 : item.easing(rawProgress);\n\n\t\t\tconst point = item.trajectory(item.from, item.to, progress, item.index, item.total);\n\n\t\t\titem.node.position = { ...point.position };\n\n\t\t\tif (point.scale !== undefined || point.opacity !== undefined) {\n\t\t\t\tif (!item.node.data) {\n\t\t\t\t\titem.node.data = {} as any;\n\t\t\t\t}\n\t\t\t\tif (point.scale !== undefined) {\n\t\t\t\t\t(item.node.data as any).scale = point.scale;\n\t\t\t\t}\n\t\t\t\tif (point.opacity !== undefined) {\n\t\t\t\t\t(item.node.data as any).opacity = point.opacity;\n\t\t\t\t\tconst currentStyle = (item.node.style || '').replace(/opacity:\\s*[^;]+;?/g, '').trim();\n\t\t\t\t\titem.node.style = `${currentStyle ? currentStyle + '; ' : ''}opacity: ${point.opacity};`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (rawProgress < 1) {\n\t\t\t\tallCompleted = false;\n\t\t\t}\n\t\t}\n\n\t\tonUpdate?.();\n\n\t\tif (!allCompleted) {\n\t\t\tclock.frame().then(frame);\n\t\t} else {\n\t\t\tonComplete?.();\n\t\t}\n\t}\n\n\tframe();\n\n\treturn () => {\n\t\tisCancelled = true;\n\t};\n}\n","import dagre from '@dagrejs/dagre';\nimport type {\n\tCanvasEdge,\n\tCanvasNode,\n\tDirectionalDisplacementOptions,\n\tRect,\n\tReflowDisplacementOptions,\n\tXYPosition\n} from './types.js';\n\n/**\n * Calculates a bounding rectangle for a set of nodes if not provided by getNodesBounds.\n */\nexport function computeNodesBoundingBox<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\tdefaultWidth = 200,\n\tdefaultHeight = 60\n): Rect {\n\tif (nodes.length === 0) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\n\tfor (const node of nodes) {\n\t\tconst width = node.measured?.width ?? (node as any).width ?? defaultWidth;\n\t\tconst height = node.measured?.height ?? (node as any).height ?? defaultHeight;\n\n\t\tminX = Math.min(minX, node.position.x);\n\t\tminY = Math.min(minY, node.position.y);\n\t\tmaxX = Math.max(maxX, node.position.x + width);\n\t\tmaxY = Math.max(maxY, node.position.y + height);\n\t}\n\n\treturn {\n\t\tx: minX,\n\t\ty: minY,\n\t\twidth: maxX - minX,\n\t\theight: maxY - minY\n\t};\n}\n\n/**\n * Pure directional displacement calculation.\n * Shifts downstream or surrounding nodes along an axis to clear space for newly inserted child nodes.\n */\nexport function calculateDirectionalDisplacement<TNode extends CanvasNode = CanvasNode>(params: {\n\texistingNodes: TNode[];\n\toriginNode: TNode;\n\tchildNodes: TNode[];\n\tgetNodesBounds?: (nodes: TNode[]) => Rect;\n\toptions?: DirectionalDisplacementOptions;\n}): Map<string, XYPosition> {\n\tconst {\n\t\texistingNodes,\n\t\toriginNode,\n\t\tchildNodes,\n\t\tgetNodesBounds,\n\t\toptions = {}\n\t} = params;\n\n\tconst {\n\t\tdirection = 'right',\n\t\tgap = 40,\n\t\tspreadSiblingLanes = false,\n\t\tsiblingTolerance = 50,\n\t\tminY\n\t} = options;\n\n\tconst displacedMap = new Map<string, XYPosition>();\n\tif (childNodes.length === 0 || existingNodes.length === 0) {\n\t\treturn displacedMap;\n\t}\n\n\tconst childBounds = getNodesBounds\n\t\t? getNodesBounds(childNodes)\n\t\t: computeNodesBoundingBox(childNodes);\n\n\tconst originWidth = originNode.measured?.width ?? (originNode as any).width ?? 200;\n\tconst originHeight = originNode.measured?.height ?? (originNode as any).height ?? 60;\n\n\t// The delta space required along primary axis\n\tconst deltaPrimary =\n\t\tdirection === 'right' || direction === 'left'\n\t\t\t? childBounds.width + gap\n\t\t\t: childBounds.height + gap;\n\n\t// Vertical displacement parameters: upward and downward row clearance\n\tconst deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);\n\tconst deltaDown = Math.max(\n\t\t0,\n\t\tchildBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap\n\t);\n\n\t// Both above and below displace equally so the row expands symmetrically\n\tconst equalDelta = Math.max(deltaUp, deltaDown);\n\n\tfor (const node of existingNodes) {\n\t\tif (node.id === originNode.id) continue;\n\n\t\tconst isSiblingRow =\n\t\t\tMath.abs(node.position.y - originNode.position.y) < siblingTolerance;\n\n\t\tswitch (direction) {\n\t\t\tcase 'right': {\n\t\t\t\tif (node.position.x > originNode.position.x) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x + deltaPrimary,\n\t\t\t\t\t\ty: node.position.y\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tif (node.position.x < originNode.position.x) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x - deltaPrimary,\n\t\t\t\t\t\ty: node.position.y\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'down': {\n\t\t\t\tif (node.position.y > originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y + deltaPrimary\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'up': {\n\t\t\t\tif (node.position.y < originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y - deltaPrimary\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'vertical': {\n\t\t\t\tif (isSiblingRow) break;\n\n\t\t\t\tif (node.position.y < originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y - equalDelta\n\t\t\t\t\t});\n\t\t\t\t} else if (node.position.y > originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y + equalDelta\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sibling lane spreading to avoid overlap among child nodes\n\tif (spreadSiblingLanes && (direction === 'right' || direction === 'left')) {\n\t\tfor (const [nodeId, pos] of displacedMap.entries()) {\n\t\t\tconst node = existingNodes.find((n) => n.id === nodeId);\n\t\t\tif (node && Math.abs(node.position.y - originNode.position.y) < siblingTolerance) {\n\t\t\t\tconst isAbove = node.position.y < originNode.position.y;\n\t\t\t\tdisplacedMap.set(nodeId, {\n\t\t\t\t\tx: pos.x,\n\t\t\t\t\ty: pos.y + (isAbove ? -deltaUp : deltaDown)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Floor constraint\n\tif (minY !== undefined) {\n\t\tfor (const [nodeId, pos] of displacedMap.entries()) {\n\t\t\tif (pos.y < minY) {\n\t\t\t\tdisplacedMap.set(nodeId, { x: pos.x, y: minY });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn displacedMap;\n}\n\n/**\n * Calculates Dagre reflow displacement preserving the position of an anchor node.\n */\nexport function calculateDagreReflowDisplacement<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(params: {\n\tcurrentNodes: TNode[];\n\tcurrentEdges: TEdge[];\n\tfutureNodes: TNode[];\n\tfutureEdges: TEdge[];\n\toptions: ReflowDisplacementOptions;\n\tgetNodesBounds?: (nodes: TNode[]) => Rect;\n}): Map<string, XYPosition> {\n\tconst {\n\t\tcurrentNodes,\n\t\tfutureNodes,\n\t\tfutureEdges,\n\t\toptions\n\t} = params;\n\n\tconst {\n\t\tanchorNodeId,\n\t\tdirection = 'LR',\n\t\tranksep = 100,\n\t\tnodesep = 25,\n\t\tdefaultNodeWidth = 200,\n\t\tdefaultNodeHeight = 60\n\t} = options;\n\n\tconst anchorNode = currentNodes.find((n) => n.id === anchorNodeId);\n\tif (!anchorNode) return new Map();\n\n\tconst g = new dagre.graphlib.Graph();\n\tg.setDefaultEdgeLabel(() => ({}));\n\tg.setGraph({ rankdir: direction, ranksep, nodesep });\n\n\tconst nodeIds = new Set(futureNodes.map((n) => n.id));\n\n\tfutureNodes.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\tg.setNode(node.id, { width: nodeWidth, height: nodeHeight });\n\t});\n\n\tfutureEdges.forEach((edge) => {\n\t\tif (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {\n\t\t\tg.setEdge(edge.source, edge.target);\n\t\t}\n\t});\n\n\tdagre.layout(g);\n\n\tconst anchorLayoutPos = g.node(anchorNodeId);\n\tif (!anchorLayoutPos) return new Map();\n\n\tconst anchorWidth = anchorNode.measured?.width ?? (anchorNode as any).width ?? defaultNodeWidth;\n\tconst anchorHeight = anchorNode.measured?.height ?? (anchorNode as any).height ?? defaultNodeHeight;\n\n\tconst anchorLayoutUpperLeft = {\n\t\tx: anchorLayoutPos.x - anchorWidth / 2,\n\t\ty: anchorLayoutPos.y - anchorHeight / 2\n\t};\n\n\tconst deltaX = anchorNode.position.x - anchorLayoutUpperLeft.x;\n\tconst deltaY = anchorNode.position.y - anchorLayoutUpperLeft.y;\n\n\tconst displacementMap = new Map<string, XYPosition>();\n\n\tcurrentNodes.forEach((node) => {\n\t\tif (node.id === anchorNodeId) return;\n\n\t\tconst layoutNode = g.node(node.id);\n\t\tif (!layoutNode) return;\n\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\tconst upperLeftX = layoutNode.x - nodeWidth / 2 + deltaX;\n\t\tconst upperLeftY = layoutNode.y - nodeHeight / 2 + deltaY;\n\n\t\tif (\n\t\t\tMath.abs(upperLeftX - node.position.x) > 1 ||\n\t\t\tMath.abs(upperLeftY - node.position.y) > 1\n\t\t) {\n\t\t\tdisplacementMap.set(node.id, { x: upperLeftX, y: upperLeftY });\n\t\t}\n\t});\n\n\treturn displacementMap;\n}\n","import type {\n\tCanvasClipboardPayload,\n\tCanvasEdge,\n\tCanvasNode,\n\tXYPosition\n} from './types.js';\n\n/**\n * Checks if an event originated from a text input, textarea, contenteditable element, or rich editor.\n */\nexport function isEventFromTextInput(event: Event | { target: EventTarget | null }): boolean {\n\tconst target = (event as any).target as HTMLElement | null;\n\tif (!target) return false;\n\tif (\n\t\ttarget.tagName === 'INPUT' ||\n\t\ttarget.tagName === 'TEXTAREA' ||\n\t\ttarget.tagName === 'SELECT' ||\n\t\ttarget.isContentEditable ||\n\t\ttarget.closest?.('[contenteditable=\"true\"]') ||\n\t\ttarget.closest?.('.ProseMirror')\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Validates that an object conforms to minimal CanvasNode shape.\n */\nexport function isValidCanvasNode(obj: unknown): obj is CanvasNode {\n\treturn (\n\t\ttypeof obj === 'object' &&\n\t\tobj !== null &&\n\t\t'id' in obj &&\n\t\ttypeof (obj as any).id === 'string' &&\n\t\t'position' in obj &&\n\t\ttypeof (obj as any).position === 'object' &&\n\t\t(obj as any).position !== null &&\n\t\t'x' in (obj as any).position &&\n\t\t'y' in (obj as any).position\n\t);\n}\n\n/**\n * Checks if a string is a valid HTTP/HTTPS URL.\n */\nexport function isValidUrl(url: string): boolean {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn parsed.protocol === 'http:' || parsed.protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Serializes nodes and optional edges into clipboard JSON payload.\n */\nexport function serializeCanvasNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(nodes: TNode[], edges?: TEdge[]): string {\n\tconst payload: CanvasClipboardPayload<TNode, TEdge> = {\n\t\tversion: 1,\n\t\tnodes,\n\t\tedges\n\t};\n\treturn JSON.stringify(payload);\n}\n\n/**\n * Parses raw JSON string into a CanvasClipboardPayload if valid.\n */\nexport function parseCanvasClipboardData<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(raw: string): CanvasClipboardPayload<TNode, TEdge> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (\n\t\t\ttypeof parsed === 'object' &&\n\t\t\tparsed !== null &&\n\t\t\tArray.isArray(parsed.nodes) &&\n\t\t\tparsed.nodes.length > 0 &&\n\t\t\tparsed.nodes.every(isValidCanvasNode)\n\t\t) {\n\t\t\treturn parsed as CanvasClipboardPayload<TNode, TEdge>;\n\t\t}\n\t} catch {\n\t\t// Not JSON or invalid payload\n\t}\n\treturn null;\n}\n\n/**\n * Generates unique new IDs for pasted nodes and updates edge references accordingly.\n */\nexport function remapPastedNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[] = [],\n\toffset: XYPosition = { x: 30, y: 30 },\n\tidGenerator: () => string = () => `node-${Math.random().toString(36).substring(2, 9)}`\n): { nodes: TNode[]; edges: TEdge[] } {\n\tconst idMap = new Map<string, string>();\n\n\tconst newNodes = nodes.map((node) => {\n\t\tconst newId = idGenerator();\n\t\tidMap.set(node.id, newId);\n\t\treturn {\n\t\t\t...node,\n\t\t\tid: newId,\n\t\t\tposition: {\n\t\t\t\tx: node.position.x + offset.x,\n\t\t\t\ty: node.position.y + offset.y\n\t\t\t},\n\t\t\tselected: true\n\t\t};\n\t});\n\n\tconst newEdges = edges\n\t\t.filter((e) => idMap.has(e.source) && idMap.has(e.target))\n\t\t.map((edge) => ({\n\t\t\t...edge,\n\t\t\tid: `edge-${Math.random().toString(36).substring(2, 9)}`,\n\t\t\tsource: idMap.get(edge.source)!,\n\t\t\ttarget: idMap.get(edge.target)!,\n\t\t\tselected: false\n\t\t}));\n\n\treturn { nodes: newNodes, edges: newEdges };\n}\n\n/**\n * Repositions nodes so their bounding box origin matches the given target position.\n */\nexport function repositionNodesTo<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\ttargetOrigin: XYPosition\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\tconst minX = Math.min(...nodes.map((n) => n.position.x));\n\tconst minY = Math.min(...nodes.map((n) => n.position.y));\n\tconst deltaX = targetOrigin.x - minX;\n\tconst deltaY = targetOrigin.y - minY;\n\n\treturn nodes.map((n) => ({\n\t\t...n,\n\t\tposition: {\n\t\t\tx: n.position.x + deltaX,\n\t\t\ty: n.position.y + deltaY\n\t\t}\n\t}));\n}\n","import type { CanvasEdge, CanvasNode, Rect } from './types.js';\n\nexport interface CanvasHeightOptions {\n\t/** Smallest height returned (default 320). */\n\tmin?: number;\n\t/** Largest height returned; taller content is scaled/zoomed (default 560). */\n\tmax?: number;\n\t/** Extra padding around the content (default 48). */\n\tpadding?: number;\n}\n\nfunction clampHeight(contentHeight: number, options: CanvasHeightOptions = {}): number {\n\tconst { min = 320, max = 560, padding = 48 } = options;\n\treturn Math.round(Math.min(max, Math.max(min, contentHeight + padding)));\n}\n\n/**\n * Calculates the bounding box extent of all positioned nodes, or `null` for an empty graph.\n */\nexport function layoutBounds<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[]\n): Rect | null {\n\tif (nodes.length === 0) return null;\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\n\tfor (const node of nodes) {\n\t\tconst x = node.position.x;\n\t\tconst y = node.position.y;\n\t\tconst width = node.width ?? 180;\n\t\tconst height = node.height ?? 80;\n\n\t\tminX = Math.min(minX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxX = Math.max(maxX, x + width);\n\t\tmaxY = Math.max(maxY, y + height);\n\t}\n\n\tif (minX === Infinity) return null;\n\treturn { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * Returns a clamped container height that fits the layout at 1:1 scale without overflow.\n */\nexport function canvasHeight<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\toptions?: CanvasHeightOptions\n): number {\n\treturn clampHeight(layoutBounds(nodes)?.height ?? 0, options);\n}\n\nconst DEFAULT_RANK_HEIGHT = 80 + 72; // Average node height + default rank separation\nconst NODES_PER_RANK = 2;\n\n/**\n * Estimates the container height to reserve before layout calculation finishes,\n * based on node and edge counts alone. Prevents layout shift (CLS).\n */\nexport function estimateCanvasHeight<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\toptions?: CanvasHeightOptions\n): number {\n\tconst ranks = Math.max(1, Math.ceil(nodes.length / NODES_PER_RANK));\n\treturn clampHeight(ranks * DEFAULT_RANK_HEIGHT, options);\n}\n\nfunction mix(a: number, b: number, t: number): number {\n\treturn a + (b - a) * t;\n}\n\n/**\n * Interpolates node positions and sizes between two layouts partway at progress `t` (0 = from, 1 = to).\n * Nodes present in both interpolate smoothly; nodes newly added in `to` snap to target.\n */\nexport function blendLayout<TNode extends CanvasNode = CanvasNode>(\n\tfrom: TNode[],\n\tto: TNode[],\n\tt: number\n): TNode[] {\n\tif (t >= 1) return to;\n\n\tconst fromMap = new Map<string, TNode>(from.map((n) => [n.id, n]));\n\n\treturn to.map((target) => {\n\t\tconst origin = fromMap.get(target.id);\n\t\tif (!origin) return target;\n\n\t\treturn {\n\t\t\t...target,\n\t\t\tposition: {\n\t\t\t\tx: mix(origin.position.x, target.position.x, t),\n\t\t\t\ty: mix(origin.position.y, target.position.y, t)\n\t\t\t},\n\t\t\twidth:\n\t\t\t\torigin.width !== undefined && target.width !== undefined\n\t\t\t\t\t? mix(origin.width, target.width, t)\n\t\t\t\t\t: target.width,\n\t\t\theight:\n\t\t\t\torigin.height !== undefined && target.height !== undefined\n\t\t\t\t\t? mix(origin.height, target.height, t)\n\t\t\t\t\t: target.height\n\t\t};\n\t});\n}\n\n/**\n * Filters out nodes that are not touched by any edge.\n *\n * During LLM streaming, nodes often arrive a chunk or token before the edge\n * that connects them. Displaying them immediately causes them to be placed as root\n * nodes only to jump into their correct position once the edge arrives.\n * Hiding orphan nodes during active streaming keeps the layout calm and jitter-free.\n */\nexport function connectedOnly<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[]\n): TNode[] {\n\tif (edges.length === 0) return nodes;\n\n\tconst linkedNodeIds = new Set<string>();\n\tfor (const edge of edges) {\n\t\tlinkedNodeIds.add(edge.source);\n\t\tlinkedNodeIds.add(edge.target);\n\t}\n\n\tconst filtered = nodes.filter((node) => linkedNodeIds.has(node.id));\n\t// If filtering would remove everything, return the original nodes\n\treturn filtered.length > 0 ? filtered : nodes;\n}\n","import { realClock, type Clock } from './clock.js';\nimport { ensureLayout } from './layout.js';\nimport { linearPositionTrajectory, runMultiNodeTransition } from './trajectory.js';\nimport type { CanvasEdge, CanvasNode, IncrementalLayoutOptions, NodeTransition, XYPosition } from './types.js';\nimport { connectedOnly } from './view.js';\n\nexport interface StreamingLayoutManagerOptions {\n\tthrottleMs?: number;\n\tglideDurationMs?: number;\n\tconnectedOnly?: boolean;\n\tclock?: Clock;\n\tlayoutOptions?: IncrementalLayoutOptions;\n\tonLayoutUpdated?: (positions: Map<string, XYPosition>) => void;\n}\n\n/**\n * Manages throttled incremental layout recalculation and smooth tweened node gliding\n * during active streaming (e.g. LLM streaming or graph generation).\n */\nexport class StreamingLayoutManager<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n> {\n\tprivate throttleMs: number;\n\tprivate glideDurationMs: number;\n\tprivate connectedOnly: boolean;\n\tprivate clock: Clock;\n\tprivate layoutOptions: IncrementalLayoutOptions;\n\tprivate onLayoutUpdated?: (positions: Map<string, XYPosition>) => void;\n\n\tprivate pendingTimer: any = null;\n\tprivate activeTransitionCancel: (() => void) | null = null;\n\tprivate targetPositions = new Map<string, XYPosition>();\n\n\tconstructor(options: StreamingLayoutManagerOptions = {}) {\n\t\tthis.throttleMs = options.throttleMs ?? 200;\n\t\tthis.glideDurationMs = options.glideDurationMs ?? 300;\n\t\tthis.connectedOnly = options.connectedOnly ?? false;\n\t\tthis.clock = options.clock ?? realClock;\n\t\tthis.layoutOptions = options.layoutOptions ?? { direction: 'LR' };\n\t\tthis.onLayoutUpdated = options.onLayoutUpdated;\n\t}\n\n\t/**\n\t * Feed incoming nodes and edges into the streaming manager.\n\t * Coalesces rapid streaming bursts and triggers smooth layout gliding.\n\t */\n\tpublic pushStreamUpdate(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t}\n\n\t\tthis.pendingTimer = setTimeout(() => {\n\t\t\tthis.pendingTimer = null;\n\t\t\tthis.recalculateAndGlide(nodes, edges, currentPositions);\n\t\t}, this.throttleMs);\n\t}\n\n\t/**\n\t * Forces immediate calculation and cancels pending throttled timers.\n\t */\n\tpublic flush(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t\tthis.pendingTimer = null;\n\t\t}\n\t\tthis.recalculateAndGlide(nodes, edges, currentPositions);\n\t}\n\n\tprivate recalculateAndGlide(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tconst targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;\n\t\tif (targetNodes.length === 0) return;\n\n\t\t// Cancel any currently running RAF glide\n\t\tif (this.activeTransitionCancel) {\n\t\t\tthis.activeTransitionCancel();\n\t\t\tthis.activeTransitionCancel = null;\n\t\t}\n\n\t\t// Incremental layout preserves existing nodes and aligns new nodes\n\t\tconst stored: Record<string, XYPosition> = {};\n\t\tfor (const [id, pos] of currentPositions.entries()) {\n\t\t\tstored[id] = pos;\n\t\t}\n\n\t\tconst layouted = ensureLayout(targetNodes, edges, {\n\t\t\t...this.layoutOptions,\n\t\t\tstoredPositions: stored\n\t\t});\n\n\t\tconst nextPositions = new Map<string, XYPosition>();\n\t\tconst transitions: NodeTransition<TNode>[] = [];\n\n\t\tfor (const node of layouted) {\n\t\t\tconst target = node.position;\n\t\t\tnextPositions.set(node.id, target);\n\n\t\t\tconst current = currentPositions.get(node.id) ?? target;\n\t\t\tconst distance = Math.hypot(target.x - current.x, target.y - current.y);\n\n\t\t\tif (distance > 2) {\n\t\t\t\ttransitions.push({\n\t\t\t\t\tnode,\n\t\t\t\t\tfrom: current,\n\t\t\t\t\tto: target,\n\t\t\t\t\ttrajectory: linearPositionTrajectory,\n\t\t\t\t\tduration: this.glideDurationMs\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.targetPositions = nextPositions;\n\n\t\tif (transitions.length > 0) {\n\t\t\tthis.activeTransitionCancel = runMultiNodeTransition(transitions, {\n\t\t\t\tclock: this.clock,\n\t\t\t\tonUpdate: () => {\n\t\t\t\t\t// Broadcast updated positions during RAF\n\t\t\t\t\tconst liveMap = new Map<string, XYPosition>(currentPositions);\n\t\t\t\t\tfor (const trans of transitions) {\n\t\t\t\t\t\tliveMap.set(trans.node.id, { ...trans.node.position });\n\t\t\t\t\t}\n\t\t\t\t\tthis.onLayoutUpdated?.(liveMap);\n\t\t\t\t},\n\t\t\t\tonComplete: () => {\n\t\t\t\t\tthis.activeTransitionCancel = null;\n\t\t\t\t\tthis.onLayoutUpdated?.(nextPositions);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tthis.onLayoutUpdated?.(nextPositions);\n\t\t}\n\t}\n\n\tpublic destroy(): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t\tthis.pendingTimer = null;\n\t\t}\n\t\tif (this.activeTransitionCancel) {\n\t\t\tthis.activeTransitionCancel();\n\t\t\tthis.activeTransitionCancel = null;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["dagre"]}
1
+ {"version":3,"sources":["../../src/canvas/clock.ts","../../src/canvas/trajectory.ts","../../src/canvas/displacement.ts","../../src/canvas/clipboard.ts","../../src/canvas/view.ts","../../src/canvas/streaming.ts"],"sourcesContent":["/**\n * The time source that canvas animations and agent performances play on.\n *\n * Defaults to `realClock` (requestAnimationFrame and setTimeout).\n * A `ManualClock` moves only when told to via `advance(ms)` or `tick(fps)`,\n * enabling deterministic frame-by-frame stepping for testing, video generation,\n * or headless server environments.\n */\n\nexport interface Clock {\n\t/** Current time in milliseconds. */\n\tnow(): number;\n\t/** Resolves on the next animation frame. */\n\tframe(): Promise<void>;\n\t/** Resolves after `ms` milliseconds of this clock's time. */\n\tsleep(ms: number): Promise<void>;\n\t/**\n\t * Whether the clock follows wall time. Live clocks stop or degrade when the\n\t * tab is in the background (browsers pause animation frames), so transitions\n\t * can commit immediately rather than leaving operations hanging.\n\t */\n\treadonly live: boolean;\n}\n\n/** Whether the browser tab is hidden (where RAF pauses). */\nexport const pageHidden = (): boolean =>\n\ttypeof document !== 'undefined' && document.hidden;\n\n/** Real-time clock using requestAnimationFrame and setTimeout. */\nexport const realClock: Clock = {\n\tlive: true,\n\tnow: () => (typeof performance !== 'undefined' ? performance.now() : Date.now()),\n\tframe: () =>\n\t\tnew Promise<void>((resolve) => {\n\t\t\tif (typeof requestAnimationFrame === 'function' && !pageHidden()) {\n\t\t\t\trequestAnimationFrame(() => resolve());\n\t\t\t} else {\n\t\t\t\tsetTimeout(resolve, 16);\n\t\t\t}\n\t\t}),\n\tsleep: (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, Math.max(0, ms)))\n};\n\nconst settle = () => new Promise<void>((resolve) => setTimeout(resolve, 0));\n\n/**\n * A deterministic clock that only moves forward when `advance` or `tick` is called.\n * Allows step-by-step frame testing and video rendering without waiting for real time.\n */\nexport class ManualClock implements Clock {\n\treadonly live = false;\n\tprivate time: number;\n\tprivate frames: (() => void)[] = [];\n\tprivate sleepers: { at: number; seq: number; resolve: () => void }[] = [];\n\tprivate seq = 0;\n\n\tconstructor(start = 0) {\n\t\tthis.time = start;\n\t}\n\n\tpublic now(): number {\n\t\treturn this.time;\n\t}\n\n\tpublic frame(): Promise<void> {\n\t\treturn new Promise<void>((resolve) => this.frames.push(resolve));\n\t}\n\n\tpublic sleep(ms: number): Promise<void> {\n\t\treturn new Promise<void>((resolve) =>\n\t\t\tthis.sleepers.push({\n\t\t\t\tat: this.time + Math.max(0, ms),\n\t\t\t\tseq: this.seq++,\n\t\t\t\tresolve\n\t\t\t})\n\t\t);\n\t}\n\n\t/** Whether any frames or sleep timers are currently pending. */\n\tpublic get pending(): boolean {\n\t\treturn this.frames.length > 0 || this.sleepers.length > 0;\n\t}\n\n\t/** Advances time by `ms`, resolving due sleep timers in order, followed by queued frames. */\n\tpublic async advance(ms: number): Promise<void> {\n\t\tconst end = this.time + Math.max(0, ms);\n\t\tawait settle();\n\n\t\twhile (true) {\n\t\t\tlet next: (typeof this.sleepers)[number] | undefined;\n\t\t\tfor (const s of this.sleepers) {\n\t\t\t\tif (s.at <= end && (!next || s.at < next.at || (s.at === next.at && s.seq < next.seq))) {\n\t\t\t\t\tnext = s;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!next) break;\n\n\t\t\tthis.sleepers.splice(this.sleepers.indexOf(next), 1);\n\t\t\tthis.time = Math.max(this.time, next.at);\n\t\t\tnext.resolve();\n\t\t\tawait settle();\n\t\t}\n\n\t\tthis.time = end;\n\t\tconst dueFrames = this.frames.splice(0, this.frames.length);\n\t\tfor (const resolveFrame of dueFrames) {\n\t\t\tresolveFrame();\n\t\t}\n\t\tawait settle();\n\t}\n\n\t/** Convenience method to advance by 1 frame at a given fps (default: 60fps = 16.67ms). */\n\tpublic async tick(fps = 60): Promise<void> {\n\t\tawait this.advance(Math.round(1000 / fps));\n\t}\n}\n","import type {\n\tCanvasNode,\n\tFanOutTrajectoryOptions,\n\tNodeTransition,\n\tTrajectoryFunction,\n\tTrajectoryPoint,\n\tXYPosition\n} from './types.js';\n\n/**\n * Standard cubic ease-out function (pure math, framework-agnostic).\n */\nexport const defaultEasing = (t: number): number => --t * t * t + 1;\n\n/**\n * Standard linear trajectory: straight line interpolation with linear scale & opacity progression.\n */\nexport const linearTrajectory: TrajectoryFunction = (\n\torigin: XYPosition,\n\ttarget: XYPosition,\n\tprogress: number\n): TrajectoryPoint => {\n\treturn {\n\t\tposition: {\n\t\t\tx: origin.x + (target.x - origin.x) * progress,\n\t\t\ty: origin.y + (target.y - origin.y) * progress\n\t\t},\n\t\tscale: progress,\n\t\topacity: Math.min(1, progress * 1.5)\n\t};\n};\n\n/**\n * Linear position trajectory: pure position interpolation without altering scale or opacity.\n */\nexport const linearPositionTrajectory: TrajectoryFunction = (\n\torigin: XYPosition,\n\ttarget: XYPosition,\n\tprogress: number\n): TrajectoryPoint => {\n\treturn {\n\t\tposition: {\n\t\t\tx: origin.x + (target.x - origin.x) * progress,\n\t\t\ty: origin.y + (target.y - origin.y) * progress\n\t\t}\n\t};\n};\n\n/**\n * Creates a fan-out trajectory where nodes arc outward from origin to target.\n */\nexport function createFanOutTrajectory(options: FanOutTrajectoryOptions = {}): TrajectoryFunction {\n\tconst {\n\t\tcurvature = 0.4,\n\t\tstartScale = 0,\n\t\tendScale = 1,\n\t\tstartOpacity = 0,\n\t\tendOpacity = 1\n\t} = options;\n\n\treturn (\n\t\torigin: XYPosition,\n\t\ttarget: XYPosition,\n\t\tprogress: number,\n\t\tindex: number,\n\t\ttotal: number\n\t): TrajectoryPoint => {\n\t\tconst dx = target.x - origin.x;\n\t\tconst dy = target.y - origin.y;\n\n\t\t// Normalized index from -1 (top) to +1 (bottom)\n\t\tconst normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;\n\n\t\t// Perpendicular control point offset to create smooth fan arc\n\t\tconst midX = origin.x + dx * 0.4;\n\t\tconst midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;\n\n\t\t// Quadratic Bezier interpolation: B(t) = (1-t)^2 * P0 + 2(1-t)t * P1 + t^2 * P2\n\t\tconst t = progress;\n\t\tconst oneMinusT = 1 - t;\n\n\t\tconst x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;\n\t\tconst y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;\n\n\t\tconst scale = startScale + (endScale - startScale) * progress;\n\t\tconst opacity = startOpacity + (endOpacity - startOpacity) * progress;\n\n\t\treturn {\n\t\t\tposition: { x, y },\n\t\t\tscale,\n\t\t\topacity\n\t\t};\n\t};\n}\n\n/**\n * Creates a custom quadratic Bezier trajectory with a specified control point resolver.\n */\nexport function createBezierTrajectory(\n\tgetControlPoint: (origin: XYPosition, target: XYPosition) => XYPosition\n): TrajectoryFunction {\n\treturn (origin: XYPosition, target: XYPosition, progress: number): TrajectoryPoint => {\n\t\tconst cp = getControlPoint(origin, target);\n\t\tconst t = progress;\n\t\tconst oneMinusT = 1 - t;\n\n\t\tconst x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;\n\t\tconst y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;\n\n\t\treturn {\n\t\t\tposition: { x, y },\n\t\t\tscale: progress,\n\t\t\topacity: progress\n\t\t};\n\t};\n}\n\nimport { realClock, type Clock } from './clock.js';\n\n/**\n * Coordinates and animates multiple node transitions simultaneously with zero per-frame array allocations.\n */\nexport function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(\n\ttransitions: NodeTransition<TNode>[],\n\toptions: {\n\t\tdefaultDuration?: number;\n\t\tdefaultEasing?: (t: number) => number;\n\t\tclock?: Clock;\n\t\tonUpdate?: () => void;\n\t\tonComplete?: () => void;\n\t} = {}\n): () => void {\n\tif (transitions.length === 0) {\n\t\toptions.onComplete?.();\n\t\treturn () => {};\n\t}\n\n\tconst {\n\t\tdefaultDuration = 400,\n\t\tdefaultEasing: easingFunc = defaultEasing,\n\t\tclock = realClock,\n\t\tonUpdate,\n\t\tonComplete\n\t} = options;\n\n\tlet isCancelled = false;\n\tconst startTime = clock.now();\n\n\tconst items = transitions.map((tr, i) => ({\n\t\t...tr,\n\t\tindex: tr.index ?? i,\n\t\ttotal: tr.total ?? transitions.length,\n\t\tduration: tr.duration ?? defaultDuration,\n\t\teasing: tr.easing ?? easingFunc,\n\t\tdelay: tr.delay ?? 0,\n\t\ttrajectory: tr.trajectory ?? linearTrajectory\n\t}));\n\n\tfunction frame() {\n\t\tif (isCancelled) return;\n\n\t\tconst now = clock.now();\n\t\tlet allCompleted = true;\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst item = items[i];\n\t\t\tconst elapsed = now - (startTime + item.delay);\n\n\t\t\tif (elapsed < 0) {\n\t\t\t\tallCompleted = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Guard zero duration and division by zero (referenced in GOTCHAS.md)\n\t\t\tconst rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);\n\t\t\tconst progress = item.duration <= 0 ? 1 : item.easing(rawProgress);\n\n\t\t\tconst point = item.trajectory(item.from, item.to, progress, item.index, item.total);\n\n\t\t\titem.node.position = { ...point.position };\n\n\t\t\tif (point.scale !== undefined || point.opacity !== undefined) {\n\t\t\t\tif (!item.node.data) {\n\t\t\t\t\titem.node.data = {} as any;\n\t\t\t\t}\n\t\t\t\tif (point.scale !== undefined) {\n\t\t\t\t\t(item.node.data as any).scale = point.scale;\n\t\t\t\t}\n\t\t\t\tif (point.opacity !== undefined) {\n\t\t\t\t\t(item.node.data as any).opacity = point.opacity;\n\t\t\t\t\tconst currentStyle = (item.node.style || '').replace(/opacity:\\s*[^;]+;?/g, '').trim();\n\t\t\t\t\titem.node.style = `${currentStyle ? currentStyle + '; ' : ''}opacity: ${point.opacity};`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (rawProgress < 1) {\n\t\t\t\tallCompleted = false;\n\t\t\t}\n\t\t}\n\n\t\tonUpdate?.();\n\n\t\tif (!allCompleted) {\n\t\t\tclock.frame().then(frame);\n\t\t} else {\n\t\t\tonComplete?.();\n\t\t}\n\t}\n\n\tframe();\n\n\treturn () => {\n\t\tisCancelled = true;\n\t};\n}\n","import dagre from '@dagrejs/dagre';\nimport type {\n\tCanvasEdge,\n\tCanvasNode,\n\tDirectionalDisplacementOptions,\n\tRect,\n\tReflowDisplacementOptions,\n\tXYPosition\n} from './types.js';\n\n/**\n * Calculates a bounding rectangle for a set of nodes if not provided by getNodesBounds.\n */\nexport function computeNodesBoundingBox<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\tdefaultWidth = 200,\n\tdefaultHeight = 60\n): Rect {\n\tif (nodes.length === 0) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\n\tfor (const node of nodes) {\n\t\tconst width = node.measured?.width ?? (node as any).width ?? defaultWidth;\n\t\tconst height = node.measured?.height ?? (node as any).height ?? defaultHeight;\n\n\t\tminX = Math.min(minX, node.position.x);\n\t\tminY = Math.min(minY, node.position.y);\n\t\tmaxX = Math.max(maxX, node.position.x + width);\n\t\tmaxY = Math.max(maxY, node.position.y + height);\n\t}\n\n\treturn {\n\t\tx: minX,\n\t\ty: minY,\n\t\twidth: maxX - minX,\n\t\theight: maxY - minY\n\t};\n}\n\n/**\n * Pure directional displacement calculation.\n * Shifts downstream or surrounding nodes along an axis to clear space for newly inserted child nodes.\n */\nexport function calculateDirectionalDisplacement<TNode extends CanvasNode = CanvasNode>(params: {\n\texistingNodes: TNode[];\n\toriginNode: TNode;\n\tchildNodes: TNode[];\n\tgetNodesBounds?: (nodes: TNode[]) => Rect;\n\toptions?: DirectionalDisplacementOptions;\n}): Map<string, XYPosition> {\n\tconst {\n\t\texistingNodes,\n\t\toriginNode,\n\t\tchildNodes,\n\t\tgetNodesBounds,\n\t\toptions = {}\n\t} = params;\n\n\tconst {\n\t\tdirection = 'right',\n\t\tgap = 40,\n\t\tspreadSiblingLanes = false,\n\t\tsiblingTolerance = 50,\n\t\tminY\n\t} = options;\n\n\tconst displacedMap = new Map<string, XYPosition>();\n\tif (childNodes.length === 0 || existingNodes.length === 0) {\n\t\treturn displacedMap;\n\t}\n\n\tconst childBounds = getNodesBounds\n\t\t? getNodesBounds(childNodes)\n\t\t: computeNodesBoundingBox(childNodes);\n\n\tconst originWidth = originNode.measured?.width ?? (originNode as any).width ?? 200;\n\tconst originHeight = originNode.measured?.height ?? (originNode as any).height ?? 60;\n\n\t// The delta space required along primary axis\n\tconst deltaPrimary =\n\t\tdirection === 'right' || direction === 'left'\n\t\t\t? childBounds.width + gap\n\t\t\t: childBounds.height + gap;\n\n\t// Vertical displacement parameters: upward and downward row clearance\n\tconst deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);\n\tconst deltaDown = Math.max(\n\t\t0,\n\t\tchildBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap\n\t);\n\n\t// Both above and below displace equally so the row expands symmetrically\n\tconst equalDelta = Math.max(deltaUp, deltaDown);\n\n\tfor (const node of existingNodes) {\n\t\tif (node.id === originNode.id) continue;\n\n\t\tconst isSiblingRow =\n\t\t\tMath.abs(node.position.y - originNode.position.y) < siblingTolerance;\n\n\t\tswitch (direction) {\n\t\t\tcase 'right': {\n\t\t\t\tif (node.position.x > originNode.position.x) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x + deltaPrimary,\n\t\t\t\t\t\ty: node.position.y\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\tif (node.position.x < originNode.position.x) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x - deltaPrimary,\n\t\t\t\t\t\ty: node.position.y\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'down': {\n\t\t\t\tif (node.position.y > originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y + deltaPrimary\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'up': {\n\t\t\t\tif (node.position.y < originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y - deltaPrimary\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'vertical': {\n\t\t\t\tif (isSiblingRow) break;\n\n\t\t\t\tif (node.position.y < originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y - equalDelta\n\t\t\t\t\t});\n\t\t\t\t} else if (node.position.y > originNode.position.y) {\n\t\t\t\t\tdisplacedMap.set(node.id, {\n\t\t\t\t\t\tx: node.position.x,\n\t\t\t\t\t\ty: node.position.y + equalDelta\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sibling lane spreading to avoid overlap among child nodes\n\tif (spreadSiblingLanes && (direction === 'right' || direction === 'left')) {\n\t\tfor (const [nodeId, pos] of displacedMap.entries()) {\n\t\t\tconst node = existingNodes.find((n) => n.id === nodeId);\n\t\t\tif (node && Math.abs(node.position.y - originNode.position.y) < siblingTolerance) {\n\t\t\t\tconst isAbove = node.position.y < originNode.position.y;\n\t\t\t\tdisplacedMap.set(nodeId, {\n\t\t\t\t\tx: pos.x,\n\t\t\t\t\ty: pos.y + (isAbove ? -deltaUp : deltaDown)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Floor constraint\n\tif (minY !== undefined) {\n\t\tfor (const [nodeId, pos] of displacedMap.entries()) {\n\t\t\tif (pos.y < minY) {\n\t\t\t\tdisplacedMap.set(nodeId, { x: pos.x, y: minY });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn displacedMap;\n}\n\n/**\n * Calculates Dagre reflow displacement preserving the position of an anchor node.\n */\nexport function calculateDagreReflowDisplacement<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(params: {\n\tcurrentNodes: TNode[];\n\tcurrentEdges: TEdge[];\n\tfutureNodes: TNode[];\n\tfutureEdges: TEdge[];\n\toptions: ReflowDisplacementOptions;\n\tgetNodesBounds?: (nodes: TNode[]) => Rect;\n}): Map<string, XYPosition> {\n\tconst {\n\t\tcurrentNodes,\n\t\tfutureNodes,\n\t\tfutureEdges,\n\t\toptions\n\t} = params;\n\n\tconst {\n\t\tanchorNodeId,\n\t\tdirection = 'LR',\n\t\tranksep = 100,\n\t\tnodesep = 25,\n\t\tdefaultNodeWidth = 200,\n\t\tdefaultNodeHeight = 60\n\t} = options;\n\n\tconst anchorNode = currentNodes.find((n) => n.id === anchorNodeId);\n\tif (!anchorNode) return new Map();\n\n\tconst g = new dagre.graphlib.Graph();\n\tg.setDefaultEdgeLabel(() => ({}));\n\tg.setGraph({ rankdir: direction, ranksep, nodesep });\n\n\tconst nodeIds = new Set(futureNodes.map((n) => n.id));\n\n\tfutureNodes.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\tg.setNode(node.id, { width: nodeWidth, height: nodeHeight });\n\t});\n\n\tfutureEdges.forEach((edge) => {\n\t\tif (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {\n\t\t\tg.setEdge(edge.source, edge.target);\n\t\t}\n\t});\n\n\tdagre.layout(g);\n\n\tconst anchorLayoutPos = g.node(anchorNodeId);\n\tif (!anchorLayoutPos) return new Map();\n\n\tconst anchorWidth = anchorNode.measured?.width ?? (anchorNode as any).width ?? defaultNodeWidth;\n\tconst anchorHeight = anchorNode.measured?.height ?? (anchorNode as any).height ?? defaultNodeHeight;\n\n\tconst anchorLayoutUpperLeft = {\n\t\tx: anchorLayoutPos.x - anchorWidth / 2,\n\t\ty: anchorLayoutPos.y - anchorHeight / 2\n\t};\n\n\tconst deltaX = anchorNode.position.x - anchorLayoutUpperLeft.x;\n\tconst deltaY = anchorNode.position.y - anchorLayoutUpperLeft.y;\n\n\tconst displacementMap = new Map<string, XYPosition>();\n\n\tcurrentNodes.forEach((node) => {\n\t\tif (node.id === anchorNodeId) return;\n\n\t\tconst layoutNode = g.node(node.id);\n\t\tif (!layoutNode) return;\n\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\tconst upperLeftX = layoutNode.x - nodeWidth / 2 + deltaX;\n\t\tconst upperLeftY = layoutNode.y - nodeHeight / 2 + deltaY;\n\n\t\tif (\n\t\t\tMath.abs(upperLeftX - node.position.x) > 1 ||\n\t\t\tMath.abs(upperLeftY - node.position.y) > 1\n\t\t) {\n\t\t\tdisplacementMap.set(node.id, { x: upperLeftX, y: upperLeftY });\n\t\t}\n\t});\n\n\treturn displacementMap;\n}\n","import type {\n\tCanvasClipboardPayload,\n\tCanvasEdge,\n\tCanvasNode,\n\tXYPosition\n} from './types.js';\n\n/**\n * Checks if an event originated from a text input, textarea, contenteditable element, or rich editor.\n */\nexport function isEventFromTextInput(event: Event | { target: EventTarget | null }): boolean {\n\tconst target = (event as any).target as HTMLElement | null;\n\tif (!target) return false;\n\tif (\n\t\ttarget.tagName === 'INPUT' ||\n\t\ttarget.tagName === 'TEXTAREA' ||\n\t\ttarget.tagName === 'SELECT' ||\n\t\ttarget.isContentEditable ||\n\t\ttarget.closest?.('[contenteditable=\"true\"]') ||\n\t\ttarget.closest?.('.ProseMirror')\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Validates that an object conforms to minimal CanvasNode shape.\n */\nexport function isValidCanvasNode(obj: unknown): obj is CanvasNode {\n\treturn (\n\t\ttypeof obj === 'object' &&\n\t\tobj !== null &&\n\t\t'id' in obj &&\n\t\ttypeof (obj as any).id === 'string' &&\n\t\t'position' in obj &&\n\t\ttypeof (obj as any).position === 'object' &&\n\t\t(obj as any).position !== null &&\n\t\t'x' in (obj as any).position &&\n\t\t'y' in (obj as any).position\n\t);\n}\n\n/**\n * Checks if a string is a valid HTTP/HTTPS URL.\n */\nexport function isValidUrl(url: string): boolean {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn parsed.protocol === 'http:' || parsed.protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Serializes nodes and optional edges into clipboard JSON payload.\n */\nexport function serializeCanvasNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(nodes: TNode[], edges?: TEdge[]): string {\n\tconst payload: CanvasClipboardPayload<TNode, TEdge> = {\n\t\tversion: 1,\n\t\tnodes,\n\t\tedges\n\t};\n\treturn JSON.stringify(payload);\n}\n\n/**\n * Parses raw JSON string into a CanvasClipboardPayload if valid.\n */\nexport function parseCanvasClipboardData<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(raw: string): CanvasClipboardPayload<TNode, TEdge> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (\n\t\t\ttypeof parsed === 'object' &&\n\t\t\tparsed !== null &&\n\t\t\tArray.isArray(parsed.nodes) &&\n\t\t\tparsed.nodes.length > 0 &&\n\t\t\tparsed.nodes.every(isValidCanvasNode)\n\t\t) {\n\t\t\treturn parsed as CanvasClipboardPayload<TNode, TEdge>;\n\t\t}\n\t} catch {\n\t\t// Not JSON or invalid payload\n\t}\n\treturn null;\n}\n\n/**\n * Generates unique new IDs for pasted nodes and updates edge references accordingly.\n */\nexport function remapPastedNodes<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[] = [],\n\toffset: XYPosition = { x: 30, y: 30 },\n\tidGenerator: () => string = () => `node-${Math.random().toString(36).substring(2, 9)}`\n): { nodes: TNode[]; edges: TEdge[] } {\n\tconst idMap = new Map<string, string>();\n\n\tconst newNodes = nodes.map((node) => {\n\t\tconst newId = idGenerator();\n\t\tidMap.set(node.id, newId);\n\t\treturn {\n\t\t\t...node,\n\t\t\tid: newId,\n\t\t\tposition: {\n\t\t\t\tx: node.position.x + offset.x,\n\t\t\t\ty: node.position.y + offset.y\n\t\t\t},\n\t\t\tselected: true\n\t\t};\n\t});\n\n\tconst newEdges = edges\n\t\t.filter((e) => idMap.has(e.source) && idMap.has(e.target))\n\t\t.map((edge) => ({\n\t\t\t...edge,\n\t\t\tid: `edge-${Math.random().toString(36).substring(2, 9)}`,\n\t\t\tsource: idMap.get(edge.source)!,\n\t\t\ttarget: idMap.get(edge.target)!,\n\t\t\tselected: false\n\t\t}));\n\n\treturn { nodes: newNodes, edges: newEdges };\n}\n\n/**\n * Repositions nodes so their bounding box origin matches the given target position.\n */\nexport function repositionNodesTo<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\ttargetOrigin: XYPosition\n): TNode[] {\n\tif (nodes.length === 0) return [];\n\tconst minX = Math.min(...nodes.map((n) => n.position.x));\n\tconst minY = Math.min(...nodes.map((n) => n.position.y));\n\tconst deltaX = targetOrigin.x - minX;\n\tconst deltaY = targetOrigin.y - minY;\n\n\treturn nodes.map((n) => ({\n\t\t...n,\n\t\tposition: {\n\t\t\tx: n.position.x + deltaX,\n\t\t\ty: n.position.y + deltaY\n\t\t}\n\t}));\n}\n","import type { CanvasEdge, CanvasNode, Rect } from './types.js';\n\nexport interface CanvasHeightOptions {\n\t/** Smallest height returned (default 320). */\n\tmin?: number;\n\t/** Largest height returned; taller content is scaled/zoomed (default 560). */\n\tmax?: number;\n\t/** Extra padding around the content (default 48). */\n\tpadding?: number;\n}\n\nfunction clampHeight(contentHeight: number, options: CanvasHeightOptions = {}): number {\n\tconst { min = 320, max = 560, padding = 48 } = options;\n\treturn Math.round(Math.min(max, Math.max(min, contentHeight + padding)));\n}\n\n/**\n * Calculates the bounding box extent of all positioned nodes, or `null` for an empty graph.\n */\nexport function layoutBounds<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[]\n): Rect | null {\n\tif (nodes.length === 0) return null;\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\n\tfor (const node of nodes) {\n\t\tconst x = node.position.x;\n\t\tconst y = node.position.y;\n\t\tconst width = node.width ?? 180;\n\t\tconst height = node.height ?? 80;\n\n\t\tminX = Math.min(minX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxX = Math.max(maxX, x + width);\n\t\tmaxY = Math.max(maxY, y + height);\n\t}\n\n\tif (minX === Infinity) return null;\n\treturn { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * Returns a clamped container height that fits the layout at 1:1 scale without overflow.\n */\nexport function canvasHeight<TNode extends CanvasNode = CanvasNode>(\n\tnodes: TNode[],\n\toptions?: CanvasHeightOptions\n): number {\n\treturn clampHeight(layoutBounds(nodes)?.height ?? 0, options);\n}\n\nconst DEFAULT_RANK_HEIGHT = 80 + 72; // Average node height + default rank separation\nconst NODES_PER_RANK = 2;\n\n/**\n * Estimates the container height to reserve before layout calculation finishes,\n * based on node and edge counts alone. Prevents layout shift (CLS).\n */\nexport function estimateCanvasHeight<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[],\n\toptions?: CanvasHeightOptions\n): number {\n\tconst ranks = Math.max(1, Math.ceil(nodes.length / NODES_PER_RANK));\n\treturn clampHeight(ranks * DEFAULT_RANK_HEIGHT, options);\n}\n\nfunction mix(a: number, b: number, t: number): number {\n\treturn a + (b - a) * t;\n}\n\n/**\n * Interpolates node positions and sizes between two layouts partway at progress `t` (0 = from, 1 = to).\n * Nodes present in both interpolate smoothly; nodes newly added in `to` snap to target.\n */\nexport function blendLayout<TNode extends CanvasNode = CanvasNode>(\n\tfrom: TNode[],\n\tto: TNode[],\n\tt: number\n): TNode[] {\n\tif (t >= 1) return to;\n\n\tconst fromMap = new Map<string, TNode>(from.map((n) => [n.id, n]));\n\n\treturn to.map((target) => {\n\t\tconst origin = fromMap.get(target.id);\n\t\tif (!origin) return target;\n\n\t\treturn {\n\t\t\t...target,\n\t\t\tposition: {\n\t\t\t\tx: mix(origin.position.x, target.position.x, t),\n\t\t\t\ty: mix(origin.position.y, target.position.y, t)\n\t\t\t},\n\t\t\twidth:\n\t\t\t\torigin.width !== undefined && target.width !== undefined\n\t\t\t\t\t? mix(origin.width, target.width, t)\n\t\t\t\t\t: target.width,\n\t\t\theight:\n\t\t\t\torigin.height !== undefined && target.height !== undefined\n\t\t\t\t\t? mix(origin.height, target.height, t)\n\t\t\t\t\t: target.height\n\t\t};\n\t});\n}\n\n/**\n * Filters out nodes that are not touched by any edge.\n *\n * During LLM streaming, nodes often arrive a chunk or token before the edge\n * that connects them. Displaying them immediately causes them to be placed as root\n * nodes only to jump into their correct position once the edge arrives.\n * Hiding orphan nodes during active streaming keeps the layout calm and jitter-free.\n */\nexport function connectedOnly<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n>(\n\tnodes: TNode[],\n\tedges: TEdge[]\n): TNode[] {\n\tif (edges.length === 0) return nodes;\n\n\tconst linkedNodeIds = new Set<string>();\n\tfor (const edge of edges) {\n\t\tlinkedNodeIds.add(edge.source);\n\t\tlinkedNodeIds.add(edge.target);\n\t}\n\n\tconst filtered = nodes.filter((node) => linkedNodeIds.has(node.id));\n\t// If filtering would remove everything, return the original nodes\n\treturn filtered.length > 0 ? filtered : nodes;\n}\n","import { realClock, type Clock } from './clock.js';\nimport { ensureLayout } from './layout.js';\nimport { linearPositionTrajectory, runMultiNodeTransition } from './trajectory.js';\nimport type { CanvasEdge, CanvasNode, IncrementalLayoutOptions, NodeTransition, XYPosition } from './types.js';\nimport { connectedOnly } from './view.js';\n\nexport interface StreamingLayoutManagerOptions {\n\tthrottleMs?: number;\n\tglideDurationMs?: number;\n\tconnectedOnly?: boolean;\n\tclock?: Clock;\n\tlayoutOptions?: IncrementalLayoutOptions;\n\tonLayoutUpdated?: (positions: Map<string, XYPosition>) => void;\n}\n\n/**\n * Manages throttled incremental layout recalculation and smooth tweened node gliding\n * during active streaming (e.g. LLM streaming or graph generation).\n */\nexport class StreamingLayoutManager<\n\tTNode extends CanvasNode = CanvasNode,\n\tTEdge extends CanvasEdge = CanvasEdge\n> {\n\tprivate throttleMs: number;\n\tprivate glideDurationMs: number;\n\tprivate connectedOnly: boolean;\n\tprivate clock: Clock;\n\tprivate layoutOptions: IncrementalLayoutOptions;\n\tprivate onLayoutUpdated?: (positions: Map<string, XYPosition>) => void;\n\n\tprivate pendingTimer: any = null;\n\tprivate activeTransitionCancel: (() => void) | null = null;\n\tprivate targetPositions = new Map<string, XYPosition>();\n\n\tconstructor(options: StreamingLayoutManagerOptions = {}) {\n\t\tthis.throttleMs = options.throttleMs ?? 200;\n\t\tthis.glideDurationMs = options.glideDurationMs ?? 300;\n\t\tthis.connectedOnly = options.connectedOnly ?? false;\n\t\tthis.clock = options.clock ?? realClock;\n\t\tthis.layoutOptions = options.layoutOptions ?? { direction: 'LR' };\n\t\tthis.onLayoutUpdated = options.onLayoutUpdated;\n\t}\n\n\t/**\n\t * Feed incoming nodes and edges into the streaming manager.\n\t * Coalesces rapid streaming bursts and triggers smooth layout gliding.\n\t */\n\tpublic pushStreamUpdate(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t}\n\n\t\tthis.pendingTimer = setTimeout(() => {\n\t\t\tthis.pendingTimer = null;\n\t\t\tthis.recalculateAndGlide(nodes, edges, currentPositions);\n\t\t}, this.throttleMs);\n\t}\n\n\t/**\n\t * Forces immediate calculation and cancels pending throttled timers.\n\t */\n\tpublic flush(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t\tthis.pendingTimer = null;\n\t\t}\n\t\tthis.recalculateAndGlide(nodes, edges, currentPositions);\n\t}\n\n\tprivate recalculateAndGlide(\n\t\tnodes: TNode[],\n\t\tedges: TEdge[],\n\t\tcurrentPositions: Map<string, XYPosition>\n\t): void {\n\t\tconst targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;\n\t\tif (targetNodes.length === 0) return;\n\n\t\t// Cancel any currently running RAF glide\n\t\tif (this.activeTransitionCancel) {\n\t\t\tthis.activeTransitionCancel();\n\t\t\tthis.activeTransitionCancel = null;\n\t\t}\n\n\t\t// Incremental layout preserves existing nodes and aligns new nodes\n\t\tconst stored: Record<string, XYPosition> = {};\n\t\tfor (const [id, pos] of currentPositions.entries()) {\n\t\t\tstored[id] = pos;\n\t\t}\n\n\t\tconst layouted = ensureLayout(targetNodes, edges, {\n\t\t\t...this.layoutOptions,\n\t\t\tstoredPositions: stored\n\t\t});\n\n\t\tconst nextPositions = new Map<string, XYPosition>();\n\t\tconst transitions: NodeTransition<TNode>[] = [];\n\n\t\tfor (const node of layouted) {\n\t\t\tconst target = node.position;\n\t\t\tnextPositions.set(node.id, target);\n\n\t\t\tconst current = currentPositions.get(node.id) ?? target;\n\t\t\tconst distance = Math.hypot(target.x - current.x, target.y - current.y);\n\n\t\t\tif (distance > 2) {\n\t\t\t\ttransitions.push({\n\t\t\t\t\tnode,\n\t\t\t\t\tfrom: current,\n\t\t\t\t\tto: target,\n\t\t\t\t\ttrajectory: linearPositionTrajectory,\n\t\t\t\t\tduration: this.glideDurationMs\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.targetPositions = nextPositions;\n\n\t\tif (transitions.length > 0) {\n\t\t\tthis.activeTransitionCancel = runMultiNodeTransition(transitions, {\n\t\t\t\tclock: this.clock,\n\t\t\t\tonUpdate: () => {\n\t\t\t\t\t// Broadcast updated positions during RAF\n\t\t\t\t\tconst liveMap = new Map<string, XYPosition>(currentPositions);\n\t\t\t\t\tfor (const trans of transitions) {\n\t\t\t\t\t\tliveMap.set(trans.node.id, { ...trans.node.position });\n\t\t\t\t\t}\n\t\t\t\t\tthis.onLayoutUpdated?.(liveMap);\n\t\t\t\t},\n\t\t\t\tonComplete: () => {\n\t\t\t\t\tthis.activeTransitionCancel = null;\n\t\t\t\t\tthis.onLayoutUpdated?.(nextPositions);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tthis.onLayoutUpdated?.(nextPositions);\n\t\t}\n\t}\n\n\tpublic destroy(): void {\n\t\tif (this.pendingTimer) {\n\t\t\tclearTimeout(this.pendingTimer);\n\t\t\tthis.pendingTimer = null;\n\t\t}\n\t\tif (this.activeTransitionCancel) {\n\t\t\tthis.activeTransitionCancel();\n\t\t\tthis.activeTransitionCancel = null;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,233 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/history/HistoryManager.ts
2
+ var HistoryManager = (_class = class {
3
+ __init() {this.undoStack = []}
4
+ __init2() {this.redoStack = []}
5
+
6
+ __init3() {this.isApplying = false}
7
+ __init4() {this.batchDepth = 0}
8
+ __init5() {this.currentBatch = null}
9
+ __init6() {this.listeners = /* @__PURE__ */ new Set()}
10
+
11
+
12
+ constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);
13
+ this.maxDepth = _nullishCoalesce(options.maxDepth, () => ( 50));
14
+ this.onStateChange = options.onStateChange;
15
+ this.cachedState = this.computeState();
16
+ }
17
+ computeState() {
18
+ const canUndo = this.undoStack.length > 0;
19
+ const canRedo = this.redoStack.length > 0;
20
+ return {
21
+ canUndo,
22
+ canRedo,
23
+ undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : "",
24
+ redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : "",
25
+ undoCount: this.undoStack.length,
26
+ redoCount: this.redoStack.length
27
+ };
28
+ }
29
+ /**
30
+ * Returns a snapshot of the current history state.
31
+ * Cached to maintain referential equality across renders for useSyncExternalStore.
32
+ */
33
+ getState() {
34
+ return this.cachedState;
35
+ }
36
+ notify() {
37
+ this.cachedState = this.computeState();
38
+ const state = this.cachedState;
39
+ _optionalChain([this, 'access', _ => _.onStateChange, 'optionalCall', _2 => _2(state)]);
40
+ this.listeners.forEach((listener) => {
41
+ try {
42
+ listener(state);
43
+ } catch (err) {
44
+ console.error("[HistoryManager] Error in state listener:", err);
45
+ }
46
+ });
47
+ }
48
+ /**
49
+ * Executes a command and pushes it onto the undo stack.
50
+ * Clears the redo stack.
51
+ */
52
+ execute(command) {
53
+ if (this.isApplying) {
54
+ command.execute();
55
+ return;
56
+ }
57
+ if (this.batchDepth > 0 && this.currentBatch) {
58
+ command.execute();
59
+ this.currentBatch.push({
60
+ ...command,
61
+ timestamp: _nullishCoalesce(command.timestamp, () => ( Date.now()))
62
+ });
63
+ return;
64
+ }
65
+ command.execute();
66
+ this.undoStack.push({
67
+ ...command,
68
+ timestamp: _nullishCoalesce(command.timestamp, () => ( Date.now()))
69
+ });
70
+ if (this.undoStack.length > this.maxDepth) {
71
+ this.undoStack.shift();
72
+ }
73
+ this.redoStack = [];
74
+ this.notify();
75
+ }
76
+ /**
77
+ * Executes a command, coalescing it with the previous command if they share
78
+ * the same `tag` and occurred within `windowMs` (default: 300ms).
79
+ * Useful for high-frequency interactions like slider scrubs or node drags.
80
+ */
81
+ executeMerged(command, options = {}) {
82
+ const windowMs = _nullishCoalesce(options.windowMs, () => ( 300));
83
+ const now = Date.now();
84
+ if (command.tag && this.undoStack.length > 0 && this.batchDepth === 0 && !this.isApplying) {
85
+ const top = this.undoStack[this.undoStack.length - 1];
86
+ const elapsed = top.timestamp ? now - top.timestamp : Infinity;
87
+ if (top.tag === command.tag && elapsed <= windowMs) {
88
+ command.execute();
89
+ const initialUndo = top.undo;
90
+ this.undoStack[this.undoStack.length - 1] = {
91
+ name: command.name,
92
+ tag: command.tag,
93
+ timestamp: now,
94
+ execute: command.execute,
95
+ undo: initialUndo,
96
+ redo: _nullishCoalesce(command.redo, () => ( command.execute))
97
+ };
98
+ this.redoStack = [];
99
+ this.notify();
100
+ return;
101
+ }
102
+ }
103
+ this.execute({ ...command, timestamp: now });
104
+ }
105
+ /**
106
+ * Batches multiple commands executed within `fn` into a single atomic undo entry.
107
+ */
108
+ batch(name, fn) {
109
+ if (this.isApplying) {
110
+ fn();
111
+ return;
112
+ }
113
+ if (this.batchDepth > 0) {
114
+ fn();
115
+ return;
116
+ }
117
+ this.batchDepth++;
118
+ this.currentBatch = [];
119
+ try {
120
+ fn();
121
+ } finally {
122
+ this.batchDepth--;
123
+ const batchCommands = this.currentBatch;
124
+ this.currentBatch = null;
125
+ if (batchCommands && batchCommands.length > 0) {
126
+ const compoundCommand = {
127
+ name,
128
+ timestamp: Date.now(),
129
+ execute: () => {
130
+ for (let i = 0; i < batchCommands.length; i++) {
131
+ (_nullishCoalesce(batchCommands[i].redo, () => ( batchCommands[i].execute)))();
132
+ }
133
+ },
134
+ undo: () => {
135
+ for (let i = batchCommands.length - 1; i >= 0; i--) {
136
+ batchCommands[i].undo();
137
+ }
138
+ },
139
+ redo: () => {
140
+ for (let i = 0; i < batchCommands.length; i++) {
141
+ (_nullishCoalesce(batchCommands[i].redo, () => ( batchCommands[i].execute)))();
142
+ }
143
+ }
144
+ };
145
+ this.undoStack.push(compoundCommand);
146
+ if (this.undoStack.length > this.maxDepth) {
147
+ this.undoStack.shift();
148
+ }
149
+ this.redoStack = [];
150
+ this.notify();
151
+ }
152
+ }
153
+ }
154
+ /**
155
+ * Reverts the most recent command on the undo stack.
156
+ * Returns true if a command was undone, false otherwise.
157
+ */
158
+ undo() {
159
+ if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
160
+ return false;
161
+ }
162
+ this.isApplying = true;
163
+ try {
164
+ const command = this.undoStack.pop();
165
+ command.undo();
166
+ this.redoStack.push(command);
167
+ return true;
168
+ } finally {
169
+ this.isApplying = false;
170
+ this.notify();
171
+ }
172
+ }
173
+ /**
174
+ * Re-applies the most recent undone command on the redo stack.
175
+ * Returns true if a command was redone, false otherwise.
176
+ */
177
+ redo() {
178
+ if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
179
+ return false;
180
+ }
181
+ this.isApplying = true;
182
+ try {
183
+ const command = this.redoStack.pop();
184
+ (_nullishCoalesce(command.redo, () => ( command.execute)))();
185
+ this.undoStack.push(command);
186
+ return true;
187
+ } finally {
188
+ this.isApplying = false;
189
+ this.notify();
190
+ }
191
+ }
192
+ /**
193
+ * Clears both undo and redo stacks.
194
+ */
195
+ clear() {
196
+ this.undoStack = [];
197
+ this.redoStack = [];
198
+ this.notify();
199
+ }
200
+ /**
201
+ * Subscribes to history state changes.
202
+ * The listener is immediately invoked with the current state.
203
+ * Returns an unsubscribe function.
204
+ */
205
+ subscribe(listener) {
206
+ this.listeners.add(listener);
207
+ listener(this.getState());
208
+ return () => {
209
+ this.listeners.delete(listener);
210
+ };
211
+ }
212
+ /**
213
+ * Utility helper to create a Command from before/after state snapshots.
214
+ */
215
+ static createSnapshotCommand(options) {
216
+ return {
217
+ name: options.name,
218
+ tag: options.tag,
219
+ execute: () => options.apply(options.after),
220
+ undo: () => options.apply(options.before),
221
+ redo: () => options.apply(options.after)
222
+ };
223
+ }
224
+ }, _class);
225
+ function createHistoryManager(options = {}) {
226
+ return new HistoryManager(options);
227
+ }
228
+
229
+
230
+
231
+
232
+ exports.HistoryManager = HistoryManager; exports.createHistoryManager = createHistoryManager;
233
+ //# sourceMappingURL=chunk-33UVEDOS.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-33UVEDOS.cjs","../src/history/HistoryManager.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-33UVEDOS.cjs","sourcesContent":[null,"import {\n Command,\n HistoryManagerOptions,\n HistoryState,\n MergedExecutionOptions,\n SnapshotCommandOptions,\n} from \"./types\";\n\n/**\n * UI-agnostic History and Undo/Redo manager supporting command closures,\n * snapshot diffs, transaction batching, and high-frequency event coalescing.\n */\nexport class HistoryManager {\n private undoStack: Command[] = [];\n private redoStack: Command[] = [];\n private readonly maxDepth: number;\n private isApplying = false;\n private batchDepth = 0;\n private currentBatch: Command[] | null = null;\n private listeners: Set<(state: HistoryState) => void> = new Set();\n private onStateChange?: (state: HistoryState) => void;\n private cachedState: HistoryState;\n\n constructor(options: HistoryManagerOptions = {}) {\n this.maxDepth = options.maxDepth ?? 50;\n this.onStateChange = options.onStateChange;\n this.cachedState = this.computeState();\n }\n\n private computeState(): HistoryState {\n const canUndo = this.undoStack.length > 0;\n const canRedo = this.redoStack.length > 0;\n return {\n canUndo,\n canRedo,\n undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : \"\",\n redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : \"\",\n undoCount: this.undoStack.length,\n redoCount: this.redoStack.length,\n };\n }\n\n /**\n * Returns a snapshot of the current history state.\n * Cached to maintain referential equality across renders for useSyncExternalStore.\n */\n public getState(): HistoryState {\n return this.cachedState;\n }\n\n private notify(): void {\n this.cachedState = this.computeState();\n const state = this.cachedState;\n this.onStateChange?.(state);\n this.listeners.forEach((listener) => {\n try {\n listener(state);\n } catch (err) {\n console.error(\"[HistoryManager] Error in state listener:\", err);\n }\n });\n }\n\n /**\n * Executes a command and pushes it onto the undo stack.\n * Clears the redo stack.\n */\n public execute(command: Command): void {\n if (this.isApplying) {\n command.execute();\n return;\n }\n\n if (this.batchDepth > 0 && this.currentBatch) {\n command.execute();\n this.currentBatch.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n return;\n }\n\n command.execute();\n\n this.undoStack.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Executes a command, coalescing it with the previous command if they share\n * the same `tag` and occurred within `windowMs` (default: 300ms).\n * Useful for high-frequency interactions like slider scrubs or node drags.\n */\n public executeMerged(\n command: Command,\n options: MergedExecutionOptions = {},\n ): void {\n const windowMs = options.windowMs ?? 300;\n const now = Date.now();\n\n if (\n command.tag &&\n this.undoStack.length > 0 &&\n this.batchDepth === 0 &&\n !this.isApplying\n ) {\n const top = this.undoStack[this.undoStack.length - 1];\n const elapsed = top.timestamp ? now - top.timestamp : Infinity;\n\n if (top.tag === command.tag && elapsed <= windowMs) {\n command.execute();\n // Replace top with merged command keeping the original initial undo\n const initialUndo = top.undo;\n this.undoStack[this.undoStack.length - 1] = {\n name: command.name,\n tag: command.tag,\n timestamp: now,\n execute: command.execute,\n undo: initialUndo,\n redo: command.redo ?? command.execute,\n };\n this.redoStack = [];\n this.notify();\n return;\n }\n }\n\n this.execute({ ...command, timestamp: now });\n }\n\n /**\n * Batches multiple commands executed within `fn` into a single atomic undo entry.\n */\n public batch(name: string, fn: () => void): void {\n if (this.isApplying) {\n fn();\n return;\n }\n\n if (this.batchDepth > 0) {\n // Already inside a batch; execute directly\n fn();\n return;\n }\n\n this.batchDepth++;\n this.currentBatch = [];\n\n try {\n fn();\n } finally {\n this.batchDepth--;\n const batchCommands = this.currentBatch;\n this.currentBatch = null;\n\n if (batchCommands && batchCommands.length > 0) {\n const compoundCommand: Command = {\n name,\n timestamp: Date.now(),\n execute: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n undo: () => {\n for (let i = batchCommands.length - 1; i >= 0; i--) {\n batchCommands[i].undo();\n }\n },\n redo: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n };\n\n this.undoStack.push(compoundCommand);\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.notify();\n }\n }\n }\n\n /**\n * Reverts the most recent command on the undo stack.\n * Returns true if a command was undone, false otherwise.\n */\n public undo(): boolean {\n if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.undoStack.pop()!;\n command.undo();\n this.redoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Re-applies the most recent undone command on the redo stack.\n * Returns true if a command was redone, false otherwise.\n */\n public redo(): boolean {\n if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.redoStack.pop()!;\n (command.redo ?? command.execute)();\n this.undoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Clears both undo and redo stacks.\n */\n public clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Subscribes to history state changes.\n * The listener is immediately invoked with the current state.\n * Returns an unsubscribe function.\n */\n public subscribe(listener: (state: HistoryState) => void): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Utility helper to create a Command from before/after state snapshots.\n */\n public static createSnapshotCommand<T>(\n options: SnapshotCommandOptions<T>,\n ): Command {\n return {\n name: options.name,\n tag: options.tag,\n execute: () => options.apply(options.after),\n undo: () => options.apply(options.before),\n redo: () => options.apply(options.after),\n };\n }\n}\n\n/**\n * Creates and initializes a new HistoryManager instance.\n */\nexport function createHistoryManager(\n options: HistoryManagerOptions = {},\n): HistoryManager {\n return new HistoryManager(options);\n}\n"]}