system-canvas-standalone 0.2.2 → 0.2.3

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.
@@ -12764,7 +12764,7 @@ var SystemCanvas = (() => {
12764
12764
 
12765
12765
  // ../react/dist/components/SystemCanvas.js
12766
12766
  var import_jsx_runtime28 = __toESM(require_jsx_runtime(), 1);
12767
- var import_react21 = __toESM(require_react(), 1);
12767
+ var import_react23 = __toESM(require_react(), 1);
12768
12768
 
12769
12769
  // ../core/dist/themes/dark.js
12770
12770
  var darkTheme = {
@@ -15150,8 +15150,305 @@ var SystemCanvas = (() => {
15150
15150
  return { pending, onHandlePointerDown };
15151
15151
  }
15152
15152
 
15153
- // ../react/dist/hooks/useZoomNavigation.js
15153
+ // ../react/dist/hooks/useMultiSelect.js
15154
15154
  var import_react6 = __toESM(require_react(), 1);
15155
+ function useMultiSelect(options) {
15156
+ const { svgRef, viewport, nodesRef, containerRef, enabled } = options;
15157
+ const [selectedIds, setSelectedIds] = (0, import_react6.useState)(() => /* @__PURE__ */ new Set());
15158
+ const [marqueeRect, setMarqueeRect] = (0, import_react6.useState)(null);
15159
+ const marqueeActiveRef = (0, import_react6.useRef)(false);
15160
+ const isDrawingRef = (0, import_react6.useRef)(false);
15161
+ const startScreenRef = (0, import_react6.useRef)(null);
15162
+ const pointerIdRef = (0, import_react6.useRef)(null);
15163
+ const selectedIdsRef = (0, import_react6.useRef)(selectedIds);
15164
+ selectedIdsRef.current = selectedIds;
15165
+ const selectNode = (0, import_react6.useCallback)((id2) => {
15166
+ setSelectedIds(/* @__PURE__ */ new Set([id2]));
15167
+ }, []);
15168
+ const toggleNode = (0, import_react6.useCallback)((id2) => {
15169
+ setSelectedIds((prev) => {
15170
+ const next = new Set(prev);
15171
+ if (next.has(id2)) {
15172
+ next.delete(id2);
15173
+ } else {
15174
+ next.add(id2);
15175
+ }
15176
+ return next;
15177
+ });
15178
+ }, []);
15179
+ const selectAll = (0, import_react6.useCallback)(() => {
15180
+ const nodes = nodesRef.current;
15181
+ if (!nodes)
15182
+ return;
15183
+ setSelectedIds(new Set(nodes.map((n) => n.id)));
15184
+ }, [nodesRef]);
15185
+ const clearSelection = (0, import_react6.useCallback)(() => {
15186
+ setSelectedIds(/* @__PURE__ */ new Set());
15187
+ }, []);
15188
+ (0, import_react6.useEffect)(() => {
15189
+ if (!enabled)
15190
+ return;
15191
+ const container = containerRef.current;
15192
+ if (!container)
15193
+ return;
15194
+ const onKeyDown = (e) => {
15195
+ if (e.code === "Space" && !e.repeat) {
15196
+ const active = document.activeElement;
15197
+ if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable) {
15198
+ return;
15199
+ }
15200
+ e.preventDefault();
15201
+ marqueeActiveRef.current = true;
15202
+ }
15203
+ };
15204
+ const onKeyUp = (e) => {
15205
+ if (e.code === "Space") {
15206
+ marqueeActiveRef.current = false;
15207
+ if (isDrawingRef.current) {
15208
+ isDrawingRef.current = false;
15209
+ startScreenRef.current = null;
15210
+ pointerIdRef.current = null;
15211
+ setMarqueeRect(null);
15212
+ }
15213
+ }
15214
+ };
15215
+ container.addEventListener("keydown", onKeyDown);
15216
+ container.addEventListener("keyup", onKeyUp);
15217
+ return () => {
15218
+ container.removeEventListener("keydown", onKeyDown);
15219
+ container.removeEventListener("keyup", onKeyUp);
15220
+ };
15221
+ }, [enabled, containerRef]);
15222
+ (0, import_react6.useEffect)(() => {
15223
+ if (!enabled)
15224
+ return;
15225
+ const container = containerRef.current;
15226
+ if (!container)
15227
+ return;
15228
+ const onKeyDown = (e) => {
15229
+ if ((e.metaKey || e.ctrlKey) && e.key === "a") {
15230
+ const active = document.activeElement;
15231
+ if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable) {
15232
+ return;
15233
+ }
15234
+ e.preventDefault();
15235
+ selectAll();
15236
+ }
15237
+ };
15238
+ container.addEventListener("keydown", onKeyDown);
15239
+ return () => {
15240
+ container.removeEventListener("keydown", onKeyDown);
15241
+ };
15242
+ }, [enabled, containerRef, selectAll]);
15243
+ (0, import_react6.useEffect)(() => {
15244
+ if (!enabled)
15245
+ return;
15246
+ const container = containerRef.current;
15247
+ if (!container)
15248
+ return;
15249
+ const onPointerDown = (e) => {
15250
+ if (!marqueeActiveRef.current)
15251
+ return;
15252
+ if (e.button !== 0)
15253
+ return;
15254
+ const target = e.target;
15255
+ if (target && typeof target.closest === "function") {
15256
+ if (target.closest(".system-canvas-node"))
15257
+ return;
15258
+ }
15259
+ const svg = svgRef.current;
15260
+ if (!svg)
15261
+ return;
15262
+ const rect = svg.getBoundingClientRect();
15263
+ const x = e.clientX - rect.left;
15264
+ const y = e.clientY - rect.top;
15265
+ startScreenRef.current = { x, y };
15266
+ isDrawingRef.current = true;
15267
+ pointerIdRef.current = e.pointerId;
15268
+ setMarqueeRect({ x1: x, y1: y, x2: x, y2: y });
15269
+ try {
15270
+ container.setPointerCapture(e.pointerId);
15271
+ } catch {
15272
+ }
15273
+ e.preventDefault();
15274
+ e.stopPropagation();
15275
+ };
15276
+ const onPointerMove = (e) => {
15277
+ if (!isDrawingRef.current)
15278
+ return;
15279
+ if (e.pointerId !== pointerIdRef.current)
15280
+ return;
15281
+ const svg = svgRef.current;
15282
+ if (!svg)
15283
+ return;
15284
+ const rect = svg.getBoundingClientRect();
15285
+ const x = e.clientX - rect.left;
15286
+ const y = e.clientY - rect.top;
15287
+ const start2 = startScreenRef.current;
15288
+ setMarqueeRect({ x1: start2.x, y1: start2.y, x2: x, y2: y });
15289
+ };
15290
+ const onPointerUp = (e) => {
15291
+ if (!isDrawingRef.current)
15292
+ return;
15293
+ if (e.pointerId !== pointerIdRef.current)
15294
+ return;
15295
+ const svg = svgRef.current;
15296
+ if (!svg)
15297
+ return;
15298
+ const rect = svg.getBoundingClientRect();
15299
+ const x = e.clientX - rect.left;
15300
+ const y = e.clientY - rect.top;
15301
+ const start2 = startScreenRef.current;
15302
+ const vp = viewport.current ?? { x: 0, y: 0, zoom: 1 };
15303
+ const topLeft = screenToCanvas(Math.min(start2.x, x), Math.min(start2.y, y), vp);
15304
+ const bottomRight = screenToCanvas(Math.max(start2.x, x), Math.max(start2.y, y), vp);
15305
+ const rectLeft = topLeft.x;
15306
+ const rectTop = topLeft.y;
15307
+ const rectRight = bottomRight.x;
15308
+ const rectBottom = bottomRight.y;
15309
+ const nodes = nodesRef.current ?? [];
15310
+ const matched = /* @__PURE__ */ new Set();
15311
+ for (const node of nodes) {
15312
+ const nRight = node.x + node.width;
15313
+ const nBottom = node.y + node.height;
15314
+ if (node.x < rectRight && nRight > rectLeft && node.y < rectBottom && nBottom > rectTop) {
15315
+ matched.add(node.id);
15316
+ }
15317
+ }
15318
+ setSelectedIds(matched);
15319
+ setMarqueeRect(null);
15320
+ isDrawingRef.current = false;
15321
+ startScreenRef.current = null;
15322
+ pointerIdRef.current = null;
15323
+ try {
15324
+ container.releasePointerCapture(e.pointerId);
15325
+ } catch {
15326
+ }
15327
+ };
15328
+ container.addEventListener("pointerdown", onPointerDown);
15329
+ container.addEventListener("pointermove", onPointerMove);
15330
+ container.addEventListener("pointerup", onPointerUp);
15331
+ return () => {
15332
+ container.removeEventListener("pointerdown", onPointerDown);
15333
+ container.removeEventListener("pointermove", onPointerMove);
15334
+ container.removeEventListener("pointerup", onPointerUp);
15335
+ };
15336
+ }, [enabled, containerRef, svgRef, viewport, nodesRef]);
15337
+ return {
15338
+ selectedIds,
15339
+ selectNode,
15340
+ toggleNode,
15341
+ selectAll,
15342
+ clearSelection,
15343
+ marqueeRect,
15344
+ marqueeActiveRef
15345
+ };
15346
+ }
15347
+
15348
+ // ../react/dist/hooks/useMultiSelectClipboard.js
15349
+ var import_react7 = __toESM(require_react(), 1);
15350
+ var clipboardSnapshot = null;
15351
+ function useMultiSelectClipboard(options) {
15352
+ const { selectedIdsRef, nodesRef, edgesRef, viewport, onNodeAdd, onEdgeAdd, canvasRef, getCursorScreenPos } = options;
15353
+ const getCursorScreenPosRef = (0, import_react7.useRef)(getCursorScreenPos);
15354
+ getCursorScreenPosRef.current = getCursorScreenPos;
15355
+ const onNodeAddRef = (0, import_react7.useRef)(onNodeAdd);
15356
+ onNodeAddRef.current = onNodeAdd;
15357
+ const onEdgeAddRef = (0, import_react7.useRef)(onEdgeAdd);
15358
+ onEdgeAddRef.current = onEdgeAdd;
15359
+ const canvasRefRef = (0, import_react7.useRef)(canvasRef);
15360
+ canvasRefRef.current = canvasRef;
15361
+ (0, import_react7.useEffect)(() => {
15362
+ const handler = (e) => {
15363
+ const active = document.activeElement;
15364
+ if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable) {
15365
+ return;
15366
+ }
15367
+ const isMod = e.metaKey || e.ctrlKey;
15368
+ if (isMod && e.key === "c") {
15369
+ const selectedIds = selectedIdsRef.current;
15370
+ if (!selectedIds || selectedIds.size === 0)
15371
+ return;
15372
+ const nodes = nodesRef.current ?? [];
15373
+ const edges = edgesRef.current ?? [];
15374
+ const vp = viewport.current ?? { x: 0, y: 0, zoom: 1 };
15375
+ const copiedNodes = nodes.filter((n) => selectedIds.has(n.id));
15376
+ if (copiedNodes.length === 0)
15377
+ return;
15378
+ const copiedEdges = edges.filter((edge) => selectedIds.has(edge.fromNode) && selectedIds.has(edge.toNode));
15379
+ clipboardSnapshot = {
15380
+ nodes: copiedNodes,
15381
+ edges: copiedEdges,
15382
+ viewportAtCopy: { ...vp }
15383
+ };
15384
+ e.preventDefault();
15385
+ return;
15386
+ }
15387
+ if (isMod && e.key === "v") {
15388
+ if (!clipboardSnapshot)
15389
+ return;
15390
+ const { nodes: srcNodes, edges: srcEdges } = clipboardSnapshot;
15391
+ if (srcNodes.length === 0)
15392
+ return;
15393
+ const oldToNew = /* @__PURE__ */ new Map();
15394
+ for (const n of srcNodes) {
15395
+ oldToNew.set(n.id, generateNodeId());
15396
+ }
15397
+ let minX = Infinity;
15398
+ let minY = Infinity;
15399
+ let maxX = -Infinity;
15400
+ let maxY = -Infinity;
15401
+ for (const n of srcNodes) {
15402
+ minX = Math.min(minX, n.x);
15403
+ minY = Math.min(minY, n.y);
15404
+ maxX = Math.max(maxX, n.x + (n.width ?? 0));
15405
+ maxY = Math.max(maxY, n.y + (n.height ?? 0));
15406
+ }
15407
+ const clusterCx = (minX + maxX) / 2;
15408
+ const clusterCy = (minY + maxY) / 2;
15409
+ const vp = viewport.current ?? { x: 0, y: 0, zoom: 1 };
15410
+ const cursorScreen = getCursorScreenPosRef.current?.();
15411
+ let targetCanvas;
15412
+ if (cursorScreen) {
15413
+ targetCanvas = screenToCanvas(cursorScreen.x, cursorScreen.y, vp);
15414
+ } else {
15415
+ targetCanvas = screenToCanvas(0, 0, vp);
15416
+ targetCanvas = { x: targetCanvas.x + 40, y: targetCanvas.y + 40 };
15417
+ }
15418
+ const dx = targetCanvas.x - clusterCx;
15419
+ const dy = targetCanvas.y - clusterCy;
15420
+ const clonedNodes = srcNodes.map((n) => ({
15421
+ ...structuredClone(n),
15422
+ id: oldToNew.get(n.id),
15423
+ x: n.x + dx,
15424
+ y: n.y + dy
15425
+ }));
15426
+ const clonedEdges = srcEdges.filter((edge) => oldToNew.has(edge.fromNode) && oldToNew.has(edge.toNode)).map((edge) => ({
15427
+ ...structuredClone(edge),
15428
+ id: generateEdgeId(),
15429
+ fromNode: oldToNew.get(edge.fromNode),
15430
+ toNode: oldToNew.get(edge.toNode)
15431
+ }));
15432
+ const ref = canvasRefRef.current;
15433
+ for (const node of clonedNodes) {
15434
+ onNodeAddRef.current(node, ref);
15435
+ }
15436
+ for (const edge of clonedEdges) {
15437
+ onEdgeAddRef.current(edge, ref);
15438
+ }
15439
+ e.preventDefault();
15440
+ return;
15441
+ }
15442
+ };
15443
+ document.addEventListener("keydown", handler);
15444
+ return () => {
15445
+ document.removeEventListener("keydown", handler);
15446
+ };
15447
+ }, [selectedIdsRef, nodesRef, edgesRef, viewport]);
15448
+ }
15449
+
15450
+ // ../react/dist/hooks/useZoomNavigation.js
15451
+ var import_react8 = __toESM(require_react(), 1);
15155
15452
  function expandRect(rect, factor) {
15156
15453
  if (factor === 1)
15157
15454
  return rect;
@@ -15189,16 +15486,16 @@ var SystemCanvas = (() => {
15189
15486
  }
15190
15487
  function useZoomNavigation(options) {
15191
15488
  const { enabled, config, nodes, currentCanvas, parentFrame, canvases, onResolveCanvas, onSeedCanvas, theme, getViewportSize, getCursorScreenPos, onEnter, onExit } = options;
15192
- const committingRef = (0, import_react6.useRef)(false);
15193
- (0, import_react6.useEffect)(() => {
15489
+ const committingRef = (0, import_react8.useRef)(false);
15490
+ (0, import_react8.useEffect)(() => {
15194
15491
  committingRef.current = false;
15195
15492
  }, [currentCanvas]);
15196
- const exitArmedRef = (0, import_react6.useRef)(false);
15197
- (0, import_react6.useEffect)(() => {
15493
+ const exitArmedRef = (0, import_react8.useRef)(false);
15494
+ (0, import_react8.useEffect)(() => {
15198
15495
  exitArmedRef.current = false;
15199
15496
  }, [currentCanvas, parentFrame]);
15200
- const prefetchRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
15201
- const prefetch = (0, import_react6.useCallback)((ref) => {
15497
+ const prefetchRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
15498
+ const prefetch = (0, import_react8.useCallback)((ref) => {
15202
15499
  if (!onResolveCanvas)
15203
15500
  return;
15204
15501
  if (canvases?.[ref])
@@ -15216,7 +15513,7 @@ var SystemCanvas = (() => {
15216
15513
  state.loading = false;
15217
15514
  });
15218
15515
  }, [canvases, onResolveCanvas, onSeedCanvas]);
15219
- const handleViewportChange = (0, import_react6.useCallback)((vp) => {
15516
+ const handleViewportChange = (0, import_react8.useCallback)((vp) => {
15220
15517
  if (!enabled)
15221
15518
  return;
15222
15519
  if (committingRef.current)
@@ -15355,7 +15652,7 @@ var SystemCanvas = (() => {
15355
15652
  onEnter,
15356
15653
  onExit
15357
15654
  ]);
15358
- const clearCommitting = (0, import_react6.useCallback)(() => {
15655
+ const clearCommitting = (0, import_react8.useCallback)(() => {
15359
15656
  committingRef.current = false;
15360
15657
  }, []);
15361
15658
  return { handleViewportChange, clearCommitting };
@@ -15363,10 +15660,10 @@ var SystemCanvas = (() => {
15363
15660
 
15364
15661
  // ../react/dist/components/Viewport.js
15365
15662
  var import_jsx_runtime22 = __toESM(require_jsx_runtime(), 1);
15366
- var import_react15 = __toESM(require_react(), 1);
15663
+ var import_react17 = __toESM(require_react(), 1);
15367
15664
 
15368
15665
  // ../react/dist/hooks/useViewport.js
15369
- var import_react7 = __toESM(require_react(), 1);
15666
+ var import_react9 = __toESM(require_react(), 1);
15370
15667
 
15371
15668
  // ../../node_modules/d3-dispatch/src/dispatch.js
15372
15669
  var noop = { value: () => {
@@ -18109,13 +18406,13 @@ var SystemCanvas = (() => {
18109
18406
  // ../react/dist/hooks/useViewport.js
18110
18407
  function useViewport(options) {
18111
18408
  const { minZoom, maxZoom, defaultViewport, onViewportChange, marqueeActiveRef } = options;
18112
- const svgRef = (0, import_react7.useRef)(null);
18113
- const groupRef = (0, import_react7.useRef)(null);
18114
- const viewport = (0, import_react7.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
18115
- const zoomBehaviorRef = (0, import_react7.useRef)(null);
18116
- const onViewportChangeRef = (0, import_react7.useRef)(onViewportChange);
18409
+ const svgRef = (0, import_react9.useRef)(null);
18410
+ const groupRef = (0, import_react9.useRef)(null);
18411
+ const viewport = (0, import_react9.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
18412
+ const zoomBehaviorRef = (0, import_react9.useRef)(null);
18413
+ const onViewportChangeRef = (0, import_react9.useRef)(onViewportChange);
18117
18414
  onViewportChangeRef.current = onViewportChange;
18118
- (0, import_react7.useEffect)(() => {
18415
+ (0, import_react9.useEffect)(() => {
18119
18416
  const svg = svgRef.current;
18120
18417
  const group = groupRef.current;
18121
18418
  if (!svg || !group)
@@ -18155,7 +18452,7 @@ var SystemCanvas = (() => {
18155
18452
  selection2.on(".zoom", null);
18156
18453
  };
18157
18454
  }, [minZoom, maxZoom]);
18158
- const fitToContent = (0, import_react7.useCallback)((nodes, animate = true) => {
18455
+ const fitToContent = (0, import_react9.useCallback)((nodes, animate = true) => {
18159
18456
  const svg = svgRef.current;
18160
18457
  if (!svg || !zoomBehaviorRef.current || nodes.length === 0)
18161
18458
  return;
@@ -18168,13 +18465,13 @@ var SystemCanvas = (() => {
18168
18465
  select_default2(svg).call(zoomBehaviorRef.current.transform, t);
18169
18466
  }
18170
18467
  }, []);
18171
- const resetZoom = (0, import_react7.useCallback)(() => {
18468
+ const resetZoom = (0, import_react9.useCallback)(() => {
18172
18469
  const svg = svgRef.current;
18173
18470
  if (!svg || !zoomBehaviorRef.current)
18174
18471
  return;
18175
18472
  select_default2(svg).transition().duration(400).call(zoomBehaviorRef.current.transform, identity2);
18176
18473
  }, []);
18177
- const setTransform = (0, import_react7.useCallback)((transform2, options2) => {
18474
+ const setTransform = (0, import_react9.useCallback)((transform2, options2) => {
18178
18475
  const svg = svgRef.current;
18179
18476
  if (!svg || !zoomBehaviorRef.current)
18180
18477
  return;
@@ -18186,7 +18483,7 @@ var SystemCanvas = (() => {
18186
18483
  sel.call(zoomBehaviorRef.current.transform, t);
18187
18484
  }
18188
18485
  }, []);
18189
- const zoomToNode = (0, import_react7.useCallback)((node, onComplete, options2) => {
18486
+ const zoomToNode = (0, import_react9.useCallback)((node, onComplete, options2) => {
18190
18487
  const svg = svgRef.current;
18191
18488
  if (!svg || !zoomBehaviorRef.current) {
18192
18489
  onComplete?.();
@@ -18380,9 +18677,9 @@ var SystemCanvas = (() => {
18380
18677
 
18381
18678
  // ../react/dist/components/RefIndicator.js
18382
18679
  var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
18383
- var import_react8 = __toESM(require_react(), 1);
18680
+ var import_react10 = __toESM(require_react(), 1);
18384
18681
  function RefIndicator({ node, theme, nodeX, nodeY, nodeWidth, nodeHeight, strokeColor, strokeWidth, corner = "bottom-right", size: sizeProp, onNavigate }) {
18385
- const [hover, setHover] = (0, import_react8.useState)(false);
18682
+ const [hover, setHover] = (0, import_react10.useState)(false);
18386
18683
  const iconKind = theme.node.refIndicator.icon;
18387
18684
  if (iconKind === "none")
18388
18685
  return null;
@@ -18453,7 +18750,7 @@ var SystemCanvas = (() => {
18453
18750
 
18454
18751
  // ../react/dist/components/CategorySlotsLayer.js
18455
18752
  var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
18456
- var import_react10 = __toESM(require_react(), 1);
18753
+ var import_react12 = __toESM(require_react(), 1);
18457
18754
 
18458
18755
  // ../react/dist/primitives/NodeColorFill.js
18459
18756
  var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
@@ -18573,9 +18870,9 @@ var SystemCanvas = (() => {
18573
18870
 
18574
18871
  // ../react/dist/primitives/NodeText.js
18575
18872
  var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
18576
- var import_react9 = __toESM(require_react(), 1);
18873
+ var import_react11 = __toESM(require_react(), 1);
18577
18874
  function NodeText({ region, value, theme, color: color2, fill, align = "start", fontWeight = 500, uppercase = false, useLabelFont = false, fontFamily, fontSize: fontSizeProp, wrap = false, maxLines, lineHeight: lineHeightProp, verticalAlign = "top" }) {
18578
- const reactId = (0, import_react9.useId)();
18875
+ const reactId = (0, import_react11.useId)();
18579
18876
  const safeId = reactId.replace(/:/g, "");
18580
18877
  if (!value)
18581
18878
  return null;
@@ -18649,8 +18946,8 @@ var SystemCanvas = (() => {
18649
18946
  // ../react/dist/components/CategorySlotsLayer.js
18650
18947
  function CategorySlotsLayer({ node, theme, canvases, slots: slotsProp }) {
18651
18948
  const slots = slotsProp ?? getCategorySlots(node, theme);
18652
- const regions = (0, import_react10.useMemo)(() => computeCategorySlotRegions(node, theme, slots), [node, theme, slots]);
18653
- const reactId = (0, import_react10.useId)();
18949
+ const regions = (0, import_react12.useMemo)(() => computeCategorySlotRegions(node, theme, slots), [node, theme, slots]);
18950
+ const reactId = (0, import_react12.useId)();
18654
18951
  const clipId = `sc-edge-clip-${reactId.replace(/:/g, "")}`;
18655
18952
  if (!slots)
18656
18953
  return null;
@@ -18883,7 +19180,7 @@ var SystemCanvas = (() => {
18883
19180
 
18884
19181
  // ../react/dist/components/ResizeHandles.js
18885
19182
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18886
- var import_react11 = __toESM(require_react(), 1);
19183
+ var import_react13 = __toESM(require_react(), 1);
18887
19184
  var HANDLE_SIZE = 7;
18888
19185
  var CORNERS = [
18889
19186
  { corner: "nw", cursor: "nwse-resize", anchor: "nw" },
@@ -18894,7 +19191,7 @@ var SystemCanvas = (() => {
18894
19191
  var cornerInset = (cornerRadius) => Math.min(cornerRadius * 0.25, 3);
18895
19192
  function ResizeHandles({ node, theme, onHandlePointerDown }) {
18896
19193
  const { x, y, width, height } = node;
18897
- const [hoveredCorner, setHoveredCorner] = (0, import_react11.useState)(null);
19194
+ const [hoveredCorner, setHoveredCorner] = (0, import_react13.useState)(null);
18898
19195
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
18899
19196
  const i = cornerInset(node.resolvedCornerRadius);
18900
19197
  const anchorPos = (anchor) => {
@@ -18919,7 +19216,7 @@ var SystemCanvas = (() => {
18919
19216
  }
18920
19217
 
18921
19218
  // ../react/dist/components/NodeRenderer.js
18922
- function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedId, editingId, onResizeHandlePointerDown, canvases, only }) {
19219
+ function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only }) {
18923
19220
  const groups = nodes.filter((n) => n.type === "group");
18924
19221
  const others = nodes.filter((n) => n.type !== "group");
18925
19222
  const common = (node) => {
@@ -18935,7 +19232,7 @@ var SystemCanvas = (() => {
18935
19232
  onContextMenu,
18936
19233
  onNavigate,
18937
19234
  onPointerDown,
18938
- isSelected: selectedId === node.id,
19235
+ isSelected: selectedIds?.has(node.id) ?? false,
18939
19236
  isEditing: editingId === node.id,
18940
19237
  slots,
18941
19238
  canvases,
@@ -18946,7 +19243,8 @@ var SystemCanvas = (() => {
18946
19243
  refCorner
18947
19244
  };
18948
19245
  };
18949
- const selectedNode = selectedId && editingId !== selectedId ? nodes.find((n) => n.id === selectedId) : void 0;
19246
+ const singleSelectedId = selectedIds?.size === 1 ? Array.from(selectedIds)[0] : null;
19247
+ const selectedNode = singleSelectedId && editingId !== singleSelectedId ? nodes.find((n) => n.id === singleSelectedId) : void 0;
18950
19248
  const renderResizeHandles = only !== "groups" && selectedNode && onResizeHandlePointerDown;
18951
19249
  return (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [only !== "non-groups" && groups.map((node) => (0, import_jsx_runtime15.jsx)(GroupNode, { ...common(node) }, node.id)), only !== "groups" && others.map((node) => {
18952
19250
  const Component = getNodeComponent(node.type);
@@ -19004,7 +19302,7 @@ var SystemCanvas = (() => {
19004
19302
 
19005
19303
  // ../react/dist/components/NodeEditor.js
19006
19304
  var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
19007
- var import_react12 = __toESM(require_react(), 1);
19305
+ var import_react14 = __toESM(require_react(), 1);
19008
19306
  function NodeEditor({ node, theme, onCommit, onCancel }) {
19009
19307
  const editableFields = useCategoryFields(node, theme);
19010
19308
  if (editableFields) {
@@ -19023,11 +19321,11 @@ var SystemCanvas = (() => {
19023
19321
  }
19024
19322
  function SingleFieldEditor({ node, theme, onCommit, onCancel }) {
19025
19323
  const initial = getInitialValue(node);
19026
- const [value, setValue] = (0, import_react12.useState)(initial);
19027
- const textareaRef = (0, import_react12.useRef)(null);
19028
- const inputRef = (0, import_react12.useRef)(null);
19029
- const committedRef = (0, import_react12.useRef)(false);
19030
- (0, import_react12.useEffect)(() => {
19324
+ const [value, setValue] = (0, import_react14.useState)(initial);
19325
+ const textareaRef = (0, import_react14.useRef)(null);
19326
+ const inputRef = (0, import_react14.useRef)(null);
19327
+ const committedRef = (0, import_react14.useRef)(false);
19328
+ (0, import_react14.useEffect)(() => {
19031
19329
  const el = textareaRef.current ?? inputRef.current;
19032
19330
  if (el) {
19033
19331
  el.focus();
@@ -19123,10 +19421,10 @@ var SystemCanvas = (() => {
19123
19421
  }
19124
19422
  }
19125
19423
  function FormEditor({ node, theme, fields, onCommit, onCancel }) {
19126
- const initial = (0, import_react12.useMemo)(() => readInitialValues(node, fields), [node, fields]);
19127
- const [values, setValues] = (0, import_react12.useState)(initial);
19128
- const committedRef = (0, import_react12.useRef)(false);
19129
- const panelRef = (0, import_react12.useRef)(null);
19424
+ const initial = (0, import_react14.useMemo)(() => readInitialValues(node, fields), [node, fields]);
19425
+ const [values, setValues] = (0, import_react14.useState)(initial);
19426
+ const committedRef = (0, import_react14.useRef)(false);
19427
+ const panelRef = (0, import_react14.useRef)(null);
19130
19428
  const width = Math.max(node.width, 240);
19131
19429
  const height = Math.max(node.height, 36 + fields.length * 44);
19132
19430
  const commit = () => {
@@ -19155,7 +19453,7 @@ var SystemCanvas = (() => {
19155
19453
  const stopPointer = (e) => {
19156
19454
  e.stopPropagation();
19157
19455
  };
19158
- (0, import_react12.useEffect)(() => {
19456
+ (0, import_react14.useEffect)(() => {
19159
19457
  const el = panelRef.current;
19160
19458
  if (!el)
19161
19459
  return;
@@ -19285,13 +19583,13 @@ var SystemCanvas = (() => {
19285
19583
 
19286
19584
  // ../react/dist/components/EdgeLabelEditor.js
19287
19585
  var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
19288
- var import_react13 = __toESM(require_react(), 1);
19586
+ var import_react15 = __toESM(require_react(), 1);
19289
19587
  var EDITOR_WIDTH = 110;
19290
19588
  function EdgeLabelEditor({ initialLabel, midpoint, theme, onCommit, onCancel }) {
19291
- const [value, setValue] = (0, import_react13.useState)(initialLabel);
19292
- const inputRef = (0, import_react13.useRef)(null);
19293
- const committedRef = (0, import_react13.useRef)(false);
19294
- (0, import_react13.useEffect)(() => {
19589
+ const [value, setValue] = (0, import_react15.useState)(initialLabel);
19590
+ const inputRef = (0, import_react15.useRef)(null);
19591
+ const committedRef = (0, import_react15.useRef)(false);
19592
+ (0, import_react15.useEffect)(() => {
19295
19593
  const el = inputRef.current;
19296
19594
  if (el) {
19297
19595
  el.focus();
@@ -19346,7 +19644,7 @@ var SystemCanvas = (() => {
19346
19644
 
19347
19645
  // ../react/dist/components/ConnectionHandles.js
19348
19646
  var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
19349
- var import_react14 = __toESM(require_react(), 1);
19647
+ var import_react16 = __toESM(require_react(), 1);
19350
19648
  var SIDES = ["top", "right", "bottom", "left"];
19351
19649
  var HANDLE_RADIUS = 4;
19352
19650
  var HANDLE_HIT_RADIUS = 10;
@@ -19355,9 +19653,9 @@ var SystemCanvas = (() => {
19355
19653
  var HOVER_SCALE = 1.42;
19356
19654
  var HOVER_TRANSITION_MS = 120;
19357
19655
  function ConnectionHandles({ node, theme, onHandlePointerDown, immediate, activeSide }) {
19358
- const [visible, setVisible] = (0, import_react14.useState)(!!immediate);
19359
- const [hoveredSide, setHoveredSide] = (0, import_react14.useState)(null);
19360
- (0, import_react14.useEffect)(() => {
19656
+ const [visible, setVisible] = (0, import_react16.useState)(!!immediate);
19657
+ const [hoveredSide, setHoveredSide] = (0, import_react16.useState)(null);
19658
+ (0, import_react16.useEffect)(() => {
19361
19659
  if (immediate) {
19362
19660
  setVisible(true);
19363
19661
  return;
@@ -19444,17 +19742,18 @@ var SystemCanvas = (() => {
19444
19742
  // ../react/dist/components/Viewport.js
19445
19743
  var HOVER_PADDING = 10;
19446
19744
  var EDGE_PROXIMITY = 16;
19447
- var Viewport = (0, import_react15.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, canvases, minZoom, maxZoom, defaultViewport, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedId, editingId, selectedEdgeId, editingEdgeId, dragOverrides, dropTargetId, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
19745
+ var Viewport = (0, import_react17.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, canvases, minZoom, maxZoom, defaultViewport, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedIds, editingId, selectedEdgeId, editingEdgeId, dragOverrides, dropTargetId, marqueeRect, marqueeActiveRef, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
19448
19746
  const { svgRef, groupRef, viewport, fitToContent, zoomToNode, setTransform } = useViewport({
19449
19747
  minZoom,
19450
19748
  maxZoom,
19451
19749
  defaultViewport,
19452
- onViewportChange
19750
+ onViewportChange,
19751
+ marqueeActiveRef
19453
19752
  });
19454
- const navigatingRef = (0, import_react15.useRef)(false);
19455
- const fadeRafRef = (0, import_react15.useRef)(null);
19456
- const fadeTimeoutRef = (0, import_react15.useRef)(null);
19457
- const triggerFade = (0, import_react15.useCallback)((durationMs) => {
19753
+ const navigatingRef = (0, import_react17.useRef)(false);
19754
+ const fadeRafRef = (0, import_react17.useRef)(null);
19755
+ const fadeTimeoutRef = (0, import_react17.useRef)(null);
19756
+ const triggerFade = (0, import_react17.useCallback)((durationMs) => {
19458
19757
  if (durationMs <= 0)
19459
19758
  return;
19460
19759
  const g = groupRef.current;
@@ -19477,7 +19776,7 @@ var SystemCanvas = (() => {
19477
19776
  }, durationMs + 16);
19478
19777
  });
19479
19778
  }, []);
19480
- (0, import_react15.useEffect)(() => {
19779
+ (0, import_react17.useEffect)(() => {
19481
19780
  return () => {
19482
19781
  if (fadeRafRef.current !== null)
19483
19782
  cancelAnimationFrame(fadeRafRef.current);
@@ -19485,10 +19784,10 @@ var SystemCanvas = (() => {
19485
19784
  clearTimeout(fadeTimeoutRef.current);
19486
19785
  };
19487
19786
  }, []);
19488
- const [hoveredNodeId, setHoveredNodeId] = (0, import_react15.useState)(null);
19489
- const [hoveredSide, setHoveredSide] = (0, import_react15.useState)(null);
19490
- const cursorPosRef = (0, import_react15.useRef)(null);
19491
- (0, import_react15.useImperativeHandle)(ref, () => ({
19787
+ const [hoveredNodeId, setHoveredNodeId] = (0, import_react17.useState)(null);
19788
+ const [hoveredSide, setHoveredSide] = (0, import_react17.useState)(null);
19789
+ const cursorPosRef = (0, import_react17.useRef)(null);
19790
+ (0, import_react17.useImperativeHandle)(ref, () => ({
19492
19791
  zoomToNode: (node, onComplete, options) => {
19493
19792
  navigatingRef.current = true;
19494
19793
  zoomToNode(node, onComplete, options);
@@ -19499,7 +19798,7 @@ var SystemCanvas = (() => {
19499
19798
  getViewport: () => viewport.current ?? { x: 0, y: 0, zoom: 1 },
19500
19799
  getCursorScreenPos: () => cursorPosRef.current
19501
19800
  }));
19502
- const renderNodes = (0, import_react15.useMemo)(() => {
19801
+ const renderNodes = (0, import_react17.useMemo)(() => {
19503
19802
  const hasDrag = dragOverrides && dragOverrides.size > 0;
19504
19803
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
19505
19804
  if (!hasDrag && !hasResize)
@@ -19512,7 +19811,7 @@ var SystemCanvas = (() => {
19512
19811
  return d ? { ...n, x: d.x, y: d.y } : n;
19513
19812
  });
19514
19813
  }, [nodes, dragOverrides, resizeOverrides]);
19515
- const renderNodeMap = (0, import_react15.useMemo)(() => {
19814
+ const renderNodeMap = (0, import_react17.useMemo)(() => {
19516
19815
  const hasDrag = dragOverrides && dragOverrides.size > 0;
19517
19816
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
19518
19817
  if (!hasDrag && !hasResize)
@@ -19523,11 +19822,11 @@ var SystemCanvas = (() => {
19523
19822
  }
19524
19823
  return m;
19525
19824
  }, [renderNodes, nodeMap, dragOverrides, resizeOverrides]);
19526
- const latestNodesRef = (0, import_react15.useRef)(nodes);
19527
- (0, import_react15.useEffect)(() => {
19825
+ const latestNodesRef = (0, import_react17.useRef)(nodes);
19826
+ (0, import_react17.useEffect)(() => {
19528
19827
  latestNodesRef.current = nodes;
19529
19828
  }, [nodes]);
19530
- const fitNow = (0, import_react15.useCallback)(() => {
19829
+ const fitNow = (0, import_react17.useCallback)(() => {
19531
19830
  const current = latestNodesRef.current;
19532
19831
  if (current.length === 0)
19533
19832
  return;
@@ -19537,7 +19836,7 @@ var SystemCanvas = (() => {
19537
19836
  fitToContent(current, animate);
19538
19837
  });
19539
19838
  }, [fitToContent]);
19540
- (0, import_react15.useEffect)(() => {
19839
+ (0, import_react17.useEffect)(() => {
19541
19840
  if (defaultViewport)
19542
19841
  return;
19543
19842
  if (autoFit !== "always")
@@ -19546,8 +19845,8 @@ var SystemCanvas = (() => {
19546
19845
  return;
19547
19846
  fitNow();
19548
19847
  }, [nodes, autoFit, defaultViewport, fitNow]);
19549
- const fittedForRef = (0, import_react15.useRef)(null);
19550
- (0, import_react15.useEffect)(() => {
19848
+ const fittedForRef = (0, import_react17.useRef)(null);
19849
+ (0, import_react17.useEffect)(() => {
19551
19850
  if (defaultViewport)
19552
19851
  return;
19553
19852
  if (autoFit !== "canvas-change" && autoFit !== "initial")
@@ -19579,7 +19878,7 @@ var SystemCanvas = (() => {
19579
19878
  triggerFade
19580
19879
  ]);
19581
19880
  const editingNode = editingId ? renderNodes.find((n) => n.id === editingId) ?? null : null;
19582
- const handleSvgPointerMove = (0, import_react15.useCallback)((event) => {
19881
+ const handleSvgPointerMove = (0, import_react17.useCallback)((event) => {
19583
19882
  const svg = svgRef.current;
19584
19883
  if (!svg)
19585
19884
  return;
@@ -19631,7 +19930,7 @@ var SystemCanvas = (() => {
19631
19930
  setHoveredSide((prev) => prev === null ? prev : null);
19632
19931
  }
19633
19932
  }, [edgeCreateEnabled, renderNodes, svgRef, viewport]);
19634
- const handleSvgPointerLeave = (0, import_react15.useCallback)(() => {
19933
+ const handleSvgPointerLeave = (0, import_react17.useCallback)(() => {
19635
19934
  setHoveredNodeId(null);
19636
19935
  setHoveredSide(null);
19637
19936
  cursorPosRef.current = null;
@@ -19660,12 +19959,17 @@ var SystemCanvas = (() => {
19660
19959
  WebkitUserSelect: "none",
19661
19960
  MozUserSelect: "none",
19662
19961
  msUserSelect: "none"
19663
- }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime22.jsx)("defs", { children: (0, import_jsx_runtime22.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime22.jsx)("path", { d: `M ${theme.grid.size} 0 L 0 0 0 ${theme.grid.size}`, fill: "none", stroke: theme.grid.color, strokeWidth: theme.grid.strokeWidth }) }) }), (0, import_jsx_runtime22.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime22.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime22.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, canvases, only: "groups" }), (0, import_jsx_runtime22.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, onResizeHandlePointerDown, canvases, only: "non-groups" }), pendingTargetNode && (0, import_jsx_runtime22.jsx)("rect", { className: "system-canvas-drop-target", x: pendingTargetNode.x - 4, y: pendingTargetNode.y - 4, width: pendingTargetNode.width + 8, height: pendingTargetNode.height + 8, rx: pendingTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.85, pointerEvents: "none" }), dropTargetNode && (0, import_jsx_runtime22.jsx)("rect", { className: "system-canvas-drop-target system-canvas-node-drop-target", x: dropTargetNode.x - 4, y: dropTargetNode.y - 4, width: dropTargetNode.width + 8, height: dropTargetNode.height + 8, rx: dropTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, strokeDasharray: "6 4", opacity: 0.9, pointerEvents: "none" }), pendingEdge && pendingSourceNode && (0, import_jsx_runtime22.jsx)(PendingEdgeRenderer, { sourceNode: pendingSourceNode, sourceSide: pendingEdge.sourceSide, cursor: pendingEdge.cursor, targetNode: pendingTargetNode, theme, defaultEdgeStyle: edgeStyle }), handlesNode && onConnectionHandlePointerDown && (0, import_jsx_runtime22.jsx)(ConnectionHandles, { node: handlesNode, theme, onHandlePointerDown: onConnectionHandlePointerDown, immediate: !!pendingEdge, activeSide: hoveredSide }), editingNode && onEditorCommit && onEditorCancel && (0, import_jsx_runtime22.jsx)(NodeEditor, { node: editingNode, theme, onCommit: onEditorCommit, onCancel: onEditorCancel }), editingEdge && editingEdgeMidpoint && onEdgeEditorCommit && onEdgeEditorCancel && (0, import_jsx_runtime22.jsx)(EdgeLabelEditor, { initialLabel: editingEdge.label ?? "", midpoint: editingEdgeMidpoint, theme, onCommit: onEdgeEditorCommit, onCancel: onEdgeEditorCancel })] })] });
19962
+ }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime22.jsx)("defs", { children: (0, import_jsx_runtime22.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime22.jsx)("path", { d: `M ${theme.grid.size} 0 L 0 0 0 ${theme.grid.size}`, fill: "none", stroke: theme.grid.color, strokeWidth: theme.grid.strokeWidth }) }) }), (0, import_jsx_runtime22.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime22.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime22.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, canvases, only: "groups" }), (0, import_jsx_runtime22.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only: "non-groups" }), pendingTargetNode && (0, import_jsx_runtime22.jsx)("rect", { className: "system-canvas-drop-target", x: pendingTargetNode.x - 4, y: pendingTargetNode.y - 4, width: pendingTargetNode.width + 8, height: pendingTargetNode.height + 8, rx: pendingTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.85, pointerEvents: "none" }), dropTargetNode && (0, import_jsx_runtime22.jsx)("rect", { className: "system-canvas-drop-target system-canvas-node-drop-target", x: dropTargetNode.x - 4, y: dropTargetNode.y - 4, width: dropTargetNode.width + 8, height: dropTargetNode.height + 8, rx: dropTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, strokeDasharray: "6 4", opacity: 0.9, pointerEvents: "none" }), selectedIds && selectedIds.size > 0 && Array.from(selectedIds).map((id2) => {
19963
+ const node = renderNodeMap.get(id2);
19964
+ if (!node)
19965
+ return null;
19966
+ return (0, import_jsx_runtime22.jsx)("rect", { x: node.x - 3, y: node.y - 3, width: node.width + 6, height: node.height + 6, rx: (node.resolvedCornerRadius ?? 0) + 3, fill: "none", stroke: theme.node.labelColor, strokeWidth: 1.5, opacity: 0.7, pointerEvents: "none" }, `halo-${id2}`);
19967
+ }), pendingEdge && pendingSourceNode && (0, import_jsx_runtime22.jsx)(PendingEdgeRenderer, { sourceNode: pendingSourceNode, sourceSide: pendingEdge.sourceSide, cursor: pendingEdge.cursor, targetNode: pendingTargetNode, theme, defaultEdgeStyle: edgeStyle }), handlesNode && onConnectionHandlePointerDown && (0, import_jsx_runtime22.jsx)(ConnectionHandles, { node: handlesNode, theme, onHandlePointerDown: onConnectionHandlePointerDown, immediate: !!pendingEdge, activeSide: hoveredSide }), editingNode && onEditorCommit && onEditorCancel && (0, import_jsx_runtime22.jsx)(NodeEditor, { node: editingNode, theme, onCommit: onEditorCommit, onCancel: onEditorCancel }), editingEdge && editingEdgeMidpoint && onEdgeEditorCommit && onEdgeEditorCancel && (0, import_jsx_runtime22.jsx)(EdgeLabelEditor, { initialLabel: editingEdge.label ?? "", midpoint: editingEdgeMidpoint, theme, onCommit: onEdgeEditorCommit, onCancel: onEdgeEditorCancel })] }), marqueeRect && (0, import_jsx_runtime22.jsx)("rect", { x: Math.min(marqueeRect.x1, marqueeRect.x2), y: Math.min(marqueeRect.y1, marqueeRect.y2), width: Math.abs(marqueeRect.x2 - marqueeRect.x1), height: Math.abs(marqueeRect.y2 - marqueeRect.y1), fill: theme.node.labelColor + "18", stroke: theme.node.labelColor, strokeWidth: 1, opacity: 0.9, pointerEvents: "none" })] });
19664
19968
  });
19665
19969
 
19666
19970
  // ../react/dist/components/Breadcrumbs.js
19667
19971
  var import_jsx_runtime23 = __toESM(require_jsx_runtime(), 1);
19668
- var import_react16 = __toESM(require_react(), 1);
19972
+ var import_react18 = __toESM(require_react(), 1);
19669
19973
  function Breadcrumbs({ breadcrumbs, theme, onNavigate }) {
19670
19974
  if (breadcrumbs.length <= 1)
19671
19975
  return null;
@@ -19686,7 +19990,7 @@ var SystemCanvas = (() => {
19686
19990
  backdropFilter: "blur(8px)"
19687
19991
  }, children: breadcrumbs.map((crumb, index) => {
19688
19992
  const isLast = index === breadcrumbs.length - 1;
19689
- return (0, import_jsx_runtime23.jsxs)(import_react16.default.Fragment, { children: [index > 0 && (0, import_jsx_runtime23.jsx)("span", { style: {
19993
+ return (0, import_jsx_runtime23.jsxs)(import_react18.default.Fragment, { children: [index > 0 && (0, import_jsx_runtime23.jsx)("span", { style: {
19690
19994
  color: theme.separatorColor,
19691
19995
  margin: "0 2px"
19692
19996
  }, children: "/" }), (0, import_jsx_runtime23.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
@@ -19710,11 +20014,11 @@ var SystemCanvas = (() => {
19710
20014
 
19711
20015
  // ../react/dist/components/AddNodeButton.js
19712
20016
  var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1);
19713
- var import_react17 = __toESM(require_react(), 1);
20017
+ var import_react19 = __toESM(require_react(), 1);
19714
20018
  function AddNodeButton({ options, addNode: addNode2, theme }) {
19715
- const [open, setOpen] = (0, import_react17.useState)(false);
19716
- const rootRef = (0, import_react17.useRef)(null);
19717
- (0, import_react17.useEffect)(() => {
20019
+ const [open, setOpen] = (0, import_react19.useState)(false);
20020
+ const rootRef = (0, import_react19.useRef)(null);
20021
+ (0, import_react19.useEffect)(() => {
19718
20022
  if (!open)
19719
20023
  return;
19720
20024
  function onDocClick(e) {
@@ -19803,7 +20107,7 @@ var SystemCanvas = (() => {
19803
20107
  } });
19804
20108
  }
19805
20109
  function MenuRow({ theme, option, onClick }) {
19806
- const [hover, setHover] = (0, import_react17.useState)(false);
20110
+ const [hover, setHover] = (0, import_react19.useState)(false);
19807
20111
  const swatchSize = 18;
19808
20112
  return (0, import_jsx_runtime24.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
19809
20113
  display: "flex",
@@ -19839,12 +20143,12 @@ var SystemCanvas = (() => {
19839
20143
 
19840
20144
  // ../react/dist/components/LaneHeaders.js
19841
20145
  var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
19842
- var import_react18 = __toESM(require_react(), 1);
20146
+ var import_react20 = __toESM(require_react(), 1);
19843
20147
  function LaneHeaders({ columns, rows, theme, getViewport, width, height, pinned = true }) {
19844
20148
  const hasColumns = columns && columns.length > 0;
19845
20149
  const hasRows = rows && rows.length > 0;
19846
- const [viewport, setViewport] = (0, import_react18.useState)(() => getViewport());
19847
- (0, import_react18.useEffect)(() => {
20150
+ const [viewport, setViewport] = (0, import_react20.useState)(() => getViewport());
20151
+ (0, import_react20.useEffect)(() => {
19848
20152
  if (!hasColumns && !hasRows)
19849
20153
  return;
19850
20154
  let raf = 0;
@@ -19918,7 +20222,7 @@ var SystemCanvas = (() => {
19918
20222
 
19919
20223
  // ../react/dist/components/NodeToolbar.js
19920
20224
  var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
19921
- var import_react19 = __toESM(require_react(), 1);
20225
+ var import_react21 = __toESM(require_react(), 1);
19922
20226
  var NODE_GAP = 10;
19923
20227
  var FLIP_MARGIN = 8;
19924
20228
  var PADDING = 6;
@@ -19926,9 +20230,9 @@ var SystemCanvas = (() => {
19926
20230
  var SWATCH_SIZE = 16;
19927
20231
  var BUTTON_SIZE = 28;
19928
20232
  var DELETE_SIZE = 14;
19929
- function NodeToolbar({ node, theme, onPatch, onDelete, getViewport, containerWidth, containerHeight, render: render2 }) {
19930
- const [viewport, setViewport] = (0, import_react19.useState)(() => getViewport());
19931
- (0, import_react19.useEffect)(() => {
20233
+ function NodeToolbar({ node, theme, onPatch, onDelete, getViewport, containerWidth, containerHeight, render: render2, selectedNodes, onMultiPatch }) {
20234
+ const [viewport, setViewport] = (0, import_react21.useState)(() => getViewport());
20235
+ (0, import_react21.useEffect)(() => {
19932
20236
  let raf = 0;
19933
20237
  let lastX = -Infinity;
19934
20238
  let lastY = -Infinity;
@@ -19946,13 +20250,28 @@ var SystemCanvas = (() => {
19946
20250
  raf = requestAnimationFrame(tick);
19947
20251
  return () => cancelAnimationFrame(raf);
19948
20252
  }, [getViewport]);
20253
+ const isMulti = selectedNodes != null && selectedNodes.length > 1;
19949
20254
  const align = theme.toolbarAlign ?? "center";
19950
- const anchorCanvasX = align === "left" ? node.x : align === "right" ? node.x + node.width : node.x + node.width / 2;
19951
- const topAnchor = canvasToScreen(anchorCanvasX, node.y, viewport);
19952
- const bottomAnchor = canvasToScreen(anchorCanvasX, node.y + node.height, viewport);
19953
- const toolbarRef = (0, import_react19.useRef)(null);
19954
- const [size, setSize] = (0, import_react19.useState)({ width: 0, height: 0 });
19955
- (0, import_react19.useEffect)(() => {
20255
+ const { anchorTopY, anchorBottomY, anchorX } = (0, import_react21.useMemo)(() => {
20256
+ if (isMulti && selectedNodes) {
20257
+ const minX = Math.min(...selectedNodes.map((n) => n.x));
20258
+ const maxX = Math.max(...selectedNodes.map((n) => n.x + n.width));
20259
+ const minY = Math.min(...selectedNodes.map((n) => n.y));
20260
+ const maxY = Math.max(...selectedNodes.map((n) => n.y + n.height));
20261
+ return {
20262
+ anchorX: (minX + maxX) / 2,
20263
+ anchorTopY: minY,
20264
+ anchorBottomY: maxY
20265
+ };
20266
+ }
20267
+ const x = align === "left" ? node.x : align === "right" ? node.x + node.width : node.x + node.width / 2;
20268
+ return { anchorX: x, anchorTopY: node.y, anchorBottomY: node.y + node.height };
20269
+ }, [isMulti, selectedNodes, align, node]);
20270
+ const topAnchor = canvasToScreen(anchorX, anchorTopY, viewport);
20271
+ const bottomAnchor = canvasToScreen(anchorX, anchorBottomY, viewport);
20272
+ const toolbarRef = (0, import_react21.useRef)(null);
20273
+ const [size, setSize] = (0, import_react21.useState)({ width: 0, height: 0 });
20274
+ (0, import_react21.useEffect)(() => {
19956
20275
  const el = toolbarRef.current;
19957
20276
  if (!el)
19958
20277
  return;
@@ -19965,7 +20284,12 @@ var SystemCanvas = (() => {
19965
20284
  ro.observe(el);
19966
20285
  return () => ro.disconnect();
19967
20286
  }, []);
19968
- let left = align === "left" ? topAnchor.x : align === "right" ? topAnchor.x - size.width : topAnchor.x - size.width / 2;
20287
+ let left;
20288
+ if (isMulti) {
20289
+ left = topAnchor.x - size.width / 2;
20290
+ } else {
20291
+ left = align === "left" ? topAnchor.x : align === "right" ? topAnchor.x - size.width : topAnchor.x - size.width / 2;
20292
+ }
19969
20293
  let top = topAnchor.y - size.height - NODE_GAP;
19970
20294
  if (top < FLIP_MARGIN) {
19971
20295
  top = bottomAnchor.y + NODE_GAP;
@@ -20002,13 +20326,51 @@ var SystemCanvas = (() => {
20002
20326
  userSelect: "none",
20003
20327
  whiteSpace: "nowrap"
20004
20328
  },
20005
- children: render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime26.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
20329
+ children: isMulti && selectedNodes && onMultiPatch ? (0, import_jsx_runtime26.jsx)(MultiToolbarContent, { selectedNodes, theme, onMultiPatch, onDelete: deleteNode }) : render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime26.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
20006
20330
  });
20007
20331
  }
20332
+ function MultiToolbarContent({ selectedNodes, theme, onMultiPatch, onDelete }) {
20333
+ const representativeNode = selectedNodes[0];
20334
+ const groups = (0, import_react21.useMemo)(() => getNodeActionsForNode(representativeNode, theme), [representativeNode, theme]);
20335
+ const showDelete = theme.showToolbarDelete === true;
20336
+ const swatchGroups = groups.filter((g) => g.kind === "swatches" || g.kind == null);
20337
+ const otherGroups = groups.filter((g) => g.kind !== "swatches" && g.kind != null && g.kind !== "menu");
20338
+ return (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [(0, import_jsx_runtime26.jsxs)("span", { style: {
20339
+ fontSize: 11,
20340
+ color: theme.breadcrumbs.textColor,
20341
+ opacity: 0.75,
20342
+ paddingRight: BUTTON_GAP,
20343
+ whiteSpace: "nowrap"
20344
+ }, children: [selectedNodes.length, " nodes"] }), swatchGroups.map((group, i) => {
20345
+ const actions = filterActionsForNode(group, representativeNode);
20346
+ if (actions.length === 0)
20347
+ return null;
20348
+ return (0, import_jsx_runtime26.jsxs)(import_react21.default.Fragment, { children: [i > 0 && (0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)("div", { style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
20349
+ const active = action.isActive?.(representativeNode) ?? false;
20350
+ const handleClick = () => {
20351
+ const patch = resolveActionPatch(action, representativeNode);
20352
+ onMultiPatch(patch);
20353
+ };
20354
+ return (0, import_jsx_runtime26.jsx)(SwatchButton, { action, active, theme, onClick: handleClick }, action.id);
20355
+ }) })] }, group.id);
20356
+ }), otherGroups.map((group) => {
20357
+ const actions = filterActionsForNode(group, representativeNode);
20358
+ if (actions.length === 0)
20359
+ return null;
20360
+ return (0, import_jsx_runtime26.jsxs)(import_react21.default.Fragment, { children: [(0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)("div", { style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
20361
+ const active = action.isActive?.(representativeNode) ?? false;
20362
+ const handleClick = () => {
20363
+ const patch = resolveActionPatch(action, representativeNode);
20364
+ onMultiPatch(patch);
20365
+ };
20366
+ return (0, import_jsx_runtime26.jsx)(IconButton, { action, active, theme, onClick: handleClick }, action.id);
20367
+ }) })] }, group.id);
20368
+ }), showDelete && (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [(0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(DeleteButton, { theme, onDelete })] })] });
20369
+ }
20008
20370
  function DefaultToolbarContent({ node, theme, onPatch, onDelete }) {
20009
- const groups = (0, import_react19.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
20371
+ const groups = (0, import_react21.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
20010
20372
  const showDelete = theme.showToolbarDelete === true;
20011
- return (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [groups.map((group, i) => (0, import_jsx_runtime26.jsxs)(import_react19.default.Fragment, { children: [i > 0 && (0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(ActionGroupView, { group, node, theme, onPatch })] }, group.id)), showDelete && (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [(0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(DeleteButton, { theme, onDelete })] })] });
20373
+ return (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [groups.map((group, i) => (0, import_jsx_runtime26.jsxs)(import_react21.default.Fragment, { children: [i > 0 && (0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(ActionGroupView, { group, node, theme, onPatch })] }, group.id)), showDelete && (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [(0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(DeleteButton, { theme, onDelete })] })] });
20012
20374
  }
20013
20375
  function Divider2({ theme }) {
20014
20376
  return (0, import_jsx_runtime26.jsx)("div", { style: {
@@ -20075,9 +20437,9 @@ var SystemCanvas = (() => {
20075
20437
  } }) : (0, import_jsx_runtime26.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
20076
20438
  }
20077
20439
  function MenuGroup({ group, actions, node, theme, onPatch }) {
20078
- const [open, setOpen] = (0, import_react19.useState)(false);
20079
- const wrapRef = (0, import_react19.useRef)(null);
20080
- (0, import_react19.useEffect)(() => {
20440
+ const [open, setOpen] = (0, import_react21.useState)(false);
20441
+ const wrapRef = (0, import_react21.useRef)(null);
20442
+ (0, import_react21.useEffect)(() => {
20081
20443
  if (!open)
20082
20444
  return;
20083
20445
  const onDown = (e) => {
@@ -20164,18 +20526,18 @@ var SystemCanvas = (() => {
20164
20526
 
20165
20527
  // ../react/dist/components/NodeContextMenuOverlay.js
20166
20528
  var import_jsx_runtime27 = __toESM(require_jsx_runtime(), 1);
20167
- var import_react20 = __toESM(require_react(), 1);
20529
+ var import_react22 = __toESM(require_react(), 1);
20168
20530
  var ESTIMATED_MENU_WIDTH = 200;
20169
20531
  var MIN_MENU_WIDTH = 160;
20170
20532
  var VIEWPORT_MARGIN = 8;
20171
20533
  function NodeContextMenuOverlay({ state, config, theme, onClose }) {
20172
- const rootRef = (0, import_react20.useRef)(null);
20173
- const [hoveredId, setHoveredId] = (0, import_react20.useState)(null);
20174
- (0, import_react20.useEffect)(() => {
20534
+ const rootRef = (0, import_react22.useRef)(null);
20535
+ const [hoveredId, setHoveredId] = (0, import_react22.useState)(null);
20536
+ (0, import_react22.useEffect)(() => {
20175
20537
  if (state)
20176
20538
  setHoveredId(null);
20177
20539
  }, [state]);
20178
- (0, import_react20.useEffect)(() => {
20540
+ (0, import_react22.useEffect)(() => {
20179
20541
  if (!state)
20180
20542
  return;
20181
20543
  function onDown(e) {
@@ -20283,8 +20645,8 @@ var SystemCanvas = (() => {
20283
20645
  // ../react/dist/components/SystemCanvas.js
20284
20646
  var CASCADE_WINDOW_MS = 1500;
20285
20647
  var CASCADE_OFFSET = 20;
20286
- var SystemCanvas = (0, import_react21.forwardRef)(function SystemCanvas2({ canvas, onResolveCanvas, canvases, rootLabel = "Home", onNavigate, onBreadcrumbClick, onBreadcrumbsChange, onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, onSelectionChange, nodeContextMenu, editable = false, onNodeAdd, onNodeUpdate, onNodesUpdate, onNodeDelete, onEdgeUpdate, onEdgeDelete, onEdgeAdd, canDropNodeOn, onNodeDrop, renderAddNodeButton, showNodeToolbar = true, renderNodeToolbar, theme: themeProp, themes: customThemes, edgeStyle = "bezier", defaultViewport, minZoom: minZoomProp, maxZoom, onViewportChange, autoFit = "canvas-change", laneHeaders = "pinned", snapToLanes = false, zoomNavigation = false, className, style }, forwardedRef) {
20287
- const zoomNavConfig = (0, import_react21.useMemo)(() => {
20648
+ var SystemCanvas = (0, import_react23.forwardRef)(function SystemCanvas2({ canvas, onResolveCanvas, canvases, rootLabel = "Home", onNavigate, onBreadcrumbClick, onBreadcrumbsChange, onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, onSelectionChange, nodeContextMenu, editable = false, onNodeAdd, onNodeUpdate, onNodesUpdate, onNodeDelete, onNodesDelete, onEdgeUpdate, onEdgeDelete, onEdgeAdd, canDropNodeOn, onNodeDrop, renderAddNodeButton, showNodeToolbar = true, renderNodeToolbar, theme: themeProp, themes: customThemes, edgeStyle = "bezier", defaultViewport, minZoom: minZoomProp, maxZoom, onViewportChange, autoFit = "canvas-change", laneHeaders = "pinned", snapToLanes = false, zoomNavigation = false, className, style }, forwardedRef) {
20649
+ const zoomNavConfig = (0, import_react23.useMemo)(() => {
20288
20650
  const defaults = {
20289
20651
  enterThreshold: 0.66,
20290
20652
  exitThreshold: 0.33,
@@ -20309,16 +20671,16 @@ var SystemCanvas = (() => {
20309
20671
  }, [zoomNavigation]);
20310
20672
  const effectiveMaxZoom = maxZoom ?? (zoomNavConfig.enabled ? 16 : 4);
20311
20673
  const effectiveMinZoom = minZoomProp ?? (zoomNavConfig.enabled ? 0.01 : 0.1);
20312
- (0, import_react21.useEffect)(() => {
20674
+ (0, import_react23.useEffect)(() => {
20313
20675
  const env = globalThis.process?.env?.NODE_ENV;
20314
20676
  if (editable && !canvases && env !== "production") {
20315
20677
  console.warn("[system-canvas] `editable` is enabled but `canvases` prop is missing. Edits to sub-canvases will not be reflected without a synchronous ref \u2192 CanvasData map.");
20316
20678
  }
20317
20679
  }, [editable, canvases]);
20318
- const [parentFrames, setParentFrames] = (0, import_react21.useState)([]);
20319
- const [pendingHandoff, setPendingHandoff] = (0, import_react21.useState)(null);
20320
- const suppressNextHandoffClearRef = (0, import_react21.useRef)(false);
20321
- const handleBreadcrumbClick = (0, import_react21.useCallback)((index) => {
20680
+ const [parentFrames, setParentFrames] = (0, import_react23.useState)([]);
20681
+ const [pendingHandoff, setPendingHandoff] = (0, import_react23.useState)(null);
20682
+ const suppressNextHandoffClearRef = (0, import_react23.useRef)(false);
20683
+ const handleBreadcrumbClick = (0, import_react23.useCallback)((index) => {
20322
20684
  setParentFrames((prev) => prev.slice(0, index));
20323
20685
  if (suppressNextHandoffClearRef.current) {
20324
20686
  suppressNextHandoffClearRef.current = false;
@@ -20335,10 +20697,10 @@ var SystemCanvas = (() => {
20335
20697
  onNavigate,
20336
20698
  onBreadcrumbClick: handleBreadcrumbClick
20337
20699
  });
20338
- (0, import_react21.useEffect)(() => {
20700
+ (0, import_react23.useEffect)(() => {
20339
20701
  onBreadcrumbsChange?.(breadcrumbs);
20340
20702
  }, [breadcrumbs, onBreadcrumbsChange]);
20341
- const theme = (0, import_react21.useMemo)(() => {
20703
+ const theme = (0, import_react23.useMemo)(() => {
20342
20704
  const registry = { ...themes, ...customThemes };
20343
20705
  const resolveByName = (name) => name && registry[name] ? registry[name] : null;
20344
20706
  if (themeProp) {
@@ -20349,22 +20711,22 @@ var SystemCanvas = (() => {
20349
20711
  }
20350
20712
  return resolveByName(currentCanvas.theme?.base) ?? resolveByName(canvas.theme?.base) ?? darkTheme;
20351
20713
  }, [themeProp, customThemes, currentCanvas.theme?.base, canvas.theme?.base]);
20352
- const { nodes, edges, nodeMap } = (0, import_react21.useMemo)(() => {
20714
+ const { nodes, edges, nodeMap } = (0, import_react23.useMemo)(() => {
20353
20715
  const resolved = resolveCanvas(currentCanvas, theme);
20354
20716
  const map = buildNodeMap(resolved.nodes);
20355
20717
  return { nodes: resolved.nodes, edges: resolved.edges, nodeMap: map };
20356
20718
  }, [currentCanvas, theme]);
20357
- const nodesRef = (0, import_react21.useRef)(nodes);
20719
+ const nodesRef = (0, import_react23.useRef)(nodes);
20358
20720
  nodesRef.current = nodes;
20359
- const viewportStateRef = (0, import_react21.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
20360
- const viewportHandleRef = (0, import_react21.useRef)(null);
20361
- const navigateToRefRef = (0, import_react21.useRef)(navigateToRef);
20721
+ const viewportStateRef = (0, import_react23.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
20722
+ const viewportHandleRef = (0, import_react23.useRef)(null);
20723
+ const navigateToRefRef = (0, import_react23.useRef)(navigateToRef);
20362
20724
  navigateToRefRef.current = navigateToRef;
20363
- const navigateToBreadcrumbRef = (0, import_react21.useRef)(navigateToBreadcrumb);
20725
+ const navigateToBreadcrumbRef = (0, import_react23.useRef)(navigateToBreadcrumb);
20364
20726
  navigateToBreadcrumbRef.current = navigateToBreadcrumb;
20365
- const breadcrumbsRef = (0, import_react21.useRef)(breadcrumbs);
20727
+ const breadcrumbsRef = (0, import_react23.useRef)(breadcrumbs);
20366
20728
  breadcrumbsRef.current = breadcrumbs;
20367
- (0, import_react21.useImperativeHandle)(forwardedRef, () => ({
20729
+ (0, import_react23.useImperativeHandle)(forwardedRef, () => ({
20368
20730
  zoomIntoNode: (nodeId, options) => {
20369
20731
  return new Promise((resolve) => {
20370
20732
  const node = nodesRef.current.find((n) => n.id === nodeId);
@@ -20396,26 +20758,64 @@ var SystemCanvas = (() => {
20396
20758
  navigateToBreadcrumbRef.current(0);
20397
20759
  }
20398
20760
  }), [forwardedRef]);
20399
- const [selectedId, setSelectedId] = (0, import_react21.useState)(null);
20400
- const [editingId, setEditingId] = (0, import_react21.useState)(null);
20401
- const [selectedEdgeId, setSelectedEdgeId] = (0, import_react21.useState)(null);
20402
- const [editingEdgeId, setEditingEdgeId] = (0, import_react21.useState)(null);
20403
- (0, import_react21.useEffect)(() => {
20404
- setSelectedId(null);
20761
+ const [editingId, setEditingId] = (0, import_react23.useState)(null);
20762
+ const [selectedEdgeId, setSelectedEdgeId] = (0, import_react23.useState)(null);
20763
+ const [editingEdgeId, setEditingEdgeId] = (0, import_react23.useState)(null);
20764
+ const svgProxyRef = (0, import_react23.useRef)(null);
20765
+ const containerRef = (0, import_react23.useRef)(null);
20766
+ const { selectedIds, selectNode, toggleNode, selectAll, clearSelection, marqueeRect, marqueeActiveRef } = useMultiSelect({
20767
+ svgRef: svgProxyRef,
20768
+ viewport: viewportStateRef,
20769
+ nodesRef,
20770
+ containerRef,
20771
+ enabled: editable
20772
+ });
20773
+ const selectedIdsRef = (0, import_react23.useRef)(selectedIds);
20774
+ (0, import_react23.useEffect)(() => {
20775
+ selectedIdsRef.current = selectedIds;
20776
+ }, [selectedIds]);
20777
+ const edgesRef = (0, import_react23.useRef)(edges);
20778
+ (0, import_react23.useEffect)(() => {
20779
+ edgesRef.current = edges;
20780
+ }, [edges]);
20781
+ useMultiSelectClipboard({
20782
+ selectedIdsRef,
20783
+ nodesRef,
20784
+ edgesRef,
20785
+ viewport: viewportStateRef,
20786
+ canvasContainerRef: containerRef,
20787
+ onNodeAdd: (node) => onNodeAdd?.(node, currentCanvasRef),
20788
+ onEdgeAdd: (edge) => onEdgeAdd?.(edge, currentCanvasRef),
20789
+ canvasRef: currentCanvasRef,
20790
+ getCursorScreenPos: () => viewportHandleRef.current?.getCursorScreenPos() ?? null
20791
+ });
20792
+ (0, import_react23.useEffect)(() => {
20793
+ clearSelection();
20405
20794
  setEditingId(null);
20406
20795
  setSelectedEdgeId(null);
20407
20796
  setEditingEdgeId(null);
20408
20797
  }, [currentCanvasRef]);
20409
- const onSelectionChangeRef = (0, import_react21.useRef)(onSelectionChange);
20410
- (0, import_react21.useEffect)(() => {
20798
+ const onSelectionChangeRef = (0, import_react23.useRef)(onSelectionChange);
20799
+ (0, import_react23.useEffect)(() => {
20411
20800
  onSelectionChangeRef.current = onSelectionChange;
20412
20801
  }, [onSelectionChange]);
20413
- const lastEmittedSelectionRef = (0, import_react21.useRef)({ kind: null, id: null, canvasRef: void 0 });
20414
- (0, import_react21.useEffect)(() => {
20802
+ const lastEmittedSelectionRef = (0, import_react23.useRef)({ kind: null, id: null, multiIds: null, canvasRef: void 0 });
20803
+ (0, import_react23.useEffect)(() => {
20415
20804
  const cb = onSelectionChangeRef.current;
20416
20805
  let next = null;
20417
- if (selectedId) {
20418
- const node = nodeMap.get(selectedId);
20806
+ if (selectedIds.size > 1) {
20807
+ const resolvedNodes = [];
20808
+ for (const id2 of selectedIds) {
20809
+ const n = nodeMap.get(id2);
20810
+ if (n)
20811
+ resolvedNodes.push(n);
20812
+ }
20813
+ if (resolvedNodes.length > 0) {
20814
+ next = { kind: "multi", nodes: resolvedNodes, canvasRef: currentCanvasRef };
20815
+ }
20816
+ } else if (selectedIds.size === 1) {
20817
+ const id2 = Array.from(selectedIds)[0];
20818
+ const node = nodeMap.get(id2);
20419
20819
  if (node)
20420
20820
  next = { kind: "node", node, canvasRef: currentCanvasRef };
20421
20821
  }
@@ -20426,21 +20826,24 @@ var SystemCanvas = (() => {
20426
20826
  }
20427
20827
  const last = lastEmittedSelectionRef.current;
20428
20828
  const nextKind = next?.kind ?? null;
20429
- const nextId = next ? next.kind === "node" ? next.node.id : next.edge.id : null;
20430
20829
  const nextRef = next?.canvasRef;
20431
- if (last.kind === nextKind && last.id === nextId && last.canvasRef === nextRef) {
20432
- return;
20830
+ if (nextKind === "multi" && next?.kind === "multi") {
20831
+ const sortedIds = next.nodes.map((n) => n.id).sort().join(",");
20832
+ if (last.kind === "multi" && last.multiIds === sortedIds && last.canvasRef === nextRef) {
20833
+ return;
20834
+ }
20835
+ lastEmittedSelectionRef.current = { kind: "multi", id: null, multiIds: sortedIds, canvasRef: nextRef };
20836
+ } else {
20837
+ const nextId = next ? next.kind === "node" ? next.node.id : next.kind === "edge" ? next.edge.id : null : null;
20838
+ if (last.kind === nextKind && last.id === nextId && last.canvasRef === nextRef) {
20839
+ return;
20840
+ }
20841
+ lastEmittedSelectionRef.current = { kind: nextKind, id: nextId, multiIds: null, canvasRef: nextRef };
20433
20842
  }
20434
- lastEmittedSelectionRef.current = {
20435
- kind: nextKind,
20436
- id: nextId,
20437
- canvasRef: nextRef
20438
- };
20439
20843
  cb?.(next);
20440
- }, [selectedId, selectedEdgeId, currentCanvasRef, nodeMap, edges]);
20441
- const containerRef = (0, import_react21.useRef)(null);
20442
- const [containerSize, setContainerSize] = (0, import_react21.useState)({ width: 0, height: 0 });
20443
- (0, import_react21.useEffect)(() => {
20844
+ }, [selectedIds, selectedEdgeId, currentCanvasRef, nodeMap, edges]);
20845
+ const [containerSize, setContainerSize] = (0, import_react23.useState)({ width: 0, height: 0 });
20846
+ (0, import_react23.useEffect)(() => {
20444
20847
  const el = containerRef.current;
20445
20848
  if (!el)
20446
20849
  return;
@@ -20455,8 +20858,8 @@ var SystemCanvas = (() => {
20455
20858
  }, []);
20456
20859
  const hasLanes = currentCanvas.columns && currentCanvas.columns.length > 0 || currentCanvas.rows && currentCanvas.rows.length > 0;
20457
20860
  const showLaneHeaders = hasLanes && laneHeaders !== "none";
20458
- const getViewportState = (0, import_react21.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
20459
- const applyLaneSnap = (0, import_react21.useCallback)((id2, patch) => {
20861
+ const getViewportState = (0, import_react23.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
20862
+ const applyLaneSnap = (0, import_react23.useCallback)((id2, patch) => {
20460
20863
  if (!snapToLanes)
20461
20864
  return patch;
20462
20865
  const cols = currentCanvas.columns;
@@ -20481,10 +20884,10 @@ var SystemCanvas = (() => {
20481
20884
  }
20482
20885
  return final;
20483
20886
  }, [snapToLanes, currentCanvas.columns, currentCanvas.rows]);
20484
- const commitResize = (0, import_react21.useCallback)((id2, patch) => {
20887
+ const commitResize = (0, import_react23.useCallback)((id2, patch) => {
20485
20888
  onNodeUpdate?.(id2, applyLaneSnap(id2, patch), currentCanvasRef);
20486
20889
  }, [onNodeUpdate, currentCanvasRef, applyLaneSnap]);
20487
- const commitDragBatch = (0, import_react21.useCallback)((updates) => {
20890
+ const commitDragBatch = (0, import_react23.useCallback)((updates) => {
20488
20891
  const final = updates.map((u) => ({
20489
20892
  id: u.id,
20490
20893
  patch: applyLaneSnap(u.id, u.patch)
@@ -20499,8 +20902,7 @@ var SystemCanvas = (() => {
20499
20902
  onNodeUpdate(id2, patch, currentCanvasRef);
20500
20903
  }
20501
20904
  }, [onNodeUpdate, onNodesUpdate, currentCanvasRef, applyLaneSnap]);
20502
- const svgProxyRef = (0, import_react21.useRef)(null);
20503
- const handleNodeDrop = (0, import_react21.useCallback)((sources, target) => {
20905
+ const handleNodeDrop = (0, import_react23.useCallback)((sources, target) => {
20504
20906
  onNodeDrop?.(sources, target, { canvasRef: currentCanvasRef });
20505
20907
  }, [onNodeDrop, currentCanvasRef]);
20506
20908
  const { dragOverrides, dropTargetId, onPointerDown: onNodePointerDown } = useNodeDrag({
@@ -20509,30 +20911,32 @@ var SystemCanvas = (() => {
20509
20911
  onCommit: commitDragBatch,
20510
20912
  svgRef: svgProxyRef,
20511
20913
  canDropNodeOn,
20512
- onNodeDrop: handleNodeDrop
20914
+ onNodeDrop: handleNodeDrop,
20915
+ selectedIdsRef
20513
20916
  });
20514
20917
  const { resizeOverrides, onHandlePointerDown: onResizeHandlePointerDown } = useNodeResize({
20515
20918
  viewport: viewportStateRef,
20516
20919
  onCommit: commitResize
20517
20920
  });
20518
- const selectedResolvedNode = (0, import_react21.useMemo)(() => {
20519
- if (!selectedId)
20921
+ const singleSelectedId = selectedIds.size === 1 ? Array.from(selectedIds)[0] : null;
20922
+ const selectedResolvedNode = (0, import_react23.useMemo)(() => {
20923
+ if (!singleSelectedId)
20520
20924
  return null;
20521
- const base = nodeMap.get(selectedId);
20925
+ const base = nodeMap.get(singleSelectedId);
20522
20926
  if (!base)
20523
20927
  return null;
20524
- const resize = resizeOverrides.get(selectedId);
20928
+ const resize = resizeOverrides.get(singleSelectedId);
20525
20929
  if (resize) {
20526
20930
  return { ...base, x: resize.x, y: resize.y, width: resize.width, height: resize.height };
20527
20931
  }
20528
- const drag = dragOverrides.get(selectedId);
20932
+ const drag = dragOverrides.get(singleSelectedId);
20529
20933
  if (drag) {
20530
20934
  return { ...base, x: drag.x, y: drag.y };
20531
20935
  }
20532
20936
  return base;
20533
- }, [selectedId, nodeMap, dragOverrides, resizeOverrides]);
20937
+ }, [singleSelectedId, nodeMap, dragOverrides, resizeOverrides]);
20534
20938
  svgProxyRef.current = viewportHandleRef.current?.getSvgElement() ?? null;
20535
- const handleEdgeCreated = (0, import_react21.useCallback)((edge) => {
20939
+ const handleEdgeCreated = (0, import_react23.useCallback)((edge) => {
20536
20940
  onEdgeAdd?.(edge, currentCanvasRef);
20537
20941
  }, [onEdgeAdd, currentCanvasRef]);
20538
20942
  const { pending: pendingEdge, onHandlePointerDown: onConnectionHandlePointerDown } = useEdgeCreate({
@@ -20541,7 +20945,7 @@ var SystemCanvas = (() => {
20541
20945
  nodesRef,
20542
20946
  onCreate: handleEdgeCreated
20543
20947
  });
20544
- const handleNavigableNodeClick = (0, import_react21.useCallback)((node) => {
20948
+ const handleNavigableNodeClick = (0, import_react23.useCallback)((node) => {
20545
20949
  const frame2 = {
20546
20950
  parentCanvasRef: currentCanvasRef,
20547
20951
  parentNodeRect: {
@@ -20568,7 +20972,7 @@ var SystemCanvas = (() => {
20568
20972
  navigateToRef(node);
20569
20973
  }
20570
20974
  }, [navigateToRef, currentCanvasRef, zoomNavConfig.enabled]);
20571
- const handleZoomEnter = (0, import_react21.useCallback)((node, targetTransform) => {
20975
+ const handleZoomEnter = (0, import_react23.useCallback)((node, targetTransform) => {
20572
20976
  const frame2 = {
20573
20977
  parentCanvasRef: currentCanvasRef,
20574
20978
  parentNodeRect: {
@@ -20582,7 +20986,7 @@ var SystemCanvas = (() => {
20582
20986
  setPendingHandoff(targetTransform);
20583
20987
  navigateToRef(node);
20584
20988
  }, [currentCanvasRef, navigateToRef]);
20585
- const handleZoomExit = (0, import_react21.useCallback)((targetTransform) => {
20989
+ const handleZoomExit = (0, import_react23.useCallback)((targetTransform) => {
20586
20990
  setPendingHandoff(targetTransform);
20587
20991
  suppressNextHandoffClearRef.current = true;
20588
20992
  navigateToBreadcrumb(breadcrumbs.length - 2);
@@ -20609,26 +21013,26 @@ var SystemCanvas = (() => {
20609
21013
  onEnter: handleZoomEnter,
20610
21014
  onExit: handleZoomExit
20611
21015
  });
20612
- const handleViewportChange = (0, import_react21.useCallback)((vp) => {
21016
+ const handleViewportChange = (0, import_react23.useCallback)((vp) => {
20613
21017
  viewportStateRef.current = vp;
20614
21018
  handleZoomNavViewportChange(vp);
20615
21019
  onViewportChange?.(vp);
20616
21020
  }, [handleZoomNavViewportChange, onViewportChange]);
20617
- const handleHandoffApplied = (0, import_react21.useCallback)(() => {
21021
+ const handleHandoffApplied = (0, import_react23.useCallback)(() => {
20618
21022
  setPendingHandoff(null);
20619
21023
  clearZoomNavCommitting();
20620
21024
  }, [clearZoomNavCommitting]);
20621
- const handleBeginEdit = (0, import_react21.useCallback)((node) => {
21025
+ const handleBeginEdit = (0, import_react23.useCallback)((node) => {
20622
21026
  setEditingId(node.id);
20623
21027
  }, []);
20624
- const handleBeginEditEdge = (0, import_react21.useCallback)((edge) => {
21028
+ const handleBeginEditEdge = (0, import_react23.useCallback)((edge) => {
20625
21029
  setEditingEdgeId(edge.id);
20626
21030
  }, []);
20627
- const [contextMenuState, setContextMenuState] = (0, import_react21.useState)(null);
20628
- (0, import_react21.useEffect)(() => {
21031
+ const [contextMenuState, setContextMenuState] = (0, import_react23.useState)(null);
21032
+ (0, import_react23.useEffect)(() => {
20629
21033
  setContextMenuState(null);
20630
21034
  }, [currentCanvasRef]);
20631
- const handleContextMenu = (0, import_react21.useCallback)((event) => {
21035
+ const handleContextMenu = (0, import_react23.useCallback)((event) => {
20632
21036
  onContextMenu?.(event);
20633
21037
  if (!nodeContextMenu)
20634
21038
  return;
@@ -20660,32 +21064,38 @@ var SystemCanvas = (() => {
20660
21064
  onNavigableNodeClick: handleNavigableNodeClick,
20661
21065
  viewport: viewportStateRef,
20662
21066
  editable,
20663
- onSelect: setSelectedId,
21067
+ onSelect: (id2) => {
21068
+ if (id2)
21069
+ selectNode(id2);
21070
+ else
21071
+ clearSelection();
21072
+ },
21073
+ onToggleSelect: toggleNode,
20664
21074
  onBeginEdit: handleBeginEdit,
20665
21075
  onSelectEdge: setSelectedEdgeId,
20666
21076
  onBeginEditEdge: handleBeginEditEdge
20667
21077
  });
20668
- const handleEditorCommit = (0, import_react21.useCallback)((patch) => {
21078
+ const handleEditorCommit = (0, import_react23.useCallback)((patch) => {
20669
21079
  if (editingId) {
20670
21080
  onNodeUpdate?.(editingId, patch, currentCanvasRef);
20671
21081
  }
20672
21082
  setEditingId(null);
20673
21083
  }, [editingId, onNodeUpdate, currentCanvasRef]);
20674
- const handleEditorCancel = (0, import_react21.useCallback)(() => {
21084
+ const handleEditorCancel = (0, import_react23.useCallback)(() => {
20675
21085
  setEditingId(null);
20676
21086
  }, []);
20677
- const handleEdgeEditorCommit = (0, import_react21.useCallback)((patch) => {
21087
+ const handleEdgeEditorCommit = (0, import_react23.useCallback)((patch) => {
20678
21088
  if (editingEdgeId) {
20679
21089
  onEdgeUpdate?.(editingEdgeId, patch, currentCanvasRef);
20680
21090
  }
20681
21091
  setEditingEdgeId(null);
20682
21092
  }, [editingEdgeId, onEdgeUpdate, currentCanvasRef]);
20683
- const handleEdgeEditorCancel = (0, import_react21.useCallback)(() => {
21093
+ const handleEdgeEditorCancel = (0, import_react23.useCallback)(() => {
20684
21094
  setEditingEdgeId(null);
20685
21095
  }, []);
20686
- const lastAddRef = (0, import_react21.useRef)(null);
20687
- const menuOptions = (0, import_react21.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
20688
- const addNode2 = (0, import_react21.useCallback)((option, position) => {
21096
+ const lastAddRef = (0, import_react23.useRef)(null);
21097
+ const menuOptions = (0, import_react23.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
21098
+ const addNode2 = (0, import_react23.useCallback)((option, position) => {
20689
21099
  let x, y;
20690
21100
  if (position) {
20691
21101
  x = position.x;
@@ -20717,23 +21127,33 @@ var SystemCanvas = (() => {
20717
21127
  const node = createNodeFromOption(option, Math.round(x), Math.round(y), void 0, theme);
20718
21128
  onNodeAdd?.(node, currentCanvasRef);
20719
21129
  }, [onNodeAdd, currentCanvasRef, theme]);
20720
- const handleKeyDown = (0, import_react21.useCallback)((e) => {
21130
+ const handleKeyDown = (0, import_react23.useCallback)((e) => {
20721
21131
  if (!editable)
20722
21132
  return;
20723
21133
  if (e.key === "Escape") {
20724
21134
  setEditingId(null);
20725
- setSelectedId(null);
21135
+ clearSelection();
20726
21136
  setEditingEdgeId(null);
20727
21137
  setSelectedEdgeId(null);
20728
21138
  return;
20729
21139
  }
20730
21140
  if (editingId || editingEdgeId)
20731
21141
  return;
21142
+ if ((e.metaKey || e.ctrlKey) && e.key === "a") {
21143
+ e.preventDefault();
21144
+ selectAll();
21145
+ return;
21146
+ }
20732
21147
  if (e.key === "Delete" || e.key === "Backspace") {
20733
- if (selectedId) {
21148
+ if (selectedIds.size > 1) {
21149
+ e.preventDefault();
21150
+ onNodesDelete?.(Array.from(selectedIds), currentCanvasRef);
21151
+ clearSelection();
21152
+ } else if (selectedIds.size === 1) {
21153
+ const id2 = Array.from(selectedIds)[0];
20734
21154
  e.preventDefault();
20735
- onNodeDelete?.(selectedId, currentCanvasRef);
20736
- setSelectedId(null);
21155
+ onNodeDelete?.(id2, currentCanvasRef);
21156
+ clearSelection();
20737
21157
  } else if (selectedEdgeId) {
20738
21158
  e.preventDefault();
20739
21159
  onEdgeDelete?.(selectedEdgeId, currentCanvasRef);
@@ -20744,11 +21164,14 @@ var SystemCanvas = (() => {
20744
21164
  editable,
20745
21165
  editingId,
20746
21166
  editingEdgeId,
20747
- selectedId,
21167
+ selectedIds,
20748
21168
  selectedEdgeId,
20749
21169
  onNodeDelete,
21170
+ onNodesDelete,
20750
21171
  onEdgeDelete,
20751
- currentCanvasRef
21172
+ currentCanvasRef,
21173
+ clearSelection,
21174
+ selectAll
20752
21175
  ]);
20753
21176
  const renderProps = { options: menuOptions, addNode: addNode2, theme };
20754
21177
  return (0, import_jsx_runtime28.jsxs)("div", { ref: containerRef, className: `system-canvas ${className ?? ""}`, tabIndex: editable ? 0 : -1, onKeyDown: handleKeyDown, style: {
@@ -20770,20 +21193,28 @@ var SystemCanvas = (() => {
20770
21193
  fontFamily: theme.node.fontFamily,
20771
21194
  fontSize: 12,
20772
21195
  backdropFilter: "blur(8px)"
20773
- }, children: "Loading..." }), (0, import_jsx_runtime28.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, canvases, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, autoFit, canvasRef: currentCanvasRef, handoffTransform: pendingHandoff, onHandoffApplied: handleHandoffApplied, handoffFadeMs: zoomNavConfig.fadeDuration, onViewportChange: handleViewportChange, onNodeClick: handleNodeClick, onNodeDoubleClick: handleNodeDoubleClick, onNodeNavigate: handleNodeNavigate, onEdgeClick: handleEdgeClick, onEdgeDoubleClick: handleEdgeDoubleClick, onCanvasClick: editable ? handleCanvasClick : void 0, onCanvasContextMenu: handleCanvasContextMenu, onNodeContextMenu: handleNodeContextMenu, onEdgeContextMenu: handleEdgeContextMenu, onNodePointerDown: editable ? onNodePointerDown : void 0, selectedId: editable ? selectedId : null, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, dropTargetId, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable }), showLaneHeaders && (0, import_jsx_runtime28.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedResolvedNode && !editingId && (0, import_jsx_runtime28.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
21196
+ }, children: "Loading..." }), (0, import_jsx_runtime28.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, canvases, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, autoFit, canvasRef: currentCanvasRef, handoffTransform: pendingHandoff, onHandoffApplied: handleHandoffApplied, handoffFadeMs: zoomNavConfig.fadeDuration, onViewportChange: handleViewportChange, onNodeClick: handleNodeClick, onNodeDoubleClick: handleNodeDoubleClick, onNodeNavigate: handleNodeNavigate, onEdgeClick: handleEdgeClick, onEdgeDoubleClick: handleEdgeDoubleClick, onCanvasClick: editable ? handleCanvasClick : void 0, onCanvasContextMenu: handleCanvasContextMenu, onNodeContextMenu: handleNodeContextMenu, onEdgeContextMenu: handleEdgeContextMenu, onNodePointerDown: editable ? onNodePointerDown : void 0, selectedIds: editable ? selectedIds : void 0, marqueeRect: editable ? marqueeRect : null, marqueeActiveRef: editable ? marqueeActiveRef : void 0, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, dropTargetId, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable }), showLaneHeaders && (0, import_jsx_runtime28.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedIds.size === 1 && selectedResolvedNode && !editingId && (0, import_jsx_runtime28.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
20774
21197
  onNodeUpdate?.(selectedResolvedNode.id, update, currentCanvasRef);
20775
21198
  }, onDelete: () => {
20776
21199
  onNodeDelete?.(selectedResolvedNode.id, currentCanvasRef);
20777
- setSelectedId(null);
20778
- }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime28.jsx)(AddNodeButton, { ...renderProps })), nodeContextMenu && (0, import_jsx_runtime28.jsx)(NodeContextMenuOverlay, { state: contextMenuState, config: nodeContextMenu, theme, onClose: () => setContextMenuState(null) })] });
21200
+ clearSelection();
21201
+ }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && showNodeToolbar && selectedIds.size > 1 && !editingId && (() => {
21202
+ const selectedResolvedNodes = Array.from(selectedIds).map((id2) => nodeMap.get(id2)).filter((n) => n != null);
21203
+ if (selectedResolvedNodes.length === 0)
21204
+ return null;
21205
+ const anchorNode = selectedResolvedNodes[0];
21206
+ return (0, import_jsx_runtime28.jsx)(NodeToolbar, { node: anchorNode, selectedNodes: selectedResolvedNodes, theme, onPatch: () => {
21207
+ }, onMultiPatch: (patch) => {
21208
+ for (const id2 of selectedIds) {
21209
+ onNodeUpdate?.(id2, patch, currentCanvasRef);
21210
+ }
21211
+ }, onDelete: () => {
21212
+ onNodesDelete?.(Array.from(selectedIds), currentCanvasRef);
21213
+ clearSelection();
21214
+ }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height });
21215
+ })(), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime28.jsx)(AddNodeButton, { ...renderProps })), nodeContextMenu && (0, import_jsx_runtime28.jsx)(NodeContextMenuOverlay, { state: contextMenuState, config: nodeContextMenu, theme, onClose: () => setContextMenuState(null) })] });
20779
21216
  });
20780
21217
 
20781
- // ../react/dist/hooks/useMultiSelect.js
20782
- var import_react22 = __toESM(require_react(), 1);
20783
-
20784
- // ../react/dist/hooks/useMultiSelectClipboard.js
20785
- var import_react23 = __toESM(require_react(), 1);
20786
-
20787
21218
  // src/index.tsx
20788
21219
  function resolveThemeOption(theme) {
20789
21220
  if (typeof theme === "string") {