system-canvas-standalone 0.2.1 → 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.
@@ -12759,12 +12759,12 @@ var SystemCanvas = (() => {
12759
12759
  render: () => render,
12760
12760
  themes: () => themes
12761
12761
  });
12762
- var import_react22 = __toESM(require_react(), 1);
12762
+ var import_react24 = __toESM(require_react(), 1);
12763
12763
  var import_client = __toESM(require_client(), 1);
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 = {
@@ -14649,15 +14649,19 @@ var SystemCanvas = (() => {
14649
14649
  // ../react/dist/hooks/useCanvasInteraction.js
14650
14650
  var import_react2 = __toESM(require_react(), 1);
14651
14651
  function useCanvasInteraction(options) {
14652
- const { onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, onNavigableNodeClick, viewport, editable, onSelect, onBeginEdit, onSelectEdge, onBeginEditEdge } = options;
14652
+ const { onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, onNavigableNodeClick, viewport, editable, onSelect, onToggleSelect, onBeginEdit, onSelectEdge, onBeginEditEdge } = options;
14653
14653
  const handleNodeClick = (0, import_react2.useCallback)((node, event) => {
14654
14654
  event.stopPropagation();
14655
14655
  if (editable) {
14656
- onSelect?.(node.id);
14657
- onSelectEdge?.(null);
14656
+ if (event.shiftKey && onToggleSelect) {
14657
+ onToggleSelect(node.id);
14658
+ } else {
14659
+ onSelect?.(node.id);
14660
+ onSelectEdge?.(null);
14661
+ }
14658
14662
  }
14659
14663
  onNodeClick?.(node);
14660
- }, [editable, onNodeClick, onSelect, onSelectEdge]);
14664
+ }, [editable, onNodeClick, onSelect, onToggleSelect, onSelectEdge]);
14661
14665
  const handleNodeDoubleClick = (0, import_react2.useCallback)((node, event) => {
14662
14666
  event.stopPropagation();
14663
14667
  onNodeDoubleClick?.(node);
@@ -14736,7 +14740,7 @@ var SystemCanvas = (() => {
14736
14740
  var import_react3 = __toESM(require_react(), 1);
14737
14741
  var DRAG_THRESHOLD = 3;
14738
14742
  function useNodeDrag(options) {
14739
- const { viewport, nodesRef, onCommit, svgRef, canDropNodeOn, onNodeDrop } = options;
14743
+ const { viewport, nodesRef, onCommit, svgRef, canDropNodeOn, onNodeDrop, selectedIdsRef } = options;
14740
14744
  const [dragOverrides, setDragOverrides] = (0, import_react3.useState)(() => /* @__PURE__ */ new Map());
14741
14745
  const [isDragging, setIsDragging] = (0, import_react3.useState)(false);
14742
14746
  const [dropTargetId, setDropTargetId] = (0, import_react3.useState)(null);
@@ -14748,6 +14752,8 @@ var SystemCanvas = (() => {
14748
14752
  onNodeDropRef.current = onNodeDrop;
14749
14753
  const svgRefRef = (0, import_react3.useRef)(svgRef);
14750
14754
  svgRefRef.current = svgRef;
14755
+ const emptySetRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
14756
+ const effectiveSelectedIdsRef = selectedIdsRef ?? emptySetRef;
14751
14757
  const dropTargetIdRef = (0, import_react3.useRef)(null);
14752
14758
  const computeDropTarget = (0, import_react3.useCallback)((clientX, clientY) => {
14753
14759
  const cb = canDropNodeOnRef.current;
@@ -14869,12 +14875,22 @@ var SystemCanvas = (() => {
14869
14875
  return;
14870
14876
  event.stopPropagation();
14871
14877
  const moving = /* @__PURE__ */ new Map();
14872
- moving.set(node.id, { startX: node.x, startY: node.y });
14873
- if (node.type === "group" && nodesRef.current) {
14874
- const children2 = getGroupChildren(node, nodesRef.current);
14875
- for (const c of children2) {
14876
- if (!moving.has(c.id)) {
14877
- moving.set(c.id, { startX: c.x, startY: c.y });
14878
+ const sel = effectiveSelectedIdsRef.current;
14879
+ if (sel && sel.size > 1 && sel.has(node.id) && nodesRef.current) {
14880
+ for (const id2 of sel) {
14881
+ const n = nodesRef.current.find((r) => r.id === id2);
14882
+ if (n) {
14883
+ moving.set(id2, { startX: n.x, startY: n.y });
14884
+ }
14885
+ }
14886
+ } else {
14887
+ moving.set(node.id, { startX: node.x, startY: node.y });
14888
+ if (node.type === "group" && nodesRef.current) {
14889
+ const children2 = getGroupChildren(node, nodesRef.current);
14890
+ for (const c of children2) {
14891
+ if (!moving.has(c.id)) {
14892
+ moving.set(c.id, { startX: c.x, startY: c.y });
14893
+ }
14878
14894
  }
14879
14895
  }
14880
14896
  }
@@ -15134,8 +15150,305 @@ var SystemCanvas = (() => {
15134
15150
  return { pending, onHandlePointerDown };
15135
15151
  }
15136
15152
 
15137
- // ../react/dist/hooks/useZoomNavigation.js
15153
+ // ../react/dist/hooks/useMultiSelect.js
15138
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);
15139
15452
  function expandRect(rect, factor) {
15140
15453
  if (factor === 1)
15141
15454
  return rect;
@@ -15173,16 +15486,16 @@ var SystemCanvas = (() => {
15173
15486
  }
15174
15487
  function useZoomNavigation(options) {
15175
15488
  const { enabled, config, nodes, currentCanvas, parentFrame, canvases, onResolveCanvas, onSeedCanvas, theme, getViewportSize, getCursorScreenPos, onEnter, onExit } = options;
15176
- const committingRef = (0, import_react6.useRef)(false);
15177
- (0, import_react6.useEffect)(() => {
15489
+ const committingRef = (0, import_react8.useRef)(false);
15490
+ (0, import_react8.useEffect)(() => {
15178
15491
  committingRef.current = false;
15179
15492
  }, [currentCanvas]);
15180
- const exitArmedRef = (0, import_react6.useRef)(false);
15181
- (0, import_react6.useEffect)(() => {
15493
+ const exitArmedRef = (0, import_react8.useRef)(false);
15494
+ (0, import_react8.useEffect)(() => {
15182
15495
  exitArmedRef.current = false;
15183
15496
  }, [currentCanvas, parentFrame]);
15184
- const prefetchRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
15185
- 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) => {
15186
15499
  if (!onResolveCanvas)
15187
15500
  return;
15188
15501
  if (canvases?.[ref])
@@ -15200,7 +15513,7 @@ var SystemCanvas = (() => {
15200
15513
  state.loading = false;
15201
15514
  });
15202
15515
  }, [canvases, onResolveCanvas, onSeedCanvas]);
15203
- const handleViewportChange = (0, import_react6.useCallback)((vp) => {
15516
+ const handleViewportChange = (0, import_react8.useCallback)((vp) => {
15204
15517
  if (!enabled)
15205
15518
  return;
15206
15519
  if (committingRef.current)
@@ -15339,7 +15652,7 @@ var SystemCanvas = (() => {
15339
15652
  onEnter,
15340
15653
  onExit
15341
15654
  ]);
15342
- const clearCommitting = (0, import_react6.useCallback)(() => {
15655
+ const clearCommitting = (0, import_react8.useCallback)(() => {
15343
15656
  committingRef.current = false;
15344
15657
  }, []);
15345
15658
  return { handleViewportChange, clearCommitting };
@@ -15347,10 +15660,10 @@ var SystemCanvas = (() => {
15347
15660
 
15348
15661
  // ../react/dist/components/Viewport.js
15349
15662
  var import_jsx_runtime22 = __toESM(require_jsx_runtime(), 1);
15350
- var import_react15 = __toESM(require_react(), 1);
15663
+ var import_react17 = __toESM(require_react(), 1);
15351
15664
 
15352
15665
  // ../react/dist/hooks/useViewport.js
15353
- var import_react7 = __toESM(require_react(), 1);
15666
+ var import_react9 = __toESM(require_react(), 1);
15354
15667
 
15355
15668
  // ../../node_modules/d3-dispatch/src/dispatch.js
15356
15669
  var noop = { value: () => {
@@ -18092,19 +18405,21 @@ var SystemCanvas = (() => {
18092
18405
 
18093
18406
  // ../react/dist/hooks/useViewport.js
18094
18407
  function useViewport(options) {
18095
- const { minZoom, maxZoom, defaultViewport, onViewportChange } = options;
18096
- const svgRef = (0, import_react7.useRef)(null);
18097
- const groupRef = (0, import_react7.useRef)(null);
18098
- const viewport = (0, import_react7.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
18099
- const zoomBehaviorRef = (0, import_react7.useRef)(null);
18100
- const onViewportChangeRef = (0, import_react7.useRef)(onViewportChange);
18408
+ const { minZoom, maxZoom, defaultViewport, onViewportChange, marqueeActiveRef } = options;
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);
18101
18414
  onViewportChangeRef.current = onViewportChange;
18102
- (0, import_react7.useEffect)(() => {
18415
+ (0, import_react9.useEffect)(() => {
18103
18416
  const svg = svgRef.current;
18104
18417
  const group = groupRef.current;
18105
18418
  if (!svg || !group)
18106
18419
  return;
18107
18420
  const zoomBehavior = zoom_default2().scaleExtent([minZoom, maxZoom]).filter((event) => {
18421
+ if (marqueeActiveRef?.current && event.type !== "wheel")
18422
+ return false;
18108
18423
  if (event.button)
18109
18424
  return false;
18110
18425
  if (event.type === "wheel")
@@ -18137,7 +18452,7 @@ var SystemCanvas = (() => {
18137
18452
  selection2.on(".zoom", null);
18138
18453
  };
18139
18454
  }, [minZoom, maxZoom]);
18140
- const fitToContent = (0, import_react7.useCallback)((nodes, animate = true) => {
18455
+ const fitToContent = (0, import_react9.useCallback)((nodes, animate = true) => {
18141
18456
  const svg = svgRef.current;
18142
18457
  if (!svg || !zoomBehaviorRef.current || nodes.length === 0)
18143
18458
  return;
@@ -18150,13 +18465,13 @@ var SystemCanvas = (() => {
18150
18465
  select_default2(svg).call(zoomBehaviorRef.current.transform, t);
18151
18466
  }
18152
18467
  }, []);
18153
- const resetZoom = (0, import_react7.useCallback)(() => {
18468
+ const resetZoom = (0, import_react9.useCallback)(() => {
18154
18469
  const svg = svgRef.current;
18155
18470
  if (!svg || !zoomBehaviorRef.current)
18156
18471
  return;
18157
18472
  select_default2(svg).transition().duration(400).call(zoomBehaviorRef.current.transform, identity2);
18158
18473
  }, []);
18159
- const setTransform = (0, import_react7.useCallback)((transform2, options2) => {
18474
+ const setTransform = (0, import_react9.useCallback)((transform2, options2) => {
18160
18475
  const svg = svgRef.current;
18161
18476
  if (!svg || !zoomBehaviorRef.current)
18162
18477
  return;
@@ -18168,7 +18483,7 @@ var SystemCanvas = (() => {
18168
18483
  sel.call(zoomBehaviorRef.current.transform, t);
18169
18484
  }
18170
18485
  }, []);
18171
- const zoomToNode = (0, import_react7.useCallback)((node, onComplete, options2) => {
18486
+ const zoomToNode = (0, import_react9.useCallback)((node, onComplete, options2) => {
18172
18487
  const svg = svgRef.current;
18173
18488
  if (!svg || !zoomBehaviorRef.current) {
18174
18489
  onComplete?.();
@@ -18362,9 +18677,9 @@ var SystemCanvas = (() => {
18362
18677
 
18363
18678
  // ../react/dist/components/RefIndicator.js
18364
18679
  var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
18365
- var import_react8 = __toESM(require_react(), 1);
18680
+ var import_react10 = __toESM(require_react(), 1);
18366
18681
  function RefIndicator({ node, theme, nodeX, nodeY, nodeWidth, nodeHeight, strokeColor, strokeWidth, corner = "bottom-right", size: sizeProp, onNavigate }) {
18367
- const [hover, setHover] = (0, import_react8.useState)(false);
18682
+ const [hover, setHover] = (0, import_react10.useState)(false);
18368
18683
  const iconKind = theme.node.refIndicator.icon;
18369
18684
  if (iconKind === "none")
18370
18685
  return null;
@@ -18435,7 +18750,7 @@ var SystemCanvas = (() => {
18435
18750
 
18436
18751
  // ../react/dist/components/CategorySlotsLayer.js
18437
18752
  var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
18438
- var import_react10 = __toESM(require_react(), 1);
18753
+ var import_react12 = __toESM(require_react(), 1);
18439
18754
 
18440
18755
  // ../react/dist/primitives/NodeColorFill.js
18441
18756
  var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
@@ -18555,9 +18870,9 @@ var SystemCanvas = (() => {
18555
18870
 
18556
18871
  // ../react/dist/primitives/NodeText.js
18557
18872
  var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
18558
- var import_react9 = __toESM(require_react(), 1);
18873
+ var import_react11 = __toESM(require_react(), 1);
18559
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" }) {
18560
- const reactId = (0, import_react9.useId)();
18875
+ const reactId = (0, import_react11.useId)();
18561
18876
  const safeId = reactId.replace(/:/g, "");
18562
18877
  if (!value)
18563
18878
  return null;
@@ -18631,8 +18946,8 @@ var SystemCanvas = (() => {
18631
18946
  // ../react/dist/components/CategorySlotsLayer.js
18632
18947
  function CategorySlotsLayer({ node, theme, canvases, slots: slotsProp }) {
18633
18948
  const slots = slotsProp ?? getCategorySlots(node, theme);
18634
- const regions = (0, import_react10.useMemo)(() => computeCategorySlotRegions(node, theme, slots), [node, theme, slots]);
18635
- 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)();
18636
18951
  const clipId = `sc-edge-clip-${reactId.replace(/:/g, "")}`;
18637
18952
  if (!slots)
18638
18953
  return null;
@@ -18865,7 +19180,7 @@ var SystemCanvas = (() => {
18865
19180
 
18866
19181
  // ../react/dist/components/ResizeHandles.js
18867
19182
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18868
- var import_react11 = __toESM(require_react(), 1);
19183
+ var import_react13 = __toESM(require_react(), 1);
18869
19184
  var HANDLE_SIZE = 7;
18870
19185
  var CORNERS = [
18871
19186
  { corner: "nw", cursor: "nwse-resize", anchor: "nw" },
@@ -18876,7 +19191,7 @@ var SystemCanvas = (() => {
18876
19191
  var cornerInset = (cornerRadius) => Math.min(cornerRadius * 0.25, 3);
18877
19192
  function ResizeHandles({ node, theme, onHandlePointerDown }) {
18878
19193
  const { x, y, width, height } = node;
18879
- const [hoveredCorner, setHoveredCorner] = (0, import_react11.useState)(null);
19194
+ const [hoveredCorner, setHoveredCorner] = (0, import_react13.useState)(null);
18880
19195
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
18881
19196
  const i = cornerInset(node.resolvedCornerRadius);
18882
19197
  const anchorPos = (anchor) => {
@@ -18901,7 +19216,7 @@ var SystemCanvas = (() => {
18901
19216
  }
18902
19217
 
18903
19218
  // ../react/dist/components/NodeRenderer.js
18904
- 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 }) {
18905
19220
  const groups = nodes.filter((n) => n.type === "group");
18906
19221
  const others = nodes.filter((n) => n.type !== "group");
18907
19222
  const common = (node) => {
@@ -18917,7 +19232,7 @@ var SystemCanvas = (() => {
18917
19232
  onContextMenu,
18918
19233
  onNavigate,
18919
19234
  onPointerDown,
18920
- isSelected: selectedId === node.id,
19235
+ isSelected: selectedIds?.has(node.id) ?? false,
18921
19236
  isEditing: editingId === node.id,
18922
19237
  slots,
18923
19238
  canvases,
@@ -18928,7 +19243,8 @@ var SystemCanvas = (() => {
18928
19243
  refCorner
18929
19244
  };
18930
19245
  };
18931
- 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;
18932
19248
  const renderResizeHandles = only !== "groups" && selectedNode && onResizeHandlePointerDown;
18933
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) => {
18934
19250
  const Component = getNodeComponent(node.type);
@@ -18986,7 +19302,7 @@ var SystemCanvas = (() => {
18986
19302
 
18987
19303
  // ../react/dist/components/NodeEditor.js
18988
19304
  var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
18989
- var import_react12 = __toESM(require_react(), 1);
19305
+ var import_react14 = __toESM(require_react(), 1);
18990
19306
  function NodeEditor({ node, theme, onCommit, onCancel }) {
18991
19307
  const editableFields = useCategoryFields(node, theme);
18992
19308
  if (editableFields) {
@@ -19005,11 +19321,11 @@ var SystemCanvas = (() => {
19005
19321
  }
19006
19322
  function SingleFieldEditor({ node, theme, onCommit, onCancel }) {
19007
19323
  const initial = getInitialValue(node);
19008
- const [value, setValue] = (0, import_react12.useState)(initial);
19009
- const textareaRef = (0, import_react12.useRef)(null);
19010
- const inputRef = (0, import_react12.useRef)(null);
19011
- const committedRef = (0, import_react12.useRef)(false);
19012
- (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)(() => {
19013
19329
  const el = textareaRef.current ?? inputRef.current;
19014
19330
  if (el) {
19015
19331
  el.focus();
@@ -19105,10 +19421,10 @@ var SystemCanvas = (() => {
19105
19421
  }
19106
19422
  }
19107
19423
  function FormEditor({ node, theme, fields, onCommit, onCancel }) {
19108
- const initial = (0, import_react12.useMemo)(() => readInitialValues(node, fields), [node, fields]);
19109
- const [values, setValues] = (0, import_react12.useState)(initial);
19110
- const committedRef = (0, import_react12.useRef)(false);
19111
- 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);
19112
19428
  const width = Math.max(node.width, 240);
19113
19429
  const height = Math.max(node.height, 36 + fields.length * 44);
19114
19430
  const commit = () => {
@@ -19137,7 +19453,7 @@ var SystemCanvas = (() => {
19137
19453
  const stopPointer = (e) => {
19138
19454
  e.stopPropagation();
19139
19455
  };
19140
- (0, import_react12.useEffect)(() => {
19456
+ (0, import_react14.useEffect)(() => {
19141
19457
  const el = panelRef.current;
19142
19458
  if (!el)
19143
19459
  return;
@@ -19267,13 +19583,13 @@ var SystemCanvas = (() => {
19267
19583
 
19268
19584
  // ../react/dist/components/EdgeLabelEditor.js
19269
19585
  var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
19270
- var import_react13 = __toESM(require_react(), 1);
19586
+ var import_react15 = __toESM(require_react(), 1);
19271
19587
  var EDITOR_WIDTH = 110;
19272
19588
  function EdgeLabelEditor({ initialLabel, midpoint, theme, onCommit, onCancel }) {
19273
- const [value, setValue] = (0, import_react13.useState)(initialLabel);
19274
- const inputRef = (0, import_react13.useRef)(null);
19275
- const committedRef = (0, import_react13.useRef)(false);
19276
- (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)(() => {
19277
19593
  const el = inputRef.current;
19278
19594
  if (el) {
19279
19595
  el.focus();
@@ -19328,7 +19644,7 @@ var SystemCanvas = (() => {
19328
19644
 
19329
19645
  // ../react/dist/components/ConnectionHandles.js
19330
19646
  var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
19331
- var import_react14 = __toESM(require_react(), 1);
19647
+ var import_react16 = __toESM(require_react(), 1);
19332
19648
  var SIDES = ["top", "right", "bottom", "left"];
19333
19649
  var HANDLE_RADIUS = 4;
19334
19650
  var HANDLE_HIT_RADIUS = 10;
@@ -19337,9 +19653,9 @@ var SystemCanvas = (() => {
19337
19653
  var HOVER_SCALE = 1.42;
19338
19654
  var HOVER_TRANSITION_MS = 120;
19339
19655
  function ConnectionHandles({ node, theme, onHandlePointerDown, immediate, activeSide }) {
19340
- const [visible, setVisible] = (0, import_react14.useState)(!!immediate);
19341
- const [hoveredSide, setHoveredSide] = (0, import_react14.useState)(null);
19342
- (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)(() => {
19343
19659
  if (immediate) {
19344
19660
  setVisible(true);
19345
19661
  return;
@@ -19426,17 +19742,18 @@ var SystemCanvas = (() => {
19426
19742
  // ../react/dist/components/Viewport.js
19427
19743
  var HOVER_PADDING = 10;
19428
19744
  var EDGE_PROXIMITY = 16;
19429
- 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) {
19430
19746
  const { svgRef, groupRef, viewport, fitToContent, zoomToNode, setTransform } = useViewport({
19431
19747
  minZoom,
19432
19748
  maxZoom,
19433
19749
  defaultViewport,
19434
- onViewportChange
19750
+ onViewportChange,
19751
+ marqueeActiveRef
19435
19752
  });
19436
- const navigatingRef = (0, import_react15.useRef)(false);
19437
- const fadeRafRef = (0, import_react15.useRef)(null);
19438
- const fadeTimeoutRef = (0, import_react15.useRef)(null);
19439
- 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) => {
19440
19757
  if (durationMs <= 0)
19441
19758
  return;
19442
19759
  const g = groupRef.current;
@@ -19459,7 +19776,7 @@ var SystemCanvas = (() => {
19459
19776
  }, durationMs + 16);
19460
19777
  });
19461
19778
  }, []);
19462
- (0, import_react15.useEffect)(() => {
19779
+ (0, import_react17.useEffect)(() => {
19463
19780
  return () => {
19464
19781
  if (fadeRafRef.current !== null)
19465
19782
  cancelAnimationFrame(fadeRafRef.current);
@@ -19467,10 +19784,10 @@ var SystemCanvas = (() => {
19467
19784
  clearTimeout(fadeTimeoutRef.current);
19468
19785
  };
19469
19786
  }, []);
19470
- const [hoveredNodeId, setHoveredNodeId] = (0, import_react15.useState)(null);
19471
- const [hoveredSide, setHoveredSide] = (0, import_react15.useState)(null);
19472
- const cursorPosRef = (0, import_react15.useRef)(null);
19473
- (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, () => ({
19474
19791
  zoomToNode: (node, onComplete, options) => {
19475
19792
  navigatingRef.current = true;
19476
19793
  zoomToNode(node, onComplete, options);
@@ -19481,7 +19798,7 @@ var SystemCanvas = (() => {
19481
19798
  getViewport: () => viewport.current ?? { x: 0, y: 0, zoom: 1 },
19482
19799
  getCursorScreenPos: () => cursorPosRef.current
19483
19800
  }));
19484
- const renderNodes = (0, import_react15.useMemo)(() => {
19801
+ const renderNodes = (0, import_react17.useMemo)(() => {
19485
19802
  const hasDrag = dragOverrides && dragOverrides.size > 0;
19486
19803
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
19487
19804
  if (!hasDrag && !hasResize)
@@ -19494,7 +19811,7 @@ var SystemCanvas = (() => {
19494
19811
  return d ? { ...n, x: d.x, y: d.y } : n;
19495
19812
  });
19496
19813
  }, [nodes, dragOverrides, resizeOverrides]);
19497
- const renderNodeMap = (0, import_react15.useMemo)(() => {
19814
+ const renderNodeMap = (0, import_react17.useMemo)(() => {
19498
19815
  const hasDrag = dragOverrides && dragOverrides.size > 0;
19499
19816
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
19500
19817
  if (!hasDrag && !hasResize)
@@ -19505,11 +19822,11 @@ var SystemCanvas = (() => {
19505
19822
  }
19506
19823
  return m;
19507
19824
  }, [renderNodes, nodeMap, dragOverrides, resizeOverrides]);
19508
- const latestNodesRef = (0, import_react15.useRef)(nodes);
19509
- (0, import_react15.useEffect)(() => {
19825
+ const latestNodesRef = (0, import_react17.useRef)(nodes);
19826
+ (0, import_react17.useEffect)(() => {
19510
19827
  latestNodesRef.current = nodes;
19511
19828
  }, [nodes]);
19512
- const fitNow = (0, import_react15.useCallback)(() => {
19829
+ const fitNow = (0, import_react17.useCallback)(() => {
19513
19830
  const current = latestNodesRef.current;
19514
19831
  if (current.length === 0)
19515
19832
  return;
@@ -19519,7 +19836,7 @@ var SystemCanvas = (() => {
19519
19836
  fitToContent(current, animate);
19520
19837
  });
19521
19838
  }, [fitToContent]);
19522
- (0, import_react15.useEffect)(() => {
19839
+ (0, import_react17.useEffect)(() => {
19523
19840
  if (defaultViewport)
19524
19841
  return;
19525
19842
  if (autoFit !== "always")
@@ -19528,8 +19845,8 @@ var SystemCanvas = (() => {
19528
19845
  return;
19529
19846
  fitNow();
19530
19847
  }, [nodes, autoFit, defaultViewport, fitNow]);
19531
- const fittedForRef = (0, import_react15.useRef)(null);
19532
- (0, import_react15.useEffect)(() => {
19848
+ const fittedForRef = (0, import_react17.useRef)(null);
19849
+ (0, import_react17.useEffect)(() => {
19533
19850
  if (defaultViewport)
19534
19851
  return;
19535
19852
  if (autoFit !== "canvas-change" && autoFit !== "initial")
@@ -19561,7 +19878,7 @@ var SystemCanvas = (() => {
19561
19878
  triggerFade
19562
19879
  ]);
19563
19880
  const editingNode = editingId ? renderNodes.find((n) => n.id === editingId) ?? null : null;
19564
- const handleSvgPointerMove = (0, import_react15.useCallback)((event) => {
19881
+ const handleSvgPointerMove = (0, import_react17.useCallback)((event) => {
19565
19882
  const svg = svgRef.current;
19566
19883
  if (!svg)
19567
19884
  return;
@@ -19613,7 +19930,7 @@ var SystemCanvas = (() => {
19613
19930
  setHoveredSide((prev) => prev === null ? prev : null);
19614
19931
  }
19615
19932
  }, [edgeCreateEnabled, renderNodes, svgRef, viewport]);
19616
- const handleSvgPointerLeave = (0, import_react15.useCallback)(() => {
19933
+ const handleSvgPointerLeave = (0, import_react17.useCallback)(() => {
19617
19934
  setHoveredNodeId(null);
19618
19935
  setHoveredSide(null);
19619
19936
  cursorPosRef.current = null;
@@ -19642,12 +19959,17 @@ var SystemCanvas = (() => {
19642
19959
  WebkitUserSelect: "none",
19643
19960
  MozUserSelect: "none",
19644
19961
  msUserSelect: "none"
19645
- }, 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" })] });
19646
19968
  });
19647
19969
 
19648
19970
  // ../react/dist/components/Breadcrumbs.js
19649
19971
  var import_jsx_runtime23 = __toESM(require_jsx_runtime(), 1);
19650
- var import_react16 = __toESM(require_react(), 1);
19972
+ var import_react18 = __toESM(require_react(), 1);
19651
19973
  function Breadcrumbs({ breadcrumbs, theme, onNavigate }) {
19652
19974
  if (breadcrumbs.length <= 1)
19653
19975
  return null;
@@ -19668,7 +19990,7 @@ var SystemCanvas = (() => {
19668
19990
  backdropFilter: "blur(8px)"
19669
19991
  }, children: breadcrumbs.map((crumb, index) => {
19670
19992
  const isLast = index === breadcrumbs.length - 1;
19671
- 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: {
19672
19994
  color: theme.separatorColor,
19673
19995
  margin: "0 2px"
19674
19996
  }, children: "/" }), (0, import_jsx_runtime23.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
@@ -19692,11 +20014,11 @@ var SystemCanvas = (() => {
19692
20014
 
19693
20015
  // ../react/dist/components/AddNodeButton.js
19694
20016
  var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1);
19695
- var import_react17 = __toESM(require_react(), 1);
20017
+ var import_react19 = __toESM(require_react(), 1);
19696
20018
  function AddNodeButton({ options, addNode: addNode2, theme }) {
19697
- const [open, setOpen] = (0, import_react17.useState)(false);
19698
- const rootRef = (0, import_react17.useRef)(null);
19699
- (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)(() => {
19700
20022
  if (!open)
19701
20023
  return;
19702
20024
  function onDocClick(e) {
@@ -19785,7 +20107,7 @@ var SystemCanvas = (() => {
19785
20107
  } });
19786
20108
  }
19787
20109
  function MenuRow({ theme, option, onClick }) {
19788
- const [hover, setHover] = (0, import_react17.useState)(false);
20110
+ const [hover, setHover] = (0, import_react19.useState)(false);
19789
20111
  const swatchSize = 18;
19790
20112
  return (0, import_jsx_runtime24.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
19791
20113
  display: "flex",
@@ -19821,12 +20143,12 @@ var SystemCanvas = (() => {
19821
20143
 
19822
20144
  // ../react/dist/components/LaneHeaders.js
19823
20145
  var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
19824
- var import_react18 = __toESM(require_react(), 1);
20146
+ var import_react20 = __toESM(require_react(), 1);
19825
20147
  function LaneHeaders({ columns, rows, theme, getViewport, width, height, pinned = true }) {
19826
20148
  const hasColumns = columns && columns.length > 0;
19827
20149
  const hasRows = rows && rows.length > 0;
19828
- const [viewport, setViewport] = (0, import_react18.useState)(() => getViewport());
19829
- (0, import_react18.useEffect)(() => {
20150
+ const [viewport, setViewport] = (0, import_react20.useState)(() => getViewport());
20151
+ (0, import_react20.useEffect)(() => {
19830
20152
  if (!hasColumns && !hasRows)
19831
20153
  return;
19832
20154
  let raf = 0;
@@ -19900,7 +20222,7 @@ var SystemCanvas = (() => {
19900
20222
 
19901
20223
  // ../react/dist/components/NodeToolbar.js
19902
20224
  var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
19903
- var import_react19 = __toESM(require_react(), 1);
20225
+ var import_react21 = __toESM(require_react(), 1);
19904
20226
  var NODE_GAP = 10;
19905
20227
  var FLIP_MARGIN = 8;
19906
20228
  var PADDING = 6;
@@ -19908,9 +20230,9 @@ var SystemCanvas = (() => {
19908
20230
  var SWATCH_SIZE = 16;
19909
20231
  var BUTTON_SIZE = 28;
19910
20232
  var DELETE_SIZE = 14;
19911
- function NodeToolbar({ node, theme, onPatch, onDelete, getViewport, containerWidth, containerHeight, render: render2 }) {
19912
- const [viewport, setViewport] = (0, import_react19.useState)(() => getViewport());
19913
- (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)(() => {
19914
20236
  let raf = 0;
19915
20237
  let lastX = -Infinity;
19916
20238
  let lastY = -Infinity;
@@ -19928,13 +20250,28 @@ var SystemCanvas = (() => {
19928
20250
  raf = requestAnimationFrame(tick);
19929
20251
  return () => cancelAnimationFrame(raf);
19930
20252
  }, [getViewport]);
20253
+ const isMulti = selectedNodes != null && selectedNodes.length > 1;
19931
20254
  const align = theme.toolbarAlign ?? "center";
19932
- const anchorCanvasX = align === "left" ? node.x : align === "right" ? node.x + node.width : node.x + node.width / 2;
19933
- const topAnchor = canvasToScreen(anchorCanvasX, node.y, viewport);
19934
- const bottomAnchor = canvasToScreen(anchorCanvasX, node.y + node.height, viewport);
19935
- const toolbarRef = (0, import_react19.useRef)(null);
19936
- const [size, setSize] = (0, import_react19.useState)({ width: 0, height: 0 });
19937
- (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)(() => {
19938
20275
  const el = toolbarRef.current;
19939
20276
  if (!el)
19940
20277
  return;
@@ -19947,7 +20284,12 @@ var SystemCanvas = (() => {
19947
20284
  ro.observe(el);
19948
20285
  return () => ro.disconnect();
19949
20286
  }, []);
19950
- 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
+ }
19951
20293
  let top = topAnchor.y - size.height - NODE_GAP;
19952
20294
  if (top < FLIP_MARGIN) {
19953
20295
  top = bottomAnchor.y + NODE_GAP;
@@ -19984,13 +20326,51 @@ var SystemCanvas = (() => {
19984
20326
  userSelect: "none",
19985
20327
  whiteSpace: "nowrap"
19986
20328
  },
19987
- 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 })
19988
20330
  });
19989
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
+ }
19990
20370
  function DefaultToolbarContent({ node, theme, onPatch, onDelete }) {
19991
- const groups = (0, import_react19.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
20371
+ const groups = (0, import_react21.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
19992
20372
  const showDelete = theme.showToolbarDelete === true;
19993
- 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 })] })] });
19994
20374
  }
19995
20375
  function Divider2({ theme }) {
19996
20376
  return (0, import_jsx_runtime26.jsx)("div", { style: {
@@ -20057,9 +20437,9 @@ var SystemCanvas = (() => {
20057
20437
  } }) : (0, import_jsx_runtime26.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
20058
20438
  }
20059
20439
  function MenuGroup({ group, actions, node, theme, onPatch }) {
20060
- const [open, setOpen] = (0, import_react19.useState)(false);
20061
- const wrapRef = (0, import_react19.useRef)(null);
20062
- (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)(() => {
20063
20443
  if (!open)
20064
20444
  return;
20065
20445
  const onDown = (e) => {
@@ -20146,18 +20526,18 @@ var SystemCanvas = (() => {
20146
20526
 
20147
20527
  // ../react/dist/components/NodeContextMenuOverlay.js
20148
20528
  var import_jsx_runtime27 = __toESM(require_jsx_runtime(), 1);
20149
- var import_react20 = __toESM(require_react(), 1);
20529
+ var import_react22 = __toESM(require_react(), 1);
20150
20530
  var ESTIMATED_MENU_WIDTH = 200;
20151
20531
  var MIN_MENU_WIDTH = 160;
20152
20532
  var VIEWPORT_MARGIN = 8;
20153
20533
  function NodeContextMenuOverlay({ state, config, theme, onClose }) {
20154
- const rootRef = (0, import_react20.useRef)(null);
20155
- const [hoveredId, setHoveredId] = (0, import_react20.useState)(null);
20156
- (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)(() => {
20157
20537
  if (state)
20158
20538
  setHoveredId(null);
20159
20539
  }, [state]);
20160
- (0, import_react20.useEffect)(() => {
20540
+ (0, import_react22.useEffect)(() => {
20161
20541
  if (!state)
20162
20542
  return;
20163
20543
  function onDown(e) {
@@ -20265,8 +20645,8 @@ var SystemCanvas = (() => {
20265
20645
  // ../react/dist/components/SystemCanvas.js
20266
20646
  var CASCADE_WINDOW_MS = 1500;
20267
20647
  var CASCADE_OFFSET = 20;
20268
- 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) {
20269
- 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)(() => {
20270
20650
  const defaults = {
20271
20651
  enterThreshold: 0.66,
20272
20652
  exitThreshold: 0.33,
@@ -20291,16 +20671,16 @@ var SystemCanvas = (() => {
20291
20671
  }, [zoomNavigation]);
20292
20672
  const effectiveMaxZoom = maxZoom ?? (zoomNavConfig.enabled ? 16 : 4);
20293
20673
  const effectiveMinZoom = minZoomProp ?? (zoomNavConfig.enabled ? 0.01 : 0.1);
20294
- (0, import_react21.useEffect)(() => {
20674
+ (0, import_react23.useEffect)(() => {
20295
20675
  const env = globalThis.process?.env?.NODE_ENV;
20296
20676
  if (editable && !canvases && env !== "production") {
20297
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.");
20298
20678
  }
20299
20679
  }, [editable, canvases]);
20300
- const [parentFrames, setParentFrames] = (0, import_react21.useState)([]);
20301
- const [pendingHandoff, setPendingHandoff] = (0, import_react21.useState)(null);
20302
- const suppressNextHandoffClearRef = (0, import_react21.useRef)(false);
20303
- 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) => {
20304
20684
  setParentFrames((prev) => prev.slice(0, index));
20305
20685
  if (suppressNextHandoffClearRef.current) {
20306
20686
  suppressNextHandoffClearRef.current = false;
@@ -20317,10 +20697,10 @@ var SystemCanvas = (() => {
20317
20697
  onNavigate,
20318
20698
  onBreadcrumbClick: handleBreadcrumbClick
20319
20699
  });
20320
- (0, import_react21.useEffect)(() => {
20700
+ (0, import_react23.useEffect)(() => {
20321
20701
  onBreadcrumbsChange?.(breadcrumbs);
20322
20702
  }, [breadcrumbs, onBreadcrumbsChange]);
20323
- const theme = (0, import_react21.useMemo)(() => {
20703
+ const theme = (0, import_react23.useMemo)(() => {
20324
20704
  const registry = { ...themes, ...customThemes };
20325
20705
  const resolveByName = (name) => name && registry[name] ? registry[name] : null;
20326
20706
  if (themeProp) {
@@ -20331,22 +20711,22 @@ var SystemCanvas = (() => {
20331
20711
  }
20332
20712
  return resolveByName(currentCanvas.theme?.base) ?? resolveByName(canvas.theme?.base) ?? darkTheme;
20333
20713
  }, [themeProp, customThemes, currentCanvas.theme?.base, canvas.theme?.base]);
20334
- const { nodes, edges, nodeMap } = (0, import_react21.useMemo)(() => {
20714
+ const { nodes, edges, nodeMap } = (0, import_react23.useMemo)(() => {
20335
20715
  const resolved = resolveCanvas(currentCanvas, theme);
20336
20716
  const map = buildNodeMap(resolved.nodes);
20337
20717
  return { nodes: resolved.nodes, edges: resolved.edges, nodeMap: map };
20338
20718
  }, [currentCanvas, theme]);
20339
- const nodesRef = (0, import_react21.useRef)(nodes);
20719
+ const nodesRef = (0, import_react23.useRef)(nodes);
20340
20720
  nodesRef.current = nodes;
20341
- const viewportStateRef = (0, import_react21.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
20342
- const viewportHandleRef = (0, import_react21.useRef)(null);
20343
- 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);
20344
20724
  navigateToRefRef.current = navigateToRef;
20345
- const navigateToBreadcrumbRef = (0, import_react21.useRef)(navigateToBreadcrumb);
20725
+ const navigateToBreadcrumbRef = (0, import_react23.useRef)(navigateToBreadcrumb);
20346
20726
  navigateToBreadcrumbRef.current = navigateToBreadcrumb;
20347
- const breadcrumbsRef = (0, import_react21.useRef)(breadcrumbs);
20727
+ const breadcrumbsRef = (0, import_react23.useRef)(breadcrumbs);
20348
20728
  breadcrumbsRef.current = breadcrumbs;
20349
- (0, import_react21.useImperativeHandle)(forwardedRef, () => ({
20729
+ (0, import_react23.useImperativeHandle)(forwardedRef, () => ({
20350
20730
  zoomIntoNode: (nodeId, options) => {
20351
20731
  return new Promise((resolve) => {
20352
20732
  const node = nodesRef.current.find((n) => n.id === nodeId);
@@ -20378,26 +20758,64 @@ var SystemCanvas = (() => {
20378
20758
  navigateToBreadcrumbRef.current(0);
20379
20759
  }
20380
20760
  }), [forwardedRef]);
20381
- const [selectedId, setSelectedId] = (0, import_react21.useState)(null);
20382
- const [editingId, setEditingId] = (0, import_react21.useState)(null);
20383
- const [selectedEdgeId, setSelectedEdgeId] = (0, import_react21.useState)(null);
20384
- const [editingEdgeId, setEditingEdgeId] = (0, import_react21.useState)(null);
20385
- (0, import_react21.useEffect)(() => {
20386
- 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();
20387
20794
  setEditingId(null);
20388
20795
  setSelectedEdgeId(null);
20389
20796
  setEditingEdgeId(null);
20390
20797
  }, [currentCanvasRef]);
20391
- const onSelectionChangeRef = (0, import_react21.useRef)(onSelectionChange);
20392
- (0, import_react21.useEffect)(() => {
20798
+ const onSelectionChangeRef = (0, import_react23.useRef)(onSelectionChange);
20799
+ (0, import_react23.useEffect)(() => {
20393
20800
  onSelectionChangeRef.current = onSelectionChange;
20394
20801
  }, [onSelectionChange]);
20395
- const lastEmittedSelectionRef = (0, import_react21.useRef)({ kind: null, id: null, canvasRef: void 0 });
20396
- (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)(() => {
20397
20804
  const cb = onSelectionChangeRef.current;
20398
20805
  let next = null;
20399
- if (selectedId) {
20400
- 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);
20401
20819
  if (node)
20402
20820
  next = { kind: "node", node, canvasRef: currentCanvasRef };
20403
20821
  }
@@ -20408,21 +20826,24 @@ var SystemCanvas = (() => {
20408
20826
  }
20409
20827
  const last = lastEmittedSelectionRef.current;
20410
20828
  const nextKind = next?.kind ?? null;
20411
- const nextId = next ? next.kind === "node" ? next.node.id : next.edge.id : null;
20412
20829
  const nextRef = next?.canvasRef;
20413
- if (last.kind === nextKind && last.id === nextId && last.canvasRef === nextRef) {
20414
- 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 };
20415
20842
  }
20416
- lastEmittedSelectionRef.current = {
20417
- kind: nextKind,
20418
- id: nextId,
20419
- canvasRef: nextRef
20420
- };
20421
20843
  cb?.(next);
20422
- }, [selectedId, selectedEdgeId, currentCanvasRef, nodeMap, edges]);
20423
- const containerRef = (0, import_react21.useRef)(null);
20424
- const [containerSize, setContainerSize] = (0, import_react21.useState)({ width: 0, height: 0 });
20425
- (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)(() => {
20426
20847
  const el = containerRef.current;
20427
20848
  if (!el)
20428
20849
  return;
@@ -20437,8 +20858,8 @@ var SystemCanvas = (() => {
20437
20858
  }, []);
20438
20859
  const hasLanes = currentCanvas.columns && currentCanvas.columns.length > 0 || currentCanvas.rows && currentCanvas.rows.length > 0;
20439
20860
  const showLaneHeaders = hasLanes && laneHeaders !== "none";
20440
- const getViewportState = (0, import_react21.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
20441
- 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) => {
20442
20863
  if (!snapToLanes)
20443
20864
  return patch;
20444
20865
  const cols = currentCanvas.columns;
@@ -20463,10 +20884,10 @@ var SystemCanvas = (() => {
20463
20884
  }
20464
20885
  return final;
20465
20886
  }, [snapToLanes, currentCanvas.columns, currentCanvas.rows]);
20466
- const commitResize = (0, import_react21.useCallback)((id2, patch) => {
20887
+ const commitResize = (0, import_react23.useCallback)((id2, patch) => {
20467
20888
  onNodeUpdate?.(id2, applyLaneSnap(id2, patch), currentCanvasRef);
20468
20889
  }, [onNodeUpdate, currentCanvasRef, applyLaneSnap]);
20469
- const commitDragBatch = (0, import_react21.useCallback)((updates) => {
20890
+ const commitDragBatch = (0, import_react23.useCallback)((updates) => {
20470
20891
  const final = updates.map((u) => ({
20471
20892
  id: u.id,
20472
20893
  patch: applyLaneSnap(u.id, u.patch)
@@ -20481,8 +20902,7 @@ var SystemCanvas = (() => {
20481
20902
  onNodeUpdate(id2, patch, currentCanvasRef);
20482
20903
  }
20483
20904
  }, [onNodeUpdate, onNodesUpdate, currentCanvasRef, applyLaneSnap]);
20484
- const svgProxyRef = (0, import_react21.useRef)(null);
20485
- const handleNodeDrop = (0, import_react21.useCallback)((sources, target) => {
20905
+ const handleNodeDrop = (0, import_react23.useCallback)((sources, target) => {
20486
20906
  onNodeDrop?.(sources, target, { canvasRef: currentCanvasRef });
20487
20907
  }, [onNodeDrop, currentCanvasRef]);
20488
20908
  const { dragOverrides, dropTargetId, onPointerDown: onNodePointerDown } = useNodeDrag({
@@ -20491,30 +20911,32 @@ var SystemCanvas = (() => {
20491
20911
  onCommit: commitDragBatch,
20492
20912
  svgRef: svgProxyRef,
20493
20913
  canDropNodeOn,
20494
- onNodeDrop: handleNodeDrop
20914
+ onNodeDrop: handleNodeDrop,
20915
+ selectedIdsRef
20495
20916
  });
20496
20917
  const { resizeOverrides, onHandlePointerDown: onResizeHandlePointerDown } = useNodeResize({
20497
20918
  viewport: viewportStateRef,
20498
20919
  onCommit: commitResize
20499
20920
  });
20500
- const selectedResolvedNode = (0, import_react21.useMemo)(() => {
20501
- if (!selectedId)
20921
+ const singleSelectedId = selectedIds.size === 1 ? Array.from(selectedIds)[0] : null;
20922
+ const selectedResolvedNode = (0, import_react23.useMemo)(() => {
20923
+ if (!singleSelectedId)
20502
20924
  return null;
20503
- const base = nodeMap.get(selectedId);
20925
+ const base = nodeMap.get(singleSelectedId);
20504
20926
  if (!base)
20505
20927
  return null;
20506
- const resize = resizeOverrides.get(selectedId);
20928
+ const resize = resizeOverrides.get(singleSelectedId);
20507
20929
  if (resize) {
20508
20930
  return { ...base, x: resize.x, y: resize.y, width: resize.width, height: resize.height };
20509
20931
  }
20510
- const drag = dragOverrides.get(selectedId);
20932
+ const drag = dragOverrides.get(singleSelectedId);
20511
20933
  if (drag) {
20512
20934
  return { ...base, x: drag.x, y: drag.y };
20513
20935
  }
20514
20936
  return base;
20515
- }, [selectedId, nodeMap, dragOverrides, resizeOverrides]);
20937
+ }, [singleSelectedId, nodeMap, dragOverrides, resizeOverrides]);
20516
20938
  svgProxyRef.current = viewportHandleRef.current?.getSvgElement() ?? null;
20517
- const handleEdgeCreated = (0, import_react21.useCallback)((edge) => {
20939
+ const handleEdgeCreated = (0, import_react23.useCallback)((edge) => {
20518
20940
  onEdgeAdd?.(edge, currentCanvasRef);
20519
20941
  }, [onEdgeAdd, currentCanvasRef]);
20520
20942
  const { pending: pendingEdge, onHandlePointerDown: onConnectionHandlePointerDown } = useEdgeCreate({
@@ -20523,7 +20945,7 @@ var SystemCanvas = (() => {
20523
20945
  nodesRef,
20524
20946
  onCreate: handleEdgeCreated
20525
20947
  });
20526
- const handleNavigableNodeClick = (0, import_react21.useCallback)((node) => {
20948
+ const handleNavigableNodeClick = (0, import_react23.useCallback)((node) => {
20527
20949
  const frame2 = {
20528
20950
  parentCanvasRef: currentCanvasRef,
20529
20951
  parentNodeRect: {
@@ -20550,7 +20972,7 @@ var SystemCanvas = (() => {
20550
20972
  navigateToRef(node);
20551
20973
  }
20552
20974
  }, [navigateToRef, currentCanvasRef, zoomNavConfig.enabled]);
20553
- const handleZoomEnter = (0, import_react21.useCallback)((node, targetTransform) => {
20975
+ const handleZoomEnter = (0, import_react23.useCallback)((node, targetTransform) => {
20554
20976
  const frame2 = {
20555
20977
  parentCanvasRef: currentCanvasRef,
20556
20978
  parentNodeRect: {
@@ -20564,7 +20986,7 @@ var SystemCanvas = (() => {
20564
20986
  setPendingHandoff(targetTransform);
20565
20987
  navigateToRef(node);
20566
20988
  }, [currentCanvasRef, navigateToRef]);
20567
- const handleZoomExit = (0, import_react21.useCallback)((targetTransform) => {
20989
+ const handleZoomExit = (0, import_react23.useCallback)((targetTransform) => {
20568
20990
  setPendingHandoff(targetTransform);
20569
20991
  suppressNextHandoffClearRef.current = true;
20570
20992
  navigateToBreadcrumb(breadcrumbs.length - 2);
@@ -20591,26 +21013,26 @@ var SystemCanvas = (() => {
20591
21013
  onEnter: handleZoomEnter,
20592
21014
  onExit: handleZoomExit
20593
21015
  });
20594
- const handleViewportChange = (0, import_react21.useCallback)((vp) => {
21016
+ const handleViewportChange = (0, import_react23.useCallback)((vp) => {
20595
21017
  viewportStateRef.current = vp;
20596
21018
  handleZoomNavViewportChange(vp);
20597
21019
  onViewportChange?.(vp);
20598
21020
  }, [handleZoomNavViewportChange, onViewportChange]);
20599
- const handleHandoffApplied = (0, import_react21.useCallback)(() => {
21021
+ const handleHandoffApplied = (0, import_react23.useCallback)(() => {
20600
21022
  setPendingHandoff(null);
20601
21023
  clearZoomNavCommitting();
20602
21024
  }, [clearZoomNavCommitting]);
20603
- const handleBeginEdit = (0, import_react21.useCallback)((node) => {
21025
+ const handleBeginEdit = (0, import_react23.useCallback)((node) => {
20604
21026
  setEditingId(node.id);
20605
21027
  }, []);
20606
- const handleBeginEditEdge = (0, import_react21.useCallback)((edge) => {
21028
+ const handleBeginEditEdge = (0, import_react23.useCallback)((edge) => {
20607
21029
  setEditingEdgeId(edge.id);
20608
21030
  }, []);
20609
- const [contextMenuState, setContextMenuState] = (0, import_react21.useState)(null);
20610
- (0, import_react21.useEffect)(() => {
21031
+ const [contextMenuState, setContextMenuState] = (0, import_react23.useState)(null);
21032
+ (0, import_react23.useEffect)(() => {
20611
21033
  setContextMenuState(null);
20612
21034
  }, [currentCanvasRef]);
20613
- const handleContextMenu = (0, import_react21.useCallback)((event) => {
21035
+ const handleContextMenu = (0, import_react23.useCallback)((event) => {
20614
21036
  onContextMenu?.(event);
20615
21037
  if (!nodeContextMenu)
20616
21038
  return;
@@ -20642,32 +21064,38 @@ var SystemCanvas = (() => {
20642
21064
  onNavigableNodeClick: handleNavigableNodeClick,
20643
21065
  viewport: viewportStateRef,
20644
21066
  editable,
20645
- onSelect: setSelectedId,
21067
+ onSelect: (id2) => {
21068
+ if (id2)
21069
+ selectNode(id2);
21070
+ else
21071
+ clearSelection();
21072
+ },
21073
+ onToggleSelect: toggleNode,
20646
21074
  onBeginEdit: handleBeginEdit,
20647
21075
  onSelectEdge: setSelectedEdgeId,
20648
21076
  onBeginEditEdge: handleBeginEditEdge
20649
21077
  });
20650
- const handleEditorCommit = (0, import_react21.useCallback)((patch) => {
21078
+ const handleEditorCommit = (0, import_react23.useCallback)((patch) => {
20651
21079
  if (editingId) {
20652
21080
  onNodeUpdate?.(editingId, patch, currentCanvasRef);
20653
21081
  }
20654
21082
  setEditingId(null);
20655
21083
  }, [editingId, onNodeUpdate, currentCanvasRef]);
20656
- const handleEditorCancel = (0, import_react21.useCallback)(() => {
21084
+ const handleEditorCancel = (0, import_react23.useCallback)(() => {
20657
21085
  setEditingId(null);
20658
21086
  }, []);
20659
- const handleEdgeEditorCommit = (0, import_react21.useCallback)((patch) => {
21087
+ const handleEdgeEditorCommit = (0, import_react23.useCallback)((patch) => {
20660
21088
  if (editingEdgeId) {
20661
21089
  onEdgeUpdate?.(editingEdgeId, patch, currentCanvasRef);
20662
21090
  }
20663
21091
  setEditingEdgeId(null);
20664
21092
  }, [editingEdgeId, onEdgeUpdate, currentCanvasRef]);
20665
- const handleEdgeEditorCancel = (0, import_react21.useCallback)(() => {
21093
+ const handleEdgeEditorCancel = (0, import_react23.useCallback)(() => {
20666
21094
  setEditingEdgeId(null);
20667
21095
  }, []);
20668
- const lastAddRef = (0, import_react21.useRef)(null);
20669
- const menuOptions = (0, import_react21.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
20670
- 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) => {
20671
21099
  let x, y;
20672
21100
  if (position) {
20673
21101
  x = position.x;
@@ -20699,23 +21127,33 @@ var SystemCanvas = (() => {
20699
21127
  const node = createNodeFromOption(option, Math.round(x), Math.round(y), void 0, theme);
20700
21128
  onNodeAdd?.(node, currentCanvasRef);
20701
21129
  }, [onNodeAdd, currentCanvasRef, theme]);
20702
- const handleKeyDown = (0, import_react21.useCallback)((e) => {
21130
+ const handleKeyDown = (0, import_react23.useCallback)((e) => {
20703
21131
  if (!editable)
20704
21132
  return;
20705
21133
  if (e.key === "Escape") {
20706
21134
  setEditingId(null);
20707
- setSelectedId(null);
21135
+ clearSelection();
20708
21136
  setEditingEdgeId(null);
20709
21137
  setSelectedEdgeId(null);
20710
21138
  return;
20711
21139
  }
20712
21140
  if (editingId || editingEdgeId)
20713
21141
  return;
21142
+ if ((e.metaKey || e.ctrlKey) && e.key === "a") {
21143
+ e.preventDefault();
21144
+ selectAll();
21145
+ return;
21146
+ }
20714
21147
  if (e.key === "Delete" || e.key === "Backspace") {
20715
- 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];
20716
21154
  e.preventDefault();
20717
- onNodeDelete?.(selectedId, currentCanvasRef);
20718
- setSelectedId(null);
21155
+ onNodeDelete?.(id2, currentCanvasRef);
21156
+ clearSelection();
20719
21157
  } else if (selectedEdgeId) {
20720
21158
  e.preventDefault();
20721
21159
  onEdgeDelete?.(selectedEdgeId, currentCanvasRef);
@@ -20726,11 +21164,14 @@ var SystemCanvas = (() => {
20726
21164
  editable,
20727
21165
  editingId,
20728
21166
  editingEdgeId,
20729
- selectedId,
21167
+ selectedIds,
20730
21168
  selectedEdgeId,
20731
21169
  onNodeDelete,
21170
+ onNodesDelete,
20732
21171
  onEdgeDelete,
20733
- currentCanvasRef
21172
+ currentCanvasRef,
21173
+ clearSelection,
21174
+ selectAll
20734
21175
  ]);
20735
21176
  const renderProps = { options: menuOptions, addNode: addNode2, theme };
20736
21177
  return (0, import_jsx_runtime28.jsxs)("div", { ref: containerRef, className: `system-canvas ${className ?? ""}`, tabIndex: editable ? 0 : -1, onKeyDown: handleKeyDown, style: {
@@ -20752,12 +21193,26 @@ var SystemCanvas = (() => {
20752
21193
  fontFamily: theme.node.fontFamily,
20753
21194
  fontSize: 12,
20754
21195
  backdropFilter: "blur(8px)"
20755
- }, 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) => {
20756
21197
  onNodeUpdate?.(selectedResolvedNode.id, update, currentCanvasRef);
20757
21198
  }, onDelete: () => {
20758
21199
  onNodeDelete?.(selectedResolvedNode.id, currentCanvasRef);
20759
- setSelectedId(null);
20760
- }, 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) })] });
20761
21216
  });
20762
21217
 
20763
21218
  // src/index.tsx
@@ -20868,7 +21323,7 @@ var SystemCanvas = (() => {
20868
21323
  onEdgeUpdate: handleEdgeUpdate,
20869
21324
  onEdgeDelete: handleEdgeDelete
20870
21325
  };
20871
- root2.render(import_react22.default.createElement(SystemCanvas, props));
21326
+ root2.render(import_react24.default.createElement(SystemCanvas, props));
20872
21327
  };
20873
21328
  doRender();
20874
21329
  return {