@kolosal-ai/rivet 0.1.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.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/anchor/index.d.ts +32 -0
- package/dist/anchor/index.js +21 -0
- package/dist/anchor/index.js.map +1 -0
- package/dist/chunk-6XRQSAQT.js +148 -0
- package/dist/chunk-6XRQSAQT.js.map +1 -0
- package/dist/chunk-AXT35ZJV.js +98 -0
- package/dist/chunk-AXT35ZJV.js.map +1 -0
- package/dist/chunk-VQYN27OK.js +36 -0
- package/dist/chunk-VQYN27OK.js.map +1 -0
- package/dist/index.d.ts +1012 -0
- package/dist/index.js +4849 -0
- package/dist/index.js.map +1 -0
- package/dist/registry-Dkk4ZKt-.d.ts +151 -0
- package/dist/svg/index.d.ts +127 -0
- package/dist/svg/index.js +126 -0
- package/dist/svg/index.js.map +1 -0
- package/dist/swimlane/index.d.ts +96 -0
- package/dist/swimlane/index.js +3 -0
- package/dist/swimlane/index.js.map +1 -0
- package/dist/types-B8AAJ60T.d.ts +515 -0
- package/package.json +70 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { ComponentType, CSSProperties } from 'react';
|
|
2
|
+
|
|
3
|
+
/** A point in world (graph) space. */
|
|
4
|
+
type Vec2 = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
};
|
|
8
|
+
/** An axis-aligned bounding box in world space. */
|
|
9
|
+
type Rect = {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A snap/alignment guide line shown while dragging, in world space. `axis: "x"`
|
|
17
|
+
* is a vertical line at world-x `position`; `axis: "y"` is horizontal at world-y
|
|
18
|
+
* `position`. `start`/`end` are the perpendicular span (so it only spans the
|
|
19
|
+
* involved nodes).
|
|
20
|
+
*/
|
|
21
|
+
type AlignmentGuide = {
|
|
22
|
+
axis: "x" | "y";
|
|
23
|
+
position: number;
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The viewport transform. Maps world space to screen space via
|
|
29
|
+
* `screen = world * zoom + { x, y }`.
|
|
30
|
+
*/
|
|
31
|
+
type Viewport = {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
zoom: number;
|
|
35
|
+
};
|
|
36
|
+
type NodeId = string;
|
|
37
|
+
type EdgeId = string;
|
|
38
|
+
/** A graph node. `data` is passed straight through to the React renderer. */
|
|
39
|
+
type RivetNode<TData = unknown> = {
|
|
40
|
+
id: NodeId;
|
|
41
|
+
/** Node type key, used to look up the React component that renders it. */
|
|
42
|
+
type?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Position in world units. Relative to {@link parentId}'s top-left when this
|
|
45
|
+
* node has a parent; otherwise an absolute world position.
|
|
46
|
+
*/
|
|
47
|
+
position: Vec2;
|
|
48
|
+
/** Measured size in world units; filled in after the DOM node mounts. */
|
|
49
|
+
size?: {
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
};
|
|
53
|
+
/** Explicit width in world units (set by {@link NodeResizer}); overrides measurement. */
|
|
54
|
+
width?: number;
|
|
55
|
+
/** Explicit height in world units (set by {@link NodeResizer}); overrides measurement. */
|
|
56
|
+
height?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Parent node id. A child renders above its parent, is positioned relative to
|
|
59
|
+
* it, and (with `extent: "parent"`) is confined to it — the basis for sub-flows
|
|
60
|
+
* and inline nodes (e.g. SVG children over an SVG container).
|
|
61
|
+
*/
|
|
62
|
+
parentId?: NodeId;
|
|
63
|
+
/** `"parent"` keeps this node inside its {@link parentId}'s bounds while dragging. */
|
|
64
|
+
extent?: "parent";
|
|
65
|
+
data: TData;
|
|
66
|
+
selected?: boolean;
|
|
67
|
+
/** True while the pointer is over the node (drives hover highlight). */
|
|
68
|
+
hovered?: boolean;
|
|
69
|
+
/** True while this node is being dragged. Set from `position` changes. */
|
|
70
|
+
dragging?: boolean;
|
|
71
|
+
/** When `false`, the node can't be dragged (still selectable). Defaults to `true`. */
|
|
72
|
+
draggable?: boolean;
|
|
73
|
+
/** When `false`, clicks and marquee never select the node. Defaults to `true`. */
|
|
74
|
+
selectable?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Accessible name for the node (`aria-label` on its wrapper, and the name
|
|
77
|
+
* screen-reader announcements use). Falls back to the wrapper's text content
|
|
78
|
+
* when omitted.
|
|
79
|
+
*/
|
|
80
|
+
ariaLabel?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Id of the {@link Swimlane} this node belongs to. Reassigned automatically as
|
|
83
|
+
* the node is dragged across lane boundaries when swimlane clamping is on.
|
|
84
|
+
*/
|
|
85
|
+
laneId?: string;
|
|
86
|
+
};
|
|
87
|
+
/** Per-edge stroke styling, applied by the renderer over the default look. */
|
|
88
|
+
type EdgeStyle = {
|
|
89
|
+
/** Stroke color (any CSS color). Defaults to the theme's edge color. */
|
|
90
|
+
stroke?: string;
|
|
91
|
+
/** Stroke width in px. Defaults to `1.5`. */
|
|
92
|
+
strokeWidth?: number;
|
|
93
|
+
/** Dash pattern in px (e.g. `[6, 4]`). Omit for a solid line. */
|
|
94
|
+
strokeDasharray?: number[];
|
|
95
|
+
/** Stroke opacity `0..1`. Defaults to `1`. */
|
|
96
|
+
opacity?: number;
|
|
97
|
+
};
|
|
98
|
+
/** Arrowhead kind drawn at an edge end. `false` (default) draws none. */
|
|
99
|
+
type EdgeMarker = "arrow" | "arrowclosed" | false;
|
|
100
|
+
/** A directed connection between two nodes (optionally between handles). */
|
|
101
|
+
type RivetEdge<TData = unknown> = {
|
|
102
|
+
id: EdgeId;
|
|
103
|
+
source: NodeId;
|
|
104
|
+
target: NodeId;
|
|
105
|
+
sourceHandle?: string;
|
|
106
|
+
targetHandle?: string;
|
|
107
|
+
/** Path shape key, resolved against the graph's `edgeTypes`. Defaults to `"bezier"`. */
|
|
108
|
+
type?: string;
|
|
109
|
+
/** Animate the stroke with a marching-dash effect. Defaults to `false`. */
|
|
110
|
+
animated?: boolean;
|
|
111
|
+
/** Per-edge stroke overrides. */
|
|
112
|
+
style?: EdgeStyle;
|
|
113
|
+
/** Arrowhead at the target end. */
|
|
114
|
+
markerEnd?: EdgeMarker;
|
|
115
|
+
/** Arrowhead at the source end. */
|
|
116
|
+
markerStart?: EdgeMarker;
|
|
117
|
+
/** Text drawn at the edge's midpoint (via the DOM label layer). */
|
|
118
|
+
label?: string;
|
|
119
|
+
/**
|
|
120
|
+
* CSS overrides merged over the built-in label chip (and over
|
|
121
|
+
* `defaultEdgeOptions.labelStyle`) — background, color, font, padding, etc.
|
|
122
|
+
*/
|
|
123
|
+
labelStyle?: CSSProperties;
|
|
124
|
+
/** Whether clicking the edge selects it. Defaults to `true`. */
|
|
125
|
+
selectable?: boolean;
|
|
126
|
+
/** Whether this edge can be reconnected by dragging an endpoint. Overrides `edgesReconnectable`. */
|
|
127
|
+
reconnectable?: boolean;
|
|
128
|
+
data?: TData;
|
|
129
|
+
selected?: boolean;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Defaults merged into every edge that doesn't set the field itself. Passed to
|
|
133
|
+
* `<Rivet>` via `defaultEdgeOptions`.
|
|
134
|
+
*/
|
|
135
|
+
type DefaultEdgeOptions = {
|
|
136
|
+
type?: string;
|
|
137
|
+
animated?: boolean;
|
|
138
|
+
style?: EdgeStyle;
|
|
139
|
+
markerEnd?: EdgeMarker;
|
|
140
|
+
markerStart?: EdgeMarker;
|
|
141
|
+
/** Label chip CSS applied to every labeled edge (under per-edge `labelStyle`). */
|
|
142
|
+
labelStyle?: CSSProperties;
|
|
143
|
+
};
|
|
144
|
+
/** The current selection, reported to `onSelectionChange`. */
|
|
145
|
+
type RivetSelection = {
|
|
146
|
+
nodes: RivetNode[];
|
|
147
|
+
edges: RivetEdge[];
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* A serializable snapshot of the graph the store owns — nodes, edges, and the
|
|
151
|
+
* viewport. Produced by {@link RivetInstance.toObject} and safe to `JSON.stringify`.
|
|
152
|
+
* Feed it back via the `nodes`/`edges` props and `defaultViewport` (or
|
|
153
|
+
* `setViewport`) to restore. Swimlanes are consumer-owned layout config (the
|
|
154
|
+
* `swimlane` prop), not part of the store — persist them alongside.
|
|
155
|
+
*/
|
|
156
|
+
type RivetSnapshot<TNodeData = unknown, TEdgeData = unknown> = {
|
|
157
|
+
nodes: RivetNode<TNodeData>[];
|
|
158
|
+
edges: RivetEdge<TEdgeData>[];
|
|
159
|
+
viewport: Viewport;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* A single change to one node, emitted by the graph to `onNodesChange`. Apply a
|
|
163
|
+
* batch to your own state with {@link applyNodeChanges}. This is the controlled
|
|
164
|
+
* API: the graph tells you what it wants to change; you decide whether to honor
|
|
165
|
+
* it by feeding the result back in via the `nodes` prop.
|
|
166
|
+
*/
|
|
167
|
+
type NodeChange<TData = unknown> = {
|
|
168
|
+
type: "position";
|
|
169
|
+
id: NodeId;
|
|
170
|
+
position?: Vec2;
|
|
171
|
+
dragging?: boolean;
|
|
172
|
+
} | {
|
|
173
|
+
type: "dimensions";
|
|
174
|
+
id: NodeId;
|
|
175
|
+
dimensions: Size;
|
|
176
|
+
resizing?: boolean;
|
|
177
|
+
} | {
|
|
178
|
+
type: "select";
|
|
179
|
+
id: NodeId;
|
|
180
|
+
selected: boolean;
|
|
181
|
+
} | {
|
|
182
|
+
type: "remove";
|
|
183
|
+
id: NodeId;
|
|
184
|
+
} | {
|
|
185
|
+
type: "add";
|
|
186
|
+
item: RivetNode<TData>;
|
|
187
|
+
} | {
|
|
188
|
+
type: "replace";
|
|
189
|
+
id: NodeId;
|
|
190
|
+
item: RivetNode<TData>;
|
|
191
|
+
};
|
|
192
|
+
/** A single change to one edge, emitted to `onEdgesChange`. See {@link NodeChange}. */
|
|
193
|
+
type EdgeChange<TData = unknown> = {
|
|
194
|
+
type: "select";
|
|
195
|
+
id: EdgeId;
|
|
196
|
+
selected: boolean;
|
|
197
|
+
} | {
|
|
198
|
+
type: "remove";
|
|
199
|
+
id: EdgeId;
|
|
200
|
+
} | {
|
|
201
|
+
type: "add";
|
|
202
|
+
item: RivetEdge<TData>;
|
|
203
|
+
} | {
|
|
204
|
+
type: "replace";
|
|
205
|
+
id: EdgeId;
|
|
206
|
+
item: RivetEdge<TData>;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* What a handle may be when dragging a connection:
|
|
210
|
+
* - `"source"` — can only start an edge.
|
|
211
|
+
* - `"target"` — can only receive an edge.
|
|
212
|
+
* - `"either"` — can do both (acts as source or target as needed).
|
|
213
|
+
*
|
|
214
|
+
* A pair is valid when one end can source and the other can target.
|
|
215
|
+
*/
|
|
216
|
+
type HandleType = "source" | "target" | "either";
|
|
217
|
+
type HandlePosition = "left" | "right" | "top" | "bottom";
|
|
218
|
+
/** A registered connection point on a node. */
|
|
219
|
+
type HandleRecord = {
|
|
220
|
+
nodeId: NodeId;
|
|
221
|
+
handleId: string;
|
|
222
|
+
type: HandleType;
|
|
223
|
+
/** Which side of the node the handle sits on; drives the edge's exit direction. */
|
|
224
|
+
position: HandlePosition;
|
|
225
|
+
/** Center offset from the node's top-left, in world units. */
|
|
226
|
+
offset: Vec2;
|
|
227
|
+
};
|
|
228
|
+
/** A proposed or committed link between two handles. */
|
|
229
|
+
type Connection = {
|
|
230
|
+
source: NodeId;
|
|
231
|
+
target: NodeId;
|
|
232
|
+
sourceHandle: string | null;
|
|
233
|
+
targetHandle: string | null;
|
|
234
|
+
};
|
|
235
|
+
/** The in-progress connection being dragged (world coordinates). */
|
|
236
|
+
type PendingConnection = {
|
|
237
|
+
source: NodeId;
|
|
238
|
+
sourceHandle: string;
|
|
239
|
+
sourceType: HandleType;
|
|
240
|
+
from: Vec2;
|
|
241
|
+
to: Vec2;
|
|
242
|
+
/**
|
|
243
|
+
* Side the loose end enters from — the snapped target handle's side while the
|
|
244
|
+
* drag is over a compatible handle, so the preview curves exactly like the edge
|
|
245
|
+
* it will commit to. Unset when hovering empty space (the end faces the source).
|
|
246
|
+
*/
|
|
247
|
+
toPosition?: HandlePosition;
|
|
248
|
+
/** Set when this drag is reconnecting an existing edge (that edge is hidden). */
|
|
249
|
+
reconnecting?: EdgeId;
|
|
250
|
+
};
|
|
251
|
+
/** Inputs to an {@link EdgePathFn}: both ends in screen space, plus their sides. */
|
|
252
|
+
type EdgePathParams = {
|
|
253
|
+
sourceX: number;
|
|
254
|
+
sourceY: number;
|
|
255
|
+
targetX: number;
|
|
256
|
+
targetY: number;
|
|
257
|
+
sourcePosition: HandlePosition;
|
|
258
|
+
targetPosition: HandlePosition;
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* The geometry an edge type produces: a polyline the renderer strokes (and picks
|
|
262
|
+
* against) plus the point where a label sits. Sampled polylines keep drawing,
|
|
263
|
+
* hit-testing, and labels uniform across built-in and custom edge types — and
|
|
264
|
+
* stay renderer-agnostic (any backend consumes the same points).
|
|
265
|
+
*/
|
|
266
|
+
type EdgePath = {
|
|
267
|
+
points: Vec2[];
|
|
268
|
+
labelX: number;
|
|
269
|
+
labelY: number;
|
|
270
|
+
};
|
|
271
|
+
/** A custom edge shape: pure geometry, drawable by any backend. */
|
|
272
|
+
type EdgePathFn = (params: EdgePathParams) => EdgePath;
|
|
273
|
+
/** Map of edge `type` keys to the path builder that shapes them. */
|
|
274
|
+
type EdgeTypes = Record<string, EdgePathFn>;
|
|
275
|
+
/** Which end of an edge a point is nearest — used for reconnection. */
|
|
276
|
+
type EdgeEnd = "source" | "target";
|
|
277
|
+
/**
|
|
278
|
+
* The runtime's reconnect gateway, bound on the store. `<Handle>` consults it on
|
|
279
|
+
* pointer-down: when a press lands within the endpoint radius of a
|
|
280
|
+
* *reconnectable* edge, the handle starts a reconnect drag for that edge instead
|
|
281
|
+
* of a new connection (hold Alt to force a new connection from an occupied
|
|
282
|
+
* handle).
|
|
283
|
+
*/
|
|
284
|
+
type ReconnectDelegate = {
|
|
285
|
+
/** The reconnectable edge endpoint near a client-space point, or null. */
|
|
286
|
+
pickEndpoint: (clientX: number, clientY: number) => {
|
|
287
|
+
edgeId: EdgeId;
|
|
288
|
+
end: EdgeEnd;
|
|
289
|
+
} | null;
|
|
290
|
+
/** Start the reconnect drag for that endpoint. */
|
|
291
|
+
begin: (edgeId: EdgeId, end: EdgeEnd) => void;
|
|
292
|
+
};
|
|
293
|
+
/** Extra per-frame inputs for the edge renderer. */
|
|
294
|
+
type EdgeDrawExtras = {
|
|
295
|
+
handles?: Map<string, HandleRecord>;
|
|
296
|
+
pending?: PendingConnection | null;
|
|
297
|
+
/** Edge under the pointer, drawn with a hover highlight. */
|
|
298
|
+
hovered?: EdgeId | null;
|
|
299
|
+
/** Edge currently being reconnected — skipped so only its pending preview shows. */
|
|
300
|
+
reconnecting?: EdgeId | null;
|
|
301
|
+
/** Resolved world positions per node (parent chains applied); anchors use these. */
|
|
302
|
+
worldPositions?: ReadonlyMap<NodeId, Vec2>;
|
|
303
|
+
/**
|
|
304
|
+
* Hysteresis memory for per-frame side resolution, keyed by
|
|
305
|
+
* `"<edgeId>:<end>"`. The renderer reads the last displayed side of every
|
|
306
|
+
* auto-resolved end from here and writes the new one back, so near-diagonal
|
|
307
|
+
* layouts don't flicker. Owned by the store (`getDisplayedSides`) so a
|
|
308
|
+
* `"live"` alignment commit can re-side to exactly what was displayed.
|
|
309
|
+
*/
|
|
310
|
+
displayedSides?: Map<string, HandlePosition>;
|
|
311
|
+
/**
|
|
312
|
+
* Nodes currently mid-drag under `edgeAlignment: "live"`. Every edge touching
|
|
313
|
+
* one renders both ends as auto (facing each other) for the frame; the
|
|
314
|
+
* resolved sides commit on drop, not here.
|
|
315
|
+
*/
|
|
316
|
+
liveAlignNodes?: ReadonlySet<NodeId>;
|
|
317
|
+
/**
|
|
318
|
+
* Places an anchor end along `side` of its node — the anchor's stray-handle
|
|
319
|
+
* position as % of that side — or null when the anchor is unknown or
|
|
320
|
+
* unmeasured (the end falls back to the side's midpoint).
|
|
321
|
+
*/
|
|
322
|
+
anchorPlacement?: (nodeId: NodeId, anchorId: string, side: HandlePosition) => number | null;
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* The seam that keeps graph and interaction code independent of how edges are
|
|
326
|
+
* drawn — swap the Canvas2D default for a custom backend without touching either.
|
|
327
|
+
*/
|
|
328
|
+
interface EdgeRenderer {
|
|
329
|
+
/** Draw all edges (and any in-progress connection) for the current frame. */
|
|
330
|
+
draw(edges: RivetEdge[], nodes: Map<NodeId, RivetNode>, viewport: Viewport, extras?: EdgeDrawExtras): void;
|
|
331
|
+
/** Screen-space label anchor per edge that has a label, from the last {@link draw}. */
|
|
332
|
+
getLabels(): Map<EdgeId, Vec2>;
|
|
333
|
+
/** Return the edge under a screen-space point, or null. `tolerance` overrides
|
|
334
|
+
* the backend's default pick distance (used for hover hysteresis). */
|
|
335
|
+
pick(x: number, y: number, tolerance?: number): EdgeId | null;
|
|
336
|
+
/** Return the edge endpoint under a screen-space point, or null (for reconnection). */
|
|
337
|
+
pickEndpoint(x: number, y: number): {
|
|
338
|
+
edgeId: EdgeId;
|
|
339
|
+
end: EdgeEnd;
|
|
340
|
+
} | null;
|
|
341
|
+
/**
|
|
342
|
+
* Screen-space endpoints per edge from the last {@link draw} — the runtime
|
|
343
|
+
* reads these to render the reconnect affordance. Optional so custom
|
|
344
|
+
* backends without it just don't get endpoint bubbles.
|
|
345
|
+
*/
|
|
346
|
+
getEndpoints?(): ReadonlyMap<EdgeId, {
|
|
347
|
+
source: Vec2;
|
|
348
|
+
target: Vec2;
|
|
349
|
+
}>;
|
|
350
|
+
/** Resize backing buffers to match the container. */
|
|
351
|
+
resize(width: number, height: number, dpr: number): void;
|
|
352
|
+
/** Release GPU / canvas resources. */
|
|
353
|
+
dispose(): void;
|
|
354
|
+
}
|
|
355
|
+
/** Construction options shared by every {@link EdgeRenderer} backend. */
|
|
356
|
+
type EdgeRendererOptions = {
|
|
357
|
+
/** Registry of custom edge path builders, merged over the built-ins. */
|
|
358
|
+
edgeTypes?: EdgeTypes;
|
|
359
|
+
/** Defaults merged into every edge (type, animated, style, markers). */
|
|
360
|
+
defaultEdgeOptions?: DefaultEdgeOptions;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Factory for an {@link EdgeRenderer} backend, passed to `<Rivet renderer={…}>`.
|
|
364
|
+
* Called once per edge canvas (committed edges + the foreground connection).
|
|
365
|
+
* Construction is synchronous; a backend that needs async setup should no-op
|
|
366
|
+
* its `draw` until ready. Defaults to Canvas2D.
|
|
367
|
+
*/
|
|
368
|
+
type EdgeRendererFactory = (canvas: HTMLCanvasElement, options: EdgeRendererOptions) => EdgeRenderer;
|
|
369
|
+
/** Props passed to every node component registered in `nodeTypes`. */
|
|
370
|
+
type NodeProps<TData = unknown> = {
|
|
371
|
+
id: NodeId;
|
|
372
|
+
type?: string;
|
|
373
|
+
data: TData;
|
|
374
|
+
selected: boolean;
|
|
375
|
+
hovered: boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Current box width in world units — the explicit {@link RivetNode.width} set
|
|
378
|
+
* by {@link NodeResizer}, falling back to the measured size. `undefined` until
|
|
379
|
+
* the node has an explicit width or has been measured.
|
|
380
|
+
*/
|
|
381
|
+
width?: number;
|
|
382
|
+
/** Current box height in world units. See {@link NodeProps.width}. */
|
|
383
|
+
height?: number;
|
|
384
|
+
/**
|
|
385
|
+
* The node's own position, in world units — relative to its
|
|
386
|
+
* {@link RivetNode.parentId}'s top-left when it has a parent, otherwise the
|
|
387
|
+
* same as {@link NodeProps.positionAbsolute}.
|
|
388
|
+
*/
|
|
389
|
+
position: Vec2;
|
|
390
|
+
/** Absolute world position, with the parent chain resolved. */
|
|
391
|
+
positionAbsolute: Vec2;
|
|
392
|
+
};
|
|
393
|
+
type NodeComponent<TData = any> = ComponentType<NodeProps<TData>>;
|
|
394
|
+
/** Map of node `type` keys to the React component that renders them. */
|
|
395
|
+
type NodeTypes = Record<string, NodeComponent>;
|
|
396
|
+
/** A width/height pair in world units. */
|
|
397
|
+
type Size = {
|
|
398
|
+
width: number;
|
|
399
|
+
height: number;
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* One swimlane — a "file" within a {@link SwimlaneGroup} "folder". Lanes stack
|
|
403
|
+
* vertically below their group's header; a node lives inside exactly one lane.
|
|
404
|
+
*/
|
|
405
|
+
type Swimlane = {
|
|
406
|
+
id: string;
|
|
407
|
+
/** Text shown in the lane's sticky left label. */
|
|
408
|
+
label?: string;
|
|
409
|
+
/** Lane height in world units. */
|
|
410
|
+
size: number;
|
|
411
|
+
/** Optional fill tint for the lane band (any CSS color). */
|
|
412
|
+
color?: string;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* A swimlane group — a "folder" of {@link Swimlane} "files". Rendered as a
|
|
416
|
+
* sticky top header band above its lanes. Groups are neither nodes nor edges;
|
|
417
|
+
* they're passed to `<Rivet>` via the `swimlane` prop.
|
|
418
|
+
*/
|
|
419
|
+
type SwimlaneGroup = {
|
|
420
|
+
id: string;
|
|
421
|
+
/** Text shown in the group's sticky header. */
|
|
422
|
+
label?: string;
|
|
423
|
+
/** Accent color (hex) for the header band and, by default, its lanes. */
|
|
424
|
+
color?: string;
|
|
425
|
+
/** Top-left origin of the group (its header top) in world space. */
|
|
426
|
+
position?: Vec2;
|
|
427
|
+
/** Nominal lane width in world units. Bands render full-viewport-width. */
|
|
428
|
+
length: number;
|
|
429
|
+
lanes: Swimlane[];
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Inner clearance kept between a node and its lane's edges when clamping. A
|
|
433
|
+
* number applies to all sides; per-side values override it.
|
|
434
|
+
*/
|
|
435
|
+
type SwimlaneMargin = {
|
|
436
|
+
top?: number;
|
|
437
|
+
right?: number;
|
|
438
|
+
bottom?: number;
|
|
439
|
+
left?: number;
|
|
440
|
+
};
|
|
441
|
+
/** Passed to a custom `renderSwimlaneHeader`. */
|
|
442
|
+
type SwimlaneHeaderProps = {
|
|
443
|
+
id: string;
|
|
444
|
+
label?: string;
|
|
445
|
+
/** The group's accent color, if any. */
|
|
446
|
+
color?: string;
|
|
447
|
+
/** True while the header is pinned to the pane top (scrolled into its band). */
|
|
448
|
+
sticky: boolean;
|
|
449
|
+
};
|
|
450
|
+
/** Passed to a custom `renderSwimlaneLabel`. */
|
|
451
|
+
type SwimlaneLabelProps = {
|
|
452
|
+
id: string;
|
|
453
|
+
label?: string;
|
|
454
|
+
color?: string;
|
|
455
|
+
groupId: string;
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* Reported to `onSwimlaneSizeChange` when a lane's height changes — from a drag
|
|
459
|
+
* of its bottom handle or from auto-fit growing it to contain a node.
|
|
460
|
+
*/
|
|
461
|
+
type SwimlaneSizeChange = {
|
|
462
|
+
laneId: string;
|
|
463
|
+
/** The lane's new height in world units. */
|
|
464
|
+
height: number;
|
|
465
|
+
/** What caused the change. */
|
|
466
|
+
reason: "resize" | "autofit";
|
|
467
|
+
};
|
|
468
|
+
/** Reported to `onLaneChange` when a dragged node moves to a different lane. */
|
|
469
|
+
type LaneChange = {
|
|
470
|
+
nodeId: NodeId;
|
|
471
|
+
/** The lane the node now belongs to, or null if dropped outside all lanes. */
|
|
472
|
+
laneId: string | null;
|
|
473
|
+
/** The lane it came from, or null if it had none. */
|
|
474
|
+
previousLaneId: string | null;
|
|
475
|
+
};
|
|
476
|
+
/** Options for {@link RivetControls.fitView}. */
|
|
477
|
+
type FitViewOptions = {
|
|
478
|
+
/** Fraction of the pane to leave as empty margin on each side (default 0.1). */
|
|
479
|
+
padding?: number;
|
|
480
|
+
/** Never zoom in past this level when fitting (default: the graph's maxZoom). */
|
|
481
|
+
maxZoom?: number;
|
|
482
|
+
/** Animate the move over this many ms (default 0 — jump instantly). A user pan/zoom cancels it. */
|
|
483
|
+
duration?: number;
|
|
484
|
+
};
|
|
485
|
+
/** Options for {@link RivetControls.centerNode}. */
|
|
486
|
+
type CenterNodeOptions = {
|
|
487
|
+
/** Absolute zoom to apply while centering (default: keep the current zoom). */
|
|
488
|
+
zoom?: number;
|
|
489
|
+
/** Animate the move over this many ms (default 0 — jump instantly). A user pan/zoom cancels it. */
|
|
490
|
+
duration?: number;
|
|
491
|
+
};
|
|
492
|
+
/**
|
|
493
|
+
* Imperative viewport controls, available via {@link useRivetControls} or on
|
|
494
|
+
* the context. Everything is clamped to the graph's `minZoom`/`maxZoom`.
|
|
495
|
+
*/
|
|
496
|
+
type RivetControls = {
|
|
497
|
+
/** Zoom in one step, anchored on the pane center. */
|
|
498
|
+
zoomIn: () => void;
|
|
499
|
+
/** Zoom out one step, anchored on the pane center. */
|
|
500
|
+
zoomOut: () => void;
|
|
501
|
+
/** Zoom to an absolute level, anchored on the pane center. */
|
|
502
|
+
zoomTo: (zoom: number) => void;
|
|
503
|
+
/** Frame all nodes within the pane. No-op when the graph is empty. */
|
|
504
|
+
fitView: (options?: FitViewOptions) => void;
|
|
505
|
+
/** Frame the currently selected nodes within the pane. No-op when nothing is selected. */
|
|
506
|
+
fitSelection: (options?: FitViewOptions) => void;
|
|
507
|
+
/** Frame an arbitrary world-space rect within the pane (e.g. a swimlane's bounds). */
|
|
508
|
+
fitBounds: (rect: Rect, options?: FitViewOptions) => void;
|
|
509
|
+
/** Pan (and optionally zoom) so a node sits at the pane center. No-op for an unknown id. */
|
|
510
|
+
centerNode: (id: NodeId, options?: CenterNodeOptions) => void;
|
|
511
|
+
getViewport: () => Viewport;
|
|
512
|
+
setViewport: (viewport: Viewport) => void;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
export type { AlignmentGuide as A, EdgeMarker as B, Connection as C, DefaultEdgeOptions as D, EdgeChange as E, EdgePath as F, EdgePathParams as G, HandlePosition as H, EdgeStyle as I, FitViewOptions as J, NodeComponent as K, LaneChange as L, Swimlane as M, NodeId as N, PendingConnection as P, Rect as R, Size as S, Vec2 as V, RivetEdge as a, NodeChange as b, RivetNode as c, NodeProps as d, NodeTypes as e, HandleType as f, Viewport as g, EdgeTypes as h, EdgeRendererFactory as i, SwimlaneGroup as j, SwimlaneMargin as k, SwimlaneHeaderProps as l, SwimlaneLabelProps as m, SwimlaneSizeChange as n, RivetSelection as o, EdgeId as p, HandleRecord as q, ReconnectDelegate as r, RivetControls as s, RivetSnapshot as t, EdgeRenderer as u, EdgeRendererOptions as v, EdgeDrawExtras as w, EdgeEnd as x, EdgePathFn as y, CenterNodeOptions as z };
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kolosal-ai/rivet",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Canvas-rendered node graph for React: DOM nodes, Canvas2D edges, swimlanes, anchors, undo/redo",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/KolosalAI/rivet.git",
|
|
9
|
+
"directory": "packages/rivet"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/KolosalAI/rivet#readme",
|
|
12
|
+
"bugs": "https://github.com/KolosalAI/rivet/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"react",
|
|
15
|
+
"node-graph",
|
|
16
|
+
"node-editor",
|
|
17
|
+
"flow",
|
|
18
|
+
"canvas",
|
|
19
|
+
"diagram",
|
|
20
|
+
"swimlane",
|
|
21
|
+
"graph-editor"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./anchor": {
|
|
31
|
+
"types": "./dist/anchor/index.d.ts",
|
|
32
|
+
"import": "./dist/anchor/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./svg": {
|
|
35
|
+
"types": "./dist/svg/index.d.ts",
|
|
36
|
+
"import": "./dist/svg/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./swimlane": {
|
|
39
|
+
"types": "./dist/swimlane/index.d.ts",
|
|
40
|
+
"import": "./dist/swimlane/index.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"react": "^18.3.0 || ^19.0.0",
|
|
51
|
+
"react-dom": "^18.3.0 || ^19.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/react": "^19.1.8",
|
|
55
|
+
"@types/react-dom": "^19.1.6",
|
|
56
|
+
"jsdom": "^26.0.0",
|
|
57
|
+
"react": "^19.1.0",
|
|
58
|
+
"react-dom": "^19.1.0",
|
|
59
|
+
"tsup": "^8.5.0",
|
|
60
|
+
"typescript": "^5.8.3",
|
|
61
|
+
"vitest": "^3.2.0",
|
|
62
|
+
"@rivet/utils": "0.0.0"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsup",
|
|
66
|
+
"check-types": "tsc --noEmit",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"clean": "rm -rf dist .turbo"
|
|
69
|
+
}
|
|
70
|
+
}
|