@kolosal-ai/rivet 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/anchor/index.d.ts +3 -3
  2. package/dist/anchor/index.js +1 -36
  3. package/dist/anchor/index.js.map +1 -1
  4. package/dist/chunk-B3YKB2QN.js +2 -0
  5. package/dist/chunk-B3YKB2QN.js.map +1 -0
  6. package/dist/chunk-CVSEMPYO.js +2 -0
  7. package/dist/chunk-CVSEMPYO.js.map +1 -0
  8. package/dist/chunk-NLE6BOPW.js +2 -0
  9. package/dist/chunk-NLE6BOPW.js.map +1 -0
  10. package/dist/chunk-RDSWIH3F.js +2 -0
  11. package/dist/chunk-RDSWIH3F.js.map +1 -0
  12. package/dist/chunk-X4VW3W4W.js +2 -0
  13. package/dist/chunk-X4VW3W4W.js.map +1 -0
  14. package/dist/chunk-XTQGGEUS.js +2 -0
  15. package/dist/chunk-XTQGGEUS.js.map +1 -0
  16. package/dist/index.d.ts +127 -40
  17. package/dist/index.js +1 -6176
  18. package/dist/index.js.map +1 -1
  19. package/dist/presence/index.d.ts +3 -3
  20. package/dist/presence/index.js +1 -37
  21. package/dist/presence/index.js.map +1 -1
  22. package/dist/{registry-ClCPnBac.d.ts → registry-Vc6r10MJ.d.ts} +24 -7
  23. package/dist/svg/index.d.ts +2 -2
  24. package/dist/svg/index.js +2 -128
  25. package/dist/svg/index.js.map +1 -1
  26. package/dist/swimlane/index.d.ts +4 -4
  27. package/dist/swimlane/index.js +1 -2
  28. package/dist/{types-D3aldmsO.d.ts → types-BPtqcAVr.d.ts} +52 -1
  29. package/dist/{use-node-lock-CbS59CRE.d.ts → use-node-lock-BFQQAr8M.d.ts} +1 -1
  30. package/package.json +1 -1
  31. package/dist/chunk-6XRQSAQT.js +0 -148
  32. package/dist/chunk-6XRQSAQT.js.map +0 -1
  33. package/dist/chunk-QHMB4WYT.js +0 -148
  34. package/dist/chunk-QHMB4WYT.js.map +0 -1
  35. package/dist/chunk-TT2N7KWU.js +0 -676
  36. package/dist/chunk-TT2N7KWU.js.map +0 -1
  37. package/dist/chunk-VQYN27OK.js +0 -36
  38. package/dist/chunk-VQYN27OK.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/swimlane/lanes.ts"],"names":["SWIMLANE_GROUP_HEADER_SIZE","SWIMLANE_LABEL_WIDTH","SWIMLANE_MIN_HEIGHT","resolveMargin","margin","ORIGIN","withAlpha","color","alpha","hex","h","full","c","n","resolveSwimlaneGroup","group","headerSize","pos","lanes","offset","lane","resolveSwimlanes","groups","flattenLanes","findLaneByCoverage","top","height","preferredLaneId","bottom","best","bestOverlap","overlap","center","nearest","bestDist","dist","requiredLaneHeights","nodes","marginBottom","bottoms","node","previous","out","need","requiredLaneHeight","min","swimlaneChromeAt","viewport","point","headerHeight","labelWidth","vx","vy","zoom","headerBottomByGroup","screenTop","screenBottom","stickyY","headerBottom","screenX","screenEndX","stickyX","clampNodeToLanes","position","size","minY","maxY","minX","clamp"],"mappings":"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","file":"chunk-XTQGGEUS.js","sourcesContent":["import { clamp } from \"@rivet/utils\"\nimport type { Rect, RivetNode, Size, SwimlaneGroup, SwimlaneMargin, Vec2, Viewport } from \"../types\"\n\n/** World height of a group's sticky header band. */\nexport const SWIMLANE_GROUP_HEADER_SIZE = 32\n/** Screen width of the sticky vertical lane-label strip, in px. */\nexport const SWIMLANE_LABEL_WIDTH = 24\n/** Floor for a lane's height when resized, in world units. */\nexport const SWIMLANE_MIN_HEIGHT = 80\n\n/** A margin with every side filled in. */\nexport type ResolvedMargin = { top: number; right: number; bottom: number; left: number }\n\n/** Normalize a `swimlaneMargin` prop (number shorthand or per-side) to all sides. */\nexport function resolveMargin(margin?: number | SwimlaneMargin): ResolvedMargin {\n if (typeof margin === \"number\") {\n return { top: margin, right: margin, bottom: margin, left: margin }\n }\n return {\n top: margin?.top ?? 16,\n right: margin?.right ?? 16,\n bottom: margin?.bottom ?? 16,\n left: margin?.left ?? 16,\n }\n}\n\n/** A lane resolved to world-space geometry (its body, below the group header). */\nexport type ResolvedLane = {\n id: string\n groupId: string\n label?: string\n color?: string\n /** World-space top edge of the lane body. */\n top: number\n height: number\n /** World x origin and nominal width (bands render effectively infinite). */\n x: number\n width: number\n}\n\n/** A group resolved to world-space geometry: its header band plus its lanes. */\nexport type ResolvedSwimlaneGroup = {\n id: string\n label?: string\n color?: string\n x: number\n width: number\n /** World top edge of the header band. */\n top: number\n /** Header band height in world units. */\n headerSize: number\n /** World bottom edge of the whole group (its last lane's bottom). */\n bottom: number\n lanes: ResolvedLane[]\n}\n\n/** A resolved lane plus how much of it is currently on screen. */\nexport type VisibleLane = ResolvedLane & {\n /** Area (world units²) of the lane's overlap with the viewport. */\n visibleArea: number\n}\n\n/**\n * A snapshot of what the pane is currently showing, produced by\n * `getViewportElements`. Read imperatively — it never subscribes to the\n * viewport, so calling it does not re-render.\n */\nexport type ViewportElements<TNodeData = unknown> = {\n /** The world-space rect currently visible in the pane. */\n rect: Rect\n /** Nodes whose world box intersects the viewport. */\n nodes: RivetNode<TNodeData>[]\n /** Visible lanes, largest on-screen area first. `lanes[0]` is the dominant lane. */\n lanes: VisibleLane[]\n}\n\nconst ORIGIN: Vec2 = { x: 0, y: 0 }\n\n/**\n * Return `color` as an `rgba()` with the given alpha. Hex (`#rgb`/`#rrggbb`) is\n * converted; any other form (already-rgba, named) is returned unchanged. Used so\n * a solid accent color renders as a subtle tint, never an opaque wash.\n */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.trim()\n if (!hex.startsWith(\"#\")) return color\n const h = hex.slice(1)\n const full =\n h.length === 3\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h\n if (full.length !== 6) return color\n const n = Number.parseInt(full, 16)\n if (Number.isNaN(n)) return color\n return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`\n}\n\n/** Lay a group out: a header band at `position.y`, then its lanes stacked below. */\nexport function resolveSwimlaneGroup(\n group: SwimlaneGroup,\n headerSize = SWIMLANE_GROUP_HEADER_SIZE,\n): ResolvedSwimlaneGroup {\n const pos = group.position ?? ORIGIN\n const lanes: ResolvedLane[] = []\n let offset = pos.y + headerSize\n for (const lane of group.lanes) {\n lanes.push({\n id: lane.id,\n groupId: group.id,\n label: lane.label,\n // Only an explicit per-lane color tints a lane; the group's accent color\n // is header-only, never a lane background wash.\n color: lane.color,\n top: offset,\n height: lane.size,\n x: pos.x,\n width: group.length,\n })\n offset += lane.size\n }\n return {\n id: group.id,\n label: group.label,\n color: group.color,\n x: pos.x,\n width: group.length,\n top: pos.y,\n headerSize,\n bottom: offset,\n lanes,\n }\n}\n\n/** Resolve every group in a `swimlane` prop to world-space geometry. */\nexport function resolveSwimlanes(\n groups: SwimlaneGroup[],\n headerSize = SWIMLANE_GROUP_HEADER_SIZE,\n): ResolvedSwimlaneGroup[] {\n return groups.map((group) => resolveSwimlaneGroup(group, headerSize))\n}\n\n/** Flatten every group's lanes into a single list. */\nexport function flattenLanes(groups: ResolvedSwimlaneGroup[]): ResolvedLane[] {\n return groups.flatMap((group) => group.lanes)\n}\n\n/**\n * The lane a node box `[top, top+height]` belongs to: the one it overlaps most\n * (ties prefer `preferredLaneId`), or — with no overlap at all — the nearest by\n * center. Returns null only when there are no lanes.\n */\nexport function findLaneByCoverage(\n lanes: readonly ResolvedLane[],\n top: number,\n height: number,\n preferredLaneId?: string,\n): ResolvedLane | null {\n if (lanes.length === 0) return null\n const bottom = top + height\n let best: ResolvedLane | null = null\n let bestOverlap = -1\n for (const lane of lanes) {\n const overlap = Math.max(0, Math.min(bottom, lane.top + lane.height) - Math.max(top, lane.top))\n if (overlap > bestOverlap || (overlap === bestOverlap && lane.id === preferredLaneId)) {\n bestOverlap = overlap\n best = lane\n }\n }\n if (bestOverlap > 0) return best\n\n // No vertical overlap with any lane — snap to the nearest by center.\n const center = top + height / 2\n let nearest: ResolvedLane | null = null\n let bestDist = Number.POSITIVE_INFINITY\n for (const lane of lanes) {\n const dist = Math.abs(lane.top + lane.height / 2 - center)\n if (dist < bestDist) {\n bestDist = dist\n nearest = lane\n }\n }\n return nearest\n}\n\n/**\n * {@link requiredLaneHeight} for several lanes in one pass over the nodes.\n *\n * The per-lane form scans every node, so auto-fit — which runs on every\n * rendered frame — cost lanes x nodes. Grouping by `laneId` once makes it\n * nodes + lanes.\n */\nexport function requiredLaneHeights(\n lanes: readonly ResolvedLane[],\n nodes: Iterable<RivetNode>,\n marginBottom: number,\n): Map<string, number> {\n const bottoms = new Map<string, number>()\n for (const node of nodes) {\n if (!node.laneId || !node.size) continue\n const bottom = node.position.y + node.size.height + marginBottom\n const previous = bottoms.get(node.laneId)\n if (previous === undefined || bottom > previous) bottoms.set(node.laneId, bottom)\n }\n const out = new Map<string, number>()\n for (const lane of lanes) {\n const bottom = bottoms.get(lane.id)\n const need = bottom === undefined ? SWIMLANE_MIN_HEIGHT : bottom - lane.top\n out.set(lane.id, need > SWIMLANE_MIN_HEIGHT ? need : SWIMLANE_MIN_HEIGHT)\n }\n return out\n}\n\n/**\n * The smallest height `lane` can take while still containing every node assigned\n * to it (by `laneId`), keeping `marginBottom` clearance below the lowest one.\n * Never returns less than {@link SWIMLANE_MIN_HEIGHT}. Backs both the resize\n * floor and auto-fit growth.\n */\nexport function requiredLaneHeight(\n lane: ResolvedLane,\n nodes: Iterable<RivetNode>,\n marginBottom: number,\n): number {\n let min = SWIMLANE_MIN_HEIGHT\n for (const node of nodes) {\n if (node.laneId !== lane.id || !node.size) continue\n const need = node.position.y + node.size.height + marginBottom - lane.top\n if (need > min) min = need\n }\n return min\n}\n\n/** A hit on sticky swimlane chrome (a group header or a lane label). */\nexport type SwimlaneChromeHit = { type: \"group\" | \"lane\"; id: string }\n\n/**\n * Which piece of sticky swimlane chrome sits under a pane-relative `point`, or\n * null. Mirrors the overlay's sticky math so a pane context-menu handler can\n * route right-clicks without the overlay needing to capture pointer events.\n */\nexport function swimlaneChromeAt(\n groups: ResolvedSwimlaneGroup[],\n viewport: Viewport,\n point: Vec2,\n headerHeight: number,\n labelWidth: number,\n): SwimlaneChromeHit | null {\n const { x: vx, y: vy, zoom } = viewport\n const headerBottomByGroup = new Map<string, number>()\n\n for (const group of groups) {\n const screenTop = group.top * zoom + vy\n const screenBottom = group.bottom * zoom + vy\n const stickyY = Math.max(0, Math.min(screenTop, screenBottom - headerHeight))\n headerBottomByGroup.set(group.id, stickyY + headerHeight)\n if (screenBottom > 0 && point.y >= stickyY && point.y <= stickyY + headerHeight) {\n return { type: \"group\", id: group.id }\n }\n }\n\n for (const group of groups) {\n const headerBottom = headerBottomByGroup.get(group.id) ?? 0\n for (const lane of group.lanes) {\n const screenX = lane.x * zoom + vx\n const screenTop = lane.top * zoom + vy\n const screenBottom = screenTop + lane.height * zoom\n const screenEndX = screenX + lane.width * zoom\n const stickyX = Math.max(0, Math.min(screenX, screenEndX - labelWidth))\n const top = Math.max(headerBottom, screenTop)\n if (\n point.x >= stickyX &&\n point.x <= stickyX + labelWidth &&\n point.y >= top &&\n point.y <= screenBottom\n ) {\n return { type: \"lane\", id: lane.id }\n }\n }\n }\n return null\n}\n\nexport type LaneClampResult = { position: Vec2; laneId: string | null }\n\n/**\n * Clamp a node so its whole body sits inside one lane, honoring `margin` (and\n * the left label strip). The target lane is chosen by {@link findLaneByCoverage}\n * — pass `preferredLaneId` to bias toward the node's current lane. Horizontally\n * only the left edge is bounded, since bands are effectively infinite.\n */\nexport function clampNodeToLanes(\n position: Vec2,\n size: Size,\n lanes: readonly ResolvedLane[],\n margin: ResolvedMargin,\n labelWidth: number,\n preferredLaneId?: string,\n): LaneClampResult {\n const lane = findLaneByCoverage(lanes, position.y, size.height, preferredLaneId)\n if (!lane) return { position, laneId: preferredLaneId ?? null }\n\n const minY = lane.top + margin.top\n const maxY = Math.max(minY, lane.top + lane.height - size.height - margin.bottom)\n const minX = lane.x + labelWidth + margin.left\n return {\n position: {\n x: Math.max(minX, position.x),\n y: clamp(position.y, minY, maxY),\n },\n laneId: lane.id,\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { R as Rect, V as Vec2, A as AlignmentGuide, c as EdgeChange, d as RivetEdge, a as NodeChange, e as RivetNode, N as NodeId, f as NodeProps, g as NodeTypes, b as HandleType, H as HandlePosition, S as Size, h as ChangeOrigin, E as EdgeId, i as HandleRecord, j as ChangeMeta, k as Viewport, l as NodeGestureEvent, m as NodeLockConflictEvent, n as RivetSelection, o as ReconnectDelegate, C as Connection, P as PendingConnection, p as EdgeTypes, D as DefaultEdgeOptions, q as EdgeRendererFactory, r as SwimlaneGroup, s as SwimlaneMargin, t as SwimlaneHeaderProps, u as SwimlaneLabelProps, v as SwimlaneSizeChange, L as LaneChange, w as RivetControls, x as RivetSnapshot, y as EdgeRenderer, z as EdgeRendererOptions, B as EdgeDrawExtras, F as EdgeEnd, G as EdgePathFn } from './types-D3aldmsO.js';
