@dschz/solid-flow 1.0.0-next.6 → 1.0.0-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter as getBezierEdgeCenter$1, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter as getEdgeCenter$1, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
1
+ import { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, Position as Position$1, ResizeControlVariant, SelectionMode as SelectionMode$1, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, addEdge as addEdge$1, areConnectionMapsEqual, calcAutoPan, calculateNodePosition, clampPosition, clampPositionToParent, createDevWarn, createMarkerIds, elementSelectionKeys, errorMessages, evaluateAbsolutePosition, fitViewport, getBezierEdgeCenter as getBezierEdgeCenter$1, getBezierPath, getBezierPath as getBezierPath$1, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter as getEdgeCenter$1, getEdgePosition, getEdgeToolbarTransform, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesBounds as getNodesBounds$1, getNodesInside, getOutgoers, getOverlappingArea, getSmoothStepPath, getSmoothStepPath as getSmoothStepPath$1, getStraightPath, getStraightPath as getStraightPath$1, getViewportForBounds, getViewportForBounds as getViewportForBounds$1, handleConnectionChange, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isInputDOMNode, isMacOs, isNodeBase, isNumeric, isRectObject, mergeAriaLabelConfig, nodeHasDimensions, nodeToRect, panBy, pointToRendererPoint, rendererPointToPoint, shallowNodeData, snapPosition } from "@xyflow/system";
2
2
  import { For, Show, createContext, createEffect, createMemo, createProjection, createSignal, createStore, flush, mapArray, merge, omit, onCleanup, onSettled, snapshot, untrack, useContext } from "solid-js";
3
3
  import { createMediaQuery } from "@solid-primitives/media";
4
4
  import { Dynamic, Portal, isServer } from "@solidjs/web";
@@ -65,6 +65,8 @@ function propDefaults(props, defaults) {
65
65
  const getEdgeId = (connection) => {
66
66
  let { source, sourceHandle, target, targetHandle } = connection;
67
67
  return `xy-edge__${source}${sourceHandle || ""}-${target}${targetHandle || ""}`;
68
+ }, isEdgeSelectable = (edge, store) => edge.selectable ?? store.defaultEdgeOptions.selectable ?? store.elementsSelectable, emitFlowError = (onError, id, message) => {
69
+ onError && onError(id, message);
68
70
  }, EdgeLabelRenderer = (props) => {
69
71
  let { store } = useInternalSolidFlow(), labelNode = () => store.domNode?.querySelector(".solid-flow__edge-labels");
70
72
  return <Show when={labelNode()}>{(root) => <Portal mount={root()}>{props.children}</Portal>}</Show>;
@@ -75,7 +77,7 @@ const getEdgeId = (connection) => {
75
77
  selectEdgeOnClick: !1,
76
78
  transparent: !1,
77
79
  style: {}
78
- }), rest = omit(_props, "x", "y", "width", "height", "selectEdgeOnClick", "transparent", "children", "class", "style"), { actions } = useInternalSolidFlow(), id = useEdgeId(), zIndex = () => actions.getEdge(id())?.zIndex;
80
+ }), rest = omit(_props, "x", "y", "width", "height", "selectEdgeOnClick", "transparent", "children", "class", "style"), { actions } = useInternalSolidFlow(), id = useEdgeId(), zIndex = () => actions.getLayoutedEdge(id())?.zIndex;
79
81
  return <EdgeLabelRenderer>
80
82
  <div role="button" tabindex={-1} class={[
81
83
  "solid-flow__edge-label",
@@ -145,6 +147,205 @@ const getEdgeId = (connection) => {
145
147
  };
146
148
  };
147
149
  return <BaseEdge path={pathData().path} labelX={pathData().labelX} labelY={pathData().labelY} label={props.label} labelStyle={props.labelStyle} markerStart={props.markerStart} markerEnd={props.markerEnd} interactionWidth={props.interactionWidth} style={props.style} />;
150
+ };
151
+ //#endregion
152
+ //#region src/core/spatial/grid.ts
153
+ /**
154
+ * A uniform spatial hash over axis-aligned rects — the plain, NON-reactive
155
+ * building block behind the flow's spatial queries (RFC-4239 dossier:
156
+ * gesture-scoped snapshots and epoch-rebuilt indexes; deliberately never a
157
+ * live-maintained reactive structure, which would re-create the round-6
158
+ * central-collection anti-pattern).
159
+ *
160
+ * Every operation is O(cells touched); with cellSize on the order of the
161
+ * query radius or median node size, inserts and queries touch O(1) cells.
162
+ * No balancing, no extent-known-up-front requirement, no dependency —
163
+ * upstream's own bake-off (quadtree vs BVH vs rbush) is why: fixed-radius
164
+ * neighborhood and rect-vs-box queries are the textbook grid case.
165
+ */
166
+ var SpatialGrid = class {
167
+ cellSize;
168
+ cells = /* @__PURE__ */ new Map();
169
+ rects = /* @__PURE__ */ new Map();
170
+ constructor(cellSize) {
171
+ this.cellSize = cellSize;
172
+ }
173
+ cellRange(rect) {
174
+ let size = this.cellSize;
175
+ return {
176
+ minX: Math.floor(rect.x / size),
177
+ maxX: Math.floor((rect.x + rect.width) / size),
178
+ minY: Math.floor(rect.y / size),
179
+ maxY: Math.floor((rect.y + rect.height) / size)
180
+ };
181
+ }
182
+ insert(id, rect) {
183
+ this.rects.set(id, rect);
184
+ let { minX, maxX, minY, maxY } = this.cellRange(rect);
185
+ for (let cx = minX; cx <= maxX; cx++) for (let cy = minY; cy <= maxY; cy++) {
186
+ let key = `${cx}:${cy}`, bucket = this.cells.get(key);
187
+ bucket ? bucket.push(id) : this.cells.set(key, [id]);
188
+ }
189
+ }
190
+ /** Ids of entries whose rect overlaps the query rect (touching counts). */
191
+ queryRect(query) {
192
+ let { minX, maxX, minY, maxY } = this.cellRange(query), seen = /* @__PURE__ */ new Set(), result = [];
193
+ for (let cx = minX; cx <= maxX; cx++) for (let cy = minY; cy <= maxY; cy++) {
194
+ let bucket = this.cells.get(`${cx}:${cy}`);
195
+ if (bucket) for (let id of bucket) {
196
+ if (seen.has(id)) continue;
197
+ seen.add(id);
198
+ let rect = this.rects.get(id);
199
+ rect.x <= query.x + query.width && rect.x + rect.width >= query.x && rect.y <= query.y + query.height && rect.y + rect.height >= query.y && result.push(id);
200
+ }
201
+ }
202
+ return result;
203
+ }
204
+ get size() {
205
+ return this.rects.size;
206
+ }
207
+ }, GestureSpatialLookup = class {
208
+ #real;
209
+ #cellSize;
210
+ #grid = null;
211
+ #queryRect = null;
212
+ constructor(real, cellSize) {
213
+ this.#real = real, this.#cellSize = cellSize;
214
+ }
215
+ /** Snapshot the current geometry into the grid (gesture start). */
216
+ arm(rectOf) {
217
+ let grid = new SpatialGrid(this.#cellSize);
218
+ for (let [id, value] of this.#real.entries()) grid.insert(id, rectOf(value));
219
+ this.#grid = grid, this.#queryRect = null;
220
+ }
221
+ /** Focus iteration on the neighborhood of the pointer (per move). */
222
+ setQueryCenter(center, radius) {
223
+ this.#queryRect = {
224
+ x: center.x - radius,
225
+ y: center.y - radius,
226
+ width: radius * 2,
227
+ height: radius * 2
228
+ };
229
+ }
230
+ /** Focus iteration on an explicit rect (box-selection gestures). */
231
+ setQueryRect(rect) {
232
+ this.#queryRect = rect;
233
+ }
234
+ /** Back to plain pass-through (gesture end). */
235
+ disarm() {
236
+ this.#grid = null, this.#queryRect = null;
237
+ }
238
+ #candidateIds() {
239
+ return !this.#grid || !this.#queryRect ? null : this.#grid.queryRect(this.#queryRect);
240
+ }
241
+ get(key) {
242
+ return this.#real.get(key);
243
+ }
244
+ has(key) {
245
+ return this.#real.has(key);
246
+ }
247
+ get size() {
248
+ return this.#real.size;
249
+ }
250
+ *keys() {
251
+ let candidates = this.#candidateIds();
252
+ if (!candidates) {
253
+ yield* this.#real.keys();
254
+ return;
255
+ }
256
+ for (let id of candidates) this.#real.has(id) && (yield id);
257
+ }
258
+ *values() {
259
+ let candidates = this.#candidateIds();
260
+ if (!candidates) {
261
+ yield* this.#real.values();
262
+ return;
263
+ }
264
+ for (let id of candidates) {
265
+ let value = this.#real.get(id);
266
+ value !== void 0 && (yield value);
267
+ }
268
+ }
269
+ *entries() {
270
+ let candidates = this.#candidateIds();
271
+ if (!candidates) {
272
+ yield* this.#real.entries();
273
+ return;
274
+ }
275
+ for (let id of candidates) {
276
+ let value = this.#real.get(id);
277
+ value !== void 0 && (yield [id, value]);
278
+ }
279
+ }
280
+ [Symbol.iterator]() {
281
+ return this.entries();
282
+ }
283
+ forEach(callback, thisArg) {
284
+ for (let [key, value] of this.entries()) callback.call(thisArg, value, key, this);
285
+ }
286
+ [Symbol.toStringTag] = "GestureSpatialLookup";
287
+ set() {
288
+ throw Error("GestureSpatialLookup is read-only");
289
+ }
290
+ getOrInsert() {
291
+ throw Error("GestureSpatialLookup is read-only");
292
+ }
293
+ getOrInsertComputed() {
294
+ throw Error("GestureSpatialLookup is read-only");
295
+ }
296
+ delete() {
297
+ throw Error("GestureSpatialLookup is read-only");
298
+ }
299
+ clear() {
300
+ throw Error("GestureSpatialLookup is read-only");
301
+ }
302
+ };
303
+ //#endregion
304
+ //#region src/components/handle/connectionGestureLookup.ts
305
+ /**
306
+ * Upstream `getClosestHandle` prefilters nodes within
307
+ * `connectionRadius + ADDITIONAL_DISTANCE` of the pointer; ADDITIONAL_DISTANCE
308
+ * is hardcoded to 250 in @xyflow/system (xyhandle/utils.ts). Tracked here with
309
+ * a safety pad: a superset of candidates is always correct (their exact
310
+ * distance filter runs after), so the pad only costs a few extra candidates.
311
+ */
312
+ const armConnectionGestureLookup = (options) => {
313
+ let { event, real, domNode, getTransform, connectionRadius } = options, containerBounds = domNode?.getBoundingClientRect();
314
+ if (!containerBounds) return real;
315
+ let radius = connectionRadius + 250 + 50, lookup = new GestureSpatialLookup(real, radius);
316
+ lookup.arm((node) => nodeToRect(node));
317
+ let update = (moveEvent) => {
318
+ lookup.setQueryCenter(pointToRendererPoint(getEventPosition(moveEvent, containerBounds), getTransform(), !1, [1, 1]), radius);
319
+ };
320
+ update(event);
321
+ let doc = getHostForElement(event.target), dispose = () => {
322
+ doc.removeEventListener("mousemove", update, !0), doc.removeEventListener("touchmove", update, !0), doc.removeEventListener("mouseup", dispose, !0), doc.removeEventListener("touchend", dispose, !0), lookup.disarm();
323
+ };
324
+ return doc.addEventListener("mousemove", update, !0), doc.addEventListener("touchmove", update, !0), doc.addEventListener("mouseup", dispose, !0), doc.addEventListener("touchend", dispose, !0), lookup;
325
+ }, buildConnectionGestureParams = (options) => {
326
+ let { event, store, actions, gestureLookup } = options;
327
+ return {
328
+ lib: store.lib,
329
+ flowId: store.id,
330
+ domNode: store.domNode,
331
+ autoPanOnConnect: store.autoPanOnConnect,
332
+ autoPanSpeed: store.autoPanSpeed,
333
+ connectionMode: store.connectionMode,
334
+ connectionRadius: store.connectionRadius,
335
+ nodeLookup: gestureLookup,
336
+ cancelConnection: actions.cancelConnection,
337
+ panBy: actions.panBy,
338
+ updateConnection: ((connection) => {
339
+ actions.setConnection(connection), flush();
340
+ }),
341
+ isValidConnection: store.isValidConnection,
342
+ onConnectStart: store.onConnectStart,
343
+ onConnectEnd: store.onConnectEnd,
344
+ getTransform: () => store.transform,
345
+ getFromHandle: () => store.connection.fromHandle,
346
+ dragThreshold: store.connectionDragThreshold,
347
+ handleDomNode: event.currentTarget
348
+ };
148
349
  }, EdgeReconnectAnchor = (props) => {
149
350
  let _props = propDefaults(props, {
150
351
  size: 25,
@@ -165,27 +366,24 @@ const getEdgeId = (connection) => {
165
366
  nodeId: edge().target,
166
367
  handleId: edge().targetHandle ?? null,
167
368
  type: "target"
168
- };
169
- XYHandle.onPointerDown(event, {
170
- lib: store.lib,
171
- flowId: store.id,
369
+ }, gestureLookup = armConnectionGestureLookup({
370
+ event,
371
+ real: nodeLookup,
172
372
  domNode: store.domNode,
373
+ getTransform: () => store.transform,
374
+ connectionRadius: store.connectionRadius
375
+ });
376
+ XYHandle.onPointerDown(event, {
377
+ ...buildConnectionGestureParams({
378
+ event,
379
+ store,
380
+ actions,
381
+ gestureLookup
382
+ }),
173
383
  nodeId: opposite.nodeId,
174
384
  handleId: opposite.handleId,
175
- autoPanOnConnect: store.autoPanOnConnect,
176
- connectionMode: store.connectionMode,
177
- connectionRadius: store.connectionRadius,
178
- nodeLookup,
179
385
  isTarget: opposite.type === "target",
180
386
  edgeUpdaterType: opposite.type,
181
- cancelConnection: actions.cancelConnection,
182
- panBy: actions.panBy,
183
- updateConnection: (connection) => {
184
- actions.setConnection(connection), flush();
185
- },
186
- isValidConnection: store.isValidConnection,
187
- onConnectStart: store.onConnectStart,
188
- onConnectEnd: store.onConnectEnd,
189
387
  onConnect: (connection) => {
190
388
  let newEdge = {
191
389
  ...edge(),
@@ -195,12 +393,7 @@ const getEdgeId = (connection) => {
195
393
  },
196
394
  onReconnectEnd: (event, connectionState) => {
197
395
  setReconnectingState(!1), store.onReconnectEnd?.(event, edge(), opposite.type, connectionState);
198
- },
199
- getTransform: () => store.transform,
200
- getFromHandle: () => store.connection.fromHandle,
201
- autoPanSpeed: store.autoPanSpeed,
202
- dragThreshold: store.connectionDragThreshold,
203
- handleDomNode: event.currentTarget
396
+ }
204
397
  });
205
398
  };
206
399
  return <EdgeLabel x={_props.position?.x} y={_props.position?.y} style={_props.style} {...rest}>
@@ -236,9 +429,81 @@ const getEdgeId = (connection) => {
236
429
  </div>
237
430
  </Show>
238
431
  </>;
239
- }, createEdgeStore = (edges) => {
240
- let [store, setStore] = createStore(edges);
241
- return [store, setStore];
432
+ }, createSelectionCommands = ({ store, setNodesStore, setEdgesStore, setSelectionRect, setSelectionRectMode, nodeLookup, edgeLookup, updateNodePositions }) => {
433
+ let unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
434
+ let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
435
+ nodesToUnselect.size && setNodesStore((nodes) => {
436
+ for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
437
+ });
438
+ let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
439
+ edgesToUnselect.size && setEdgesStore((edges) => {
440
+ for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
441
+ }), flush();
442
+ }, addSelectedNodes = (ids) => {
443
+ let isMultiSelection = store.multiselectionKeyPressed, idSet = new Set(ids);
444
+ setNodesStore((nodes) => {
445
+ for (let node of nodes) {
446
+ let nodeWillBeSelected = idSet.has(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
447
+ node.selected !== selected && (node.selected = selected);
448
+ }
449
+ }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
450
+ }, addSelectedEdges = (ids) => {
451
+ let isMultiSelection = store.multiselectionKeyPressed, idSet = new Set(ids);
452
+ setEdgesStore((edges) => {
453
+ for (let edge of edges) {
454
+ let edgeWillBeSelected = idSet.has(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
455
+ edge.selected !== selected && (edge.selected = selected);
456
+ }
457
+ }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
458
+ };
459
+ return {
460
+ unselectNodesAndEdges,
461
+ addSelectedNodes,
462
+ addSelectedEdges,
463
+ handleNodeSelection: (id, unselect, nodeRef) => {
464
+ let node = store.nodes.find((n) => n.id === id);
465
+ if (!node) {
466
+ emitFlowError(store.onError, "012", errorMessages.error012(id));
467
+ return;
468
+ }
469
+ setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
470
+ nodes: [node],
471
+ edges: []
472
+ }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]);
473
+ },
474
+ handleEdgeSelection: (id) => {
475
+ let edge = edgeLookup[id];
476
+ if (!edge) {
477
+ emitFlowError(store.onError, "012", errorMessages.error012(id));
478
+ return;
479
+ }
480
+ isEdgeSelectable(edge, store) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
481
+ nodes: [],
482
+ edges: [edge]
483
+ }) : addSelectedEdges([id]));
484
+ },
485
+ moveSelectedNodes: (direction, factor) => {
486
+ let nodeUpdates = /* @__PURE__ */ new Map(), xVelo = store.snapGrid?.[0] ?? 5, yVelo = store.snapGrid?.[1] ?? 5, xDiff = direction.x * xVelo * factor, yDiff = direction.y * yVelo * factor;
487
+ for (let node of nodeLookup.values()) {
488
+ if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
489
+ let nextPosition = {
490
+ x: node.internals.positionAbsolute.x + xDiff,
491
+ y: node.internals.positionAbsolute.y + yDiff
492
+ };
493
+ store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
494
+ let { position } = calculateNodePosition({
495
+ nodeId: node.id,
496
+ nextPosition,
497
+ nodeLookup,
498
+ nodeExtent: store.nodeExtent,
499
+ nodeOrigin: store.nodeOrigin,
500
+ onError: store.onError
501
+ });
502
+ nodeUpdates.set(node.id, { position });
503
+ }
504
+ updateNodePositions(nodeUpdates);
505
+ }
506
+ };
242
507
  }, STEP = .5 / 2, rectsEqual = (a, b) => a === b || !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height, rectsOverlap = (a, b) => a.x <= b.x + b.width && a.x + a.width >= b.x && a.y <= b.y + b.height && a.y + a.height >= b.y, createCullingViewport = (source) => createMemo(() => {
243
508
  let { width, height } = source;
244
509
  if (!width || !height) return null;
@@ -369,14 +634,48 @@ var RecordMapFacade = class {
369
634
  }
370
635
  };
371
636
  //#endregion
372
- //#region src/core/projections/connections.ts
637
+ //#region src/core/measurementIngest.ts
373
638
  /**
374
- * Lookup keys for the connection index. Each edge is registered under six
375
- * keys for both of its endpoints: the node, the node+handle-type, and (when
376
- * a handle id is present) the node+type+handle:
377
- * `${nodeId}` · `${nodeId}-${type}` · `${nodeId}-${type}-${handleId}`
639
+ * The measurement ingest lifecycle (WP3): everything that flows FROM the DOM
640
+ * measuring pass INTO the data graph, plus the garbage collection that keeps
641
+ * the measurements root aligned with graph membership. The DOM side (resize
642
+ * observers, the idle-scheduled measuring pass) lives in createSolidFlow;
643
+ * headless usage never calls these.
378
644
  */
379
- const connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
645
+ const createMeasurementIngest = ({ setMeasurementsStore, setNodesStore, nodes }) => (createEffect(() => new Set(nodes().map((n) => n.id)), (currentIds) => {
646
+ setMeasurementsStore((draft) => {
647
+ for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
648
+ });
649
+ }), {
650
+ applyMeasurementWrites: (writes) => {
651
+ setMeasurementsStore((draft) => {
652
+ for (let write of writes) if (write.hidden) {
653
+ let entry = draft[write.id];
654
+ entry && (entry.handleBounds = void 0);
655
+ } else draft[write.id] = {
656
+ measured: write.measured,
657
+ handleBounds: write.handleBounds
658
+ };
659
+ });
660
+ },
661
+ applyNodeChanges: (changes) => {
662
+ changes.length !== 0 && setNodesStore((nodes) => {
663
+ let nodeById = new Map(nodes.map((node) => [node.id, node]));
664
+ for (let change of changes) {
665
+ let node = nodeById.get(change.id);
666
+ if (node) switch (change.type) {
667
+ case "dimensions":
668
+ change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
669
+ ...node.measured,
670
+ ...change.dimensions
671
+ };
672
+ break;
673
+ case "position": node.position = change.position ?? node.position;
674
+ }
675
+ }
676
+ });
677
+ }
678
+ }), connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ? `-${type}-${handleId}` : `-${type}` : ""}`, pairKey = (aNode, aHandle, bNode, bHandle) => `${aNode}-${aHandle}--${bNode}-${bHandle}`, createConnections = (source) => createProjection(() => {
380
679
  let out = {}, add = (key, entry, connection) => {
381
680
  (out[key] ??= {})[entry] = connection;
382
681
  };
@@ -395,7 +694,20 @@ const connectionKey = (nodeId, type, handleId) => `${nodeId}${type ? handleId ?
395
694
  let out = {};
396
695
  for (let edge of source.edges) out[edge.id] = edge;
397
696
  return out;
398
- }, {}, { key: "id" });
697
+ }, {}, { key: "id" }), createRowRecordProjection = (rowStores) => {
698
+ let assigned = /* @__PURE__ */ new Map();
699
+ return createProjection((draft) => {
700
+ let seen = /* @__PURE__ */ new Set();
701
+ for (let { id, store } of rowStores()) {
702
+ let row = store.row;
703
+ row && (seen.add(id), assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row));
704
+ }
705
+ for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
706
+ }, {}, {
707
+ key: null,
708
+ shallow: !0
709
+ });
710
+ };
399
711
  function isManualZIndexMode(zIndexMode) {
400
712
  return zIndexMode === "manual";
401
713
  }
