@pixerate/editor-react 0.5.1 → 0.6.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.
@@ -0,0 +1,824 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/canvas/index.ts
21
+ var canvas_exports = {};
22
+ __export(canvas_exports, {
23
+ AgentPresenceLayer: () => AgentPresenceLayer,
24
+ boundsOf: () => import_canvas6.boundsOf,
25
+ calculateDagreReflowDisplacement: () => import_canvas6.calculateDagreReflowDisplacement,
26
+ calculateDirectionalDisplacement: () => import_canvas6.calculateDirectionalDisplacement,
27
+ centerNodes: () => import_canvas6.centerNodes,
28
+ centre: () => import_canvas6.centre,
29
+ computeNodesBoundingBox: () => import_canvas6.computeNodesBoundingBox,
30
+ createBezierTrajectory: () => import_canvas6.createBezierTrajectory,
31
+ createFanOutTrajectory: () => import_canvas6.createFanOutTrajectory,
32
+ defaultEasing: () => import_canvas6.defaultEasing,
33
+ ensureLayout: () => import_canvas6.ensureLayout,
34
+ estimateNodeSize: () => import_canvas6.estimateNodeSize,
35
+ flowSide: () => import_canvas6.flowSide,
36
+ getCenteredNodePosition: () => import_canvas6.getCenteredNodePosition,
37
+ getLayoutedNodes: () => import_canvas6.getLayoutedNodes,
38
+ isEventFromTextInput: () => import_canvas6.isEventFromTextInput,
39
+ isValidCanvasNode: () => import_canvas6.isValidCanvasNode,
40
+ isValidUrl: () => import_canvas6.isValidUrl,
41
+ linearPositionTrajectory: () => import_canvas6.linearPositionTrajectory,
42
+ linearTrajectory: () => import_canvas6.linearTrajectory,
43
+ overlaps: () => import_canvas6.overlaps,
44
+ parseCanvasClipboardData: () => import_canvas6.parseCanvasClipboardData,
45
+ placeBeside: () => import_canvas6.placeBeside,
46
+ remapPastedNodes: () => import_canvas6.remapPastedNodes,
47
+ repositionNodesTo: () => import_canvas6.repositionNodesTo,
48
+ runMultiNodeTransition: () => import_canvas6.runMultiNodeTransition,
49
+ serializeCanvasNodes: () => import_canvas6.serializeCanvasNodes,
50
+ useCanvasClipboard: () => useCanvasClipboard,
51
+ useCanvasDocking: () => useCanvasDocking,
52
+ useCanvasExplosion: () => useCanvasExplosion,
53
+ useCanvasGraph: () => useCanvasGraph,
54
+ useCanvasLayout: () => useCanvasLayout,
55
+ useCanvasShortcuts: () => useCanvasShortcuts,
56
+ useStreamingCanvas: () => useStreamingCanvas
57
+ });
58
+ module.exports = __toCommonJS(canvas_exports);
59
+
60
+ // src/canvas/AgentPresenceLayer.tsx
61
+ var import_jsx_runtime = require("react/jsx-runtime");
62
+ function AgentPresenceLayer({ presences, className = "" }) {
63
+ if (!presences || presences.length === 0) return null;
64
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
65
+ "div",
66
+ {
67
+ className: `pixerate-agent-presence-layer pointer-events-none absolute inset-0 z-50 overflow-hidden ${className}`,
68
+ "aria-hidden": "true",
69
+ children: presences.map((agent) => {
70
+ if (!agent.cursorPosition) return null;
71
+ const { x, y } = agent.cursorPosition;
72
+ const color = agent.color || "#3b82f6";
73
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
74
+ "div",
75
+ {
76
+ className: "absolute transition-transform duration-75 ease-out",
77
+ style: {
78
+ transform: `translate3d(${x}px, ${y}px, 0)`
79
+ },
80
+ children: [
81
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
82
+ "svg",
83
+ {
84
+ className: "h-5 w-5 drop-shadow-md",
85
+ viewBox: "0 0 24 24",
86
+ fill: "none",
87
+ xmlns: "http://www.w3.org/2000/svg",
88
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
89
+ "path",
90
+ {
91
+ d: "M5.65376 12.3673H5.46026L5.31717 12.4976L0.500002 16.8829L0.500002 1.19841L11.7841 12.3673H5.65376Z",
92
+ fill: color,
93
+ stroke: "white",
94
+ strokeWidth: "1"
95
+ }
96
+ )
97
+ }
98
+ ),
99
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
100
+ "div",
101
+ {
102
+ className: "mt-1 flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium text-white shadow-md whitespace-nowrap",
103
+ style: { backgroundColor: color },
104
+ children: [
105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: agent.name }),
106
+ agent.statusMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "opacity-80 text-[10px]", children: [
107
+ "(",
108
+ agent.statusMessage,
109
+ ")"
110
+ ] })
111
+ ]
112
+ }
113
+ )
114
+ ]
115
+ },
116
+ agent.agentId
117
+ );
118
+ })
119
+ }
120
+ );
121
+ }
122
+
123
+ // src/canvas/useStreamingCanvas.ts
124
+ var import_react = require("react");
125
+ var import_canvas = require("@pixerate/editor/canvas");
126
+ function useStreamingCanvas(options) {
127
+ const managerRef = (0, import_react.useRef)(null);
128
+ (0, import_react.useEffect)(() => {
129
+ const manager = new import_canvas.StreamingLayoutManager({
130
+ throttleMs: options.throttleMs,
131
+ glideDurationMs: options.glideDurationMs,
132
+ layoutOptions: options.layoutOptions,
133
+ onLayoutUpdated: options.onPositionsChange
134
+ });
135
+ managerRef.current = manager;
136
+ return () => {
137
+ manager.destroy();
138
+ managerRef.current = null;
139
+ };
140
+ }, [options.throttleMs, options.glideDurationMs, options.onPositionsChange]);
141
+ const pushStreamUpdate = (0, import_react.useCallback)(
142
+ (nodes, edges, currentPositions) => {
143
+ managerRef.current?.pushStreamUpdate(nodes, edges, currentPositions);
144
+ },
145
+ []
146
+ );
147
+ const flush = (0, import_react.useCallback)(
148
+ (nodes, edges, currentPositions) => {
149
+ managerRef.current?.flush(nodes, edges, currentPositions);
150
+ },
151
+ []
152
+ );
153
+ return {
154
+ pushStreamUpdate,
155
+ flush
156
+ };
157
+ }
158
+
159
+ // src/canvas/useCanvasGraph.ts
160
+ var import_react2 = require("react");
161
+ function useCanvasGraph(options = {}) {
162
+ const [nodes, setNodesState] = (0, import_react2.useState)(options.initialNodes ?? []);
163
+ const [edges, setEdgesState] = (0, import_react2.useState)(options.initialEdges ?? []);
164
+ const baselineNodesRef = (0, import_react2.useRef)(options.initialNodes ?? []);
165
+ const baselineEdgesRef = (0, import_react2.useRef)(options.initialEdges ?? []);
166
+ const setNodes = (0, import_react2.useCallback)(
167
+ (updater) => {
168
+ setNodesState((prev) => {
169
+ const next = typeof updater === "function" ? updater(prev) : updater;
170
+ options.onNodesChange?.(next);
171
+ return next;
172
+ });
173
+ },
174
+ [options.onNodesChange]
175
+ );
176
+ const setEdges = (0, import_react2.useCallback)(
177
+ (updater) => {
178
+ setEdgesState((prev) => {
179
+ const next = typeof updater === "function" ? updater(prev) : updater;
180
+ options.onEdgesChange?.(next);
181
+ return next;
182
+ });
183
+ },
184
+ [options.onEdgesChange]
185
+ );
186
+ const addNode = (0, import_react2.useCallback)(
187
+ (node) => {
188
+ setNodes((prev) => [...prev, node]);
189
+ },
190
+ [setNodes]
191
+ );
192
+ const removeNodes = (0, import_react2.useCallback)(
193
+ (ids) => {
194
+ const idSet = new Set(ids);
195
+ setNodes((prev) => prev.filter((n) => !idSet.has(n.id)));
196
+ setEdges((prev) => prev.filter((e) => !idSet.has(e.source) && !idSet.has(e.target)));
197
+ },
198
+ [setNodes, setEdges]
199
+ );
200
+ const updateNodeData = (0, import_react2.useCallback)(
201
+ (id, patch) => {
202
+ setNodes(
203
+ (prev) => prev.map((node) => {
204
+ if (node.id !== id) return node;
205
+ return {
206
+ ...node,
207
+ data: { ...node.data, ...patch }
208
+ };
209
+ })
210
+ );
211
+ },
212
+ [setNodes]
213
+ );
214
+ const updateNodePosition = (0, import_react2.useCallback)(
215
+ (id, position) => {
216
+ setNodes(
217
+ (prev) => prev.map((node) => {
218
+ if (node.id !== id) return node;
219
+ return {
220
+ ...node,
221
+ position: { ...position }
222
+ };
223
+ })
224
+ );
225
+ },
226
+ [setNodes]
227
+ );
228
+ const addEdge = (0, import_react2.useCallback)(
229
+ (edge) => {
230
+ setEdges((prev) => [...prev, edge]);
231
+ },
232
+ [setEdges]
233
+ );
234
+ const removeEdges = (0, import_react2.useCallback)(
235
+ (ids) => {
236
+ const idSet = new Set(ids);
237
+ setEdges((prev) => prev.filter((e) => !idSet.has(e.id)));
238
+ },
239
+ [setEdges]
240
+ );
241
+ const resetBaseline = (0, import_react2.useCallback)(() => {
242
+ baselineNodesRef.current = nodes;
243
+ baselineEdgesRef.current = edges;
244
+ }, [nodes, edges]);
245
+ const getDifferences = (0, import_react2.useCallback)(() => {
246
+ const currentNodesMap = new Map(nodes.map((n) => [n.id, n]));
247
+ const currentEdgesMap = new Map(edges.map((e) => [e.id, e]));
248
+ const baselineNodesMap = new Map(baselineNodesRef.current.map((n) => [n.id, n]));
249
+ const baselineEdgesMap = new Map(baselineEdgesRef.current.map((e) => [e.id, e]));
250
+ const changedNodes = [];
251
+ const removedNodes = [];
252
+ const changedEdges = [];
253
+ const removedEdges = [];
254
+ for (const [id, node] of currentNodesMap) {
255
+ const base = baselineNodesMap.get(id);
256
+ if (!base || base.position.x !== node.position.x || base.position.y !== node.position.y) {
257
+ changedNodes.push(node);
258
+ }
259
+ }
260
+ for (const [id, node] of baselineNodesMap) {
261
+ if (!currentNodesMap.has(id)) {
262
+ removedNodes.push(node);
263
+ }
264
+ }
265
+ for (const [id, edge] of currentEdgesMap) {
266
+ const base = baselineEdgesMap.get(id);
267
+ if (!base || base.source !== edge.source || base.target !== edge.target) {
268
+ changedEdges.push(edge);
269
+ }
270
+ }
271
+ for (const [id, edge] of baselineEdgesMap) {
272
+ if (!currentEdgesMap.has(id)) {
273
+ removedEdges.push(edge);
274
+ }
275
+ }
276
+ return { changedNodes, changedEdges, removedNodes, removedEdges };
277
+ }, [nodes, edges]);
278
+ return {
279
+ nodes,
280
+ edges,
281
+ setNodes,
282
+ setEdges,
283
+ addNode,
284
+ removeNodes,
285
+ updateNodeData,
286
+ updateNodePosition,
287
+ addEdge,
288
+ removeEdges,
289
+ resetBaseline,
290
+ getDifferences
291
+ };
292
+ }
293
+
294
+ // src/canvas/useCanvasLayout.ts
295
+ var import_react3 = require("react");
296
+ var import_canvas2 = require("@pixerate/editor/canvas");
297
+ function useCanvasLayout(options) {
298
+ const { nodes, edges, setNodes, getNodesBounds, screenToFlowPosition } = options;
299
+ const applyLayout = (0, import_react3.useCallback)(
300
+ (layoutOptions = {}) => {
301
+ const layouted = (0, import_canvas2.getLayoutedNodes)(nodes, edges, getNodesBounds, layoutOptions);
302
+ setNodes(layouted);
303
+ return layouted;
304
+ },
305
+ [nodes, edges, getNodesBounds, setNodes]
306
+ );
307
+ const applyIncrementalLayout = (0, import_react3.useCallback)(
308
+ (incrementalOptions = {}) => {
309
+ const storedPositions = incrementalOptions.storedPositions ?? Object.fromEntries(nodes.map((n) => [n.id, n.position]));
310
+ const blended = (0, import_canvas2.ensureLayout)(nodes, edges, {
311
+ ...incrementalOptions,
312
+ storedPositions
313
+ });
314
+ setNodes(blended);
315
+ return blended;
316
+ },
317
+ [nodes, edges, setNodes]
318
+ );
319
+ const center = (0, import_react3.useCallback)(
320
+ (screenDimensions) => {
321
+ if (!screenToFlowPosition || !getNodesBounds) {
322
+ console.warn(
323
+ "[useCanvasLayout] Centering requires screenToFlowPosition and getNodesBounds options."
324
+ );
325
+ return;
326
+ }
327
+ const centered = (0, import_canvas2.centerNodes)(nodes, screenToFlowPosition, getNodesBounds, screenDimensions);
328
+ setNodes(centered);
329
+ },
330
+ [nodes, screenToFlowPosition, getNodesBounds, setNodes]
331
+ );
332
+ const getPlacementPosition = (0, import_react3.useCallback)(
333
+ (size, side = "right", nearNodeId, placementOptions = {}) => {
334
+ const rects = {};
335
+ for (const node of nodes) {
336
+ const width = node.measured?.width ?? node.width ?? 200;
337
+ const height = node.measured?.height ?? node.height ?? 60;
338
+ rects[node.id] = {
339
+ x: node.position.x,
340
+ y: node.position.y,
341
+ width,
342
+ height
343
+ };
344
+ }
345
+ return (0, import_canvas2.placeBeside)(rects, size, side, nearNodeId, placementOptions);
346
+ },
347
+ [nodes]
348
+ );
349
+ return {
350
+ applyLayout,
351
+ applyIncrementalLayout,
352
+ center,
353
+ getPlacementPosition,
354
+ getCenteredNodePosition: import_canvas2.getCenteredNodePosition
355
+ };
356
+ }
357
+
358
+ // src/canvas/useCanvasClipboard.ts
359
+ var import_react4 = require("react");
360
+ var import_canvas3 = require("@pixerate/editor/canvas");
361
+ function useCanvasClipboard(options = {}) {
362
+ const copySelectedNodes = (0, import_react4.useCallback)(
363
+ (selectedNodes, connectedEdges, event) => {
364
+ const native = event && "nativeEvent" in event ? event.nativeEvent : event;
365
+ if (native && (0, import_canvas3.isEventFromTextInput)(native)) {
366
+ return false;
367
+ }
368
+ if (selectedNodes.length === 0) return false;
369
+ const serialized = (0, import_canvas3.serializeCanvasNodes)(selectedNodes, connectedEdges);
370
+ const clipboardData = event?.clipboardData || native?.clipboardData;
371
+ if (clipboardData) {
372
+ clipboardData.setData("application/json", serialized);
373
+ clipboardData.setData("text/plain", serialized);
374
+ event?.preventDefault?.();
375
+ } else if (typeof navigator !== "undefined" && navigator.clipboard) {
376
+ navigator.clipboard.writeText(serialized).catch(() => {
377
+ });
378
+ }
379
+ return true;
380
+ },
381
+ []
382
+ );
383
+ const handlePasteEvent = (0, import_react4.useCallback)(
384
+ async (event) => {
385
+ const native = "nativeEvent" in event ? event.nativeEvent : event;
386
+ if ((0, import_canvas3.isEventFromTextInput)(native)) {
387
+ return false;
388
+ }
389
+ const clipboardData = native?.clipboardData || event?.clipboardData;
390
+ if (!clipboardData) return false;
391
+ const prevent = () => {
392
+ event?.preventDefault?.();
393
+ native?.preventDefault?.();
394
+ };
395
+ const jsonText = clipboardData.getData("application/json");
396
+ if (jsonText) {
397
+ const parsed = (0, import_canvas3.parseCanvasClipboardData)(jsonText);
398
+ if (parsed && parsed.nodes.length > 0) {
399
+ prevent();
400
+ const remapped = (0, import_canvas3.remapPastedNodes)(parsed.nodes, parsed.edges);
401
+ await options.onPasteNodes?.(remapped.nodes, remapped.edges);
402
+ return true;
403
+ }
404
+ }
405
+ const text = clipboardData.getData("text/plain")?.trim();
406
+ if (text) {
407
+ const parsed = (0, import_canvas3.parseCanvasClipboardData)(text);
408
+ if (parsed && parsed.nodes.length > 0) {
409
+ prevent();
410
+ const remapped = (0, import_canvas3.remapPastedNodes)(parsed.nodes, parsed.edges);
411
+ await options.onPasteNodes?.(remapped.nodes, remapped.edges);
412
+ return true;
413
+ }
414
+ if ((0, import_canvas3.isValidUrl)(text)) {
415
+ prevent();
416
+ options.onPasteUrl?.(text);
417
+ return true;
418
+ }
419
+ if (options.onPasteText) {
420
+ prevent();
421
+ options.onPasteText(text);
422
+ return true;
423
+ }
424
+ }
425
+ return false;
426
+ },
427
+ [options]
428
+ );
429
+ return {
430
+ copySelectedNodes,
431
+ handlePasteEvent
432
+ };
433
+ }
434
+
435
+ // src/canvas/useCanvasShortcuts.ts
436
+ var import_react5 = require("react");
437
+ var import_canvas4 = require("@pixerate/editor/canvas");
438
+ function useCanvasShortcuts(options = {}) {
439
+ const {
440
+ targetElement,
441
+ enabled = true,
442
+ onCopy,
443
+ onCut,
444
+ onPaste,
445
+ onDelete,
446
+ onDuplicate,
447
+ onSelectAll,
448
+ onGroup,
449
+ onUngroup,
450
+ onZoomIn,
451
+ onZoomOut,
452
+ onFitView,
453
+ getSelectedNodes
454
+ } = options;
455
+ (0, import_react5.useEffect)(() => {
456
+ if (!enabled) return;
457
+ const target = targetElement ?? (typeof window !== "undefined" ? window : null);
458
+ if (!target) return;
459
+ function handleKeyDown(event) {
460
+ const keyboardEvent = event;
461
+ if ((0, import_canvas4.isEventFromTextInput)(keyboardEvent)) {
462
+ return;
463
+ }
464
+ const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
465
+ const isMod = isMac ? keyboardEvent.metaKey : keyboardEvent.ctrlKey;
466
+ const key = keyboardEvent.key.toLowerCase();
467
+ const selectedNodes = getSelectedNodes ? getSelectedNodes() : [];
468
+ if ((keyboardEvent.key === "Backspace" || keyboardEvent.key === "Delete") && !isMod) {
469
+ if (selectedNodes.length > 0 && onDelete) {
470
+ keyboardEvent.preventDefault();
471
+ onDelete(selectedNodes);
472
+ }
473
+ return;
474
+ }
475
+ if (isMod && key === "d") {
476
+ if (selectedNodes.length > 0 && onDuplicate) {
477
+ keyboardEvent.preventDefault();
478
+ onDuplicate(selectedNodes);
479
+ }
480
+ return;
481
+ }
482
+ if (isMod && key === "a" && onSelectAll) {
483
+ keyboardEvent.preventDefault();
484
+ onSelectAll();
485
+ return;
486
+ }
487
+ if (isMod && key === "g" && !keyboardEvent.shiftKey && onGroup) {
488
+ if (selectedNodes.length > 0) {
489
+ keyboardEvent.preventDefault();
490
+ onGroup(selectedNodes);
491
+ }
492
+ return;
493
+ }
494
+ if (isMod && key === "g" && keyboardEvent.shiftKey && onUngroup) {
495
+ if (selectedNodes.length > 0) {
496
+ keyboardEvent.preventDefault();
497
+ onUngroup(selectedNodes);
498
+ }
499
+ return;
500
+ }
501
+ if (isMod && (key === "+" || key === "=") && onZoomIn) {
502
+ keyboardEvent.preventDefault();
503
+ onZoomIn();
504
+ return;
505
+ }
506
+ if (isMod && key === "-" && onZoomOut) {
507
+ keyboardEvent.preventDefault();
508
+ onZoomOut();
509
+ return;
510
+ }
511
+ if (isMod && key === "0" && onFitView) {
512
+ keyboardEvent.preventDefault();
513
+ onFitView();
514
+ return;
515
+ }
516
+ }
517
+ target.addEventListener("keydown", handleKeyDown);
518
+ return () => {
519
+ target.removeEventListener("keydown", handleKeyDown);
520
+ };
521
+ }, [
522
+ targetElement,
523
+ enabled,
524
+ onCopy,
525
+ onCut,
526
+ onPaste,
527
+ onDelete,
528
+ onDuplicate,
529
+ onSelectAll,
530
+ onGroup,
531
+ onUngroup,
532
+ onZoomIn,
533
+ onZoomOut,
534
+ onFitView,
535
+ getSelectedNodes
536
+ ]);
537
+ }
538
+
539
+ // src/canvas/useCanvasDocking.ts
540
+ var import_react6 = require("react");
541
+ function useCanvasDocking(options = {}) {
542
+ const {
543
+ strategy = "center-point",
544
+ dockableTypes,
545
+ targetTypes,
546
+ onDockHover,
547
+ onDockLeave,
548
+ onDockDrop
549
+ } = options;
550
+ const activeDockTargetRef = (0, import_react6.useRef)(null);
551
+ const isDockable = (0, import_react6.useCallback)(
552
+ (node) => {
553
+ if (!dockableTypes || dockableTypes.length === 0) return true;
554
+ return !!node.type && dockableTypes.includes(node.type);
555
+ },
556
+ [dockableTypes]
557
+ );
558
+ const isTarget = (0, import_react6.useCallback)(
559
+ (node) => {
560
+ if (!targetTypes || targetTypes.length === 0) return true;
561
+ return !!node.type && targetTypes.includes(node.type);
562
+ },
563
+ [targetTypes]
564
+ );
565
+ const findDockTarget = (0, import_react6.useCallback)(
566
+ (draggedNode, candidates, cursorPos) => {
567
+ if (!isDockable(draggedNode)) return null;
568
+ const draggedWidth = draggedNode.measured?.width ?? draggedNode.width ?? 200;
569
+ const draggedHeight = draggedNode.measured?.height ?? draggedNode.height ?? 60;
570
+ const draggedCenter = {
571
+ x: draggedNode.position.x + draggedWidth / 2,
572
+ y: draggedNode.position.y + draggedHeight / 2
573
+ };
574
+ const validCandidates = candidates.filter(
575
+ (c) => c.id !== draggedNode.id && isTarget(c)
576
+ );
577
+ let bestTarget = null;
578
+ let maxIntersectionArea = 0;
579
+ let minDistance = Infinity;
580
+ for (const candidate of validCandidates) {
581
+ const candWidth = candidate.measured?.width ?? candidate.width ?? 200;
582
+ const candHeight = candidate.measured?.height ?? candidate.height ?? 60;
583
+ const candRect = {
584
+ x: candidate.position.x,
585
+ y: candidate.position.y,
586
+ width: candWidth,
587
+ height: candHeight
588
+ };
589
+ const overlapX = Math.max(
590
+ 0,
591
+ Math.min(draggedNode.position.x + draggedWidth, candRect.x + candRect.width) - Math.max(draggedNode.position.x, candRect.x)
592
+ );
593
+ const overlapY = Math.max(
594
+ 0,
595
+ Math.min(draggedNode.position.y + draggedHeight, candRect.y + candRect.height) - Math.max(draggedNode.position.y, candRect.y)
596
+ );
597
+ const overlapArea = overlapX * overlapY;
598
+ if (strategy === "max-overlap") {
599
+ if (overlapArea > maxIntersectionArea) {
600
+ maxIntersectionArea = overlapArea;
601
+ bestTarget = candidate;
602
+ }
603
+ } else if (strategy === "center-point") {
604
+ const candCenter = {
605
+ x: candRect.x + candRect.width / 2,
606
+ y: candRect.y + candRect.height / 2
607
+ };
608
+ const dist = Math.hypot(
609
+ draggedCenter.x - candCenter.x,
610
+ draggedCenter.y - candCenter.y
611
+ );
612
+ if (overlapArea > 0 && dist < minDistance) {
613
+ minDistance = dist;
614
+ bestTarget = candidate;
615
+ }
616
+ } else if (strategy === "first") {
617
+ if (overlapArea > 0) return candidate;
618
+ }
619
+ }
620
+ return bestTarget;
621
+ },
622
+ [isDockable, isTarget, strategy]
623
+ );
624
+ const updateDragOver = (0, import_react6.useCallback)(
625
+ (draggedNode, candidates, cursorPos) => {
626
+ const target = findDockTarget(draggedNode, candidates, cursorPos);
627
+ if (target?.id !== activeDockTargetRef.current?.id) {
628
+ if (activeDockTargetRef.current) {
629
+ onDockLeave?.(activeDockTargetRef.current, draggedNode);
630
+ }
631
+ activeDockTargetRef.current = target;
632
+ if (target) {
633
+ onDockHover?.(target, draggedNode);
634
+ }
635
+ }
636
+ },
637
+ [findDockTarget, onDockHover, onDockLeave]
638
+ );
639
+ const finalizeDock = (0, import_react6.useCallback)(
640
+ (draggedNode) => {
641
+ if (activeDockTargetRef.current) {
642
+ onDockDrop?.(activeDockTargetRef.current, draggedNode);
643
+ activeDockTargetRef.current = null;
644
+ }
645
+ },
646
+ [onDockDrop]
647
+ );
648
+ return {
649
+ findDockTarget,
650
+ updateDragOver,
651
+ finalizeDock,
652
+ getActiveDockTarget: () => activeDockTargetRef.current
653
+ };
654
+ }
655
+
656
+ // src/canvas/useCanvasExplosion.ts
657
+ var import_react7 = require("react");
658
+ var import_canvas5 = require("@pixerate/editor/canvas");
659
+ function useCanvasExplosion(target) {
660
+ const snapshotsRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
661
+ const explodeNode = (0, import_react7.useCallback)(
662
+ (parentNodeId, options) => {
663
+ const { nodes, edges, setNodes, setEdges } = target;
664
+ const parent = nodes.find((n) => n.id === parentNodeId);
665
+ if (!parent || options.childNodes.length === 0) return;
666
+ const originPos = { ...parent.position };
667
+ const displacedMap = options.displacementStrategy === "directional" ? (0, import_canvas5.calculateDirectionalDisplacement)({
668
+ existingNodes: nodes,
669
+ originNode: parent,
670
+ childNodes: options.childNodes,
671
+ options: options.directionalOptions
672
+ }) : /* @__PURE__ */ new Map();
673
+ const snapshot = {
674
+ parentId: parentNodeId,
675
+ childNodeIds: options.childNodes.map((c) => c.id),
676
+ childEdgeIds: (options.childEdges ?? []).map((e) => e.id),
677
+ displacedNodes: Array.from(displacedMap.entries()).map(([id]) => {
678
+ const node = nodes.find((n) => n.id === id);
679
+ return { id, originalPosition: { ...node.position } };
680
+ }),
681
+ timestamp: Date.now()
682
+ };
683
+ snapshotsRef.current.set(parentNodeId, snapshot);
684
+ const initialChildren = options.childNodes.map((child) => ({
685
+ ...child,
686
+ position: { ...originPos }
687
+ }));
688
+ const mergedNodes = [...nodes, ...initialChildren];
689
+ setNodes(mergedNodes);
690
+ if (options.childEdges && options.childEdges.length > 0) {
691
+ setEdges((prev) => [...prev, ...options.childEdges]);
692
+ }
693
+ const trajectory = options.trajectory ?? (0, import_canvas5.createFanOutTrajectory)();
694
+ const transitions = [];
695
+ options.childNodes.forEach((child, index) => {
696
+ const liveChild = mergedNodes.find((n) => n.id === child.id);
697
+ if (liveChild) {
698
+ transitions.push({
699
+ node: liveChild,
700
+ from: originPos,
701
+ to: child.position,
702
+ trajectory,
703
+ index,
704
+ total: options.childNodes.length,
705
+ duration: options.duration ?? 400,
706
+ easing: options.easing
707
+ });
708
+ }
709
+ });
710
+ displacedMap.forEach((targetPos, nodeId) => {
711
+ const node = mergedNodes.find((n) => n.id === nodeId);
712
+ if (node) {
713
+ transitions.push({
714
+ node,
715
+ from: { ...node.position },
716
+ to: targetPos,
717
+ trajectory: import_canvas5.linearPositionTrajectory,
718
+ duration: options.duration ?? 400,
719
+ easing: options.easing
720
+ });
721
+ }
722
+ });
723
+ (0, import_canvas5.runMultiNodeTransition)(transitions, {
724
+ onUpdate: () => setNodes([...mergedNodes]),
725
+ onComplete: options.onComplete
726
+ });
727
+ },
728
+ [target]
729
+ );
730
+ const collapseNode = (0, import_react7.useCallback)(
731
+ (parentNodeId, options = {}) => {
732
+ const { nodes, setNodes, setEdges } = target;
733
+ const snapshot = snapshotsRef.current.get(parentNodeId);
734
+ if (!snapshot) return;
735
+ const parent = nodes.find((n) => n.id === parentNodeId);
736
+ const originPos = parent ? { ...parent.position } : { x: 0, y: 0 };
737
+ const childIdSet = new Set(snapshot.childNodeIds);
738
+ const edgeIdSet = new Set(snapshot.childEdgeIds);
739
+ const transitions = [];
740
+ nodes.forEach((node) => {
741
+ if (childIdSet.has(node.id)) {
742
+ transitions.push({
743
+ node,
744
+ from: { ...node.position },
745
+ to: originPos,
746
+ duration: options.duration ?? 300,
747
+ easing: options.easing,
748
+ trajectory: options.trajectory
749
+ });
750
+ }
751
+ });
752
+ snapshot.displacedNodes.forEach((disp) => {
753
+ const node = nodes.find((n) => n.id === disp.id);
754
+ if (node) {
755
+ transitions.push({
756
+ node,
757
+ from: { ...node.position },
758
+ to: disp.originalPosition,
759
+ duration: options.duration ?? 300,
760
+ easing: options.easing,
761
+ trajectory: import_canvas5.linearPositionTrajectory
762
+ });
763
+ }
764
+ });
765
+ (0, import_canvas5.runMultiNodeTransition)(transitions, {
766
+ onUpdate: () => setNodes([...nodes]),
767
+ onComplete: () => {
768
+ setNodes((prev) => prev.filter((n) => !childIdSet.has(n.id)));
769
+ setEdges((prev) => prev.filter((e) => !edgeIdSet.has(e.id)));
770
+ snapshotsRef.current.delete(parentNodeId);
771
+ options.onComplete?.();
772
+ }
773
+ });
774
+ },
775
+ [target]
776
+ );
777
+ return {
778
+ explodeNode,
779
+ collapseNode,
780
+ isExploded: (parentNodeId) => snapshotsRef.current.has(parentNodeId),
781
+ getSnapshot: (parentNodeId) => snapshotsRef.current.get(parentNodeId)
782
+ };
783
+ }
784
+
785
+ // src/canvas/index.ts
786
+ var import_canvas6 = require("@pixerate/editor/canvas");
787
+ // Annotate the CommonJS export names for ESM import in node:
788
+ 0 && (module.exports = {
789
+ AgentPresenceLayer,
790
+ boundsOf,
791
+ calculateDagreReflowDisplacement,
792
+ calculateDirectionalDisplacement,
793
+ centerNodes,
794
+ centre,
795
+ computeNodesBoundingBox,
796
+ createBezierTrajectory,
797
+ createFanOutTrajectory,
798
+ defaultEasing,
799
+ ensureLayout,
800
+ estimateNodeSize,
801
+ flowSide,
802
+ getCenteredNodePosition,
803
+ getLayoutedNodes,
804
+ isEventFromTextInput,
805
+ isValidCanvasNode,
806
+ isValidUrl,
807
+ linearPositionTrajectory,
808
+ linearTrajectory,
809
+ overlaps,
810
+ parseCanvasClipboardData,
811
+ placeBeside,
812
+ remapPastedNodes,
813
+ repositionNodesTo,
814
+ runMultiNodeTransition,
815
+ serializeCanvasNodes,
816
+ useCanvasClipboard,
817
+ useCanvasDocking,
818
+ useCanvasExplosion,
819
+ useCanvasGraph,
820
+ useCanvasLayout,
821
+ useCanvasShortcuts,
822
+ useStreamingCanvas
823
+ });
824
+ //# sourceMappingURL=index.cjs.map