2
- export { I as CenterNodeOptions, J as EdgeMarker, K as EdgePath, M as EdgePathParams, O as EdgeStyle, Q as FitViewOptions, T as NodeComponent } from './types-D3aldmsO.js';
1
+ import { R as Rect, V as Vec2, A as AlignmentGuide, c as EdgeChange, d as RivetEdge, a as NodeChange, e as RivetNode, N as NodeId, f as NodeProps, g as NodeTypes, b as HandleType, H as HandlePosition, S as Size, h as ChangeOrigin, E as EdgeId, i as HandleRecord, j as HandleIndex, k as ChangeMeta, l as Viewport, L as LaneChange, m as NodeGestureEvent, n as NodeLockConflictEvent, o as RivetSelection, p as ReconnectDelegate, C as Connection, P as PendingConnection, q as EdgeTypes, D as DefaultEdgeOptions, r as EdgeRendererFactory, s as SwimlaneGroup, t as SwimlaneMargin, u as SwimlaneHeaderProps, v as SwimlaneLabelProps, w as SwimlaneSizeChange, x as RivetControls, y as RivetSnapshot, z as EdgeRenderer, B as EdgeRendererOptions, F as EdgeDrawExtras, G as EdgeEnd, I as EdgePathFn } from './types-BPtqcAVr.js';
2
+ export { J as CenterNodeOptions, K as EdgeMarker, M as EdgePath, O as EdgePathParams, Q as EdgeStyle, T as FitViewOptions, U as NodeComponent } from './types-BPtqcAVr.js';
3
3
  import { ReactNode, CSSProperties, MouseEvent, RefObject } from 'react';