@@ -439,11 +751,7 @@ const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (sourc
439
751
  x,
440
752
  y
441
753
  }, row.internals.z = z;
442
- } else source.nodes.length;
443
- }
444
- if (userNode.parentId) {
445
- let parent = entryById.get(userNode.parentId);
446
- parent?.store.row.internals.positionAbsolute.x, parent?.store.row.internals.z;
754
+ } else source.nodes.length, emitFlowError(source.onError, "parent-missing", `Parent node ${userNode.parentId} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);
447
755
  }
448
756
  return { row };
449
757
  }, {}, { key: "id" }), entry = {
@@ -456,19 +764,8 @@ const EMPTY_AUTO_INDEX = /* @__PURE__ */ new Map(), createInternalNodes = (sourc
456
764
  id,
457
765
  store
458
766
  };
459
- }, { keyed: (userNode) => userNode.id }), assigned = /* @__PURE__ */ new Map();
460
- return createProjection((draft) => {
461
- let seen = /* @__PURE__ */ new Set();
462
- for (let { id, store } of rowStores()) {
463
- seen.add(id);
464
- let row = store.row;
465
- assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row);
466
- }
467
- for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
468
- }, {}, {
469
- key: null,
470
- shallow: !0
471
- });
767
+ }, { keyed: (userNode) => userNode.id });
768
+ return createRowRecordProjection(rowStores);
472
769
  };
473
770
  function calculateChildXYZ(childNode, parentNode, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode) {
474
771
  let { x: parentX, y: parentY } = parentNode.internals.positionAbsolute, childDimensions = getNodeDimensions(childNode), positionWithOrigin = getNodePositionWithOrigin(childNode, nodeOrigin), clampedPosition = isCoordinateExtent(childNode.extent) ? clampPosition(positionWithOrigin, childNode.extent, childDimensions) : positionWithOrigin, absolutePosition = clampPosition({
@@ -513,24 +810,14 @@ const createLayoutedEdges = (source) => {
513
810
  let rowStores = mapArray(() => source.edges, (edgeAccessor) => ({
514
811
  id: edgeAccessor().id,
515
812
  store: createProjection(() => {
516
- let edge = edgeAccessor(), row = buildRow(source, edge);
517
- return source.nodeLookup.get(edge.source)?.internals.handleBounds, source.nodeLookup.get(edge.target)?.internals.handleBounds, { row };
813
+ let edge = edgeAccessor();
814
+ return { row: buildRow(source, edge) };
518
815
  }, { row: null }, { key: "id" })
519
- }), { keyed: (edge) => edge.id }), assigned = /* @__PURE__ */ new Map();
520
- return createProjection((draft) => {
521
- let seen = /* @__PURE__ */ new Set();
522
- for (let { id, store } of rowStores()) {
523
- let row = store.row;
524
- row && (seen.add(id), assigned.get(id) !== row && (assigned.set(id, row), draft[id] = row));
525
- }
526
- for (let id of assigned.keys()) seen.has(id) || (assigned.delete(id), delete draft[id]);
527
- }, {}, {
528
- key: null,
529
- shallow: !0
530
- });
816
+ }), { keyed: (edge) => edge.id });
817
+ return createRowRecordProjection(rowStores);
531
818
  }, buildRow = (source, edge) => {
532
819
  let sourceNode = source.nodeLookup.get(edge.source), targetNode = source.nodeLookup.get(edge.target);
533
- if (!sourceNode || !targetNode) return source.nodeLookup.size, null;
820
+ if (!sourceNode || !targetNode) return null;
534
821
  let edgePosition = getEdgePosition({
535
822
  id: edge.id,
536
823
  sourceNode,
@@ -560,7 +847,33 @@ const createLayoutedEdges = (source) => {
560
847
  let out = {};
561
848
  for (let node of source.nodes) node.parentId && (out[node.parentId] = !0);
562
849
  return out;
563
- }, {}, { key: "id" }), getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
850
+ }, {}, { key: "id" }), createSeededGraphStores = (props, config) => {
851
+ props.nodes !== void 0 && props.defaultNodes, props.edges !== void 0 && props.defaultEdges;
852
+ let [nodesStore, setNodesStore] = createStore(props.nodes ?? [...props.defaultNodes ?? []]), [edgesStore, setEdgesStore] = createStore(props.edges ?? [...props.defaultEdges ?? []]), nodeSeedAdopted = props.nodes !== void 0 || props.defaultNodes !== void 0, edgeSeedAdopted = props.edges !== void 0 || props.defaultEdges !== void 0;
853
+ return createEffect(() => {
854
+ let next = config().nodes;
855
+ if (next) for (let node of next);
856
+ return { next };
857
+ }, ({ next }) => {
858
+ next && (nodeSeedAdopted = !0, setNodesStore(() => next));
859
+ }, { defer: !0 }), createEffect(() => {
860
+ let next = config().edges;
861
+ if (next) for (let edge of next);
862
+ return { next };
863
+ }, ({ next }) => {
864
+ next && (edgeSeedAdopted = !0, setEdgesStore(() => next));
865
+ }, { defer: !0 }), createEffect(() => ({
866
+ nodes: config().defaultNodes,
867
+ edges: config().defaultEdges
868
+ }), ({ nodes: defaultNodes, edges: defaultEdges }) => {
869
+ defaultNodes && !nodeSeedAdopted && config().nodes === void 0 && (nodeSeedAdopted = !0, setNodesStore(() => [...defaultNodes])), defaultEdges && !edgeSeedAdopted && config().edges === void 0 && (edgeSeedAdopted = !0, setEdgesStore(() => [...defaultEdges]));
870
+ }, { defer: !0 }), {
871
+ nodesStore,
872
+ setNodesStore,
873
+ edgesStore,
874
+ setEdgesStore
875
+ };
876
+ }, getInitialViewport = (fitView, initialViewport, width, height, nodeLookup) => {
564
877
  if (fitView && !initialViewport && width && height) {
565
878
  let bounds = getInternalNodesBounds(nodeLookup, { filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)) });
566
879
  return getViewportForBounds$1(bounds, width, height, .5, 2, .1);
@@ -571,9 +884,7 @@ const createLayoutedEdges = (source) => {
571
884
  zoom: 1
572
885
  };
573
886
  }, createFlowState = (props, injections = {}) => {
574
- let _props = merge(getDefaultFlowStateProps(), props), initialNodeTypes = injections.initialNodeTypes ?? {}, initialEdgeTypes = injections.initialEdgeTypes ?? {}, prefersDark = injections.prefersDark ?? (() => _props.colorModeSSR === "dark"), [config, setConfig] = createSignal(_props), [ariaLabelConfig, setAriaLabelConfig] = createSignal(() => mergeAriaLabelConfig(config().ariaLabelConfig)), [ariaLiveMessage, setAriaLiveMessage] = createSignal(() => config().ariaLiveMessage), [clickConnectStartHandle, setClickConnectStartHandle] = createSignal(void 0), [connection, setConnection] = createSignal(initialConnection), [domNode, setDomNode] = createSignal(null), [dragging, setDragging] = createSignal(!1), [elementsSelectable, setElementsSelectable] = createSignal(() => config().elementsSelectable), [height, setHeight] = createSignal(() => config().height), [minZoom, _setMinZoom] = createSignal(() => config().minZoom), [maxZoom, _setMaxZoom] = createSignal(() => config().maxZoom), [nodesConnectable, setNodesConnectable] = createSignal(() => config().nodesDraggable), [nodesDraggable, setNodesDraggable] = createSignal(() => config().nodesDraggable), [panZoom, setPanZoom] = createSignal(null), [selectionRect, setSelectionRect] = createSignal(), [selectionRectMode, setSelectionRectMode] = createSignal(), [snapGrid, setSnapGrid] = createSignal(() => config().snapGrid), [translateExtent, _setTranslateExtent] = createSignal(() => config().translateExtent ?? infiniteExtent), [width, setWidth] = createSignal(() => config().width), [selectionKeyPressed, setSelectionKeyPressed] = createSignal(!1), [multiselectionKeyPressed, setMultiselectionKeyPressed] = createSignal(!1), [deleteKeyPressed, setDeleteKeyPressed] = createSignal(!1), [panActivationKeyPressed, setPanActivationKeyPressed] = createSignal(!1), [zoomActivationKeyPressed, setZoomActivationKeyPressed] = createSignal(!1);
575
- props.nodes !== void 0 && props.defaultNodes, props.edges !== void 0 && props.defaultEdges;
576
- let [nodesStore, setNodesStore] = createStore(props.nodes ?? [...props.defaultNodes ?? []]), [edgesStore, setEdgesStore] = createStore(props.edges ?? [...props.defaultEdges ?? []]), nodeSeedAdopted = props.nodes !== void 0 || props.defaultNodes !== void 0, edgeSeedAdopted = props.edges !== void 0 || props.defaultEdges !== void 0, [measurementsStore, setMeasurementsStore] = createStore({}), internalNodes = createInternalNodes({
887
+ let _props = merge(getDefaultFlowStateProps(), props), initialNodeTypes = injections.initialNodeTypes ?? {}, initialEdgeTypes = injections.initialEdgeTypes ?? {}, prefersDark = injections.prefersDark ?? (() => _props.colorModeSSR === "dark"), [config, setConfig] = createSignal(_props), ariaLabelConfig = createMemo(() => mergeAriaLabelConfig(config().ariaLabelConfig)), [ariaLiveMessage, setAriaLiveMessage] = createSignal(() => config().ariaLiveMessage), [clickConnectStartHandle, setClickConnectStartHandle] = createSignal(void 0), [connection, setConnection] = createSignal(initialConnection), [domNode, setDomNode] = createSignal(null), [dragging, setDragging] = createSignal(!1), [elementsSelectable, setElementsSelectable] = createSignal(() => config().elementsSelectable), [height, setHeight] = createSignal(() => config().height), minZoom = createMemo(() => config().minZoom), maxZoom = createMemo(() => config().maxZoom), [nodesConnectable, setNodesConnectable] = createSignal(() => config().nodesConnectable), [nodesDraggable, setNodesDraggable] = createSignal(() => config().nodesDraggable), [panZoom, setPanZoom] = createSignal(null), [selectionRect, setSelectionRect] = createSignal(), [selectionRectMode, setSelectionRectMode] = createSignal(), [snapGrid, setSnapGrid] = createSignal(() => config().snapGrid), translateExtent = createMemo(() => config().translateExtent ?? infiniteExtent), [width, setWidth] = createSignal(() => config().width), [selectionKeyPressed, setSelectionKeyPressed] = createSignal(!1), [multiselectionKeyPressed, setMultiselectionKeyPressed] = createSignal(!1), [deleteKeyPressed, setDeleteKeyPressed] = createSignal(!1), [panActivationKeyPressed, setPanActivationKeyPressed] = createSignal(!1), [zoomActivationKeyPressed, setZoomActivationKeyPressed] = createSignal(!1), { nodesStore, setNodesStore, edgesStore, setEdgesStore } = createSeededGraphStores(props, config), [measurementsStore, setMeasurementsStore] = createStore({}), internalNodes = createInternalNodes({
577
888
  get nodes() {
578
889
  return nodesStore;
579
890
  },
@@ -591,26 +902,12 @@ const createLayoutedEdges = (source) => {
591
902
  },
592
903
  get zIndexMode() {
593
904
  return config().zIndexMode;
905
+ },
906
+ get onError() {
907
+ return config().onFlowError;
594
908
  }
595
909
  }), nodeLookup = new RecordMapFacade(internalNodes), initialViewport = getInitialViewport(_props.fitView, _props.initialViewport, _props.width ?? 0, _props.height ?? 0, nodeLookup), [viewportStore, setViewportStore] = createStore(_props.viewport ?? initialViewport);
596
- createEffect(() => {
597
- let next = config().nodes;
598
- if (next) for (let node of next);
599
- return { next };
600
- }, ({ next }) => {
601
- next && (nodeSeedAdopted = !0, setNodesStore(() => next));
602
- }, { defer: !0 }), createEffect(() => {
603
- let next = config().edges;
604
- if (next) for (let edge of next);
605
- return { next };
606
- }, ({ next }) => {
607
- next && (edgeSeedAdopted = !0, setEdgesStore(() => next));
608
- }, { defer: !0 }), createEffect(() => ({
609
- nodes: config().defaultNodes,
610
- edges: config().defaultEdges
611
- }), ({ nodes: defaultNodes, edges: defaultEdges }) => {
612
- defaultNodes && !nodeSeedAdopted && config().nodes === void 0 && (nodeSeedAdopted = !0, setNodesStore(() => [...defaultNodes])), defaultEdges && !edgeSeedAdopted && config().edges === void 0 && (edgeSeedAdopted = !0, setEdgesStore(() => [...defaultEdges]));
613
- }, { defer: !0 }), createEffect(() => config().viewport, (next) => {
910
+ createEffect(() => config().viewport, (next) => {
614
911
  next && setViewportStore(() => next);
615
912
  }, { defer: !0 });
616
913
  let transform = createMemo(() => [
@@ -622,25 +919,33 @@ const createLayoutedEdges = (source) => {
622
919
  if (nodes.length === 0) return !1;
623
920
  for (let node of nodes) if (!node.hidden && (node.measured?.width === void 0 || node.measured?.height === void 0)) return !1;
624
921
  return !0;
625
- }), store = merge({
922
+ }), resolvedColorMode = createMemo(() => {
923
+ let mode = config().colorMode;
924
+ return mode === "system" ? prefersDark() ? "dark" : "light" : mode;
925
+ }), projectedConnection = createMemo(() => {
926
+ let state = connection();
927
+ return state.inProgress ? {
928
+ ...state,
929
+ to: pointToRendererPoint(state.to, transform())
930
+ } : state;
931
+ }), connectionFromHandle = createMemo(() => connection().fromHandle ?? null, { equals: (a, b) => a === b || !!a && !!b && a.nodeId === b.nodeId && a.type === b.type && a.id === b.id }), connectionTargetByHandle = createProjection((draft) => {
932
+ let state = connection(), toHandle = state.inProgress ? state.toHandle : null, key = toHandle ? connectionKey(toHandle.nodeId, toHandle.type, toHandle.id ?? null) : null;
933
+ for (let existing of Object.keys(draft)) existing !== key && delete draft[existing];
934
+ key && (draft[key] = state.isValid ? "valid" : "invalid");
935
+ }, {}, { key: null }), connectionOriginByHandle = createProjection((draft) => {
936
+ let fromHandle = connection().fromHandle ?? clickConnectStartHandle(), fromKey = fromHandle ? connectionKey(fromHandle.nodeId, fromHandle.type, fromHandle.id ?? null) : null, siblingKey = fromHandle ? connectionKey(fromHandle.nodeId, fromHandle.type === "source" ? "target" : "source", fromHandle.id ?? null) : null;
937
+ for (let existing of Object.keys(draft)) existing !== fromKey && existing !== siblingKey && delete draft[existing];
938
+ fromKey && (draft[fromKey] = "from"), siblingKey && (draft[siblingKey] = "excluded");
939
+ }, {}, { key: null }), mergedNodeTypes = createMemo(() => ({
940
+ ...initialNodeTypes,
941
+ ...config().nodeTypes
942
+ })), mergedEdgeTypes = createMemo(() => ({
943
+ ...initialEdgeTypes,
944
+ ...config().edgeTypes
945
+ })), selectedNodesView = createMemo(() => nodesStore.filter((node) => node.selected)), selectedEdgesView = createMemo(() => edgesStore.filter((edge) => edge.selected)), store = merge({
626
946
  width: 0,
627
947
  height: 0
628
948
  }, config, {
629
- get _colorMode() {
630
- return config().colorMode;
631
- },
632
- get _colorModeSSR() {
633
- return config().colorModeSSR;
634
- },
635
- get _connection() {
636
- return connection();
637
- },
638
- get _nodeTypes() {
639
- return config().nodeTypes;
640
- },
641
- get _edgeTypes() {
642
- return config().edgeTypes;
643
- },
644
949
  get ariaLabelConfig() {
645
950
  return ariaLabelConfig();
646
951
  },
@@ -651,14 +956,19 @@ const createLayoutedEdges = (source) => {
651
956
  return clickConnectStartHandle();
652
957
  },
653
958
  get colorMode() {
654
- return this._colorMode === "system" ? prefersDark() ? "dark" : "light" : this._colorMode;
959
+ return resolvedColorMode();
655
960
  },
656
961
  get connection() {
657
- let state = connection();
658
- return {
659
- ...state,
660
- to: state.inProgress ? pointToRendererPoint(state.to, this.transform) : state.to
661
- };
962
+ return projectedConnection();
963
+ },
964
+ get connectionFromHandle() {
965
+ return connectionFromHandle();
966
+ },
967
+ get connectionTargetByHandle() {
968
+ return connectionTargetByHandle;
969
+ },
970
+ get connectionOriginByHandle() {
971
+ return connectionOriginByHandle;
662
972
  },
663
973
  get domNode() {
664
974
  return domNode();
@@ -667,10 +977,7 @@ const createLayoutedEdges = (source) => {
667
977
  return dragging();
668
978
  },
669
979
  get edgeTypes() {
670
- return {
671
- ...initialEdgeTypes,
672
- ...this._edgeTypes
673
- };
980
+ return mergedEdgeTypes();
674
981
  },
675
982
  get elementsSelectable() {
676
983
  return elementsSelectable();
@@ -703,19 +1010,16 @@ const createLayoutedEdges = (source) => {
703
1010
  return nodesDraggable();
704
1011
  },
705
1012
  get nodeTypes() {
706
- return {
707
- ...initialNodeTypes,
708
- ...this._nodeTypes
709
- };
1013
+ return mergedNodeTypes();
710
1014
  },
711
1015
  get panZoom() {
712
1016
  return panZoom();
713
1017
  },
714
1018
  get selectedNodes() {
715
- return nodesStore.filter((node) => node.selected);
1019
+ return selectedNodesView();
716
1020
  },
717
1021
  get selectedEdges() {
718
- return edgesStore.filter((edge) => edge.selected);
1022
+ return selectedEdgesView();
719
1023
  },
720
1024
  get selectionRect() {
721
1025
  return selectionRect();
@@ -804,7 +1108,7 @@ const createLayoutedEdges = (source) => {
804
1108
  return store.onError;
805
1109
  },
806
1110
  nodeLookup
807
- }), visibleEdgeIds = createMemo(() => Object.keys(layoutedEdges)), getEdge = (id) => layoutedEdges[id], fitView = async (options) => store.panZoom ? await fitViewport({
1111
+ }), visibleEdgeIds = createMemo(() => Object.keys(layoutedEdges)), getLayoutedEdge = (id) => layoutedEdges[id], fitView = async (options) => store.panZoom ? await fitViewport({
808
1112
  nodes: nodeLookup,
809
1113
  width: store.width,
810
1114
  height: store.height,
@@ -830,33 +1134,11 @@ const createLayoutedEdges = (source) => {
830
1134
  setNodesStore((nodes) => {
831
1135
  for (let node of nodes) nodeDragItems.has(node.id) && (node.dragging = dragging, node.position = nodeDragItems.get(node.id).position);
832
1136
  });
833
- }, applyMeasurementWrites = (writes) => {
834
- setMeasurementsStore((draft) => {
835
- for (let write of writes) if (write.hidden) {
836
- let entry = draft[write.id];
837
- entry && (entry.handleBounds = void 0);
838
- } else draft[write.id] = {
839
- measured: write.measured,
840
- handleBounds: write.handleBounds
841
- };
842
- });
843
- }, applyNodeChanges = (changes) => {
844
- changes.length !== 0 && setNodesStore((nodes) => {
845
- let nodeById = new Map(nodes.map((node) => [node.id, node]));
846
- for (let change of changes) {
847
- let node = nodeById.get(change.id);
848
- if (node) switch (change.type) {
849
- case "dimensions":
850
- change.setAttributes && (node.width = change.dimensions?.width ?? node.width, node.height = change.dimensions?.height ?? node.height), node.measured = {
851
- ...node.measured,
852
- ...change.dimensions
853
- };
854
- break;
855
- case "position": node.position = change.position ?? node.position;
856
- }
857
- }
858
- });
859
- }, markInitialNodesMeasured = () => {
1137
+ }, { applyMeasurementWrites, applyNodeChanges } = createMeasurementIngest({
1138
+ setMeasurementsStore,
1139
+ setNodesStore,
1140
+ nodes: () => nodesStore
1141
+ }), markInitialNodesMeasured = () => {
860
1142
  initialNodesMeasured = !0, tryInitialFitView();
861
1143
  }, requestMeasure = () => {}, setMeasureRequester = (fn) => {
862
1144
  requestMeasure = fn;
@@ -871,66 +1153,16 @@ const createLayoutedEdges = (source) => {
871
1153
  ease: options?.ease,
872
1154
  interpolate: options?.interpolate
873
1155
  }), Promise.resolve(!0)) : Promise.resolve(!1);
874
- }, setPaneClickDistance = (distance) => {
875
- store.panZoom?.setClickDistance(distance);
876
- }, unselectNodesAndEdges = ({ nodes: _nodes, edges } = {}) => {
877
- let nodesToUnselect = new Set((_nodes || store.nodes).map(({ id }) => id));
878
- nodesToUnselect.size && setNodesStore((nodes) => {
879
- for (let node of nodes) nodesToUnselect.has(node.id) && (node.selected = !1);
880
- });
881
- let edgesToUnselect = new Set((edges ?? store.edges).map(({ id }) => id));
882
- edgesToUnselect.size && setEdgesStore((edges) => {
883
- for (let edge of edges) edgesToUnselect.has(edge.id) && (edge.selected = !1);
884
- }), flush();
885
- }, addSelectedNodes = (ids) => {
886
- let isMultiSelection = store.multiselectionKeyPressed;
887
- setNodesStore((nodes) => {
888
- for (let node of nodes) {
889
- let nodeWillBeSelected = ids.includes(node.id), selected = isMultiSelection && node.selected || nodeWillBeSelected;
890
- node.selected !== selected && (node.selected = selected);
891
- }
892
- }), isMultiSelection || unselectNodesAndEdges({ nodes: [] }), flush();
893
- }, addSelectedEdges = (ids) => {
894
- let isMultiSelection = store.multiselectionKeyPressed;
895
- setEdgesStore((edges) => {
896
- for (let edge of edges) {
897
- let edgeWillBeSelected = ids.includes(edge.id), selected = isMultiSelection && edge.selected || edgeWillBeSelected;
898
- edge.selected !== selected && (edge.selected = selected);
899
- }
900
- }), isMultiSelection || unselectNodesAndEdges({ edges: [] }), flush();
901
- }, handleNodeSelection = (id, unselect, nodeRef) => {
902
- let node = store.nodes.find((n) => n.id === id);
903
- node && (setSelectionRect(void 0), setSelectionRectMode(void 0), node.selected ? (unselect || node.selected && store.multiselectionKeyPressed) && (unselectNodesAndEdges({
904
- nodes: [node],
905
- edges: []
906
- }), requestAnimationFrame(() => nodeRef?.blur())) : addSelectedNodes([id]));
907
- }, handleEdgeSelection = (id) => {
908
- let edge = edgeLookup[id];
909
- edge && (edge.selectable || store.elementsSelectable && edge.selectable === void 0) && (setSelectionRect(void 0), setSelectionRectMode(void 0), edge.selected ? edge.selected && store.multiselectionKeyPressed && unselectNodesAndEdges({
910
- nodes: [],
911
- edges: [edge]
912
- }) : addSelectedEdges([id]));
913
- }, moveSelectedNodes = (direction, factor) => {
914
- let nodeUpdates = /* @__PURE__ */ new Map(), xVelo = store.snapGrid?.[0] ?? 5, yVelo = store.snapGrid?.[1] ?? 5, xDiff = direction.x * xVelo * factor, yDiff = direction.y * yVelo * factor;
915
- for (let node of nodeLookup.values()) {
916
- if (!(node.selected && (node.draggable || store.nodesDraggable && node.draggable === void 0))) continue;
917
- let nextPosition = {
918
- x: node.internals.positionAbsolute.x + xDiff,
919
- y: node.internals.positionAbsolute.y + yDiff
920
- };
921
- store.snapGrid && (nextPosition = snapPosition(nextPosition, store.snapGrid));
922
- let { position } = calculateNodePosition({
923
- nodeId: node.id,
924
- nextPosition,
925
- nodeLookup,
926
- nodeExtent: store.nodeExtent,
927
- nodeOrigin: store.nodeOrigin,
928
- onError: store.onError
929
- });
930
- nodeUpdates.set(node.id, { position });
931
- }
932
- updateNodePositions(nodeUpdates);
933
- }, panBy$1 = (delta) => panBy({
1156
+ }, stableSetViewport = (viewport) => setViewportStore(() => viewport), { unselectNodesAndEdges, addSelectedNodes, addSelectedEdges, handleNodeSelection, handleEdgeSelection, moveSelectedNodes } = createSelectionCommands({
1157
+ store,
1158
+ setNodesStore,
1159
+ setEdgesStore,
1160
+ setSelectionRect,
1161
+ setSelectionRectMode,
1162
+ nodeLookup,
1163
+ edgeLookup,
1164
+ updateNodePositions
1165
+ }), panBy$1 = (delta) => panBy({
934
1166
  delta,
935
1167
  panZoom: store.panZoom,
936
1168
  transform: store.transform,
@@ -1007,8 +1239,27 @@ const createLayoutedEdges = (source) => {
1007
1239
  get snapGrid() {
1008
1240
  return store.snapGrid;
1009
1241
  }
1010
- }, getNodeRect = (node) => {
1011
- let nodeToUse = isNode(node) ? node : nodeLookup.get(node.id), position = nodeToUse.parentId ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, store.nodeOrigin) : nodeToUse.position, nodeWithPosition = {
1242
+ }, intersectionGrid = null, intersectionRows = null, queryIntersectionCandidates = (rect) => untrack(() => {
1243
+ if (!intersectionGrid) {
1244
+ let grid = new SpatialGrid(300), rows = /* @__PURE__ */ new Map();
1245
+ for (let node of store.nodes) {
1246
+ let internalNode = nodeLookup.get(node.id);
1247
+ internalNode && (grid.insert(node.id, nodeToRect(internalNode)), rows.set(node.id, node));
1248
+ }
1249
+ intersectionGrid = grid, intersectionRows = rows, queueMicrotask(() => {
1250
+ intersectionGrid = null, intersectionRows = null;
1251
+ });
1252
+ }
1253
+ let rows = intersectionRows, result = [];
1254
+ for (let id of intersectionGrid.queryRect(rect)) {
1255
+ let row = rows.get(id);
1256
+ row && result.push(row);
1257
+ }
1258
+ return result;
1259
+ }), getNodeRect = (node) => {
1260
+ let nodeToUse = isNode(node) ? node : nodeLookup.get(node.id);
1261
+ if (!nodeToUse) return null;
1262
+ let position = nodeToUse.parentId ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, store.nodeOrigin) : nodeToUse.position, nodeWithPosition = {
1012
1263
  ...nodeToUse,
1013
1264
  position,
1014
1265
  width: nodeToUse.measured?.width ?? nodeToUse.width,
@@ -1062,7 +1313,7 @@ const createLayoutedEdges = (source) => {
1062
1313
  x,
1063
1314
  y,
1064
1315
  zoom
1065
- ], _snapGrid !== null, _snapGrid || [1, 1]);
1316
+ ], !!_snapGrid, _snapGrid || [1, 1]);
1066
1317
  },
1067
1318
  flowToScreenPosition: (position) => {
1068
1319
  if (!store.domNode) return position;
@@ -1126,14 +1377,18 @@ const createLayoutedEdges = (source) => {
1126
1377
  let remainingNodes = store.nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id));
1127
1378
  store.onNodesDelete?.(matchingNodes), setNodesStore(() => remainingNodes);
1128
1379
  }
1129
- return {
1380
+ let deletedNodes = matchingNodes ?? [], deletedEdges = matchingEdges ?? [];
1381
+ return (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
1382
+ nodes: deletedNodes,
1383
+ edges: deletedEdges
1384
+ }), {
1130
1385
  deletedNodes: matchingNodes,
1131
1386
  deletedEdges: matchingEdges
1132
1387
  };
1133
1388
  },
1134
1389
  getIntersectingNodes: (nodeOrRect, partially = !0, nodesToIntersect) => {
1135
1390
  let isRect = isRectObject(nodeOrRect), nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
1136
- return nodeRect ? (nodesToIntersect || store.nodes).filter((n) => {
1391
+ return nodeRect ? (nodesToIntersect ?? queryIntersectionCandidates(nodeRect)).filter((n) => {
1137
1392
  let internalNode = nodeLookup.get(n.id);
1138
1393
  if (!internalNode || !isRect && n.id === nodeOrRect.id) return !1;
1139
1394
  let currNodeRect = nodeToRect(internalNode), overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
@@ -1189,10 +1444,6 @@ const createLayoutedEdges = (source) => {
1189
1444
  extent: store.translateExtent
1190
1445
  }), ({ panZoom, extent }) => {
1191
1446
  panZoom?.setTranslateExtent(extent);
1192
- }), createEffect(() => new Set(store.nodes.map((n) => n.id)), (currentIds) => {
1193
- setMeasurementsStore((draft) => {
1194
- for (let id of Object.keys(draft)) currentIds.has(id) || delete draft[id];
1195
- });
1196
1447
  }), {
1197
1448
  store,
1198
1449
  flow,
@@ -1204,14 +1455,12 @@ const createLayoutedEdges = (source) => {
1204
1455
  parentIds,
1205
1456
  connections,
1206
1457
  actions: {
1207
- getEdge,
1458
+ getLayoutedEdge,
1208
1459
  applyInitialFitView,
1209
1460
  applyMeasurementWrites,
1210
1461
  applyNodeChanges,
1211
1462
  markInitialNodesMeasured,
1212
1463
  setMeasureRequester,
1213
- resetStoreValues,
1214
- setAriaLabelConfig,
1215
1464
  setAriaLiveMessage,
1216
1465
  setClickConnectStartHandle,
1217
1466
  setConfig,
@@ -1219,15 +1468,11 @@ const createLayoutedEdges = (source) => {
1219
1468
  setDeleteKeyPressed,
1220
1469
  setDomNode,
1221
1470
  setDragging,
1222
- get setEdges() {
1223
- return setEdgesStore;
1224
- },
1471
+ setEdges: setEdgesStore,
1225
1472
  setElementsSelectable,
1226
1473
  setHeight,
1227
1474
  setMultiselectionKeyPressed,
1228
- get setNodes() {
1229
- return setNodesStore;
1230
- },
1475
+ setNodes: setNodesStore,
1231
1476
  setNodesConnectable,
1232
1477
  setNodesDraggable,
1233
1478
  setPanActivationKeyPressed,
@@ -1235,9 +1480,7 @@ const createLayoutedEdges = (source) => {
1235
1480
  setSelectionKeyPressed,
1236
1481
  setSelectionRect,
1237
1482
  setSelectionRectMode,
1238
- get setViewport() {
1239
- return (viewport) => setViewportStore(() => viewport);
1240
- },
1483
+ setViewport: stableSetViewport,
1241
1484
  setWidth,
1242
1485
  setZoomActivationKeyPressed,
1243
1486
  addEdge,
@@ -1246,7 +1489,6 @@ const createLayoutedEdges = (source) => {
1246
1489
  zoomOut,
1247
1490
  fitView,
1248
1491
  setCenter,
1249
- setPaneClickDistance,
1250
1492
  unselectNodesAndEdges,
1251
1493
  addSelectedNodes,
1252
1494
  addSelectedEdges,
@@ -1258,25 +1500,35 @@ const createLayoutedEdges = (source) => {
1258
1500
  reset
1259
1501
  }
1260
1502
  };
1503
+ }, createEdgeStore = (edges) => {
1504
+ let [store, setStore] = typeof edges == "function" ? createStore(edges, []) : createStore(edges);
1505
+ return [store, setStore];
1261
1506
  }, createNodeStore = (nodes) => {
1262
- let [store, setStore] = createStore(nodes);
1507
+ let [store, setStore] = typeof nodes == "function" ? createStore(nodes, []) : createStore(nodes);
1263
1508
  return [store, setStore];
1264
1509
  }, EdgeWrapper = (props) => {
1265
- let edgeRef, { store, actions } = useInternalSolidFlow(), edgeId = () => props.edgeId, edge = () => actions.getEdge(edgeId()), edgeType = () => edge().type ?? "default", selectable = () => edge().selectable ?? store.elementsSelectable, focusable = () => edge().focusable ?? store.edgesFocusable, edgeComponent = () => store.edgeTypes[edgeType()], markerStartUrl = () => edge().markerStart ? `url('#${getMarkerId(edge().markerStart, store.id)}')` : void 0, markerEndUrl = () => edge().markerEnd ? `url('#${getMarkerId(edge().markerEnd, store.id)}')` : void 0, onClick = (event) => {
1510
+ let edgeRef, { store, actions } = useInternalSolidFlow(), edgeId = () => props.edgeId, edge = () => actions.getLayoutedEdge(edgeId()), edgeType = () => edge().type ?? "default", selectable = () => isEdgeSelectable(edge(), store), focusable = () => edge().focusable ?? store.edgesFocusable, edgeTypeValid = () => edgeType() in store.edgeTypes, edgeComponent = () => store.edgeTypes[edgeTypeValid() ? edgeType() : "default"];
1511
+ createEffect(() => ({
1512
+ valid: edgeTypeValid(),
1513
+ edgeType: edgeType()
1514
+ }), ({ valid, edgeType }) => {
1515
+ valid || emitFlowError(store.onError, "011", errorMessages.error011(edgeType));
1516
+ });
1517
+ let markerStartUrl = () => edge().markerStart ? `url('#${getMarkerId(edge().markerStart, store.id)}')` : void 0, markerEndUrl = () => edge().markerEnd ? `url('#${getMarkerId(edge().markerEnd, store.id)}')` : void 0, onClick = (event) => {
1266
1518
  selectable() && actions.handleEdgeSelection(edgeId()), props.onEdgeClick?.({
1267
1519
  edge: edge(),
1268
1520
  event
1269
1521
  });
1270
- }, onPointerEvent = (event, type) => {
1271
- ({
1272
- contextmenu: props.onEdgeContextMenu,
1273
- pointerenter: props.onEdgePointerEnter,
1274
- pointerleave: props.onEdgePointerLeave
1275
- })[type]?.({
1276
- edge: edge(),
1277
- event
1278
- });
1279
- }, onKeyDown = (event) => {
1522
+ }, onContextMenu = (event) => props.onEdgeContextMenu?.({
1523
+ edge: edge(),
1524
+ event
1525
+ }), onPointerEnter = (event) => props.onEdgePointerEnter?.({
1526
+ edge: edge(),
1527
+ event
1528
+ }), onPointerLeave = (event) => props.onEdgePointerLeave?.({
1529
+ edge: edge(),
1530
+ event
1531
+ }), onKeyDown = (event) => {
1280
1532
  store.disableKeyboardA11y || !elementSelectionKeys.includes(event.key) || !selectable() || (event.key === "Escape" ? actions.unselectNodesAndEdges({ edges: [edge()] }) : actions.addSelectedEdges([edge().id]));
1281
1533
  }, ariaLabel = () => edge().ariaLabel ?? `Edge from ${edge().source} to ${edge().target}`, culled = createMemo(() => isEdgeCulled(edge(), store.cullingViewport));
1282
1534
  return <EdgeIdContext value={edgeId}>
@@ -1295,7 +1547,7 @@ const createLayoutedEdges = (source) => {
1295
1547
  selectable: !!selectable()
1296
1548
  },
1297
1549
  edge().class
1298
- ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={(e) => onPointerEvent(e, "contextmenu")} onPointerEnter={(e) => onPointerEvent(e, "pointerenter")} onPointerLeave={(e) => onPointerEvent(e, "pointerleave")} {...edge().domAttributes}>
1550
+ ]} onClick={onClick} onKeyDown={(e) => focusable() && onKeyDown(e)} onContextMenu={onContextMenu} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave} {...edge().domAttributes}>
1299
1551
  <Dynamic component={edgeComponent()} id={edge().id} source={edge().source} target={edge().target} sourceX={edge().sourceX} sourceY={edge().sourceY} targetX={edge().targetX} targetY={edge().targetY} sourcePosition={edge().sourcePosition} targetPosition={edge().targetPosition} animated={edge().animated} selected={edge().selected} label={edge().label} labelStyle={edge().labelStyle} data={edge().data} style={edge().style} interactionWidth={edge().interactionWidth} selectable={selectable()} deletable={edge().deletable ?? !0} type={edgeType()} sourceHandleId={edge().sourceHandle} targetHandleId={edge().targetHandle} markerStart={markerStartUrl()} markerEnd={markerEndUrl()} />
1300
1552
  </g>
1301
1553
  </svg>
@@ -1419,7 +1671,7 @@ const Handle = (props) => {
1419
1671
  position: "top",
1420
1672
  isConnectableStart: !0,
1421
1673
  isConnectableEnd: !0
1422
- }), { store, nodeLookup, connections, actions } = useInternalSolidFlow(), rest = omit(_props, "id", "type", "position", "isConnectable", "isConnectableStart", "isConnectableEnd", "isValidConnection", "onConnect", "onDisconnect", "children", "class", "style"), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => _props.isConnectable ?? nodeConnectable(), isTarget = () => _props.type === "target", handleId = () => _props.id ?? null, connectionInProcess = () => !!store.connection.fromHandle, connectingFrom = () => store.connection.fromHandle && store.connection.fromHandle.nodeId === nodeId() && store.connection.fromHandle.type === _props.type && store.connection.fromHandle.id === handleId(), connectingTo = () => store.connection.toHandle && store.connection.toHandle.nodeId === nodeId() && store.connection.toHandle.type === _props.type && store.connection.toHandle.id === handleId(), isPossibleTargetHandle = () => store.connectionMode === "strict" ? store.connection.fromHandle?.type !== _props.type : nodeId() !== store.connection.fromHandle?.nodeId || handleId() !== store.connection.fromHandle?.id, valid = () => !!(connectingTo() && store.connection.isValid), prevConnections = null;
1674
+ }), { store, nodeLookup, connections, actions } = useInternalSolidFlow(), rest = omit(_props, "id", "type", "position", "isConnectable", "isConnectableStart", "isConnectableEnd", "isValidConnection", "onConnect", "onDisconnect", "children", "class", "style"), nodeId = useNodeId(), nodeConnectable = useNodeConnectable(), connectable = () => _props.isConnectable ?? nodeConnectable(), isTarget = () => _props.type === "target", handleId = () => _props.id ?? null, originState = () => store.connectionOriginByHandle[connectionKey(nodeId(), _props.type, handleId())], connectingFrom = () => originState() === "from", targetState = () => store.connectionTargetByHandle[connectionKey(nodeId(), _props.type, handleId())], connectingTo = () => targetState() !== void 0, valid = () => targetState() === "valid", prevConnections = null;
1423
1675
  createEffect(() => {
1424
1676
  if (!_props.onConnect && !_props.onDisconnect) return null;
1425
1677
  let rec = connections[connectionKey(nodeId(), _props.type, _props.id)], map = /* @__PURE__ */ new Map();
@@ -1437,37 +1689,25 @@ const Handle = (props) => {
1437
1689
  }, edge = store.onBeforeConnect?.(handleConnection) ?? handleConnection;
1438
1690
  actions.addEdge(edge), store.onConnect?.(handleConnection);
1439
1691
  }, onPointerDown = (event) => {
1692
+ let gestureLookup = armConnectionGestureLookup({
1693
+ event,
1694
+ real: nodeLookup,
1695
+ domNode: store.domNode,
1696
+ getTransform: () => store.transform,
1697
+ connectionRadius: store.connectionRadius
1698
+ });
1440
1699
  XYHandle.onPointerDown(event, {
1700
+ ...buildConnectionGestureParams({
1701
+ event,
1702
+ store,
1703
+ actions,
1704
+ gestureLookup
1705
+ }),
1441
1706
  handleId: handleId(),
1442
1707
  nodeId: nodeId(),
1443
1708
  isTarget: isTarget(),
1444
- connectionRadius: store.connectionRadius,
1445
- domNode: store.domNode,
1446
- nodeLookup,
1447
- connectionMode: store.connectionMode,
1448
- lib: store.lib,
1449
- autoPanOnConnect: store.autoPanOnConnect,
1450
- flowId: store.id,
1451
1709
  isValidConnection: _props.isValidConnection ?? store.isValidConnection,
1452
- updateConnection: ((connection) => {
1453
- actions.setConnection(connection), flush();
1454
- }),
1455
- cancelConnection: actions.cancelConnection,
1456
- panBy: actions.panBy,
1457
- onConnect: onConnectExtended,
1458
- onConnectStart: (event, startParams) => {
1459
- store.onConnectStart?.(event, {
1460
- nodeId: startParams.nodeId,
1461
- handleId: startParams.handleId,
1462
- handleType: startParams.handleType
1463
- });
1464
- },
1465
- onConnectEnd: store.onConnectEnd,
1466
- getTransform: () => store.transform,
1467
- getFromHandle: () => store.connection.fromHandle,
1468
- autoPanSpeed: store.autoPanSpeed,
1469
- dragThreshold: store.connectionDragThreshold,
1470
- handleDomNode: event.currentTarget
1710
+ onConnect: onConnectExtended
1471
1711
  });
1472
1712
  }, onClick = (event) => {
1473
1713
  if (!nodeId() || !store.clickConnectStartHandle && !_props.isConnectableStart) return;
@@ -1502,7 +1742,7 @@ const Handle = (props) => {
1502
1742
  isValid && connection && onConnectExtended(connection);
1503
1743
  let connectionClone = structuredClone(snapshot(store.connection));
1504
1744
  delete connectionClone.inProgress, connectionClone.toPosition = connectionClone.toHandle ? connectionClone.toHandle.position : null, store.onClickConnectEnd?.(event, connectionClone), actions.setClickConnectStartHandle(void 0);
1505
- }, connectionIndicator = () => connectable() && (!connectionInProcess() || isPossibleTargetHandle()) && (connectionInProcess() || store.clickConnectStartHandle ? _props.isConnectableEnd : _props.isConnectableStart);
1745
+ };
1506
1746
  return <div {...rest} role="button" aria-label={store.ariaLabelConfig["handle.ariaLabel"]} tabindex={-1} data-handleid={handleId()} data-nodeid={nodeId()} data-handlepos={_props.position} data-id={`${store.id}-${nodeId()}-${_props.id || null}-${_props.type}`} onClick={store.clickConnect ? onClick : void 0} onPointerDown={onPointerDown} style={_props.style} class={[
1507
1747
  "solid-flow__handle",
1508
1748
  `solid-flow__handle-${_props.position}`,
@@ -1518,7 +1758,7 @@ const Handle = (props) => {
1518
1758
  connectablestart: _props.isConnectableStart,
1519
1759
  connectableend: _props.isConnectableEnd,
1520
1760
  connectable: !!connectable(),
1521
- connectionindicator: connectionIndicator()
1761
+ excluded: !!originState()
1522
1762
  }
1523
1763
  ]}>
1524
1764
  {_props.children}
@@ -1617,7 +1857,7 @@ const NodeWrapper = (props) => {
1617
1857
  let el = nodeRef();
1618
1858
  el && props.resizeObserver?.unobserve(el);
1619
1859
  });
1620
- let nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeType()], isParentNode = () => !!parentIds[node().id], transform = () => {
1860
+ let nodeId = () => node().id, nodeType = () => node().type ?? "default", deletable = () => node().deletable ?? !0, selectable = () => node().selectable ?? store.elementsSelectable, focusable = () => node().focusable ?? store.nodesFocusable, draggable = () => node().draggable ?? store.nodesDraggable, connectable = () => node().connectable ?? store.nodesConnectable, userNode = () => node().internals.userNode, nodeTypeValid = () => nodeType() in store.nodeTypes, nodeComponent = () => store.nodeTypes[nodeTypeValid() ? nodeType() : "default"], isParentNode = () => !!parentIds[node().id], transform = () => {
1621
1861
  let { x, y } = node().internals.positionAbsolute;
1622
1862
  return `translate(${x}px, ${y}px)`;
1623
1863
  }, sizeStyle = () => {
@@ -1628,17 +1868,18 @@ const NodeWrapper = (props) => {
1628
1868
  ...h ? { height: toPxString(h) } : {}
1629
1869
  };
1630
1870
  }, culled = createMemo(() => isNodeCulled(node(), store.cullingViewport)), style = () => ({
1871
+ ...sizeStyle(),
1631
1872
  "z-index": node().internals.z,
1632
1873
  transform: transform(),
1633
1874
  visibility: culled() || !nodeHasDimensions(node()) ? "hidden" : "visible",
1634
- "pointer-events": culled() ? "none" : void 0,
1635
- ...sizeStyle(),
1636
- ...node().style ?? {}
1875
+ "pointer-events": culled() ? "none" : void 0
1637
1876
  });
1638
1877
  createEffect(() => ({
1639
1878
  valid: nodeTypeValid(),
1640
1879
  nodeType: nodeType()
1641
- }), ({ valid, nodeType }) => {}), createEffect(() => ({
1880
+ }), ({ valid, nodeType }) => {
1881
+ valid || emitFlowError(store.onError, "003", errorMessages.error003(nodeType));
1882
+ }), createEffect(() => ({
1642
1883
  id: node().id,
1643
1884
  nodeElement: nodeRef(),
1644
1885
  nodeType: nodeType(),
@@ -1893,7 +2134,7 @@ const InitialNodeTypesMap = {
1893
2134
  pendingEntries = updateEntries, scheduleIdleCallback(() => {
1894
2135
  let updates = new Map(pendingEntries);
1895
2136
  pendingEntries = void 0;
1896
- let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode);
2137
+ let { updatedInternals, measurementWrites, changes, parentExpandChildren } = measureNodeInternals(updates, nodeLookup, store.domNode, store.nodeExtent);
1897
2138
  updatedInternals && (actions.applyMeasurementWrites(measurementWrites), flush(), parentExpandChildren.length > 0 && changes.push(...handleExpandParent(parentExpandChildren, nodeLookup, (parentId) => store.nodes.filter((node) => node.parentId === parentId), store.nodeOrigin)), actions.applyNodeChanges(changes), flush(), actions.markInitialNodesMeasured());
1898
2139
  });
1899
2140
  };
@@ -1914,29 +2155,31 @@ function useInternalSolidFlow() {
1914
2155
  //#region src/components/connection/ConnectionLine.tsx
1915
2156
  /** Internal component rendering the in-progress connection line. */
1916
2157
  const ConnectionLine = (props) => {
1917
- let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid);
1918
- return <Show when={store.connection.inProgress}>
1919
- <svg class="solid-flow__container solid-flow__connectionline" width={store.width} height={store.height} style={props.containerStyle}>
1920
- <g class={["solid-flow__connection", connectionStatus()]}>
1921
- <Show when={props.component} fallback={<InternalConnectionLine style={props.style} />}>
1922
- {(CustomComponent) => {
2158
+ let { store } = useInternalSolidFlow(), connectionStatus = () => getConnectionStatus(store.connection.isValid), inProgress = createMemo(() => {
2159
+ let state = store.connection;
2160
+ return state.inProgress ? state : null;
2161
+ });
2162
+ return <Show when={inProgress()}>
2163
+ {(connection) => <svg class="solid-flow__container solid-flow__connectionline" width={store.width} height={store.height} style={props.containerStyle}>
2164
+ <g class={["solid-flow__connection", connectionStatus()]}>
2165
+ <Show when={props.component} fallback={<InternalConnectionLine style={props.style} connection={connection()} />}>
2166
+ {(CustomComponent) => {
1923
2167
  let UserConnectionLine = CustomComponent();
1924
- return <UserConnectionLine connectionLineType={store.connectionLineType} connectionLineStyle={props.style} fromNode={store.connection.fromNode} fromHandle={store.connection.fromHandle} fromX={store.connection.from.x} fromY={store.connection.from.y} toX={store.connection.to.x} toY={store.connection.to.y} fromPosition={store.connection.fromPosition} toPosition={store.connection.toPosition} connectionStatus={connectionStatus()} toNode={store.connection.toNode} toHandle={store.connection.toHandle} />;
2168
+ return <UserConnectionLine connectionLineType={store.connectionLineType} connectionLineStyle={props.style} fromNode={connection().fromNode} fromHandle={connection().fromHandle} fromX={connection().from.x} fromY={connection().from.y} toX={connection().to.x} toY={connection().to.y} fromPosition={connection().fromPosition} toPosition={connection().toPosition} connectionStatus={connectionStatus()} toNode={connection().toNode} toHandle={connection().toHandle} />;
1925
2169
  }}
1926
- </Show>
1927
- </g>
1928
- </svg>
2170
+ </Show>
2171
+ </g>
2172
+ </svg>}
1929
2173
  </Show>;
1930
2174
  }, InternalConnectionLine = (props) => {
1931
2175
  let { store } = useInternalSolidFlow(), path = () => {
1932
- if (!store.connection.inProgress) return;
1933
2176
  let pathParams = {
1934
- sourceX: store.connection.from.x,
1935
- sourceY: store.connection.from.y,
1936
- sourcePosition: store.connection.fromPosition,
1937
- targetX: store.connection.to.x,
1938
- targetY: store.connection.to.y,
1939
- targetPosition: store.connection.toPosition
2177
+ sourceX: props.connection.from.x,
2178
+ sourceY: props.connection.from.y,
2179
+ sourcePosition: props.connection.fromPosition,
2180
+ targetX: props.connection.to.x,
2181
+ targetY: props.connection.to.y,
2182
+ targetPosition: props.connection.toPosition
1940
2183
  };
1941
2184
  switch (store.connectionLineType) {
1942
2185
  case "default": {
@@ -1985,19 +2228,26 @@ const ConnectionLine = (props) => {
1985
2228
  </defs>
1986
2229
  </svg>
1987
2230
  </Show>;
1988
- }, EdgeRenderer = (props) => {
1989
- let { store, actions } = useInternalSolidFlow(), [focusedEdgeId, setFocusedEdgeId] = createSignal(null), onFocusIn = (event) => {
1990
- let edgeElement = event.target.closest("[data-id]");
1991
- setFocusedEdgeId(edgeElement?.getAttribute("data-id") ?? null);
2231
+ }, createFocusedIdTracker = () => {
2232
+ let [focusedId, setFocusedId] = createSignal(null);
2233
+ return {
2234
+ focusedId,
2235
+ onFocusIn: (event) => {
2236
+ let element = event.target.closest("[data-id]");
2237
+ setFocusedId(element?.getAttribute("data-id") ?? null);
2238
+ },
2239
+ onFocusOut: () => setFocusedId(null)
1992
2240
  };
1993
- return <div class="solid-flow__edges" onFocusIn={onFocusIn} onFocusOut={() => setFocusedEdgeId(null)}>
2241
+ }, EdgeRenderer = (props) => {
2242
+ let { store, actions } = useInternalSolidFlow(), { focusedId: focusedEdgeId, onFocusIn, onFocusOut } = createFocusedIdTracker();
2243
+ return <div class="solid-flow__edges" onFocusIn={onFocusIn} onFocusOut={onFocusOut}>
1994
2244
  <MarkerDefinition />
1995
2245
 
1996
2246
  <For each={store.visibleEdgeIds}>
1997
2247
  {(edgeId) => {
1998
2248
  let unmounted = createMemo(() => {
1999
2249
  if (!store.onlyRenderVisibleElements || focusedEdgeId() === edgeId) return !1;
2000
- let edge = actions.getEdge(edgeId);
2250
+ let edge = actions.getLayoutedEdge(edgeId);
2001
2251
  return !!edge && isEdgeCulled(edge, store.cullingViewport);
2002
2252
  });
2003
2253
  return <Show when={!unmounted()}>
@@ -2020,11 +2270,8 @@ const ConnectionLine = (props) => {
2020
2270
  onCleanup(() => {
2021
2271
  resizeObserver?.disconnect();
2022
2272
  });
2023
- let [focusedNodeId, setFocusedNodeId] = createSignal(null), onFocusIn = (event) => {
2024
- let nodeElement = event.target.closest("[data-id]");
2025
- setFocusedNodeId(nodeElement?.getAttribute("data-id") ?? null);
2026
- };
2027
- return <div class="solid-flow__container solid-flow__nodes" onFocusIn={onFocusIn} onFocusOut={() => setFocusedNodeId(null)}>
2273
+ let { focusedId: focusedNodeId, onFocusIn, onFocusOut } = createFocusedIdTracker();
2274
+ return <div class="solid-flow__container solid-flow__nodes" onFocusIn={onFocusIn} onFocusOut={onFocusOut}>
2028
2275
  <For each={store.visibleNodeIds}>
2029
2276
  {(nodeId) => {
2030
2277
  let unmounted = createMemo(() => {
@@ -2043,10 +2290,10 @@ const ConnectionLine = (props) => {
2043
2290
  for (let item of a) if (!b.has(item)) return !1;
2044
2291
  return !0;
2045
2292
  }, Pane = (props) => {
2046
- let { store, nodeLookup, edgeLookup, connections, actions } = useInternalSolidFlow(), [containerRef, setContainerRef] = createSignal(), container, containerBounds = null, connectionEndedOnPane = !1, selectionInProgress = !1, selectedNodeIds = /* @__PURE__ */ new Set(), selectedEdgeIds = /* @__PURE__ */ new Set(), autoPanId = 0, position = {
2293
+ let { store, nodeLookup, edgeLookup, connections, actions } = useInternalSolidFlow(), [containerRef, setContainerRef] = createSignal(), container, containerBounds = null, connectionEndedOnPane = !1, selectionInProgress = !1, selectionSpatialLookup = new GestureSpatialLookup(nodeLookup, 400), selectedNodeIds = /* @__PURE__ */ new Set(), selectedEdgeIds = /* @__PURE__ */ new Set(), autoPanId = 0, position = {
2047
2294
  x: 0,
2048
2295
  y: 0
2049
- }, autoPanStarted = !1, autoPanOnSelection = () => props.autoPanOnSelection ?? !0, paneClickDistance = () => props.paneClickDistance ?? 1, _panOnDrag = () => store.panActivationKeyPressed || props.panOnDrag, isSelecting = () => store.selectionKeyPressed || !!store.selectionRect || props.selectionOnDrag && _panOnDrag() !== !0, isSelectionEnabled = () => store.elementsSelectable && (isSelecting() || store.selectionRectMode === "user"), onClick = (event) => {
2296
+ }, autoPanStarted = !1, autoPanOnSelection = () => props.autoPanOnSelection ?? !0, paneClickDistance = () => props.paneClickDistance ?? 0, _panOnDrag = () => store.panActivationKeyPressed || props.panOnDrag, isSelecting = () => store.selectionKeyPressed || !!store.selectionRect || props.selectionOnDrag && _panOnDrag() !== !0, isSelectionEnabled = () => store.elementsSelectable && (isSelecting() || store.selectionRectMode === "user"), onClick = (event) => {
2050
2297
  if (event.target === container) {
2051
2298
  if (selectionInProgress || store.connection.inProgress || connectionEndedOnPane) {
2052
2299
  selectionInProgress = !1, connectionEndedOnPane = !1;
@@ -2058,7 +2305,7 @@ const ConnectionLine = (props) => {
2058
2305
  if (event.pointerType === "touch" && _panOnDrag() !== !1 && !store.selectionKeyPressed || (containerBounds = container?.getBoundingClientRect() ?? null, !containerBounds)) return;
2059
2306
  let eventTargetIsContainer = event.target === container, isNoKeyEvent = !eventTargetIsContainer && !!event.target.closest(".nokey"), isSelectionActive = props.selectionOnDrag && eventTargetIsContainer || store.selectionKeyPressed;
2060
2307
  if (isNoKeyEvent || !isSelecting() || !isSelectionActive || event.button !== 0 || !event.isPrimary) return;
2061
- event.target?.setPointerCapture?.(event.pointerId), selectionInProgress = !1, autoPanStarted = !1;
2308
+ event.target?.setPointerCapture?.(event.pointerId), selectionSpatialLookup.arm((node) => nodeToRect(node)), selectionInProgress = !1, autoPanStarted = !1;
2062
2309
  let { x, y } = getEventPosition(event, containerBounds), userSelectionFlowOrigin = pointToRendererPoint({
2063
2310
  x,
2064
2311
  y
@@ -2085,14 +2332,21 @@ const ConnectionLine = (props) => {
2085
2332
  width: Math.abs(mouseX - screenStart.x),
2086
2333
  height: Math.abs(mouseY - screenStart.y)
2087
2334
  }, prevSelectedNodeIds = selectedNodeIds, prevSelectedEdgeIds = selectedEdgeIds;
2088
- selectedNodeIds = new Set(getNodesInside(nodeLookup, nextUserSelectRect, store.transform, store.selectionMode === SelectionMode$1.Partial, !0).map((n) => n.id));
2089
- let edgesSelectable = store.defaultEdgeOptions.selectable ?? !0;
2090
- selectedEdgeIds = /* @__PURE__ */ new Set();
2335
+ {
2336
+ let [tx, ty, zoom] = store.transform;
2337
+ selectionSpatialLookup.setQueryRect({
2338
+ x: (nextUserSelectRect.x - tx) / zoom,
2339
+ y: (nextUserSelectRect.y - ty) / zoom,
2340
+ width: nextUserSelectRect.width / zoom,
2341
+ height: nextUserSelectRect.height / zoom
2342
+ });
2343
+ }
2344
+ selectedNodeIds = new Set(getNodesInside(selectionSpatialLookup, nextUserSelectRect, store.transform, store.selectionMode === SelectionMode$1.Partial, !0).map((n) => n.id)), selectedEdgeIds = /* @__PURE__ */ new Set();
2091
2345
  for (let nodeId of selectedNodeIds) {
2092
2346
  let nodeConnections = connections[nodeId];
2093
2347
  if (nodeConnections) for (let { edgeId } of Object.values(nodeConnections)) {
2094
2348
  let edge = edgeLookup[edgeId];
2095
- edge && (edge.selectable ?? edgesSelectable) && selectedEdgeIds.add(edgeId);
2349
+ edge && isEdgeSelectable(edge, store) && selectedEdgeIds.add(edgeId);
2096
2350
  }
2097
2351
  }
2098
2352
  isSetEqual(prevSelectedNodeIds, selectedNodeIds) || actions.setNodes((nodes) => {
@@ -2273,7 +2527,7 @@ const ConnectionLine = (props) => {
2273
2527
  <div class="solid-flow__selection" style={styles()} />
2274
2528
  </Show>;
2275
2529
  }, NodeSelection = (props) => {
2276
- let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = () => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null;
2530
+ let { store, nodeLookup, actions } = useInternalSolidFlow(), [ref, setRef] = createSignal(), bounds = createMemo(() => store.selectionRectMode === "nodes" ? getInternalNodesBounds(nodeLookup, { filter: (node) => !!node.selected }) : null);
2277
2531
  createEffect(() => ({
2278
2532
  el: ref(),
2279
2533
  focusable: !store.disableKeyboardA11y
@@ -2281,13 +2535,13 @@ const ConnectionLine = (props) => {
2281
2535
  focusable && el?.focus({ preventScroll: !0 });
2282
2536
  });
2283
2537
  let onContextMenu = (event) => {
2284
- let selectedNodes = store.nodes.filter((n) => n.selected);
2538
+ let selectedNodes = store.selectedNodes;
2285
2539
  props.onSelectionContextMenu?.({
2286
2540
  nodes: selectedNodes,
2287
2541
  event
2288
2542
  });
2289
2543
  }, onClick = (event) => {
2290
- let selectedNodes = store.nodes.filter((n) => n.selected);
2544
+ let selectedNodes = store.selectedNodes;
2291
2545
  props.onSelectionClick?.({
2292
2546
  nodes: selectedNodes,
2293
2547
  event
@@ -2336,29 +2590,13 @@ const ConnectionLine = (props) => {
2336
2590
  Solid Flow
2337
2591
  </a>
2338
2592
  </Panel>
2339
- </Show>;
2340
- //#endregion
2341
- //#region src/hooks/useSolidFlow.ts
2342
- /**
2343
- * Hook for accessing the flow instance: `{ flow, commands }` plus the
2344
- * commands spread at the top level for upstream familiarity.
2345
- *
2346
- * `flow` and `commands` are stable identities, so destructuring them is safe:
2347
- * `const { flow, commands } = useSolidFlow()`.
2348
- *
2349
- * @public
2350
- * @returns the flow's read struct and write surface
2351
- */
2352
- function useSolidFlow() {
2353
- let { flow, commands } = useInternalSolidFlow();
2354
- return {
2355
- ...commands,
2356
- flow,
2357
- commands
2358
- };
2359
- }
2360
- //#endregion
2361
- //#region src/components/utility/KeyHandler.ts
2593
+ </Show>, MODIFIER_FLAG = {
2594
+ alt: "altKey",
2595
+ control: "ctrlKey",
2596
+ ctrl: "ctrlKey",
2597
+ meta: "metaKey",
2598
+ shift: "shiftKey"
2599
+ };
2362
2600
  function isKeyObject(key) {
2363
2601
  return typeof key == "object" && !!key;
2364
2602
  }
@@ -2390,18 +2628,13 @@ function matchesKey(event, keyDef) {
2390
2628
  function matchesKeyArray(event, keyDefs) {
2391
2629
  return keyDefs ? (Array.isArray(keyDefs) ? keyDefs : [keyDefs]).some((keyDef) => matchesKey(event, keyDef)) : !1;
2392
2630
  }
2393
- const MODIFIER_FLAG = {
2394
- alt: "altKey",
2395
- control: "ctrlKey",
2396
- ctrl: "ctrlKey",
2397
- meta: "metaKey",
2398
- shift: "shiftKey"
2399
- };
2400
2631
  /**
2401
2632
  * Whether an event's modifier flags PROVE this key definition cannot be held
2402
2633
  * right now: the key itself is a modifier whose flag is off, or one of its
2403
2634
  * required modifiers is off. Definitions built on non-modifier keys are never
2404
- * contradicted (their state isn't derivable from flags).
2635
+ * contradicted (their state isn't derivable from flags). This is the
2636
+ * self-heal for OS overlays that swallow keyups without blurring the window
2637
+ * (upstream xyflow#5679).
2405
2638
  */
2406
2639
  function isContradicted(flags, keyDef) {
2407
2640
  let keyFlag = MODIFIER_FLAG[getKeyString(keyDef).toLowerCase()];
@@ -2418,6 +2651,28 @@ function allContradicted(flags, keyDefs) {
2418
2651
  let keys = Array.isArray(keyDefs) ? keyDefs : [keyDefs];
2419
2652
  return keys.length > 0 && keys.every((keyDef) => isContradicted(flags, keyDef));
2420
2653
  }
2654
+ //#endregion
2655
+ //#region src/hooks/useSolidFlow.ts
2656
+ /**
2657
+ * Hook for accessing the flow instance: `{ flow, commands }` plus the
2658
+ * commands spread at the top level for upstream familiarity.
2659
+ *
2660
+ * `flow` and `commands` are stable identities, so destructuring them is safe:
2661
+ * `const { flow, commands } = useSolidFlow()`.
2662
+ *
2663
+ * @public
2664
+ * @returns the flow's read struct and write surface
2665
+ */
2666
+ function useSolidFlow() {
2667
+ let { flow, commands } = useInternalSolidFlow();
2668
+ return {
2669
+ ...commands,
2670
+ flow,
2671
+ commands
2672
+ };
2673
+ }
2674
+ //#endregion
2675
+ //#region src/components/utility/KeyHandler.ts
2421
2676
  const KeyHandler = (props) => {
2422
2677
  let { store, actions } = useInternalSolidFlow(), { deleteElements } = useSolidFlow(), _props = {
2423
2678
  get selectionKey() {
@@ -2452,14 +2707,11 @@ const KeyHandler = (props) => {
2452
2707
  }, handleWindowBlur = () => {
2453
2708
  resetKeysAndSelection(), cancelPointerGestures();
2454
2709
  }, handleDelete = async () => {
2455
- let selectedNodes = store.nodes.filter((node) => node.selected), selectedEdges = store.edges.filter((edge) => edge.selected), { deletedNodes, deletedEdges } = await deleteElements({
2710
+ let selectedNodes = store.selectedNodes, selectedEdges = store.selectedEdges;
2711
+ await deleteElements({
2456
2712
  nodes: selectedNodes,
2457
2713
  edges: selectedEdges
2458
2714
  });
2459
- (deletedNodes.length > 0 || deletedEdges.length > 0) && store.onDelete?.({
2460
- nodes: deletedNodes,
2461
- edges: deletedEdges
2462
- });
2463
2715
  };
2464
2716
  return isServer || (createEventListenerMap(window, {
2465
2717
  keydown: (event) => {
@@ -2477,34 +2729,23 @@ const KeyHandler = (props) => {
2477
2729
  capture: !0,
2478
2730
  passive: !0
2479
2731
  })), null;
2480
- }, SolidFlow = (props) => {
2732
+ }, FLOW_PROP_KEYS = /* @__PURE__ */ "ariaLabelConfig.ariaLiveMessage.attributionPosition.autoPanOnConnect.autoPanOnNodeDrag.autoPanOnNodeFocus.autoPanOnSelection.autoPanSpeed.class.clickConnect.colorMode.colorModeSSR.connectionDragThreshold.connectionLineComponent.connectionLineContainerStyle.connectionLineStyle.connectionLineType.connectionMode.connectionRadius.defaultEdgeOptions.defaultEdges.defaultMarkerColor.defaultNodes.deleteKey.disableKeyboardA11y.edgeTypes.edges.edgesFocusable.elementsSelectable.elevateEdgesOnSelect.elevateNodesOnSelect.fitView.fitViewOptions.height.id.initialViewport.isValidConnection.maxZoom.minZoom.multiSelectionKey.noDragClass.noPanClass.noWheelClass.nodeClickDistance.nodeDragThreshold.nodeExtent.nodeOrigin.nodeTypes.nodes.nodesConnectable.nodesDraggable.nodesFocusable.onBeforeConnect.onBeforeDelete.onBeforeReconnect.onClickConnectEnd.onClickConnectStart.onConnect.onConnectEnd.onConnectStart.onDelete.onEdgeClick.onEdgeContextMenu.onEdgePointerEnter.onEdgePointerLeave.onEdgesDelete.onFlowError.onInit.onMove.onMoveEnd.onMoveStart.onNodeClick.onNodeContextMenu.onNodeDrag.onNodeDragStart.onNodeDragStop.onNodePointerEnter.onNodePointerLeave.onNodePointerMove.onNodesDelete.onPaneClick.onPaneContextMenu.onReconnect.onReconnectEnd.onReconnectStart.onSelectionChange.onSelectionClick.onSelectionContextMenu.onSelectionDrag.onSelectionDragStart.onSelectionDragStop.onSelectionEnd.onSelectionStart.onlyRenderVisibleElements.panActivationKey.panOnDrag.panOnScroll.panOnScrollMode.panOnScrollSpeed.paneClickDistance.preventScrolling.proOptions.selectNodesOnDrag.selectionKey.selectionMode.selectionOnDrag.snapGrid.style.translateExtent.viewport.width.zIndexMode.zoomActivationKey.zoomOnDoubleClick.zoomOnPinch.zoomOnScroll".split("."), SolidFlow = (props) => {
2481
2733
  let [domNodeRef, setDomNodeRef] = createSignal(), domNode, _props = merge({
2482
2734
  ...getDefaultFlowStateProps(),
2483
2735
  colorMode: "light",
2484
- deleteKeyCode: "Backspace",
2485
- defaultViewport: {
2486
- x: 0,
2487
- y: 0,
2488
- zoom: 1
2489
- },
2490
- multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
2491
2736
  nodeClickDistance: 0,
2492
2737
  panOnScroll: !1,
2493
- panActivationKeyCode: "Space",
2494
2738
  preventScrolling: !0,
2495
2739
  panOnDrag: !0,
2496
2740
  panOnScrollSpeed: .5,
2497
2741
  panOnScrollMode: "free",
2498
2742
  paneClickDistance: 0,
2499
- reconnectRadius: 10,
2500
- selectionKeyCode: "Shift",
2501
2743
  selectionOnDrag: !1,
2502
2744
  translateExtent: infiniteExtent,
2503
- zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
2504
2745
  zoomOnPinch: !0,
2505
2746
  zoomOnDoubleClick: !0,
2506
2747
  zoomOnScroll: !0
2507
- }, props), htmlProps = omit(_props, "nodes", "edges", "defaultNodes", "defaultEdges", "nodeTypes", "edgeTypes", "width", "height", "fitView", "fitViewOptions", "nodeOrigin", "nodeDragThreshold", "paneClickDistance", "nodeClickDistance", "minZoom", "maxZoom", "zIndexMode", "initialViewport", "viewport", "translateExtent", "nodeExtent", "selectionKey", "panActivationKey", "deleteKey", "multiSelectionKey", "zoomActivationKey", "panOnDrag", "panOnScroll", "panOnScrollMode", "panOnScrollSpeed", "selectionOnDrag", "selectNodesOnDrag", "preventScrolling", "zoomOnScroll", "zoomOnDoubleClick", "zoomOnPinch", "onlyRenderVisibleElements", "autoPanOnConnect", "autoPanOnNodeDrag", "autoPanOnNodeFocus", "autoPanOnSelection", "autoPanSpeed", "connectionRadius", "connectionMode", "connectionLineType", "connectionLineComponent", "connectionLineStyle", "connectionLineContainerStyle", "connectionDragThreshold", "isValidConnection", "clickConnect", "reconnectRadius", "selectionMode", "elementsSelectable", "nodesDraggable", "nodesConnectable", "nodesFocusable", "edgesFocusable", "disableKeyboardA11y", "ariaLabelConfig", "ariaLiveMessage", "colorMode", "colorModeSSR", "class", "style", "snapGrid", "defaultMarkerColor", "defaultEdgeOptions", "elevateNodesOnSelect", "elevateEdgesOnSelect", "noDragClass", "noPanClass", "noWheelClass", "attributionPosition", "proOptions", "onInit", "onMoveStart", "onMove", "onMoveEnd", "onFlowError", "onNodeClick", "onNodeContextMenu", "onNodeDrag", "onNodeDragStart", "onNodeDragStop", "onNodePointerEnter", "onNodePointerMove", "onNodePointerLeave", "onEdgeClick", "onEdgeContextMenu", "onEdgePointerEnter", "onEdgePointerLeave", "onPaneClick", "onPaneContextMenu", "onSelectionChange", "onSelectionClick", "onSelectionContextMenu", "onSelectionDrag", "onSelectionDragStart", "onSelectionDragStop", "onSelectionStart", "onSelectionEnd", "onConnect", "onConnectStart", "onConnectEnd", "onReconnect", "onReconnectStart", "onReconnectEnd", "onClickConnectStart", "onClickConnectEnd", "onBeforeConnect", "onBeforeReconnect", "onDelete", "onBeforeDelete", "deleteKeyCode", "selectionKeyCode", "panActivationKeyCode", "multiSelectionKeyCode", "zoomActivationKeyCode", "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2748
+ }, props), htmlProps = omit(_props, ...FLOW_PROP_KEYS, "children"), TypedSolidFlowContext = SolidFlowContext, solidFlow = useContext(TypedSolidFlowContext) ?? createSolidFlow(_props), { store, actions } = solidFlow;
2508
2749
  onSettled(() => (actions.applyInitialFitView(_props.fitView), actions.setConfig(_props), actions.setDomNode(domNode), () => {
2509
2750
  actions.reset();
2510
2751
  })), createEffect(() => domNodeRef(), (el) => {
@@ -2513,11 +2754,6 @@ const KeyHandler = (props) => {
2513
2754
  actions.setWidth(el.clientWidth), actions.setHeight(el.clientHeight);
2514
2755
  });
2515
2756
  return observer.observe(el), () => observer.disconnect();
2516
- }), createEffect(() => ({
2517
- panZoom: store.panZoom,
2518
- distance: _props.paneClickDistance
2519
- }), ({ panZoom, distance }) => {
2520
- panZoom?.setClickDistance(distance);
2521
2757
  });
2522
2758
  let selectedElements = createMemo(() => ({
2523
2759
  nodes: store.selectedNodes,
@@ -2526,7 +2762,7 @@ const KeyHandler = (props) => {
2526
2762
  createEffect(() => selectedElements(), (params) => {
2527
2763
  untrack(() => _props.onSelectionChange)?.(params);
2528
2764
  });
2529
- let rootStyle = () => ({
2765
+ let asyncSeedGuard = () => (_props.nodes?.length, _props.edges?.length, null), rootStyle = () => ({
2530
2766
  width: toPxString(_props.width),
2531
2767
  height: toPxString(_props.height),
2532
2768
  ..._props.style
@@ -2537,7 +2773,13 @@ const KeyHandler = (props) => {
2537
2773
  "solid-flow",
2538
2774
  "solid-flow__container",
2539
2775
  _props.class,
2540
- store.colorMode
2776
+ store.colorMode,
2777
+ {
2778
+ connecting: !!store.connectionFromHandle || !!store.clickConnectStartHandle,
2779
+ "connecting-from-source": (store.connectionFromHandle ?? store.clickConnectStartHandle)?.type === "source",
2780
+ "connecting-from-target": (store.connectionFromHandle ?? store.clickConnectStartHandle)?.type === "target",
2781
+ "connection-strict": store.connectionMode === "strict"
2782
+ }
2541
2783
  ]} style={rootStyle()} onScroll={(e) => {
2542
2784
  e.currentTarget.scrollTo({
2543
2785
  top: 0,
@@ -2546,12 +2788,13 @@ const KeyHandler = (props) => {
2546
2788
  });
2547
2789
  }} {...htmlProps}>
2548
2790
  <TypedSolidFlowContext value={solidFlow}>
2791
+ {asyncSeedGuard()}
2549
2792
  <KeyHandler selectionKey={_props.selectionKey} deleteKey={_props.deleteKey} panActivationKey={_props.panActivationKey} multiSelectionKey={_props.multiSelectionKey} zoomActivationKey={_props.zoomActivationKey} />
2550
2793
  <Zoom panOnScrollMode={_props.panOnScrollMode} preventScrolling={_props.preventScrolling} zoomOnScroll={_props.zoomOnScroll} zoomOnDoubleClick={_props.zoomOnDoubleClick} zoomOnPinch={_props.zoomOnPinch} panOnScroll={_props.panOnScroll} panOnScrollSpeed={_props.panOnScrollSpeed} panOnDrag={_props.panOnDrag} paneClickDistance={_props.paneClickDistance} selectionOnDrag={_props.selectionOnDrag} onMoveStart={_props.onMoveStart} onMove={_props.onMove} onMoveEnd={_props.onMoveEnd} onViewportInitialized={_props.onInit} initialViewport={_props.viewport || _props.initialViewport}>
2551
2794
  <Pane onPaneClick={_props.onPaneClick} onPaneContextMenu={_props.onPaneContextMenu} onSelectionStart={_props.onSelectionStart} onSelectionEnd={_props.onSelectionEnd} panOnDrag={_props.panOnDrag} selectionOnDrag={_props.selectionOnDrag} paneClickDistance={_props.paneClickDistance} autoPanOnSelection={_props.autoPanOnSelection}>
2552
2795
  <Viewport>
2553
2796
  <div class="solid-flow__container solid-flow__viewport-back" />
2554
- <EdgeRenderer reconnectRadius={_props.reconnectRadius} onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} defaultEdgeOptions={_props.defaultEdgeOptions} />
2797
+ <EdgeRenderer onEdgeClick={_props.onEdgeClick} onEdgeContextMenu={_props.onEdgeContextMenu} onEdgePointerEnter={_props.onEdgePointerEnter} onEdgePointerLeave={_props.onEdgePointerLeave} />
2555
2798
  <div class="solid-flow__container solid-flow__edge-labels" />
2556
2799
  <ConnectionLine type={_props.connectionLineType} component={_props.connectionLineComponent} containerStyle={_props.connectionLineContainerStyle} style={_props.connectionLineStyle} />
2557
2800
  <NodeRenderer nodeClickDistance={_props.nodeClickDistance} onNodeClick={_props.onNodeClick} onNodeContextMenu={_props.onNodeContextMenu} onNodePointerEnter={_props.onNodePointerEnter} onNodePointerMove={_props.onNodePointerMove} onNodePointerLeave={_props.onNodePointerLeave} onNodeDrag={_props.onNodeDrag} onNodeDragStart={_props.onNodeDragStart} onNodeDragStop={_props.onNodeDragStop} />
@@ -2609,8 +2852,8 @@ function useConnection() {
2609
2852
  * @returns store with an array of nodes
2610
2853
  */
2611
2854
  function useNodes() {
2612
- let { store } = useInternalSolidFlow();
2613
- return () => store.nodes;
2855
+ let { flow } = useInternalSolidFlow();
2856
+ return () => flow.nodes;
2614
2857
  }
2615
2858
  /**
2616
2859
  * Hook for getting the current edges from the store.
@@ -2619,8 +2862,8 @@ function useNodes() {
2619
2862
  * @returns store with an array of edges
2620
2863
  */
2621
2864
  function useEdges() {
2622
- let { store } = useInternalSolidFlow();
2623
- return () => store.edges;
2865
+ let { flow } = useInternalSolidFlow();
2866
+ return () => flow.edges;
2624
2867
  }
2625
2868
  /**
2626
2869
  * Hook for getting the current viewport from the store.
@@ -2678,6 +2921,52 @@ function useInternalNode(id) {
2678
2921
  return () => flow.internalNodes[id()];
2679
2922
  }
2680
2923
  //#endregion
2924
+ //#region src/hooks/useKeyPress.ts
2925
+ /**
2926
+ * Reactive "is this key (combo) held right now?" — the Solid Flow
2927
+ * counterpart of React Flow's `useKeyPress`, usable anywhere (no flow
2928
+ * context required).
2929
+ *
2930
+ * The definition is an Accessor per house convention — `useKeyPress(() =>
2931
+ * "a")`, `useKeyPress(() => ["a", "d"])`, or `useKeyPress(() => ({ key:
2932
+ * "s", modifier: ["meta"] }))`; swapping the definition resets the state.
2933
+ *
2934
+ * Hardened beyond upstream:
2935
+ * - Combos re-activate when the base key is re-pressed while the modifier
2936
+ * stays held (upstream's oldest open key bug, xyflow#2248).
2937
+ * - Stuck modifiers self-heal from the flags later keyboard/pointer/wheel
2938
+ * events carry (OS overlays swallow keyups without blurring — the macOS
2939
+ * screenshot HUD; xyflow#5679), and window blur resets.
2940
+ */
2941
+ function useKeyPress(keys) {
2942
+ let [pressed, setPressed] = createSignal(!1);
2943
+ if (createEffect(() => keys(), () => {
2944
+ setPressed(!1);
2945
+ }, { defer: !0 }), !isServer) {
2946
+ let reconcile = (event) => {
2947
+ pressed() && allContradicted(event, keys()) && (setPressed(!1), flush());
2948
+ };
2949
+ createEventListenerMap(window, {
2950
+ keydown: (event) => {
2951
+ reconcile(event), matchesKeyArray(event, keys()) && (setPressed(!0), flush());
2952
+ },
2953
+ keyup: (event) => {
2954
+ reconcile(event), matchesKeyArray(event, keys()) && (setPressed(!1), flush());
2955
+ },
2956
+ blur: () => {
2957
+ setPressed(!1), flush();
2958
+ }
2959
+ }), createEventListenerMap(window, {
2960
+ pointerdown: reconcile,
2961
+ wheel: reconcile
2962
+ }, {
2963
+ capture: !0,
2964
+ passive: !0
2965
+ });
2966
+ }
2967
+ return pressed;
2968
+ }
2969
+ //#endregion
2681
2970
  //#region src/hooks/useNodeConnections.ts
2682
2971
  /**
2683
2972
  * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
@@ -2863,7 +3152,7 @@ const DotPattern = (props) => {
2863
3152
  { selected: !!_props.selected },
2864
3153
  _props.class
2865
3154
  ]} x={_props.x} y={_props.y} rx={_props.borderRadius} ry={_props.borderRadius} width={_props.width} height={_props.height} shape-rendering={_props.shapeRendering} style={style()} onClick={_props.onClick ? (event) => _props.onClick(event, _props.id) : void 0} />;
2866
- }, getAttrFunction = (func) => func instanceof Function ? func : () => func, MiniMap = (props) => {
3155
+ }, getAttrFunction = (value) => value instanceof Function ? value : () => value, MiniMap = (props) => {
2867
3156
  let { store, nodeLookup } = useInternalSolidFlow(), _props = propDefaults(props, {
2868
3157
  position: "bottom-right",
2869
3158
  nodeClass: "",
@@ -2873,26 +3162,47 @@ const DotPattern = (props) => {
2873
3162
  width: 200,
2874
3163
  height: 150,
2875
3164
  nodeBorderRadius: 5,
3165
+ offsetScale: 5,
2876
3166
  nodeStrokeWidth: 2,
2877
3167
  style: {}
2878
- }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth", "nodeComponent", "onClick", "onNodeClick"), nodeColorFunc = () => _props.nodeColor === void 0 ? void 0 : getAttrFunction(_props.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(_props.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(_props.nodeClass), shapeRendering = () => typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), getViewBB = () => ({
3168
+ }), paneProps = omit(_props, "class", "style", "position", "nodeClass", "nodeStrokeColor", "nodeColor", "pannable", "zoomable", "inversePan", "zoomStep", "offsetScale", "bgColor", "width", "height", "maskColor", "maskStrokeColor", "maskStrokeWidth", "nodeBorderRadius", "nodeStrokeWidth", "nodeComponent", "onClick", "onNodeClick"), nodeColorFunc = () => _props.nodeColor === void 0 ? void 0 : getAttrFunction(_props.nodeColor), nodeStrokeColorFunc = () => getAttrFunction(_props.nodeStrokeColor), nodeClassFunc = () => getAttrFunction(_props.nodeClass), shapeRendering = typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision", labelledBy = createMemo(() => `solid-flow__minimap-desc-${store.id}`), viewBB = createMemo(() => ({
2879
3169
  x: -store.viewport.x / store.viewport.zoom,
2880
3170
  y: -store.viewport.y / store.viewport.zoom,
2881
3171
  width: store.width / store.viewport.zoom,
2882
3172
  height: store.height / store.viewport.zoom
2883
- }), getBoundingRect = () => {
2884
- let viewBB = getViewBB();
2885
- return nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(nodeLookup), viewBB) : viewBB;
2886
- }, getScaledWidth = () => getBoundingRect().width / _props.width, getScaledHeight = () => getBoundingRect().height / _props.height, getViewScale = () => Math.max(getScaledWidth(), getScaledHeight()), getViewWidth = () => getViewScale() * _props.width, getViewHeight = () => getViewScale() * _props.height, getOffset = () => 5 * getViewScale(), getX = () => {
2887
- let boundingRect = getBoundingRect();
2888
- return boundingRect.x - (getViewWidth() - boundingRect.width) / 2 - getOffset();
3173
+ })), rectsEqual = (a, b) => a === b || !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height, sampleBounds = () => untrack(() => {
3174
+ if (nodeLookup.size === 0) return null;
3175
+ let bounds = getInternalNodesBounds(nodeLookup);
3176
+ return Number.isFinite(bounds.x) && Number.isFinite(bounds.width) ? bounds : null;
3177
+ }), [graphBounds, setGraphBounds] = createSignal(sampleBounds(), { equals: rectsEqual });
3178
+ createEffect(() => store.dragging, (dragging) => {
3179
+ if (!dragging) {
3180
+ setGraphBounds(sampleBounds());
3181
+ return;
3182
+ }
3183
+ let raf = requestAnimationFrame(function tick() {
3184
+ setGraphBounds(sampleBounds()), raf = requestAnimationFrame(tick);
3185
+ });
3186
+ return () => cancelAnimationFrame(raf);
3187
+ }), createEffect(() => ({
3188
+ count: store.nodes.length,
3189
+ initialized: store.nodesInitialized
3190
+ }), () => {
3191
+ setGraphBounds(sampleBounds());
3192
+ }), createEffect(() => null, () => {
3193
+ let interval = setInterval(() => setGraphBounds(sampleBounds()), 500);
3194
+ return () => clearInterval(interval);
3195
+ });
3196
+ let boundingRect = createMemo(() => {
3197
+ let view = viewBB(), bounds = graphBounds();
3198
+ return bounds ? getBoundsOfRects(bounds, view) : view;
3199
+ }), viewScale = createMemo(() => Math.max(boundingRect().width / _props.width, boundingRect().height / _props.height)), getViewWidth = () => viewScale() * _props.width, getViewHeight = () => viewScale() * _props.height, getOffset = () => _props.offsetScale * viewScale(), getX = () => {
3200
+ let rect = boundingRect();
3201
+ return rect.x - (getViewWidth() - rect.width) / 2 - getOffset();
2889
3202
  }, getY = () => {
2890
- let boundingRect = getBoundingRect();
2891
- return boundingRect.y - (getViewHeight() - boundingRect.height) / 2 - getOffset();
2892
- }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * getViewScale() : void 0, prevNodeIds = [], nodeIds = () => {
2893
- let currentNodeIds = store.nodes.map((node) => node.id), currentSet = new Set(currentNodeIds);
2894
- return (prevNodeIds.length !== currentNodeIds.length || !prevNodeIds.every((id) => currentSet.has(id))) && (prevNodeIds = currentNodeIds), prevNodeIds;
2895
- };
3203
+ let rect = boundingRect();
3204
+ return rect.y - (getViewHeight() - rect.height) / 2 - getOffset();
3205
+ }, getViewboxWidth = () => getViewWidth() + getOffset() * 2, getViewboxHeight = () => getViewHeight() + getOffset() * 2, strokeWidth = () => _props.maskStrokeWidth ? _props.maskStrokeWidth * viewScale() : void 0, nodeIds = createMemo(() => store.nodes.map((node) => node.id), { equals: (a, b) => a.length === b.length && a.every((id, i) => id === b[i]) });
2896
3206
  return <Panel position={_props.position} data-testid="solid-flow__minimap" class={["solid-flow__minimap", _props.class]} style={{
2897
3207
  "--xy-minimap-background-color-props": _props.bgColor,
2898
3208
  ..._props.style
@@ -2909,7 +3219,7 @@ const DotPattern = (props) => {
2909
3219
  domNode: el,
2910
3220
  panZoom,
2911
3221
  getTransform: () => store.transform,
2912
- getViewScale
3222
+ getViewScale: viewScale
2913
3223
  });
2914
3224
  return setMinimap(instance), () => {
2915
3225
  instance.destroy();
@@ -2947,14 +3257,20 @@ const DotPattern = (props) => {
2947
3257
  <title id={labelledBy()}>{store.ariaLabelConfig["minimap.ariaLabel"]}</title>
2948
3258
  <For keyed={!1} each={nodeIds()}>
2949
3259
  {(nodeId) => {
2950
- let node = createMemo(() => nodeLookup.get(nodeId())), nodeVisible = () => !!(node() && nodeHasDimensions(node()) && !node().hidden);
2951
- return <Show when={nodeVisible() && getNodeDimensions(node())}>
2952
- {(nodeDimensions) => <Dynamic component={_props.nodeComponent ?? MiniMapNode} id={nodeId()} x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={_props.nodeBorderRadius} strokeWidth={_props.nodeStrokeWidth} shapeRendering={shapeRendering()} width={nodeDimensions().width} height={nodeDimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, node())} strokeColor={nodeStrokeColorFunc().call(null, node())} class={nodeClassFunc().call(null, node())} style={node().style} onClick={_props.onNodeClick ? onSvgNodeClick : void 0} />}
3260
+ let visibleNode = createMemo(() => {
3261
+ let row = nodeLookup.get(nodeId());
3262
+ return row && nodeHasDimensions(row) && !row.hidden ? row : null;
3263
+ });
3264
+ return <Show when={visibleNode()}>
3265
+ {(node) => {
3266
+ let dimensions = () => getNodeDimensions(node()), userNode = () => node().internals.userNode;
3267
+ return <Dynamic component={_props.nodeComponent ?? MiniMapNode} id={nodeId()} x={node().internals.positionAbsolute.x} y={node().internals.positionAbsolute.y} borderRadius={_props.nodeBorderRadius} strokeWidth={_props.nodeStrokeWidth} shapeRendering={shapeRendering} width={dimensions().width} height={dimensions().height} selected={node().selected} color={nodeColorFunc()?.call(null, userNode())} strokeColor={nodeStrokeColorFunc().call(null, userNode())} class={nodeClassFunc().call(null, userNode())} style={node().style} onClick={_props.onNodeClick ? onSvgNodeClick : void 0} />;
3268
+ }}
2953
3269
  </Show>;
2954
3270
  }}
2955
3271
  </For>
2956
3272
  <path class="solid-flow__minimap-mask" d={`M${getX() - getOffset()},${getY() - getOffset()}h${getViewboxWidth() + getOffset() * 2}v${getViewboxHeight() + getOffset() * 2}h${-getViewboxWidth() - getOffset() * 2}z
2957
- M${getViewBB().x},${getViewBB().y}h${getViewBB().width}v${getViewBB().height}h${-getViewBB().width}z`} fill-rule="evenodd" pointer-events="none" />
3273
+ M${viewBB().x},${viewBB().y}h${viewBB().width}v${viewBB().height}h${-viewBB().width}z`} fill-rule="evenodd" pointer-events="none" />
2958
3274
  </svg>;
