@kolosal-ai/rivet 0.1.0 → 0.1.1

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.
package/dist/index.d.ts CHANGED
@@ -201,6 +201,25 @@ declare const DEFAULT_ALIGNMENT_HYSTERESIS = 1.2;
201
201
  * absorbs).
202
202
  */
203
203
  declare function resolveFacingSide(node: Rect, peer: Rect, previous?: HandlePosition, hysteresis?: number): HandlePosition;
204
+ /**
205
+ * {@link resolveFacingSide} restricted to the sides an end can actually reach.
206
+ *
207
+ * A node only offers connection points where its handles are. Letting an auto
208
+ * or live-aligned end resolve freely puts the edge on a bare side — a
209
+ * left/right node gets its edge stuck to the top midpoint, where the consumer
210
+ * put nothing. Passing the sides that carry a compatible handle keeps the edge
211
+ * on a real connection point.
212
+ *
213
+ * An empty `allowed` means "unconstrained" — a node with no registered handles
214
+ * attaches at side midpoints, so every side is equally valid, and anchors place
215
+ * themselves along whichever side faces the peer.
216
+ *
217
+ * When the freely-resolved side isn't reachable, a still-reachable `previous`
218
+ * wins over re-picking: with the peer straight above a left/right node the raw
219
+ * choice hangs on the sign of a near-zero `dx`, which would flicker frame to
220
+ * frame.
221
+ */
222
+ declare function resolveFacingSideAmong(node: Rect, peer: Rect, allowed: readonly HandlePosition[], previous?: HandlePosition, hysteresis?: number): HandlePosition;
204
223
 
205
224
  /** Resolved graph context for a pane right-click, passed to `onPaneContextMenu`. */