4
- import { e as AnchorRecord, g as AnchorRegistrationOptions, A as AnchorRef, c as AnchorOptions, R as ResolvedAnchorOptions, f as AnchorRegistration } from './registry-ClCPnBac.js';
5
- import { b as PresenceTargetKey, p as PresenceRegistry, h as LockRegistry, k as Peer, e as LocalPresence, o as PresenceOptions, C as CanInteractWithLocked, l as PeerConnection } from './use-node-lock-CbS59CRE.js';
6
- export { L as LOCK_DEFAULT_REFUSED, f as LockIntent, g as LockInteraction, i as LockTarget, N as NodeLock, y as useNodeLock, z as useNodeLockAllows } from './use-node-lock-CbS59CRE.js';
7
- import { ResolvedLane, ViewportElements, ResolvedSwimlaneGroup, ResolvedMargin } from './swimlane/index.js';
4
+ import { e as AnchorRecord, g as AnchorRegistrationOptions, A as AnchorRef, c as AnchorOptions, R as ResolvedAnchorOptions, f as AnchorRegistration } from './registry-Vc6r10MJ.js';
5
+ import { b as PresenceTargetKey, p as PresenceRegistry, h as LockRegistry, k as Peer, e as LocalPresence, o as PresenceOptions, C as CanInteractWithLocked, l as PeerConnection } from './use-node-lock-BFQQAr8M.js';
6
+ export { L as LOCK_DEFAULT_REFUSED, f as LockIntent, g as LockInteraction, i as LockTarget, N as NodeLock, y as useNodeLock, z as useNodeLockAllows } from './use-node-lock-BFQQAr8M.js';
7
+ import { ResolvedLane, ResolvedMargin, ViewportElements, ResolvedSwimlaneGroup } from './swimlane/index.js';
8
8
 