2959
3275
  }}
2960
3276
  </Show>
@@ -3066,7 +3382,7 @@ const DotPattern = (props) => {
3066
3382
  </EdgeLabel>
3067
3383
  </Show>;
3068
3384
  }, NodeToolbar = (props) => {
3069
- let { store } = useInternalSolidFlow(), { flow, getNodesBounds } = useSolidFlow(), _props = propDefaults(props, {
3385
+ let { store, flow, commands } = useInternalSolidFlow(), _props = propDefaults(props, {
3070
3386
  offset: 10,
3071
3387
  position: "top",
3072
3388
  align: "center",
@@ -3078,7 +3394,7 @@ const DotPattern = (props) => {
3078
3394
  let node = flow.internalNodes[nodeId];
3079
3395
  return node && res.push(node), res;
3080
3396
  }, []), transform = () => {
3081
- let nodeRect = getNodesBounds(toolbarNodes());
3397
+ let nodeRect = commands.getNodesBounds(toolbarNodes());
3082
3398
  return nodeRect ? getNodeToolbarTransform(nodeRect, store.viewport, _props.position, _props.offset, _props.align) : "";
3083
3399
  }, zIndex = () => {
3084
3400
  let nodes = toolbarNodes();
@@ -3101,4 +3417,4 @@ const DotPattern = (props) => {
3101
3417
  </Show>;
3102
3418
  }, SelectionMode = SelectionMode$1, getEdgeCenter = getEdgeCenter$1, getBezierEdgeCenter = getBezierEdgeCenter$1;
3103
3419
  //#endregion
3104
- export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, MiniMapNode, NodeRenderer, NodeResizer, NodeSelection, NodeToolbar, NodeWrapper, OutputNode, PanOnScrollMode, Pane, Panel, Position, ResizeControl, ResizeControlVariant, Selection, SelectionMode, SmoothStepEdge, SmoothStepEdgeInternal, SolidFlow, SolidFlowProvider, StepEdge, StepEdgeInternal, StraightEdge, StraightEdgeInternal, Viewport, ViewportPortal, Zoom, addEdge, connectionKey, createEdgeStore, createNodeStore, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, useColorMode, useConnection, useEdgeId, useEdges, useInternalNode, useNodeConnections, useNodeId, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };
3420
+ export { Background, BaseEdge, BezierEdge, BezierEdgeInternal, ConnectionLine, ConnectionLineType, ConnectionMode, ControlButton, Controls, DefaultNode, EdgeLabel, EdgeLabelRenderer, EdgeReconnectAnchor, EdgeRenderer, EdgeToolbar, EdgeWrapper, GroupNode, Handle, InputNode, Marker, MarkerDefinition, MarkerType, MiniMap, MiniMapNode, NodeRenderer, NodeResizer, NodeSelection, NodeToolbar, NodeWrapper, OutputNode, PanOnScrollMode, Pane, Panel, Position, ResizeControl, ResizeControlVariant, Selection, SelectionMode, SmoothStepEdge, SmoothStepEdgeInternal, SolidFlow, SolidFlowProvider, StepEdge, StepEdgeInternal, StraightEdge, StraightEdgeInternal, Viewport, ViewportPortal, Zoom, addEdge, connectionKey, createEdgeStore, createNodeStore, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, useColorMode, useConnection, useEdgeId, useEdges, useInternalNode, useKeyPress, useNodeConnections, useNodeId, useNodes, useNodesData, useNodesInitialized, useSolidFlow, useUpdateNodeInternals, useViewport, useViewportInitialized };