206
225
  type PaneContextMenuContext = {
@@ -1009,4 +1028,4 @@ declare function rectsIntersect(a: Rect, b: Rect): boolean;
1009
1028
 
1010
1029
  declare const VERSION = "0.0.0";
1011
1030
 
1012
- export { AlignmentGuide, BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, Canvas2DEdgeRenderer, type CloneOptions, type ClonedElements, Connection, Controls, type ControlsProps, DEFAULT_ALIGNMENT_HYSTERESIS, DefaultEdgeOptions, DefaultNode, type EdgeAlignment, EdgeChange, EdgeDrawExtras, EdgeEnd, type EdgeEndpoint, EdgeId, EdgePathFn, EdgeRenderer, EdgeRendererFactory, EdgeRendererOptions, EdgeTypes, GroupNode, Handle, HandlePosition, type HandleProps, HandleRecord, HandleType, MiniMap, type MiniMapProps, NodeChange, NodeId, NodeProps, NodeResizer, type NodeResizerProps, NodeTypes, type PaneContextMenuContext, PendingConnection, ReconnectDelegate, Rect, Rivet, RivetControls, RivetEdge, type RivetInstance, RivetNode, type RivetProps, RivetSelection, RivetSnapshot, type RivetStore, Size, VERSION, Vec2, Viewport, alignRect, applyEdgeChanges, applyNodeChanges, boundingRect, canvas2DEdgeRenderer, clampChildToParent, cloneElements, createRivetStore, edgeEndpointHandle, facingSide, getBezierPath, getSmoothStepPath, getStepPath, getStraightPath, handleKey, parseEdgeEndpoint, parseSideHandleId, rectsIntersect, resolveFacingSide, screenToWorld, serializeGraph, snapToGrid, useRivet, useRivetContext, useRivetControls, useRivetFocusedNode, useRivetHistory, useRivetViewport, viewportToCss, visibleWorldRect, worldToScreen, zoomAt };
1031
+ export { AlignmentGuide, BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, Canvas2DEdgeRenderer, type CloneOptions, type ClonedElements, Connection, Controls, type ControlsProps, DEFAULT_ALIGNMENT_HYSTERESIS, DefaultEdgeOptions, DefaultNode, type EdgeAlignment, EdgeChange, EdgeDrawExtras, EdgeEnd, type EdgeEndpoint, EdgeId, EdgePathFn, EdgeRenderer, EdgeRendererFactory, EdgeRendererOptions, EdgeTypes, GroupNode, Handle, HandlePosition, type HandleProps, HandleRecord, HandleType, MiniMap, type MiniMapProps, NodeChange, NodeId, NodeProps, NodeResizer, type NodeResizerProps, NodeTypes, type PaneContextMenuContext, PendingConnection, ReconnectDelegate, Rect, Rivet, RivetControls, RivetEdge, type RivetInstance, RivetNode, type RivetProps, RivetSelection, RivetSnapshot, type RivetStore, Size, VERSION, Vec2, Viewport, alignRect, applyEdgeChanges, applyNodeChanges, boundingRect, canvas2DEdgeRenderer, clampChildToParent, cloneElements, createRivetStore, edgeEndpointHandle, facingSide, getBezierPath, getSmoothStepPath, getStepPath, getStraightPath, handleKey, parseEdgeEndpoint, parseSideHandleId, rectsIntersect, resolveFacingSide, resolveFacingSideAmong, screenToWorld, serializeGraph, snapToGrid, useRivet, useRivetContext, useRivetControls, useRivetFocusedNode, useRivetHistory, useRivetViewport, viewportToCss, visibleWorldRect, worldToScreen, zoomAt };
package/dist/index.js CHANGED
@@ -313,6 +313,22 @@ function resolveFacingSide(node, peer, previous, hysteresis = DEFAULT_ALIGNMENT_
313
313
  if (Math.abs(dx) > Math.abs(dy) * hysteresis) return dx > 0 ? "right" : "left";
314
314
  return dy > 0 ? "bottom" : "top";
315
315
  }
316
+ function bestAllowedSide(node, peer, allowed) {
317
+ const dx = peer.x + peer.width / 2 - (node.x + node.width / 2);
318
+ const dy = peer.y + peer.height / 2 - (node.y + node.height / 2);
319
+ const score = { left: -dx, right: dx, top: -dy, bottom: dy };
320
+ let best = null;
321
+ for (const side of allowed) {
322
+ if (best === null || score[side] > score[best]) best = side;
323
+ }
324
+ return best;
325
+ }
326
+ function resolveFacingSideAmong(node, peer, allowed, previous, hysteresis = DEFAULT_ALIGNMENT_HYSTERESIS) {
327
+ const resolved = resolveFacingSide(node, peer, previous, hysteresis);
328
+ if (allowed.length === 0 || allowed.includes(resolved)) return resolved;
329
+ if (previous && allowed.includes(previous)) return previous;
330
+ return bestAllowedSide(node, peer, allowed) ?? resolved;
331
+ }
316
332
 
317
333
  // src/edge-ends.ts
318
334
  function displayedSideKey(edgeId, end) {
@@ -2187,7 +2203,7 @@ var ARROW_DIRS = {
2187
2203
  ArrowLeft: { x: -1, y: 0 },
2188
2204
  ArrowRight: { x: 1, y: 0 }
2189
2205
  };
2190
- function createKeyboardHandler(store) {
2206
+ function createKeyboardHandler(store, getSnapGrid) {
2191
2207
  return (event) => {
2192
2208
  if (event.defaultPrevented) return;
2193
2209
  const active = document.activeElement;
@@ -2208,15 +2224,16 @@ function createKeyboardHandler(store) {
2208
2224
  const movers = store.getMovers(store.getSelectedNodes());
2209
2225
  if (movers.length === 0) return;
2210
2226
  event.preventDefault();
2211
- const step = event.shiftKey ? ARROW_STEP * ARROW_STEP_SHIFT : ARROW_STEP;
2227
+ const snapGrid = getSnapGrid?.() ?? null;
2228
+ const factor = event.shiftKey ? ARROW_STEP_SHIFT : 1;
2229
+ const stepX = (snapGrid?.[0] || ARROW_STEP) * factor;
2230
+ const stepY = (snapGrid?.[1] || ARROW_STEP) * factor;
2212
2231
  for (const id of movers) {
2213
2232
  const node = store.nodes.get(id);
2214
2233
  if (!node) continue;
2215
- store.moveNode(
2216
- id,
2217
- { x: node.position.x + dir.x * step, y: node.position.y + dir.y * step },
2218
- true
2219
- );
2234
+ let next = { x: node.position.x + dir.x * stepX, y: node.position.y + dir.y * stepY };
2235
+ if (snapGrid) next = snapToGrid(next, snapGrid);
2236
+ store.moveNode(id, next, true);
2220
2237
  }
2221
2238
  };
2222
2239
  }
@@ -2722,6 +2739,16 @@ function handleOnSide(nodeId, side, role, handles) {
2722
2739
  }
2723
2740
  return null;
2724
2741
  }
2742
+ function reachableSides(nodeId, role, handles) {
2743
+ if (!handles) return [];
2744
+ const sides = [];
2745
+ for (const record of handles.values()) {
2746
+ if (record.nodeId !== nodeId) continue;
2747
+ if (!WANTED_TYPES[role].includes(record.type)) continue;
2748
+ if (!sides.includes(record.position)) sides.push(record.position);
2749
+ }
2750
+ return sides;
2751
+ }
2725
2752
  function resolveEnd(edge, role, node, peer, frame) {
2726
2753
  const { handles, viewport, worldPositions, extras } = frame;
2727
2754
  const handleId = role === "source" ? edge.sourceHandle : edge.targetHandle;
@@ -2759,9 +2786,10 @@ function resolveEnd(edge, role, node, peer, frame) {
2759
2786
  return endAt(pinnedSide);
2760
2787
  }
2761
2788
  const key = displayedSideKey(edge.id, role);
2762
- const side = resolveFacingSide(
2789
+ const side = resolveFacingSideAmong(
2763
2790
  rect,
2764
2791
  nodeRect(peer, worldPositions),
2792
+ anchorId ? [] : reachableSides(node.id, role, handles),
2765
2793
  extras?.displayedSides?.get(key)
2766
2794
  );
2767
2795
  extras?.displayedSides?.set(key, side);
@@ -3164,7 +3192,7 @@ function useRivetRuntime(params) {
3164
3192
  }
3165
3193
  pan.end(event);
3166
3194
  };
3167
- const onKeyDown = createKeyboardHandler(store);
3195
+ const onKeyDown = createKeyboardHandler(store, () => reconnectRef.current.snapGrid);
3168
3196
  let focusRetryFrame = 0;
3169
3197
  const unsubscribeFocus = store.subscribeFocus((id) => {
3170
3198
  if (focusRetryFrame) {
@@ -4641,7 +4669,8 @@ function Rivet({
4641
4669
  zoomSpeed,
4642
4670
  minZoom,
4643
4671
  maxZoom,
4644
- gridGap
4672
+ gridGap,
4673
+ snapGrid: cfg.snapGrid
4645
4674
  });
4646
4675
  const contextValue = useMemo(
4647
4676
  () => ({
@@ -4844,6 +4873,6 @@ function useRivet() {
4844
4873
  // src/index.ts
4845
4874
  var VERSION = "0.0.0";
4846
4875
 
4847
- export { BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, Canvas2DEdgeRenderer, Controls, DEFAULT_ALIGNMENT_HYSTERESIS, DefaultNode, GroupNode, Handle, MiniMap, NodeResizer, Rivet, VERSION, alignRect, applyEdgeChanges, applyNodeChanges, boundingRect, canvas2DEdgeRenderer, clampChildToParent, cloneElements, createRivetStore, edgeEndpointHandle, facingSide, getBezierPath, getSmoothStepPath, getStepPath, getStraightPath, handleKey, parseEdgeEndpoint, parseSideHandleId, rectsIntersect, resolveFacingSide, screenToWorld, serializeGraph, snapToGrid, useRivet, viewportToCss, visibleWorldRect, worldToScreen, zoomAt };
4876
+ export { BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, Canvas2DEdgeRenderer, Controls, DEFAULT_ALIGNMENT_HYSTERESIS, DefaultNode, GroupNode, Handle, MiniMap, NodeResizer, Rivet, VERSION, alignRect, applyEdgeChanges, applyNodeChanges, boundingRect, canvas2DEdgeRenderer, clampChildToParent, cloneElements, createRivetStore, edgeEndpointHandle, facingSide, getBezierPath, getSmoothStepPath, getStepPath, getStraightPath, handleKey, parseEdgeEndpoint, parseSideHandleId, rectsIntersect, resolveFacingSide, resolveFacingSideAmong, screenToWorld, serializeGraph, snapToGrid, useRivet, viewportToCss, visibleWorldRect, worldToScreen, zoomAt };
4848
4877
  //# sourceMappingURL=index.js.map
4849
4878
  //# sourceMappingURL=index.js.map