9
9
  /**
10
10
  * Find the closest edge alignment between the moving rect and any static rect,
@@ -226,6 +226,12 @@ declare function resolveFacingSideAmong(node: Rect, peer: Rect, allowed: readonl
226
226
  /** Composite key for the handle/anchor registries. */
227
227
  declare function handleKey(nodeId: NodeId, handleId: string): string;
228
228
 
229
+ /**
230
+ * The store's public contract — the shape consumers and every internal module
231
+ * depend on, kept apart from `RivetGraphStore`'s implementation so reading the
232
+ * interface never means scrolling past 1,600 lines of it.
233
+ */
234
+
229
235
  type RivetStoreInit = {
230
236
  nodes: RivetNode[];
231
237
  edges: RivetEdge[];
@@ -268,10 +274,29 @@ type RemoteChanges = {
268
274
  * - **viewport** and **frame** channels drive the canvas (and the label
269
275
  * layer's imperative positioning) without touching React at all.
270
276
  */
277
+ /**
278
+ * Everything a node drag reads that isn't the graph itself. Assembled once from
279
+ * props and pushed to the store, rather than threaded through context to the
280
+ * two components that start a drag — they hold a store already, and every field
281
+ * here is read together or not at all.
282
+ */
283
+ type NodeDragConfig = {
284
+ /** Grid to snap dragged nodes to (`[x, y]` world units), or null for free movement. */
285
+ snapGrid: [number, number] | null;
286
+ /** Whether a single-node drag shows alignment guides and snaps to neighbours. */
287
+ guides: boolean;
288
+ lanes: readonly ResolvedLane[];
289
+ clampToSwimlane: boolean;
290
+ swimlaneMargin: ResolvedMargin;
291
+ swimlaneLabelWidth: number;
292
+ onLaneChange?: (change: LaneChange) => void;
293
+ };
271
294
  type RivetStore = {
272
295
  readonly nodes: Map<NodeId, RivetNode>;
273
296
  readonly edges: Map<EdgeId, RivetEdge>;
274
297
  readonly handles: Map<string, HandleRecord>;
298
+ /** The same records, indexed for the per-node questions edge ends ask. */
299
+ readonly handleIndex: HandleIndex;
275
300
  getNodeVersion: (id: NodeId) => number;
276
301
  subscribeNode: (id: NodeId, listener: () => void) => () => void;
277
302
  registerNodeElement: (id: NodeId, el: HTMLElement) => void;
@@ -374,6 +399,18 @@ type RivetStore = {
374
399
  setEdgeAlignment: (mode: EdgeAlignment) => void;
375
400
  /** The current edge-alignment policy (the runtime reads it per frame). */
376
401
  getEdgeAlignment: () => EdgeAlignment;
402
+ /**
403
+ * Set how far the losing axis must dominate before a resolved side switches
404
+ * axes (the `edgeAlignmentHysteresis` prop). Clamped to `>= 1`: below that the
405
+ * band would make an axis change *easier* than plain dominance, which flickers
406
+ * rather than resists, and 1 is already "no band at all".
407
+ */
408
+ setAlignmentHysteresis: (ratio: number) => void;
409
+ /** The current hysteresis ratio — read per frame, and at each commit. */
410
+ getAlignmentHysteresis: () => number;
411
+ /** Config every node drag reads — see {@link NodeDragConfig}. */
412
+ setNodeDragConfig: (config: NodeDragConfig) => void;
413
+ getNodeDragConfig: () => NodeDragConfig;
377
414
  /**
378
415
  * Hysteresis memory for per-frame side resolution, keyed by
379
416
  * `displayedSideKey(edgeId, end)`. The render loop hands this map to the
@@ -561,8 +598,12 @@ type RivetStore = {
561
598
  * Register (or re-bind) an anchor. Idempotent on the `(nodeId, anchorId)`
562
599
  * key: calling again with a new element re-attaches it, keeping any
563
600
  * last-known geometry until the next successful measurement.
601
+ *
602
+ * Several elements bind one anchor to content that renders as fragments — a
603
+ * rich-text mark split by another mark, a graphic that is a group of paths.
604
+ * They're measured as one run: first line of the first, last line of the last.
564
605
  */
565
- registerAnchor: (nodeId: NodeId, anchorId: string, element: Element, options?: AnchorRegistrationOptions) => void;
606
+ registerAnchor: (nodeId: NodeId, anchorId: string, element: Element | readonly Element[], options?: AnchorRegistrationOptions) => void;
566
607
  /**
567
608
  * Mark an anchor's element gone without removing the anchor — the stale
568
609
  * retention signal. Geometry (and the stray handles rendered from it) is
@@ -616,6 +657,7 @@ type RivetStore = {
616
657
  requestCursorRender: () => void;
617
658
  bindCursorRenderRequester: (fn: (() => void) | null) => void;
618
659
  };
660
+
619
661
  /** Create a graph store. The public entry point; the class is an implementation detail. */
620
662
  declare function createRivetStore(init: RivetStoreInit): RivetStore;
621
663
 
@@ -952,6 +994,25 @@ type RivetProps = {
952
994
  * renderer resolves its facing side per frame and never writes it back.
953
995
  */
954
996
  edgeAlignment?: EdgeAlignment;
997
+ /**
998
+ * How far the losing axis must dominate before a resolved side switches axes.
999
+ * Defaults to `DEFAULT_ALIGNMENT_HYSTERESIS` (`1.2`), i.e. the other axis has
1000
+ * to win by 20% before an end changes from a horizontal side to a vertical
1001
+ * one or back.
1002
+ *
1003
+ * The band is what stops a near-diagonal layout flickering between sides
1004
+ * frame to frame; it's a ratio rather than a distance so it reads the same at
1005
+ * every zoom and node size. Raise it for dense graphs of small nodes, where
1006
+ * 20% of a short center-to-center distance is a few pixels of pointer travel;
1007
+ * lower it toward `1` (no band) when ends feel reluctant to follow a node
1008
+ * that has clearly moved. Values below `1` are clamped: they'd make an axis
1009
+ * change *easier* than plain dominance, which flickers rather than resists.
1010
+ *
1011
+ * Applies to both halves of the same resolution — the per-frame `"live"`
1012
+ * render and the side committed on drop — so what you see mid-drag stays what
1013
+ * you get.
1014
+ */
1015
+ edgeAlignmentHysteresis?: number;
955
1016
  /** Called when an endpoint drag re-points an edge (the edge is already updated). */
956
1017
  onReconnect?: (edge: RivetEdge, connection: Connection) => void;
957
1018
  /** Called when an endpoint reconnection drag starts. */
@@ -988,33 +1049,15 @@ type RivetProps = {
988
1049
  * The graph surface. Stacks a background grid canvas, an edge canvas, and a DOM
989
1050
  * node layer — all driven by one shared viewport (see {@link useRivetRuntime}).
990
1051
  */
991
- declare function Rivet({ defaultNodes, defaultEdges, nodes: controlledNodes, edges: controlledEdges, onNodesChange, onEdgesChange, defaultViewport, nodeTypes, edgeTypes, defaultEdgeOptions, ariaLabel, renderer, minZoom, maxZoom, gridGap, snapGrid, alignmentGuides, anchorOptions, peers, onLocalPresence, presenceOptions, lockedNodes, canInteractWithLocked, onNodeLockConflict, panOnDrag, selectionOnDrag, selectionKeyCode, multiSelectionKeyCode, scrollToPan, zoomSpeed, swimlane, clampToSwimlane, swimlaneMargin, swimlaneHeaderHeight, swimlaneResizable, swimlaneBottomReveal, renderSwimlaneHeader, renderSwimlaneLabel, onSwimlaneSizeChange, onPaneContextMenu, onSwimlaneHeaderContextMenu, onSwimlaneGroupHeaderContextMenu, onLaneChange, isValidConnection, mapConnection, onConnect, edgesReconnectable, edgeAlignment, onReconnect, onReconnectStart, onReconnectEnd, onNodeDragStart, onNodeDragEnd, onSelectionChange, onFocusChange, className, style, children, }: RivetProps): React.ReactElement;
1052
+ declare function Rivet({ defaultNodes, defaultEdges, nodes: controlledNodes, edges: controlledEdges, onNodesChange, onEdgesChange, defaultViewport, nodeTypes, edgeTypes, defaultEdgeOptions, ariaLabel, renderer, minZoom, maxZoom, gridGap, snapGrid, alignmentGuides, anchorOptions, peers, onLocalPresence, presenceOptions, lockedNodes, canInteractWithLocked, onNodeLockConflict, panOnDrag, selectionOnDrag, selectionKeyCode, multiSelectionKeyCode, scrollToPan, zoomSpeed, swimlane, clampToSwimlane, swimlaneMargin, swimlaneHeaderHeight, swimlaneResizable, swimlaneBottomReveal, renderSwimlaneHeader, renderSwimlaneLabel, onSwimlaneSizeChange, onPaneContextMenu, onSwimlaneHeaderContextMenu, onSwimlaneGroupHeaderContextMenu, onLaneChange, isValidConnection, mapConnection, onConnect, edgesReconnectable, edgeAlignment, edgeAlignmentHysteresis, onReconnect, onReconnectStart, onReconnectEnd, onNodeDragStart, onNodeDragEnd, onSelectionChange, onFocusChange, className, style, children, }: RivetProps): React.ReactElement;
992
1053
 
993
- type RivetContextValue = {
994
- store: RivetStore;
995
- nodeTypes: NodeTypes;
996
- paneRef: RefObject<HTMLDivElement | null>;
997
- /** Imperative viewport controls (zoom, fit-view). */
998
- controls: RivetControls;
999
- /** Keys that make a click add to (toggle in) the selection. */
1000
- multiSelectionKeys: string[];
1001
- /**
1002
- * Publish a message to the pane's visually-hidden live region, so screen
1003
- * readers narrate keyboard interactions (grab/move/drop, connect).
1004
- */
1005
- announce: (message: string) => void;
1006
- /** Id of the pane's hidden keyboard-instructions element (`aria-describedby` target). */
1007
- nodeDescriptionId: string;
1008
- /** Defaults merged into every edge — the label layer reads `labelStyle`. */
1009
- defaultEdgeOptions?: DefaultEdgeOptions;
1010
- /** Grid to snap dragged nodes to (`[x, y]` world units), or null for free movement. */
1011
- snapGrid: [number, number] | null;
1012
- /** Whether a single-node drag shows alignment guides and snaps to neighbors. */
1013
- alignmentGuides: boolean;
1014
- /** Resolved placement policy for anchor chrome (dots + stray handles). */
1015
- anchorOptions: ResolvedAnchorOptions;
1016
- /** Snapshot the nodes and lanes currently on screen. Imperative — never subscribes. */
1017
- getViewportElements: () => ViewportElements;
1054
+ /**
1055
+ * Swimlane layout — the volatile half. Lane heights change on a resize drag and
1056
+ * on auto-fit, and a context re-renders every consumer when its value changes,
1057
+ * whichever fields they actually read. Kept apart so a lane resize re-renders
1058
+ * the overlay rather than every node.
1059
+ */
1060
+ type RivetSwimlaneContextValue = {
1018
1061
  /** Resolved swimlane groups (header + lanes, world space). Empty when unused. */
1019
1062
  swimlaneGroups: ResolvedSwimlaneGroup[];
1020
1063
  /** Every group's lanes, flattened — the clamp targets. */
@@ -1039,8 +1082,29 @@ type RivetContextValue = {
1039
1082
  renderSwimlaneHeader?: (props: SwimlaneHeaderProps) => ReactNode;
1040
1083
  /** Consumer override for the sticky lane label content. */
1041
1084
  renderSwimlaneLabel?: (props: SwimlaneLabelProps) => ReactNode;
1042
- /** Called when a dragged node crosses into a different lane. */
1043
- onLaneChange?: (change: LaneChange) => void;
1085
+ };
1086
+ /** Everything that isn't swimlane layout — stable across an interaction. */
1087
+ type RivetCoreContextValue = {
1088
+ store: RivetStore;
1089
+ nodeTypes: NodeTypes;
1090
+ paneRef: RefObject<HTMLDivElement | null>;
1091
+ /** Imperative viewport controls (zoom, fit-view). */
1092
+ controls: RivetControls;
1093
+ /** Keys that make a click add to (toggle in) the selection. */
1094
+ multiSelectionKeys: string[];
1095
+ /**
1096
+ * Publish a message to the pane's visually-hidden live region, so screen
1097
+ * readers narrate keyboard interactions (grab/move/drop, connect).
1098
+ */
1099
+ announce: (message: string) => void;
1100
+ /** Id of the pane's hidden keyboard-instructions element (`aria-describedby` target). */
1101
+ nodeDescriptionId: string;
1102
+ /** Defaults merged into every edge — the label layer reads `labelStyle`. */
1103
+ defaultEdgeOptions?: DefaultEdgeOptions;
1104
+ /** Resolved placement policy for anchor chrome (dots + stray handles). */
1105
+ anchorOptions: ResolvedAnchorOptions;
1106
+ /** Snapshot the nodes and lanes currently on screen. Imperative — never subscribes. */
1107
+ getViewportElements: () => ViewportElements;
1044
1108
  /** Veto a proposed connection; return false to reject. */
1045
1109
  isValidConnection?: (connection: Connection) => boolean;
1046
1110
  /** Rewrite a validated connection before commit; `null` cancels the drop. */
@@ -1048,7 +1112,14 @@ type RivetContextValue = {
1048
1112
  /** Called after a valid connection is made. */
1049
1113
  onConnect?: (connection: Connection) => void;
1050
1114
  };
1051
- declare function useRivetContext(): RivetContextValue;
1115
+ /** The full context, as {@link useRivetContext} returns it. */
1116
+ type RivetContextValue = RivetCoreContextValue & RivetSwimlaneContextValue;
1117
+ /**
1118
+ * Both halves as one object — the shape `useRivetContext` has always had for
1119
+ * consumers, and what the package exports under that name. Re-renders on
1120
+ * either half, which is what asking for everything means.
1121
+ */
1122
+ declare function useFullRivetContext(): RivetContextValue;
1052
1123
  /** Access the imperative viewport controls from inside `<Rivet>`. */
1053
1124
  declare function useRivetControls(): RivetControls;
1054
1125
  /** Subscribe to the live viewport — re-renders on every pan/zoom. */
@@ -1213,7 +1284,19 @@ type RivetInstance<TNodeData = unknown, TEdgeData = unknown> = RivetControls & {
1213
1284
  * Idempotent on `(nodeId, anchorId)`; the returned registration re-binds a
1214
1285
  * replaced element or unregisters explicitly.
1215
1286
  */
1216
- registerAnchor: (nodeId: NodeId, anchorId: string, element: Element, options?: AnchorRegistrationOptions) => AnchorRegistration;
1287
+ registerAnchor: (nodeId: NodeId, anchorId: string, element: Element | readonly Element[], options?: AnchorRegistrationOptions) => AnchorRegistration;
1288
+ /**
1289
+ * Mark an anchor's element gone without removing the anchor: geometry is
1290
+ * retained and its edges stay put, so a display that unmounts — an editor
1291
+ * swapping its DOM, a panel closing — doesn't move anything.
1292
+ *
1293
+ * The same signal `<Anchor>` sends when it unmounts, and what
1294
+ * {@link AnchorRegistration.rebind}`(null)` does, as a call that needs no
1295
+ * registration handle in hand. A consumer syncing anchors from a document
1296
+ * (see the Tiptap recipe) re-registers on every change and would otherwise
1297
+ * have to keep a handle per anchor purely to be able to detach.
1298
+ */
1299
+ detachAnchor: (nodeId: NodeId, anchorId: string) => void;
1217
1300
  /**
1218
1301
  * Explicitly remove an anchor and its chrome. Unmounting `<Anchor>` (or
1219
1302
  * rebinding `null`) only *detaches* — geometry and edges are retained — so
@@ -1330,6 +1413,12 @@ declare class Canvas2DEdgeRenderer implements EdgeRenderer {
1330
1413
  private readonly defaults;
1331
1414
  /** Screen-space polylines from the last frame, keyed by edge id, for picking. */
1332
1415
  private readonly geometry;
1416
+ /**
1417
+ * Screen-space bounding box per edge, so a pick rejects almost everything in
1418
+ * four comparisons instead of walking every segment of every polyline. Filled
1419
+ * in the same pass that stores the geometry.
1420
+ */
1421
+ private readonly bounds;
1333
1422
  /** Screen-space endpoints per edge, for endpoint (reconnection) picking. */
1334
1423
  private readonly endpoints;
1335
1424
  /** Screen-space label anchor per edge that has a label. */
@@ -1388,6 +1477,4 @@ declare function visibleWorldRect(viewport: Viewport, width: number, height: num
1388
1477
  /** True if two axis-aligned rectangles overlap (used for node culling). */
1389
1478
  declare function rectsIntersect(a: Rect, b: Rect): boolean;
1390
1479
 
1391
- declare const VERSION = "0.0.0";
1392
-
1393
- export { AlignmentGuide, BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, CanInteractWithLocked, Canvas2DEdgeRenderer, ChangeMeta, ChangeOrigin, 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, LockRegistry, MiniMap, type MiniMapProps, NodeChange, NodeGestureEvent, NodeId, NodeLockConflictEvent, NodeProps, NodeResizer, type NodeResizerProps, NodeTypes, type PaneContextMenuContext, PendingConnection, ReconnectDelegate, Rect, type RemoteChanges, 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 };
1480
+ export { AlignmentGuide, BUILTIN_EDGE_TYPES, BUILTIN_NODE_TYPES, CanInteractWithLocked, Canvas2DEdgeRenderer, ChangeMeta, ChangeOrigin, 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, LockRegistry, MiniMap, type MiniMapProps, NodeChange, NodeGestureEvent, NodeId, NodeLockConflictEvent, NodeProps, NodeResizer, type NodeResizerProps, NodeTypes, type PaneContextMenuContext, PendingConnection, ReconnectDelegate, Rect, type RemoteChanges, Rivet, RivetControls, RivetEdge, type RivetInstance, RivetNode, type RivetProps, RivetSelection, RivetSnapshot, type RivetStore, Size, 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, useFullRivetContext as useRivetContext, useRivetControls, useRivetFocusedNode, useRivetHistory, useRivetViewport, viewportToCss, visibleWorldRect, worldToScreen, zoomAt };