@skygraph/core 0.0.0-placeholder.0 → 0.5.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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -6
  3. package/dist/calendar.cjs +404 -0
  4. package/dist/calendar.cjs.map +1 -0
  5. package/dist/calendar.d.cts +209 -0
  6. package/dist/calendar.d.ts +209 -0
  7. package/dist/calendar.js +12 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/chunk-5CCD5Q4B.js +36 -0
  10. package/dist/chunk-5CCD5Q4B.js.map +1 -0
  11. package/dist/chunk-7FA2TBSZ.js +1211 -0
  12. package/dist/chunk-7FA2TBSZ.js.map +1 -0
  13. package/dist/chunk-AB3RLBLW.js +357 -0
  14. package/dist/chunk-AB3RLBLW.js.map +1 -0
  15. package/dist/chunk-GEMALROQ.js +258 -0
  16. package/dist/chunk-GEMALROQ.js.map +1 -0
  17. package/dist/chunk-IY6LJU3L.js +256 -0
  18. package/dist/chunk-IY6LJU3L.js.map +1 -0
  19. package/dist/chunk-KMGRNPLG.js +453 -0
  20. package/dist/chunk-KMGRNPLG.js.map +1 -0
  21. package/dist/chunk-Y7FTBBYX.js +397 -0
  22. package/dist/chunk-Y7FTBBYX.js.map +1 -0
  23. package/dist/chunk-ZWB7JGAJ.js +488 -0
  24. package/dist/chunk-ZWB7JGAJ.js.map +1 -0
  25. package/dist/form.cjs +498 -0
  26. package/dist/form.cjs.map +1 -0
  27. package/dist/form.d.cts +130 -0
  28. package/dist/form.d.ts +130 -0
  29. package/dist/form.js +8 -0
  30. package/dist/form.js.map +1 -0
  31. package/dist/graph.cjs +1269 -0
  32. package/dist/graph.cjs.map +1 -0
  33. package/dist/graph.d.cts +671 -0
  34. package/dist/graph.d.ts +671 -0
  35. package/dist/graph.js +34 -0
  36. package/dist/graph.js.map +1 -0
  37. package/dist/index.cjs +3860 -0
  38. package/dist/index.cjs.map +1 -0
  39. package/dist/index.d.cts +144 -0
  40. package/dist/index.d.ts +144 -0
  41. package/dist/index.js +477 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/runtime-internal.cjs +339 -0
  44. package/dist/runtime-internal.cjs.map +1 -0
  45. package/dist/runtime-internal.d.cts +130 -0
  46. package/dist/runtime-internal.d.ts +130 -0
  47. package/dist/runtime-internal.js +67 -0
  48. package/dist/runtime-internal.js.map +1 -0
  49. package/dist/table.cjs +537 -0
  50. package/dist/table.cjs.map +1 -0
  51. package/dist/table.d.cts +215 -0
  52. package/dist/table.d.ts +215 -0
  53. package/dist/table.js +16 -0
  54. package/dist/table.js.map +1 -0
  55. package/dist/tree.cjs +442 -0
  56. package/dist/tree.cjs.map +1 -0
  57. package/dist/tree.d.cts +76 -0
  58. package/dist/tree.d.ts +76 -0
  59. package/dist/tree.js +8 -0
  60. package/dist/tree.js.map +1 -0
  61. package/dist/types-B-rKQJ4R.d.cts +35 -0
  62. package/dist/types-B-rKQJ4R.d.ts +35 -0
  63. package/dist/virtual.cjs +283 -0
  64. package/dist/virtual.cjs.map +1 -0
  65. package/dist/virtual.d.cts +96 -0
  66. package/dist/virtual.d.ts +96 -0
  67. package/dist/virtual.js +9 -0
  68. package/dist/virtual.js.map +1 -0
  69. package/package.json +98 -18
  70. package/index.js +0 -3
@@ -0,0 +1,671 @@
1
+ import { C as Core } from './types-B-rKQJ4R.cjs';
2
+
3
+ /**
4
+ * GraphEngine types — the diagram (Visio-like) domain engine.
5
+ *
6
+ * Design intent (see ARCHITECTURE.md / chat-derived spec):
7
+ *
8
+ * - The engine knows ONLY topology + minimal geometry needed for routing /
9
+ * hit-testing. It does NOT know about React, DOM, SVG, canvas — those live
10
+ * in `@skygraph/react/<Diagram component>`.
11
+ * - A Node has an `outline` (rect / ellipse / polygon / flattened path),
12
+ * a `transform` (position in parent space), an optional `parentId` for
13
+ * hierarchy (groups, sub-groups), and an `anchorPolicy` describing how to
14
+ * generate connection points along the outline.
15
+ * - An Edge connects two anchors (each by node id + either an anchor id or
16
+ * a normalized perimeter parameter `s ∈ [0..1]`).
17
+ * - All anchor IDs are stable across `anchorPolicy` changes for the same
18
+ * outline kind, so existing edges do not get "torn off" when the user
19
+ * bumps `k` from 3 to 5.
20
+ */
21
+ type NodeId = string;
22
+ type EdgeId = string;
23
+ type AnchorId = string;
24
+ /**
25
+ * 2D point in the local coordinate space of an outline.
26
+ * The first element is x, the second is y.
27
+ */
28
+ type Point = readonly [number, number];
29
+ /**
30
+ * Axis-aligned bounding box in world space (no rotation).
31
+ * Computed from `transform` + `outline`. Used for broad-phase collision /
32
+ * spatial indexing in the routing layer.
33
+ */
34
+ interface AABB {
35
+ x: number;
36
+ y: number;
37
+ w: number;
38
+ h: number;
39
+ }
40
+ /**
41
+ * Oriented bounding box in world space — a rectangle that may be rotated
42
+ * relative to the world axes. Returned by {@link GraphEngine.getNodeOBB}
43
+ * and consumed by selection / manipulator overlays that need a tight fit
44
+ * around a rotated node. See `obb.ts` for the helper module.
45
+ *
46
+ * `halfWidth` / `halfHeight` are pre-multiplied by world scale, so an OBB
47
+ * with `angle === 0` describes the same rectangle as the matching AABB
48
+ * (modulo the centre-vs-top-left representation).
49
+ */
50
+ interface OBB$1 {
51
+ readonly center: Point;
52
+ readonly halfWidth: number;
53
+ readonly halfHeight: number;
54
+ readonly angle: number;
55
+ }
56
+ /**
57
+ * Visual outline of a node, expressed in the node's local coordinate space.
58
+ *
59
+ * - `rect` / `ellipse` are parametric primitives; the engine generates anchors
60
+ * along their boundary on demand.
61
+ * - `polygon` carries explicit vertices.
62
+ * - `path` is a "flattened approximation" — the visual layer (React) is free
63
+ * to render a curve (SVG path, Bezier, ...) but commits a discrete polyline
64
+ * to the engine. This way the engine never needs to know about cubic Beziers.
65
+ */
66
+ type Outline = {
67
+ readonly kind: 'rect';
68
+ readonly w: number;
69
+ readonly h: number;
70
+ } | {
71
+ readonly kind: 'ellipse';
72
+ readonly rx: number;
73
+ readonly ry: number;
74
+ } | {
75
+ readonly kind: 'polygon';
76
+ readonly verts: readonly Point[];
77
+ } | {
78
+ readonly kind: 'path';
79
+ readonly flatten: readonly Point[];
80
+ };
81
+ /**
82
+ * 2D affine-ish transform of a node within its parent.
83
+ * Only translate is required; rotate/scale are optional for the v0 engine.
84
+ */
85
+ interface NodeTransform {
86
+ /** X position in the parent's local space. */
87
+ x: number;
88
+ /** Y position in the parent's local space. */
89
+ y: number;
90
+ /** Rotation in radians. Optional; default 0. */
91
+ rot?: number;
92
+ /** Uniform scale factor. Optional; default 1. */
93
+ scale?: number;
94
+ }
95
+ /**
96
+ * Strategy for generating anchor points on a node outline.
97
+ *
98
+ * - `perEdge: { k: 1..10 }` — primitives only (rect, polygon).
99
+ * Corners are always anchors. `k` evenly-spaced anchors per edge,
100
+ * not counting corners (open interval) — bumping `k` does NOT shift
101
+ * existing anchor IDs.
102
+ *
103
+ * - `byLength: { density }` — uniform along the perimeter at the given
104
+ * distance step (in local units). Works for every outline kind.
105
+ *
106
+ * - `manual: anchors[]` — explicit anchor list with stable IDs and
107
+ * normalized perimeter parameter `s ∈ [0..1]`.
108
+ */
109
+ type AnchorPolicy = {
110
+ readonly kind: 'perEdge';
111
+ readonly k: number;
112
+ } | {
113
+ readonly kind: 'byLength';
114
+ readonly density: number;
115
+ } | {
116
+ readonly kind: 'manual';
117
+ readonly anchors: readonly {
118
+ id: AnchorId;
119
+ s: number;
120
+ }[];
121
+ };
122
+ /**
123
+ * A point on the outline where edges can attach.
124
+ * `s` is the normalized perimeter parameter (0..1, monotonically increasing
125
+ * around the outline). Persisting an edge endpoint by `s` makes it stable
126
+ * under outline edits / anchor regeneration.
127
+ */
128
+ interface Anchor {
129
+ /** Stable id assigned by the anchor generator (e.g. `nw`, `edge-0:1of3`). */
130
+ id: AnchorId;
131
+ /** Normalized perimeter parameter; `0` is the start of the outline traversal. */
132
+ s: number;
133
+ /** Local coordinates on the outline (x, y). */
134
+ point: Point;
135
+ }
136
+ /**
137
+ * A graph node — building block of the diagram. Can be a "shape" (with a
138
+ * visible outline), a "group" (`outline.kind === 'rect'` with `w=h=0` plus
139
+ * children, or any other size — visual layer decides whether to render
140
+ * border / background), or a leaf widget. The engine itself does NOT
141
+ * distinguish — it only sees outline + transform + children-by-parentId.
142
+ */
143
+ interface GraphNode {
144
+ /** Stable identity; unique within the graph. */
145
+ id: NodeId;
146
+ /** Parent node id, or `null` for root-level nodes. */
147
+ parentId: NodeId | null;
148
+ /** Position / rotation / scale in parent space. */
149
+ transform: NodeTransform;
150
+ /** Local-space outline. */
151
+ outline: Outline;
152
+ /** How anchors are generated along the outline. */
153
+ anchorPolicy: AnchorPolicy;
154
+ /** Optional application payload; the engine never inspects it. */
155
+ data?: unknown;
156
+ /** Monotonic revision; increments on transform / outline / anchorPolicy change. */
157
+ geomRevision: number;
158
+ }
159
+ /**
160
+ * An edge is a connection between two anchors on (possibly different) nodes.
161
+ *
162
+ * `endpoint.anchor` may be:
163
+ * - an `AnchorId` referring to an explicit / generated anchor;
164
+ * - a `{ s }` parameter binding the edge to a perimeter position even when
165
+ * the anchor list is regenerated.
166
+ */
167
+ type EdgeEndpoint = {
168
+ readonly node: NodeId;
169
+ readonly anchor: AnchorId;
170
+ } | {
171
+ readonly node: NodeId;
172
+ readonly anchor: {
173
+ readonly s: number;
174
+ };
175
+ };
176
+ interface GraphEdge {
177
+ /** Stable identity; unique within the graph. */
178
+ id: EdgeId;
179
+ /** Source endpoint. */
180
+ from: EdgeEndpoint;
181
+ /** Target endpoint. */
182
+ to: EdgeEndpoint;
183
+ /**
184
+ * Routing hint:
185
+ * • `straight` — single L line between the two anchors.
186
+ * • `orthogonal` — axis-aligned polyline (right-angled). Combined
187
+ * with `routeAroundNodes` in `<Diagram>` the
188
+ * router avoids other nodes via A*. The visual
189
+ * layer also draws rounded corners.
190
+ * • `bezier` — single cubic Bezier curve. Control handles
191
+ * extend perpendicular to each node's nearest
192
+ * side, matching React Flow's `getBezierPath`.
193
+ * • `manual` — explicit waypoint polyline.
194
+ */
195
+ routing: 'straight' | 'orthogonal' | 'bezier' | 'manual';
196
+ /** Optional waypoints for `manual` routing (in world coordinates). */
197
+ waypoints?: readonly Point[];
198
+ /** Optional application payload. */
199
+ data?: unknown;
200
+ }
201
+ /**
202
+ * Snapshot of the current graph state — read-model the engine exposes.
203
+ * This object is immutable from the consumer's perspective; the engine
204
+ * publishes a new snapshot on every commit by writing into the Core store
205
+ * under the `$graph.` prefix.
206
+ */
207
+ interface GraphState {
208
+ nodes: ReadonlyMap<NodeId, GraphNode>;
209
+ edges: ReadonlyMap<EdgeId, GraphEdge>;
210
+ /** Per-node list of incident edges (incoming + outgoing) for O(degree) lookup. */
211
+ edgesByNode: ReadonlyMap<NodeId, readonly EdgeId[]>;
212
+ }
213
+ /** Patch type for `moveNode` / `updateNode`. */
214
+ interface NodeUpdate {
215
+ transform?: Partial<NodeTransform>;
216
+ outline?: Outline;
217
+ anchorPolicy?: AnchorPolicy;
218
+ data?: unknown;
219
+ }
220
+ /** Options for `addNode`. */
221
+ interface NodeInit {
222
+ /** Optional pre-set id. If omitted, the engine generates one. */
223
+ id?: NodeId;
224
+ /** Parent for hierarchy. `null` (default) places the node at the root. */
225
+ parentId?: NodeId | null;
226
+ /** Transform in parent space. Defaults to `{ x: 0, y: 0 }`. */
227
+ transform?: NodeTransform;
228
+ /** Outline; defaults to a 100×60 rect. */
229
+ outline?: Outline;
230
+ /** Anchor strategy; defaults to `{ kind: 'perEdge', k: 1 }`. */
231
+ anchorPolicy?: AnchorPolicy;
232
+ /** Application payload. */
233
+ data?: unknown;
234
+ }
235
+ /** Options for `addEdge`. */
236
+ interface EdgeInit {
237
+ /** Optional pre-set id. If omitted, the engine generates one. */
238
+ id?: EdgeId;
239
+ from: EdgeEndpoint;
240
+ to: EdgeEndpoint;
241
+ /** Routing strategy. Defaults to `'straight'`. */
242
+ routing?: 'straight' | 'orthogonal' | 'bezier' | 'manual';
243
+ waypoints?: readonly Point[];
244
+ data?: unknown;
245
+ }
246
+ /**
247
+ * Public API of the Graph engine. Created via `createGraph(core, options?)`.
248
+ * The engine is a thin façade over a path-based Core store under `$graph.`.
249
+ */
250
+ interface GraphEngine {
251
+ /** Add a node and return its assigned id. */
252
+ addNode(init?: NodeInit): NodeId;
253
+ /** Remove a node and all edges incident to it. */
254
+ removeNode(id: NodeId): void;
255
+ /** Patch a node's transform / outline / anchorPolicy / data. */
256
+ updateNode(id: NodeId, patch: NodeUpdate): void;
257
+ /** Move a node by an absolute new (x, y) in parent space. */
258
+ moveNode(id: NodeId, x: number, y: number): void;
259
+ /** Re-parent a node (groups / sub-groups). `null` = root. */
260
+ setParent(id: NodeId, parentId: NodeId | null): void;
261
+ /** Add an edge between two endpoints. */
262
+ addEdge(init: EdgeInit): EdgeId;
263
+ /** Remove an edge by id. */
264
+ removeEdge(id: EdgeId): void;
265
+ /** Get a single node (or undefined). */
266
+ getNode(id: NodeId): GraphNode | undefined;
267
+ /** Get a single edge (or undefined). */
268
+ getEdge(id: EdgeId): GraphEdge | undefined;
269
+ /** Snapshot of the entire graph state. */
270
+ getState(): GraphState;
271
+ /** Direct children (immediate parentId match). */
272
+ childrenOf(id: NodeId | null): NodeId[];
273
+ /** Edges incident to a node (incoming + outgoing). */
274
+ edgesOf(id: NodeId): EdgeId[];
275
+ /** Compute anchors for a node according to its `anchorPolicy`. */
276
+ anchorsOf(id: NodeId): Anchor[];
277
+ /** Compute axis-aligned bounding box of a node in world coordinates. */
278
+ boundsOf(id: NodeId): AABB;
279
+ /**
280
+ * Compute the oriented bounding box of a node in world coordinates.
281
+ *
282
+ * For an axis-aligned node (`transform.rot === 0` everywhere up the
283
+ * parent chain) the OBB and AABB describe the same rectangle. When any
284
+ * ancestor has a non-zero rotation, the OBB is the tight fit and
285
+ * `boundsOf` (AABB) becomes a strict superset.
286
+ */
287
+ getNodeOBB(id: NodeId): OBB$1;
288
+ /**
289
+ * Subscribe to commits — callback fires after every snapshot publish
290
+ * (any `addNode`, `removeNode`, `addEdge`, etc.). Returns an unsubscribe fn.
291
+ *
292
+ * Prefer this for React adapters: it sidesteps the `$graph.snapshot.<id>`
293
+ * path lookup that varies per engine instance.
294
+ */
295
+ subscribe(cb: () => void): () => void;
296
+ /** Group multiple mutations into one history entry. Optional `label` is
297
+ * attached to the resulting `HistoryEntry`. */
298
+ transaction(fn: () => void, label?: string): void;
299
+ /**
300
+ * Add a label to the history without changing the visible state.
301
+ *
302
+ * Behaviour depends on context:
303
+ *
304
+ * - **Inside `transaction(fn)`**: commits the in-progress pending
305
+ * entry as a sub-boundary (with `label`) and starts a fresh
306
+ * capture for the rest of the transaction. Use this to split one
307
+ * batch into multiple undo entries.
308
+ *
309
+ * - **Outside a transaction**: re-labels the most recent undo entry.
310
+ * Does NOT push a duplicate snapshot — auto-push has already
311
+ * captured every meaningful boundary, so a standalone push would
312
+ * only clutter the stack.
313
+ */
314
+ pushHistory(label?: string): void;
315
+ /** Restore the previous state, returning `true` if anything was undone. */
316
+ undo(): boolean;
317
+ /** Re-apply the most recently undone state, returning `true` on success. */
318
+ redo(): boolean;
319
+ /** `true` when there is at least one entry on the undo stack. */
320
+ canUndo(): boolean;
321
+ /** `true` when there is at least one entry on the redo stack. */
322
+ canRedo(): boolean;
323
+ /** Drop all undo and redo entries (state itself is not affected). */
324
+ clearHistory(): void;
325
+ /**
326
+ * Reset the graph (drops all nodes / edges, clears history).
327
+ * @internal Test helper.
328
+ */
329
+ clear(): void;
330
+ }
331
+ /** Options accepted by `createGraph`. */
332
+ interface GraphEngineOptions {
333
+ /** Optional debug name for the engine instance. */
334
+ name?: string;
335
+ }
336
+
337
+ /**
338
+ * Reactive diagram engine — the fourth member of the form / table / tree /
339
+ * **graph** family. State is mirrored into the Core store under `$graph.`
340
+ * for cross-engine coordination, but the in-memory maps are the source of
341
+ * truth and the public read API.
342
+ *
343
+ * v0 scope (initial Graph engine stream — see CHANGELOG):
344
+ * - Node / Edge model + parent hierarchy
345
+ * - addNode / removeNode / updateNode / moveNode / setParent
346
+ * - addEdge / removeEdge
347
+ * - anchor generator (perEdge / byLength / manual)
348
+ * - bounds computation (AABB, no rotation)
349
+ * - geomRevision bump on geometry-affecting updates
350
+ *
351
+ * NOT in v0:
352
+ * - orthogonal routing (with obstacle avoidance)
353
+ * - undo / redo (covered separately by `plugins/history`)
354
+ * - snap-to-grid, drag-and-drop palette
355
+ * - rotated bounding boxes (OBB)
356
+ */
357
+ declare function createGraph(core: Core, options?: GraphEngineOptions): GraphEngine;
358
+
359
+ /** Options for {@link routeOrthogonal}. */
360
+ interface RouteOrthogonalOptions {
361
+ /**
362
+ * Bend preference for the no-obstacle case.
363
+ * - `auto` (default) picks H-then-V or V-then-H based on which leg is longer.
364
+ * - `hv` forces horizontal first.
365
+ * - `vh` forces vertical first.
366
+ *
367
+ * Ignored when `obstacles` is non-empty — A* picks bends from costs.
368
+ */
369
+ preferred?: 'auto' | 'hv' | 'vh';
370
+ /**
371
+ * Rectangular obstacles to avoid (in world coordinates). When omitted /
372
+ * empty, the router takes the cheap L-route path.
373
+ */
374
+ obstacles?: readonly AABB[];
375
+ /**
376
+ * Discretisation step for A*. Smaller values give smoother paths at the
377
+ * cost of more search nodes. Default: 10.
378
+ */
379
+ gridSize?: number;
380
+ /**
381
+ * Inflate every obstacle by this margin (world units) before clipping.
382
+ * Useful to keep edges from hugging node borders. Default: 0.
383
+ */
384
+ inflate?: number;
385
+ /**
386
+ * Hard cap on A* expansions. Acts as a safety net so a pathological
387
+ * search never hangs the renderer. Default: 5000.
388
+ */
389
+ maxNodes?: number;
390
+ /**
391
+ * AABB of the source node. When provided, the route exits the side of
392
+ * the rectangle nearest to `end` with a short perpendicular stub. The
393
+ * raw `start` point is kept as the first polyline vertex so the edge
394
+ * still meets the anchor exactly.
395
+ */
396
+ sourceBounds?: AABB;
397
+ /**
398
+ * AABB of the target node. When provided, the route enters the side of
399
+ * the rectangle nearest to `start` with a short perpendicular stub. The
400
+ * raw `end` point is kept as the last polyline vertex.
401
+ */
402
+ targetBounds?: AABB;
403
+ /**
404
+ * Length (world units) of the perpendicular stub generated when
405
+ * `sourceBounds` / `targetBounds` is set. Defaults to `max(20, gridSize)`
406
+ * — matches React Flow's `offset = 20` for smoothstep edges.
407
+ */
408
+ stubLength?: number;
409
+ /**
410
+ * Snap intermediate waypoints to a grid of this step. Defaults to the
411
+ * effective `gridSize`. Set to `0` to disable snapping in the no-
412
+ * obstacle path. A* already operates on the grid so this option only
413
+ * affects the L-route fallback.
414
+ */
415
+ snap?: number;
416
+ /**
417
+ * Where the central bend sits on the connector when both
418
+ * `sourceBounds` and `targetBounds` are provided.
419
+ *
420
+ * - `0` → bend right at the source stub
421
+ * - `0.5` → midpoint between the two stubs (default — matches
422
+ * React Flow's `stepPosition`)
423
+ * - `1` → bend right at the target stub
424
+ *
425
+ * Ignored when bounds are missing (we fall back to the legacy L-route)
426
+ * or when A* is engaged (it picks bends from path-cost, not position).
427
+ */
428
+ stepPosition?: number;
429
+ }
430
+ /**
431
+ * Compute an orthogonal (right-angled) path between two points.
432
+ *
433
+ * - With no `sourceBounds` / `targetBounds` and no obstacles: a single-
434
+ * bend L-route, identical to the v0 implementation.
435
+ * - With `sourceBounds` / `targetBounds`: the route exits/enters from
436
+ * the side of each rectangle nearest to the other endpoint.
437
+ * - With obstacles: 4-connected A* on a grid of step `gridSize`. Uses
438
+ * a turn penalty so straight runs are preferred. Falls back to the
439
+ * L-route if A* cannot find a path within `maxNodes` expansions or
440
+ * either endpoint is unreachable.
441
+ *
442
+ * The third argument also accepts the legacy bare-string form
443
+ * (`'auto' | 'hv' | 'vh'`) for backwards compatibility with the v0 API.
444
+ */
445
+ declare function routeOrthogonal(start: Point, end: Point, options?: RouteOrthogonalOptions | 'auto' | 'hv' | 'vh'): readonly Point[];
446
+ /**
447
+ * Build an SVG path `d` attribute from a polyline.
448
+ *
449
+ * pointsToPath([[0,0], [10,0], [10,5]]) → "M 0 0 L 10 0 L 10 5"
450
+ */
451
+ declare function pointsToPath(points: readonly Point[]): string;
452
+ /**
453
+ * Build a rounded-corner SVG path from an orthogonal polyline.
454
+ *
455
+ * Every interior bend (where two consecutive segments meet at a right
456
+ * angle) is replaced with a quadratic Bezier whose control point sits
457
+ * exactly at the corner — this produces the visual signature of
458
+ * "smooth-step" edges found in React Flow / draw.io / Lucidchart
459
+ * without changing the underlying polyline geometry.
460
+ *
461
+ * pointsToRoundedPath([[0,0], [10,0], [10,5]], 4)
462
+ * → "M 0 0 L 6 0 Q 10 0 10 4 L 10 5"
463
+ *
464
+ * `radius` is clamped to half of the shorter adjacent segment so the
465
+ * curves never overshoot. Passing `0` makes it identical to
466
+ * {@link pointsToPath}.
467
+ */
468
+ declare function pointsToRoundedPath(points: readonly Point[], radius: number): string;
469
+ /** Side of an AABB that an endpoint exits / enters through. */
470
+ type Side = 'top' | 'right' | 'bottom' | 'left';
471
+ interface BezierPathOptions {
472
+ /** Source point — where the curve starts. */
473
+ source: Point;
474
+ /** Side of the source node the curve exits through. */
475
+ sourceSide: Side;
476
+ /** Target point — where the curve ends. */
477
+ target: Point;
478
+ /** Side of the target node the curve enters through. */
479
+ targetSide: Side;
480
+ /**
481
+ * Curve intensity. `0.25` is React Flow's default — strong enough
482
+ * to feel natural, soft enough not to loop. Range: `0..1`.
483
+ */
484
+ curvature?: number;
485
+ }
486
+ /**
487
+ * Cubic-bezier SVG path between two endpoints, with control handles
488
+ * extending perpendicular to each side. Mirrors React Flow's
489
+ * `getBezierPath` algorithm.
490
+ *
491
+ * `sourceSide` / `targetSide` tell the function which face of each
492
+ * node the endpoint sits on so the curve always exits and enters
493
+ * perpendicular to the node outline — this is the difference between
494
+ * a "wavy" edge and a "deliberate" one.
495
+ *
496
+ * Use {@link nearestSide} to derive the side from an AABB + opposite
497
+ * point when the caller doesn't already know it.
498
+ */
499
+ declare function getBezierPath(opts: BezierPathOptions): string;
500
+ /**
501
+ * Compute the intersection of the line from `box`'s centre to
502
+ * `opposite` with the rectangle's border. Returns the border point and
503
+ * the side it sits on.
504
+ *
505
+ * This is the "floating anchor" trick used by React Flow's
506
+ * FloatingEdges example and most graph viz libraries: instead of
507
+ * binding an endpoint to a fixed corner or midpoint, you bind it to
508
+ * the centre and let it slide along the perimeter following the
509
+ * direction to the other node. The result is dramatically cleaner on
510
+ * dense / unstructured graphs because every edge picks the closest
511
+ * face automatically as nodes are dragged around.
512
+ */
513
+ declare function floatingAnchor(box: AABB, opposite: Point): {
514
+ point: Point;
515
+ side: Side;
516
+ };
517
+ /**
518
+ * Pick the side of `box` whose midpoint is closest to `target`. Same
519
+ * logic as the internal helper used by orthogonal routing — exposed
520
+ * publicly so consumers of `getBezierPath` don't need to re-implement
521
+ * it.
522
+ */
523
+ declare function nearestSide(box: AABB, target: Point): Side;
524
+ /**
525
+ * Resolved edge endpoint geometry — output of {@link resolveEdgeEndpoint}.
526
+ *
527
+ * `point` is in world space, sitting on (or just outside of) the
528
+ * `box` border. `side` records which face the connector exits / enters
529
+ * so downstream consumers (bezier control handles, orthogonal stubs)
530
+ * can stay perpendicular.
531
+ */
532
+ interface ResolvedEndpoint {
533
+ point: Point;
534
+ side: Side;
535
+ }
536
+ /**
537
+ * Compute the intersection of the segment between the centre of
538
+ * `intersectionBox` and `targetCenter` with the perimeter of
539
+ * `intersectionBox`.
540
+ *
541
+ * **Source / credit.** Direct port of the algorithm used by xyflow
542
+ * (React Flow) for FloatingEdges and by mxGraph / draw.io's
543
+ * `mxPerimeter.RectanglePerimeter`. The closed-form derivation is in
544
+ * https://math.stackexchange.com/questions/1724792 — it parametrises
545
+ * the rectangle as the manhattan-norm rhombus `|x| + |y| = 1` and
546
+ * solves for the unit intersection in one step, which is faster and
547
+ * numerically more stable than the angle-based formulation.
548
+ *
549
+ * We use this as the canonical endpoint resolver for every routing
550
+ * mode (`straight` / `orthogonal` / `bezier`). The visible benefit is
551
+ * stability: nothing in this function depends on user-supplied
552
+ * anchors — the side, the point, the angle of approach are all pure
553
+ * functions of the two rectangles' bounding boxes, so dragging a node
554
+ * around never produces the "wrong-side-of-the-box" / "edge cuts
555
+ * through the node body" artifacts that the anchor-honouring resolver
556
+ * used to leak.
557
+ *
558
+ * Returns the world-space intersection point. When `box` has zero
559
+ * area (degenerate node) the function returns its centre so callers
560
+ * can still draw a useful edge to it.
561
+ */
562
+ declare function getNodeIntersection(intersectionBox: AABB, targetCenter: Point): Point;
563
+ /**
564
+ * Classify which face of `box` the `intersectionPoint` sits on.
565
+ * Companion to {@link getNodeIntersection} — together they replace the
566
+ * old anchor-driven endpoint resolver.
567
+ *
568
+ * The function rounds both the box position and the point to integer
569
+ * pixels (matches the xyflow heuristic) before comparing, which keeps
570
+ * floating-point noise from picking the wrong side when an
571
+ * intersection lands almost exactly on a corner.
572
+ */
573
+ declare function getEdgePosition(box: AABB, intersectionPoint: Point): Side;
574
+ /**
575
+ * Single canonical edge endpoint resolver used by `<Diagram>` for
576
+ * every routing mode. Wraps {@link getNodeIntersection} +
577
+ * {@link getEdgePosition} into the `{ point, side }` shape the
578
+ * adapters expect, and applies a small outward `padding` so the
579
+ * arrowhead marker has room to sit between the path tip and the node
580
+ * border (matches React Flow / draw.io behaviour).
581
+ *
582
+ * **Important: anchor information is intentionally ignored here.**
583
+ * The `<Diagram>` API still accepts `from: { anchor: 'nw' }` etc. for
584
+ * back-compat and semantic intent, but the visual layer now derives
585
+ * the endpoint purely from bounding boxes. This is the floating-edges
586
+ * approach taken by every mainstream diagram library — it's what
587
+ * makes drag-around-the-canvas behaviour feel correct on randomly
588
+ * laid-out graphs.
589
+ */
590
+ declare function resolveEdgeEndpoint(sourceBox: AABB, targetBox: AABB, padding?: number): ResolvedEndpoint;
591
+ /**
592
+ * Decide which face an endpoint sits on, combining two cues:
593
+ *
594
+ * 1. **Anchor position** — if `anchor` is already at one of `box`'s
595
+ * borders (within `tolerance`), the corresponding face is returned
596
+ * as-is. This lets consumers express intent ("exit from the right
597
+ * side") simply by placing the anchor on that border.
598
+ *
599
+ * 2. **Geometry fallback** — when the anchor is interior to `box`
600
+ * (or no border match) we fall back to {@link nearestSide} pointed
601
+ * at the opposite endpoint. This is the right answer for centre
602
+ * anchors and produces stable sides for big graphs where every
603
+ * pair of nodes has a different relative position.
604
+ *
605
+ * Returns the side along with a `confident` flag — `true` when the
606
+ * decision came from the anchor position (case 1), `false` when it
607
+ * fell back to geometry (case 2). Consumers that want to override the
608
+ * fallback in special cases can branch on the flag.
609
+ *
610
+ * Corner anchors (touching two borders simultaneously like `nw` / `ne`)
611
+ * are disambiguated by which face points more strongly at the opposite
612
+ * endpoint. **The disambiguation is also clamped to faces that face the
613
+ * other node** — a `ne` anchor on the source whose target sits to its
614
+ * left will pick `left` instead of `right`, because using `right` would
615
+ * route the edge AWAY from the target. This is the key fix for the
616
+ * "stub-then-180°-loop" artifact visible on randomly-laid-out graphs.
617
+ */
618
+ declare function inferSide(box: AABB, anchor: Point, opposite: Point, tolerance?: number): {
619
+ side: Side;
620
+ confident: boolean;
621
+ };
622
+
623
+ /**
624
+ * Oriented Bounding Box — a rectangle in world space described by its centre,
625
+ * half-extents along its local axes, and a rotation angle.
626
+ *
627
+ * Used when a node has a non-zero `transform.rot`: the AABB is no longer a
628
+ * tight fit, so callers that need a tight collision shape (selection
629
+ * outline, manipulator handles, etc.) should use the OBB instead.
630
+ *
631
+ * Conventions:
632
+ * - `center` is the centre of the rectangle in world coordinates.
633
+ * - `halfWidth` / `halfHeight` are the half-extents along the OBB's own
634
+ * local axes (already pre-multiplied by the world scale).
635
+ * - `angle` is the rotation of the OBB's local frame relative to world,
636
+ * in radians, positive counter-clockwise.
637
+ *
638
+ * For an axis-aligned node (`angle === 0`) the OBB and AABB coincide, modulo
639
+ * the (centre + half-extents) vs (top-left + size) representation.
640
+ */
641
+ interface OBB {
642
+ readonly center: Point;
643
+ readonly halfWidth: number;
644
+ readonly halfHeight: number;
645
+ readonly angle: number;
646
+ }
647
+ /**
648
+ * Compute the four corners of an OBB in world space.
649
+ *
650
+ * Order is: top-left → top-right → bottom-right → bottom-left in OBB-local
651
+ * space, after applying the rotation. Useful for SVG `<polygon>` rendering
652
+ * and for AABB derivation.
653
+ */
654
+ declare function obbCorners(obb: OBB): readonly [Point, Point, Point, Point];
655
+ /**
656
+ * Build the tightest axis-aligned bounding box that contains a given OBB.
657
+ *
658
+ * For a non-zero `angle` the resulting AABB is strictly larger (in area)
659
+ * than the OBB — it has to enclose all four rotated corners. This is the
660
+ * canonical "broad-phase" box used by the routing layer.
661
+ */
662
+ declare function aabbFromOBB(obb: OBB): AABB;
663
+ /**
664
+ * Test whether a world-space point lies inside an OBB.
665
+ *
666
+ * Inverse-rotates the point into OBB-local space and checks the
667
+ * half-extents. O(1).
668
+ */
669
+ declare function obbContainsPoint(obb: OBB, p: Point): boolean;
670
+
671
+ export { type AABB, type Anchor, type AnchorId, type AnchorPolicy, type BezierPathOptions, type EdgeEndpoint, type EdgeId, type EdgeInit, type GraphEdge, type GraphEngine, type GraphEngineOptions, type GraphNode, type GraphState, type NodeId, type NodeInit, type NodeTransform, type NodeUpdate, type OBB$1 as OBB, type Outline, type Point, type ResolvedEndpoint, type RouteOrthogonalOptions, type Side, aabbFromOBB, createGraph, floatingAnchor, getBezierPath, getEdgePosition, getNodeIntersection, inferSide, nearestSide, obbContainsPoint, obbCorners, pointsToPath, pointsToRoundedPath, resolveEdgeEndpoint, routeOrthogonal };