@weasel-js/diagram 1.10.1 → 1.11.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/dist/index.d.ts CHANGED
@@ -1,8 +1,12 @@
1
- import { P as Port, a as PortsOptions, D as DiagramNodeLike, b as DiagramNodeReader, c as PortAnchor, d as DiagramGroup, R as Row, O as Outline, e as PortSpec, B as BodySpec, f as DiagramNode, L as LayoutFn, g as LayoutOptions, M as MeasureRowText, G as GraphSource, F as ForceOptions, h as LayoutResult, i as Graph, j as LayoutDirection } from './layout.entry-BUkiRKuX.js';
2
- export { k as BodyFloor, l as BodyNodeSpec, m as Bounds, n as BuildBodyOptions, o as BuildGraphOptions, C as COMPASS, p as DEFAULT_GROUP_INSET, q as DEFAULT_NODE_GAP, r as DEFAULT_PORTS, s as DEFAULT_RANK_GAP, t as DIAGRAM_TRAIT_KEY, u as DiagramNodeEntry, v as ForceBody, w as ForceRelaxation, x as Forest, y as GraphEdge, z as GraphGroup, A as GraphNode, E as GraphNodeLike, H as GroupInset, I as LayoutAxes, J as RowBox, K as RowNodeData, N as RowPort, Q as RowPortBox, S as RowTextStyle, T as Slot, U as axesFor, V as backEdges, W as bodyOutline, X as bodyTrait, Y as boxForContent, Z as buildBody, _ as buildGraph, $ as canvasMeasure, a0 as contentBox, a1 as createDiagramNodes, a2 as dataKeyReader, a3 as diagramNodeOf, a4 as expandGroupEdges, a5 as force, a6 as forceRelaxation, a7 as forestOf, a8 as graphOrder, a9 as insetOf, aa as isPinned, ab as layered, ac as layoutBody, ad as layoutRowPorts, ae as measureBody, af as outlinePath, ag as outlinePolyline, ah as packAcross, ai as packClusters, aj as pinnedSet, ak as portOf, al as portsOf, am as portsOnOutline, an as rankGaps, ao as ranksOf, ap as rayHit, aq as seededOrder, ar as settle, as as sizeToBody, at as translated, au as tree, av as untilSettled } from './layout.entry-BUkiRKuX.js';
3
- import { Vec2, DerivedDep, Path, SceneRegistry, PoseDescriptor, ChromeState, CommandFill, CommandStroke, CursorSpec, Affordance, InvocationCtx, Stroke, GestureBinding, Action, Contribution, RenderLayer, MarkerRef, AddNodeSpec, RectPose, Scene, NodeId, Fill, NodeShapeEntry, Paint, View, SelectionApi } from '@weasel-js/core';
4
- import { Vec2 as Vec2$1 } from '@weasel-js/core/math';
1
+ import { f as Port, g as DiagramNodeLike, h as DiagramNodeReader, d as LayoutOptions, i as LayoutResult, G as Graph, c as LayoutFn, j as GraphSource, k as LayoutPlanner, l as LayoutDirection, L as LayoutPorts, m as LayoutPlan, n as GraphNodeLike } from './body-2lC7IiIA.js';
2
+ export { o as BodyFloor, p as BodyNodeSpec, B as BodySpec, q as Bounds, r as BuildBodyOptions, s as BuildGraphOptions, t as DEFAULT_GROUP_INSET, u as DEFAULT_NODE_GAP, v as DEFAULT_RANK_GAP, w as DIAGRAM_TRAIT_KEY, D as DiagramGroup, e as DiagramNode, x as DiagramNodeEntry, E as EdgeLabelRoom, y as GraphEdge, z as GraphGroup, A as GraphNode, C as GroupInset, F as LayoutAxes, M as MeasureRowText, O as Outline, H as PORT_LABEL_GAP, I as PerimeterPortLabelBox, P as PortAnchor, J as PortLabelPlacement, K as PortSite, b as PortSpec, a as Row, N as RowBox, Q as RowNodeData, S as RowPort, T as RowPortBox, R as RowTextStyle, U as Slot, V as anchorOffset, W as axesFor, X as bodyOutline, Y as bodyTrait, Z as boxForContent, _ as buildBody, $ as buildGraph, a0 as canvasMeasure, a1 as contentBox, a2 as createDiagramNodes, a3 as dataKeyReader, a4 as diagramNodeOf, a5 as expandGroupEdges, a6 as graphOrder, a7 as insetOf, a8 as isPinned, a9 as layoutBody, aa as layoutRowPorts, ab as marginOf, ac as measureBody, ad as outlinePath, ae as packAcross, af as packClusters, ag as pinnedSet, ah as planOf, ai as plannerPlan, aj as portLabelBox, ak as portLabelsOf, al as rankGaps, am as seededOrder, an as settle, ao as sizeToBody, ap as translated, aq as untilSettled } from './body-2lC7IiIA.js';
3
+ import { P as PortsOptions, F as ForceOptions } from './layout.entry-BoWFcR4G.js';
4
+ export { C as COMPASS, D as DEFAULT_PORTS, a as ForceBody, b as ForceRelaxation, c as Forest, L as LayeredPlan, d as backEdges, e as chain, f as footprintNode, g as force, h as forceRelaxation, i as forestOf, l as layered, j as layeredPlan, o as outlinePolyline, p as portOf, k as portSitesOf, m as portsOf, n as portsOnOutline, r as ranksByLength, q as ranksOf, s as rayHit, t as tree } from './layout.entry-BoWFcR4G.js';
5
+ import { Vec2, DerivedDep, Path, SceneRegistry, PoseDescriptor, ChromeState, CommandFill, CommandStroke, CursorSpec, Affordance, InvocationCtx, Stroke, GestureBinding, Action, Contribution, RenderLayer, NodeId, Scene, GatedLayer, Fill, NodeShapeEntry, RectPose, Paint, View, SelectionApi } from '@weasel-js/core';
6
+ import { f as EdgeEnd, e as DiagramSpec, b as DataEdge } from './fromData-GKqRgPFs.js';
7
+ export { A as ANCHOR_DERIVE_POSE, g as DIAGRAM_ANCHOR, h as DIAGRAM_GROUP, i as DataGroup, j as DataMark, c as DataNode, k as DiagramAnchor, D as DiagramData, l as DiagramDerivePose, m as DiagramEdge, n as DiagramLabel, o as DiagramMark, d as DiagramRouter, p as DiagramSceneData, a as DiagramSceneOptions, q as DiagramTextStyle, E as EdgeStyle, G as GROUP_DERIVE_POSE, r as GroupPoseOptions, N as NodeStyle, s as anchorDerivePose, t as boxAround, u as diagramAnchorOf, v as diagramEdgeOf, w as diagramGroupOf, x as diagramLabelOf, y as diagramMarkOf, z as diagramScene, B as edgeIdOf, C as groupDerivePose, F as layoutOwnsPort, H as layoutOwnsRoute, I as markShape, J as registerMarkShape } from './fromData-GKqRgPFs.js';
5
8
  import { EasingFn } from '@weasel-js/animator';
9
+ import { Vec2 as Vec2$1 } from '@weasel-js/core/math';
6
10
  import * as react from 'react';
7
11
  import '@weasel-js/geom';
8
12
  import '@weasel-js/simulation';
@@ -24,24 +28,6 @@ import '@weasel-js/simulation';
24
28
  * `markerStart` / `markerEnd` any other stroke carries.
25
29
  */
26
30
 
27
- /** Where an edge attaches at one end. */
28
- interface EdgeEnd {
29
- /** The port to leave from, when the endpoint declares one. Falls back to
30
- * the nearest port, so an edge drawn before ports were thought about still
31
- * meets the shape. */
32
- port?: string;
33
- }
34
- /** The trait on an edge's `data`, under the same key participants use. */
35
- interface DiagramEdge {
36
- from: EdgeEnd;
37
- to: EdgeEnd;
38
- /** Registry key for the router. Resolved by whoever builds the edge's
39
- * `derivePath`; unknown keys fall back to `straight`. */
40
- router?: string;
41
- /** Points in world coordinates the route must pass through, in order. The
42
- * author owns these; the router owns everything between them. */
43
- waypoints?: readonly Vec2[];
44
- }
45
31
  /** What a router is given: the two resolved ends, and the author's waypoints
46
32
  * between them. */
47
33
  interface RouteRequest {
@@ -75,13 +61,9 @@ declare const orthogonal: Router;
75
61
  declare const bezier: Router;
76
62
  /** The routers this package ships. A consumer's own go in the same shape. */
77
63
  declare const ROUTERS: Readonly<Record<string, Router>>;
78
- /** The edge trait on a node's data, or `null`. */
79
- declare function diagramEdgeOf(node: {
80
- data: unknown;
81
- }): DiagramEdge | null;
82
64
  /**
83
- * The port an end resolves to: the one it named, else the port facing the
84
- * other end most directly.
65
+ * The port an end resolves to: the one it named, at whichever of its places
66
+ * faces `toward` most directly, else the port facing `toward` most directly.
85
67
  *
86
68
  * "Facing" rather than "nearest": a port on the far side of a wide node can be
87
69
  * closer in a straight line than the one pointing at the target.
@@ -153,6 +135,14 @@ declare const PORT_AFFORDANCE_KIND: "layer:diagram-ports";
153
135
  interface ParticipantPose<TPose> {
154
136
  node: DiagramNodeLike;
155
137
  pose: TPose;
138
+ /** Not drawn: on a hidden layer, or hidden on its own or by an ancestor.
139
+ * `sceneParticipants` answers it from the scene's `nodeGates`. */
140
+ hidden?: boolean;
141
+ /** Out of reach: locked, or on a locked layer. */
142
+ locked?: boolean;
143
+ /** The scene layer the node is painted on, which the asking view may not
144
+ * paint (`ChromeState.layerIsPainted`, `ViewApi.layerIsPainted`). */
145
+ layer?: string;
156
146
  }
157
147
  /** Where the affordance gets its participants. A thunk rather than a scene so
158
148
  * the geometry is testable without one, and so a consumer whose participants
@@ -178,13 +168,21 @@ interface PortAffordanceOptions<TPose> {
178
168
  /** Which participants show their ports. Default: all of them. Gate on
179
169
  * selection or hover by reading `state`. */
180
170
  shows?: (node: DiagramNodeLike, state: ChromeState) => boolean;
171
+ /** Which of a participant's ports show. Default: all of them. A port
172
+ * this turns away is not painted or grabbable, and passed with the connect
173
+ * options (as `diagramPorts` and `DiagramView` do), no connect lands on it. */
174
+ showsPort?: (port: Port) => boolean;
181
175
  /** Screen-pixel hit radius. Default 8, the kit's `HANDLE_BASE_PX`. */
182
176
  hitRadiusPx?: number;
183
- /** Screen-pixel side of the painted square. Default 7. Omit `paint` entirely
184
- * by passing 0 — a port the consumer draws itself is still hittable. */
177
+ /** Screen-pixel size of the painted mark: a square's side, or a circle's
178
+ * diameter. Default 7. Omit `paint` entirely by passing 0 — a port the
179
+ * consumer draws itself is still hittable. */
185
180
  sizePx?: number;
186
- fill?: CommandFill;
181
+ /** One fill for every port, or a function of the port — its `type`, say. */
182
+ fill?: CommandFill | ((port: Port) => CommandFill);
187
183
  stroke?: CommandStroke;
184
+ /** Default `'square'`. */
185
+ shape?: 'square' | 'circle';
188
186
  cursor?: CursorSpec;
189
187
  /** Default `'exclusive'` on pointer gestures: a drag from a port is a
190
188
  * connect, never a move of the node underneath it. */
@@ -250,6 +248,10 @@ interface ConnectActionOptions<TPose> {
250
248
  geometry?: PoseDescriptor<TPose>;
251
249
  /** Default {@link defaultCanConnect}. */
252
250
  canConnect?: CanConnect;
251
+ /** Which ports a connect may land on: a port this turns away is never a
252
+ * target. The port affordance's option of the same name, so a port that is
253
+ * not shown cannot be dropped onto. Default: all of them. */
254
+ showsPort?: (port: Port) => boolean;
253
255
  /** How near the pointer must come to a port, in world units, before the
254
256
  * edge snaps to it. Default 16. */
255
257
  snapRadius?: number;
@@ -365,20 +367,6 @@ declare function selfLoop(ports: readonly Port[], standoff: number): SelfLoop |
365
367
  * data, styling, z-order and selection.
366
368
  */
367
369
 
368
- /** Where along an edge a label sits. */
369
- interface DiagramLabel {
370
- /** `'start'`, `'mid'` and `'end'` are 0, 0.5 and 1; a number is that
371
- * fraction of the route's length. */
372
- at: 'start' | 'mid' | 'end' | number;
373
- /** World units between the route and the label's near side, to the **left
374
- * of the direction of travel** — above a left-to-right edge. Default 0,
375
- * which centers the label on the line. */
376
- offset?: number;
377
- }
378
- /** The label trait on a node's data, or `null`. */
379
- declare function diagramLabelOf(node: {
380
- data: unknown;
381
- }): DiagramLabel | null;
382
370
  /** Options for {@link labelDerivePose}. `geometry` defaults to the kit's
383
371
  * `AUTO_POSE_DESCRIPTOR`. */
384
372
  interface LabelPoseOptions<TPose> {
@@ -406,245 +394,11 @@ declare const LABEL_DERIVE_POSE: (node: {
406
394
  }, deps: readonly (DerivedDep<unknown, unknown, string> | undefined)[]) => unknown;
407
395
 
408
396
  /**
409
- * A group's box, and anything pinned to a point on another node.
410
- *
411
- * The box is an ordinary node that depends on its members and derives its pose
412
- * from theirs: their union, grown by the inset. Drag a member and the box
413
- * follows; lay the diagram out and the box lands around wherever the members
414
- * went. Nothing has to keep it in sync, because nothing stores it.
415
- *
416
- * An anchored node — a group's title is the first — derives its pose from one
417
- * dependency's bounds the same way, so it travels with the box it labels.
418
- */
419
-
420
- /** Where an anchored node sits on its dependency. */
421
- interface DiagramAnchor {
422
- /** The point on the dependency's bounds, normalized as a port's is. */
423
- at: PortAnchor;
424
- /** The point on the node's own bounds that lands there. Default `at`, so
425
- * `{ u: 0, v: 0 }` puts the node's top-left on the dependency's. */
426
- self?: PortAnchor;
427
- /** Then moved by this much, in world units. */
428
- offset?: Vec2$1;
429
- }
430
- /** The group trait on a node's data, or `null`. */
431
- declare function diagramGroupOf(node: {
432
- data: unknown;
433
- }): DiagramGroup | null;
434
- /** The anchor trait on a node's data, or `null`. */
435
- declare function diagramAnchorOf(node: {
436
- data: unknown;
437
- }): DiagramAnchor | null;
438
- /** Options for {@link groupDerivePose} and {@link anchorDerivePose}.
439
- * `geometry` defaults to the kit's `AUTO_POSE_DESCRIPTOR`. */
440
- interface GroupPoseOptions<TPose> {
441
- geometry?: PoseDescriptor<TPose>;
442
- }
443
- /** A `derivePose` function as the scene calls it: the node, then what it depends on, in `dependsOn` order. */
444
- type DiagramDerivePose<TPose> = (node: {
445
- pose: TPose;
446
- data: unknown;
447
- }, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null;
448
- /**
449
- * A `derivePose` that draws a group's box around its members: their union,
450
- * grown by the trait's inset. `null` — the authored pose stands — when the node
451
- * carries no group trait or every member is gone.
452
- */
453
- declare function groupDerivePose<TPose>(opts?: GroupPoseOptions<TPose>): DiagramDerivePose<TPose>;
454
- /** A group's box around the union of its members: grown by its inset, and
455
- * widened evenly to its `minWidth`. */
456
- declare function boxAround(union: {
457
- x: number;
458
- y: number;
459
- width: number;
460
- height: number;
461
- }, group: DiagramGroup): {
462
- x: number;
463
- y: number;
464
- width: number;
465
- height: number;
466
- };
467
- /**
468
- * A `derivePose` that keeps a node at a point on its dependency's bounds.
469
- * `null` when the node carries no anchor trait or the dependency is gone.
470
- */
471
- declare function anchorDerivePose<TPose>(opts?: GroupPoseOptions<TPose>): DiagramDerivePose<TPose>;
472
- /** Registry key for {@link GROUP_DERIVE_POSE}. */
473
- declare const DIAGRAM_GROUP = "diagram:group";
474
- /** Registry key for {@link ANCHOR_DERIVE_POSE}. */
475
- declare const DIAGRAM_ANCHOR = "diagram:anchor";
476
- /** The default group derivation, a stable reference so `toJSON` can find its key. */
477
- declare const GROUP_DERIVE_POSE: DiagramDerivePose<unknown>;
478
- /** The default anchor derivation, a stable reference for the same reason. */
479
- declare const ANCHOR_DERIVE_POSE: DiagramDerivePose<unknown>;
480
-
481
- /**
482
- * `diagramScene` — a laid-out diagram from plain `{ nodes, edges }`.
483
- *
484
- * Every box is a `buildBody` body, so a data node can say anything a body
485
- * can: outline, rows, padding, ports. Sizes are estimated from character
486
- * counts unless `measure` is given, so layout runs without a canvas and gives
487
- * the same answer in node and in a browser.
397
+ * A live layout's producers: what each frame of a run says about where the
398
+ * participants are. `forceProducer` relaxes; `easedProducer` walks to a
399
+ * destination it knows. `useLiveLayout` (`live.ts`) runs them.
488
400
  */
489
401
 
490
- /** One node of a {@link DiagramData}: its rows of text, and optionally its outline, ports, size, and starting place. */
491
- interface DataNode {
492
- id: string;
493
- /** Shorthand for one `label` row per line. Ignored when `rows` is given. */
494
- lines?: readonly string[];
495
- rows?: readonly Row[];
496
- /** Default `DiagramSceneOptions.outline`, which defaults to `'rect'`. */
497
- outline?: Outline;
498
- /** Perimeter ports, replacing the four compass midpoints. */
499
- ports?: readonly PortSpec[];
500
- /** Layout leaves this node at `at`. */
501
- pinned?: boolean;
502
- /** Where the node starts, before layout. Unset, nodes start on the
503
- * diagonal in source order, which is what within-rank order is seeded
504
- * from. */
505
- at?: Vec2$1;
506
- /** The smallest the box may be. Its content grows it past this. */
507
- width?: number;
508
- height?: number;
509
- /** Default `DiagramSceneOptions.padding`. */
510
- padding?: number;
511
- /** Default `DiagramSceneOptions.gap`. */
512
- gap?: number;
513
- /** Default `DiagramSceneOptions.verticalAlign`. */
514
- verticalAlign?: BodySpec['verticalAlign'];
515
- }
516
- /** One edge of a {@link DiagramData}, between two nodes or groups by id. */
517
- interface DataEdge {
518
- /** A node's id, or a group's: an edge naming a group meets its box. */
519
- from: string;
520
- to: string;
521
- /** The port to leave from or arrive at. Unset, the end facing the other
522
- * node most directly. */
523
- fromPort?: string;
524
- toPort?: string;
525
- label?: string;
526
- /** Where along the edge the label sits. Default `DiagramSceneOptions.labelPlacement`. */
527
- labelPlacement?: DiagramLabel;
528
- /** Default `DiagramSceneOptions.router`. */
529
- router?: DiagramRouter;
530
- /** World points the route passes through. Layout does not move them. */
531
- waypoints?: readonly Vec2$1[];
532
- }
533
- /** A box drawn around some nodes, which layout keeps together. */
534
- interface DataGroup {
535
- /** Shares one namespace with node ids. */
536
- id: string;
537
- /** Node ids, and the ids of groups nested in this one. A node or group a
538
- * later group also names stays in this one; an id that is neither is
539
- * ignored, and so is one that would put a group inside itself. */
540
- members: readonly string[];
541
- /** Drawn inside the box, above its members. */
542
- label?: string;
543
- /** Between the box and its members, and around the label. Default
544
- * `DiagramSceneOptions.groupPadding`. */
545
- padding?: number;
546
- }
547
- /** A diagram as plain data, the input to `diagramScene`. */
548
- interface DiagramData {
549
- nodes: readonly DataNode[];
550
- edges: readonly DataEdge[];
551
- /** A group may be a member of another. `layered`, `tree`, and `force` keep
552
- * each group's members together; `'none'` ignores groups but still draws
553
- * them. */
554
- groups?: readonly DataGroup[];
555
- }
556
- /** The colors and outline a node is drawn with. */
557
- interface NodeStyle {
558
- fill: string;
559
- stroke: string;
560
- text: string;
561
- strokeWidth?: number;
562
- dash?: number[];
563
- }
564
- /** The stroke, markers, and label color an edge is drawn with. */
565
- interface EdgeStyle {
566
- stroke: string;
567
- width?: number;
568
- dash?: number[];
569
- /** `null` for none. Default none. */
570
- markerStart?: MarkerRef | null;
571
- /** `null` for none. Default `'arrow'`. */
572
- markerEnd?: MarkerRef | null;
573
- /** Default `stroke`. */
574
- label?: string;
575
- }
576
- /** How an edge is drawn between its ends. */
577
- type DiagramRouter = 'straight' | 'bezier' | 'orthogonal';
578
- /** The face a diagram node's text is set in. */
579
- type DiagramTextStyle = {
580
- fontFamily: string;
581
- fontSize: number;
582
- fontWeight?: number;
583
- };
584
- /** The `data` of a scene node `diagramScene` makes: its diagram role, and what it paints. */
585
- interface DiagramSceneData {
586
- diagram?: DiagramNode | DiagramEdge | {
587
- label: DiagramLabel;
588
- } | {
589
- anchor: DiagramAnchor;
590
- };
591
- fill?: {
592
- paint: {
593
- color: string;
594
- };
595
- };
596
- stroke?: Stroke;
597
- text?: string;
598
- style?: DiagramTextStyle;
599
- }
600
- /** A scene node spec, as `diagramScene` returns them. */
601
- type DiagramSpec = AddNodeSpec<DiagramSceneData, 'main', RectPose>;
602
- /** How `diagramScene` lays out, routes, and styles a {@link DiagramData}. */
603
- interface DiagramSceneOptions {
604
- /** A layout by name, or any `LayoutFn`. Default `'layered'`. `'none'`
605
- * leaves every node at its `at`. */
606
- layout?: 'layered' | 'tree' | 'force' | 'none' | LayoutFn;
607
- /** Merged over `{ order: 'barycenter' }`. */
608
- layoutOptions?: LayoutOptions;
609
- /** Default `'bezier'`. */
610
- router?: DiagramRouter;
611
- /** The text style rows fall back on. Default 12px sans-serif. */
612
- font?: {
613
- fontFamily: string;
614
- fontSize: number;
615
- };
616
- /** Measures one run of text. Default an estimate from character count, so
617
- * layout needs no canvas; pass `canvasMeasure(ctx)` for real widths. */
618
- measure?: MeasureRowText;
619
- /** Default `'rect'`. */
620
- outline?: Outline;
621
- /** Between a box's outline and its rows. Default 8. */
622
- padding?: number;
623
- /** Between adjacent rows. Default 4. */
624
- gap?: number;
625
- /** Where a box's rows sit when it is taller than they need. Default `'center'`. */
626
- verticalAlign?: BodySpec['verticalAlign'];
627
- /** The narrowest a box may be. Default 64. */
628
- minWidth?: number;
629
- /** Where edge labels sit. Default `{ at: 'mid', offset: 9 }`. */
630
- labelPlacement?: DiagramLabel;
631
- nodeStyle?: (node: DataNode) => NodeStyle;
632
- /** Default 12. */
633
- groupPadding?: number;
634
- /** A group's box and label. `text` colors the label. */
635
- groupStyle?: (group: DataGroup) => NodeStyle;
636
- edgeStyle?: (edge: DataEdge) => EdgeStyle;
637
- }
638
- /** The scene node id `diagramScene` gives an edge, built from its ends, ports, and label. */
639
- declare const edgeIdOf: (e: DataEdge) => string;
640
- /** Every spec carries an id — a node's own, `<id>/<k>` for its text,
641
- * `<edge id>/label`, a group's own and `<group id>/label` — so a later call's
642
- * specs can be reconciled against this one's. Throws on a repeated id among
643
- * nodes and groups, which names two things as one. An edge whose
644
- * end is not a node is dropped, and so is a repeat of an earlier edge with
645
- * the same ends, ports and label: it would draw exactly on top of the first. */
646
- declare function diagramScene(data: DiagramData, opts?: DiagramSceneOptions): DiagramSpec[];
647
-
648
402
  /** What a live producer is told, and what it answers with. */
649
403
  interface LiveLayoutCtx<TPose> {
650
404
  /** Nodes another gesture is moving, and where each is drawn. */
@@ -700,14 +454,16 @@ interface EasedProducerOptions {
700
454
  * over.
701
455
  */
702
456
  declare function easedProducer<TPose>(graph: Graph, algorithm: LayoutFn, opts?: EasedProducerOptions): LiveLayoutProducer<TPose>;
457
+
703
458
  /** Options for {@link useLiveLayout}. Only `scene` and `source` are required. */
704
459
  interface UseLiveLayoutOptions<TPose> {
705
460
  scene: Scene<unknown, string, TPose>;
706
461
  /** Where the graph is read from. The same thunk the port affordance takes. */
707
462
  source: GraphSource<TPose>;
708
- /** A key in `LAYOUTS`, or a layout of the consumer's own. `'force'` relaxes
709
- * continuously; anything else eases to its answer. Default `'force'`. */
710
- algorithm?: string | LayoutFn;
463
+ /** A key in `LAYOUTS`, a layout of the consumer's own, or a planner that
464
+ * answers later. `'force'` relaxes continuously; anything else eases to its
465
+ * answer. Default `'force'`. */
466
+ algorithm?: string | LayoutFn | LayoutPlanner;
711
467
  layout?: LayoutOptions;
712
468
  /** The relaxation's forces, and when a live run of it counts as at rest. */
713
469
  force?: Omit<ForceProducerOptions<TPose>, 'geometry'>;
@@ -721,6 +477,9 @@ interface UseLiveLayoutOptions<TPose> {
721
477
  /** Fires once the run has settled or been stopped and its poses are in the
722
478
  * document, with how many participants moved. */
723
479
  onSettle?: (moved: number) => void;
480
+ /** Hears why a `LayoutPlanner` failed to answer, and no run starts. Unset,
481
+ * the failure goes to `console.error`. */
482
+ onError?: (error: unknown) => void;
724
483
  /** Whether a node or edge appearing or disappearing re-heats the run.
725
484
  * Default true — a diagram that ignores a new edge looks broken. */
726
485
  reheatOnGraphChange?: boolean;
@@ -730,9 +489,11 @@ interface UseLiveLayoutOptions<TPose> {
730
489
  }
731
490
  /** Controls for a live layout run, from {@link useLiveLayout}. */
732
491
  interface LiveLayout {
733
- /** Heat and run. Idempotent while running. */
492
+ /** Heat and run. Idempotent while running, which includes waiting on a
493
+ * planner's answer. */
734
494
  start(): void;
735
- /** Finish now: commit where the nodes stand. */
495
+ /** Finish now: commit where the nodes stand. A planner's answer still to
496
+ * come is dropped. */
736
497
  stop(): void;
737
498
  /** Abandon: the document keeps the poses it had. */
738
499
  cancel(): void;
@@ -756,20 +517,11 @@ declare const EASED_LAYOUTS: Readonly<Record<string, LayoutFn>>;
756
517
  *
757
518
  * An edge running down the ranks leaves its source by the face toward the
758
519
  * next rank and arrives at its target's face toward the last, so it reads
759
- * along the direction the diagram flows. An edge that skips ranks gets
760
- * waypoints through the gaps in each rank it crosses, so it goes around the
761
- * nodes between its ends rather than through them.
762
- *
763
- * An edge running back up the ranks, or within one, is left to the ends'
764
- * facing rule.
520
+ * along the direction the diagram flows; one running back up the ranks does
521
+ * the same the other way round. An edge that spans ranks runs through the
522
+ * waypoints `layeredPlan` placed for it.
765
523
  */
766
524
 
767
- type Rect = {
768
- x: number;
769
- y: number;
770
- width: number;
771
- height: number;
772
- };
773
525
  /** One edge to route, by the ids of its ends: a node's or a group's. */
774
526
  interface RouteEdge {
775
527
  id: string;
@@ -782,27 +534,72 @@ interface LayeredRoute {
782
534
  fromPort?: string;
783
535
  toPort?: string;
784
536
  waypoints?: readonly Vec2$1[];
537
+ /** The point on the route layout left the edge's label room beside, as a
538
+ * `DiagramLabel`'s `{ near }`. */
539
+ labelAt?: Vec2$1;
785
540
  }
786
- /** What {@link layeredRoutes} reads: the ranked graph, and where everything landed. */
541
+ /** What {@link layeredRoutes} reads: where `layeredPlan` put things. */
787
542
  interface LayeredRouteInput {
788
- /** The graph layout ranked, for which rank each node is in. */
789
- graph: Graph;
790
543
  edges: readonly RouteEdge[];
791
- /** Every node and group box where it was placed. */
792
- boxes: ReadonlyMap<string, Rect>;
544
+ /** Each node's rank, from `layeredPlan`. Empty for a layout with none,
545
+ * which names no ports. */
546
+ ranks: ReadonlyMap<string, number>;
547
+ /** Each spanning edge's waypoints, from `layeredPlan`. */
548
+ waypoints: ReadonlyMap<string, readonly Vec2$1[]>;
549
+ /** Where each labeled edge's label room is among its waypoints, from
550
+ * `layeredPlan`. */
551
+ labels?: ReadonlyMap<string, Vec2$1>;
793
552
  /** Each group's node members, through nested groups. */
794
553
  members: ReadonlyMap<string, readonly string[]>;
795
554
  /** Ids whose ports are the four compass midpoints, which are the only ones
796
555
  * a route names. */
797
556
  compass: ReadonlySet<string>;
798
557
  direction?: LayoutDirection;
799
- /** How far a route keeps from the boxes it passes. */
800
- clearance: number;
801
558
  }
802
559
  /** Ports and waypoints for each edge of a laid-out `layered` diagram that
803
560
  * needs any, keyed by edge id. */
804
561
  declare function layeredRoutes(input: LayeredRouteInput): Map<string, LayeredRoute>;
805
562
 
563
+ /**
564
+ * The edge half of writing a layout to a scene: each edge's waypoints, and
565
+ * where its label sits among them.
566
+ *
567
+ * Layout writes only what it owns. An edge's route is its own while the edge
568
+ * has no waypoints or still has the ones layout last wrote (`laidOut`); a
569
+ * label's place is its own while the label sits at the default middle or at a
570
+ * `{ near }`, and its edge's route is layout's.
571
+ */
572
+
573
+ /** Edge routes for `applyLayout` to write alongside a layout's moves. */
574
+ interface LayoutRoutes {
575
+ /** Every edge the layout laid out. Each one layout owns gets its route
576
+ * from `waypoints`, or loses the one a layout gave it before. */
577
+ edges: ReadonlySet<string>;
578
+ waypoints: ReadonlyMap<string, readonly Vec2$1[]>;
579
+ labels?: ReadonlyMap<string, Vec2$1>;
580
+ /** The ports layout names. Each end layout owns gets its port from here,
581
+ * or loses the one a layout named before. */
582
+ ports?: ReadonlyMap<string, LayoutPorts>;
583
+ }
584
+ /** The routes `plan` laid out for `graph`'s edges. A layout that routes
585
+ * nothing routes every edge straight, taking back what `layered` wrote. */
586
+ declare function routesOf(graph: Graph, plan: LayoutPlan): LayoutRoutes;
587
+ /** A scene as {@link routeWrites} reads it. */
588
+ interface RoutedScene {
589
+ get(id: never): {
590
+ data: unknown;
591
+ } | undefined;
592
+ isLocked(id: never): boolean;
593
+ renderOrderNodes(): readonly {
594
+ id: string;
595
+ data: unknown;
596
+ dependsOn?: readonly string[] | 'children';
597
+ }[];
598
+ }
599
+ /** The new `data` for every edge and label whose route or place changes. A
600
+ * locked edge or label is left as it is. */
601
+ declare function routeWrites(scene: RoutedScene, routes: LayoutRoutes): Map<string, unknown>;
602
+
806
603
  /**
807
604
  * Running a layout against a scene: `LAYOUTS`, one batch of pose writes, and
808
605
  * the action that reaches both. A layout is a pure function of the graph;
@@ -816,12 +613,14 @@ interface ApplyLayoutOptions<TPose> {
816
613
  geometry?: PoseDescriptor<TPose>;
817
614
  /** The undo entry's name. Default `'Layout'`. */
818
615
  label?: string;
616
+ /** Edge routes to write in the same batch, from `routesOf`. */
617
+ routes?: LayoutRoutes;
819
618
  }
820
619
  /**
821
620
  * Write a layout's answer to the scene, as one undoable batch.
822
621
  *
823
- * Returns how many nodes moved, which is zero for a layout that had nothing to
824
- * do — nothing is written and no history entry is pushed.
622
+ * Returns how many nodes moved. A layout with nothing to do, nodes or routes,
623
+ * writes nothing and pushes no history entry.
825
624
  *
826
625
  * Nodes are **translated**, never re-posed from the layout's numbers: a node's
827
626
  * pose can be a path or anything else a consumer wired, and only its own
@@ -869,11 +668,18 @@ declare function createLayoutAction<TPose>(opts: LayoutActionOptions<TPose>): Ac
869
668
  */
870
669
 
871
670
  /** The minimum a participant source needs from a scene. Narrower than `Scene`
872
- * so a consumer can pass anything that answers these two. */
671
+ * so a consumer can pass anything that answers these. `layers` and each
672
+ * node's `layer`, `parent`, `hidden` and `locked` feed `nodeGates`; a stand-in
673
+ * without them gates nothing. */
873
674
  interface ParticipantScene<TPose> {
675
+ readonly layers?: readonly GatedLayer[];
874
676
  renderOrderNodes(): readonly {
875
677
  id: string;
876
678
  kind: 'leaf' | 'container';
679
+ layer?: string;
680
+ parent?: string | null;
681
+ hidden?: boolean;
682
+ locked?: boolean;
877
683
  data: unknown;
878
684
  pose: TPose;
879
685
  dependsOn?: readonly string[] | 'children';
@@ -887,7 +693,11 @@ interface ParticipantScene<TPose> {
887
693
  childrenOf(id: string): readonly string[];
888
694
  }
889
695
  /**
890
- * Every node in `scene`, at the pose it is **painted** at.
696
+ * Every node in `scene`, at the pose it is **painted** at, marked `hidden` or
697
+ * `locked` where the scene's `nodeGates` says so, and carrying its `layer` so
698
+ * a view that does not paint that layer can turn its ports away too. The
699
+ * marks keep ports off such a node; the graph reads past them, so a hidden
700
+ * node is still laid out.
891
701
  *
892
702
  * `effectivePose`, not `node.pose`: an override is what a gesture is currently
893
703
  * showing, and a port that answers from the document pose while its node is
@@ -895,7 +705,9 @@ interface ParticipantScene<TPose> {
895
705
  *
896
706
  * One source answers both the ports and the graph.
897
707
  */
898
- declare function sceneParticipants<TPose>(scene: ParticipantScene<TPose>): GraphSource<TPose>;
708
+ declare function sceneParticipants<TPose>(scene: ParticipantScene<TPose>): () => Iterable<ParticipantPose<TPose> & {
709
+ node: GraphNodeLike;
710
+ }>;
899
711
  /**
900
712
  * The composed layer, ready for `CanvasExtensionApi.registerLayer` — the only
901
713
  * attach route that is hit-tested. Handing this to `SceneCanvas`'s `layers`
@@ -937,12 +749,39 @@ declare function diagramShape<TPose>(opts?: DiagramShapeOptions<TPose>): NodeSha
937
749
  */
938
750
  declare function registerDiagramShape<TPose>(opts?: DiagramShapeOptions<TPose>): () => void;
939
751
 
752
+ /**
753
+ * `DiagramView`'s two-way sync with the data it was built from: new specs are
754
+ * reconciled into the scene, and what a drag or a layout run changed is
755
+ * reported back, so a consumer keeping the diagram as data can store it.
756
+ * `diagramLayoutOf` reads the same report off a `diagramScene` build.
757
+ */
758
+
940
759
  /** Where a moved node now stands. */
941
760
  interface NodeMove {
942
761
  id: string;
943
762
  x: number;
944
763
  y: number;
945
764
  }
765
+ /** An edge whose route or ports a layout run rewrote. `id` is the edge's
766
+ * scene id, `edgeIdOf` of its data edge; `waypoints` is empty when the run
767
+ * took a route away and left the edge straight, and `ports` names the ends
768
+ * layout owns. Store them as the data edge's `routed` and `routedPorts`,
769
+ * which stay layout's, not as `waypoints`, `fromPort` and `toPort`, which
770
+ * would be the author's and rename the edge. */
771
+ interface EdgeRoute {
772
+ id: string;
773
+ waypoints: readonly Vec2$1[];
774
+ ports: LayoutPorts;
775
+ }
776
+ /** What layout placed in a `diagramScene` build, in the shape `DiagramView`'s
777
+ * `onMove` reports: every node not pinned, where it stands, and every edge
778
+ * whose route or ports layout wrote. Store them the way `onMove` says and
779
+ * the next build leaves those nodes where they are. */
780
+ declare function diagramLayoutOf(specs: readonly DiagramSpec[]): {
781
+ moves: NodeMove[];
782
+ routes: EdgeRoute[];
783
+ };
784
+
946
785
  /** Props for `DiagramView`. */
947
786
  interface DiagramViewProps {
948
787
  /** Every spec needs an id, which `diagramScene` gives them. A new array is
@@ -950,8 +789,12 @@ interface DiagramViewProps {
950
789
  * selection and anything moved since stay put unless the new specs change
951
790
  * them. Pass a new `key` to start over instead. */
952
791
  specs: readonly DiagramSpec[];
953
- width: number;
954
- height: number;
792
+ /** The view opens fitted to the diagram, and fits again when the size or a
793
+ * fit option changes or a layout run settles, until the user pans or zooms;
794
+ * from then on the view is theirs. Omit both to fill the parent, which
795
+ * then needs a size of its own; a resize of the parent is a size change. */
796
+ width?: number;
797
+ height?: number;
955
798
  /** A participant id, or null for none. */
956
799
  selected?: string | null;
957
800
  /** Reports what each click leaves picked, `null` for none — a re-click on the
@@ -959,12 +802,18 @@ interface DiagramViewProps {
959
802
  * `selected` does not follow it; only a change to `selected` is pushed in. */
960
803
  onSelect?: (id: string | null) => void;
961
804
  /** Set to let nodes be dragged. Reports every node a drag or a layout run
962
- * moved. Store each as the data node's `at` with `pinned: true`, or the
963
- * next `diagramScene` lays it out again. */
964
- onMove?: (moves: readonly NodeMove[]) => void;
965
- /** Set to make ports grabbable. Dragging one port onto another reports the
805
+ * moved, and every edge whose route or ports a layout run rewrote. Store
806
+ * each move as the data node's `at` with `pinned: true`, and each route as
807
+ * the data edge's `routed` and `routedPorts`, or the next `diagramScene`
808
+ * lays them out again. */
809
+ onMove?: (moves: readonly NodeMove[], routes: readonly EdgeRoute[]) => void;
810
+ /** Ports are grabbable when this is set, unless `ports` says otherwise. Dragging one port onto another reports the
966
811
  * edge it describes and draws nothing; add it to the data to keep it. */
967
812
  onConnect?: (edge: DataEdge) => void;
813
+ /** Whether ports are drawn. `'connect'` draws them and lets one be dragged
814
+ * onto another; `'show'` only draws them. Default: `'connect'` when
815
+ * `onConnect` is set, else `'none'`. */
816
+ ports?: 'connect' | 'show' | 'none';
968
817
  /** Whether the canvas's keyboard shortcuts — held Space to pan among them —
969
818
  * run. They listen page-wide, so a view-only diagram beside other UI would
970
819
  * claim keys meant for the page. Default: on when `onMove` or `onConnect`
@@ -975,28 +824,29 @@ interface DiagramViewProps {
975
824
  canConnect?: CanConnect;
976
825
  /** How ports look and answer the pointer, and the router a connect's
977
826
  * preview draws with. */
978
- portOptions?: Pick<DiagramContributionOptions<RectPose>, 'fill' | 'stroke' | 'sizePx' | 'hitRadiusPx' | 'cursor' | 'shows' | 'snapRadius' | 'router'>;
827
+ portOptions?: Pick<DiagramContributionOptions<RectPose>, 'fill' | 'stroke' | 'shape' | 'sizePx' | 'hitRadiusPx' | 'cursor' | 'shows' | 'showsPort' | 'snapRadius' | 'router'>;
979
828
  /** How a layout run from {@link DiagramViewApi.layout} behaves: its forces,
980
- * how many frames an eased layout takes, its easing. */
829
+ * how many frames an eased layout takes, its easing, and `onError`, which
830
+ * hears a `LayoutPlanner` that failed to answer. */
981
831
  live?: Omit<UseLiveLayoutOptions<RectPose>, 'scene' | 'source' | 'algorithm'>;
982
- /** The smallest scale the initial fit may use. Unset, the whole diagram fits
832
+ /** The smallest scale the fit may use. Unset, the whole diagram fits
983
833
  * however small that makes it; set, it stops here and the rest is a pan away. */
984
834
  minScale?: number;
985
- /** The largest scale the initial fit may use. Default 1, so a small
835
+ /** The largest scale the fit may use. Default 1, so a small
986
836
  * diagram opens at its own size rather than blown up. */
987
837
  maxScale?: number;
988
838
  /** Which part of a diagram too big for the box it opens on: its middle
989
839
  * (default) or its top-left, where a layered flow begins. */
990
840
  anchor?: 'center' | 'start';
991
- /** Space kept clear around the diagram by the initial fit, in CSS pixels.
841
+ /** Space kept clear around the diagram by the fit, in CSS pixels.
992
842
  * Default 16. */
993
843
  fitPadding?: number;
994
844
  /** What the canvas paints behind the diagram. Unset, it is transparent. */
995
845
  background?: Paint;
996
- /** The view, controlled. Set it and the initial fit is skipped; pair it
846
+ /** The view, controlled. Set it and the fit is skipped; pair it
997
847
  * with `onViewChange` or panning does nothing. */
998
848
  view?: View;
999
- /** Every view change, from a pan, a zoom or the initial fit. */
849
+ /** Every view change, from a pan, a zoom or a refit. */
1000
850
  onViewChange?: (view: View) => void;
1001
851
  className?: string;
1002
852
  }
@@ -1004,11 +854,15 @@ interface DiagramViewProps {
1004
854
  interface DiagramViewApi {
1005
855
  /** Lay the diagram out again from where its nodes stand, animated. `'force'`
1006
856
  * (default) relaxes until it settles; any other layout eases to its answer.
1007
- * What moved is reported to `onMove` once the run settles. */
1008
- layout(algorithm?: 'layered' | 'tree' | 'force' | LayoutFn): void;
857
+ * A `LayoutPlanner` is handed the diagram's graph and eased to once it
858
+ * answers; a drag, an edit or another `layout` call before then drops the
859
+ * answer. What moved is reported to `onMove` once the run settles. */
860
+ layout(algorithm?: DiagramLayout): void;
1009
861
  /** The run `layout` started: stop it where it stands, or cancel it. */
1010
862
  live: LiveLayout;
1011
863
  }
864
+ /** What {@link DiagramViewApi.layout} runs. */
865
+ type DiagramLayout = 'layered' | 'tree' | 'force' | 'chain' | LayoutFn | LayoutPlanner;
1012
866
  /** A diagram: drag or wheel to pan, zoom, and pick a node; drag nodes with
1013
867
  * `onMove`, join ports with `onConnect`. Labels paint only in a registered
1014
868
  * font family; `diagramScene` defaults to sans-serif, so call
@@ -1021,4 +875,4 @@ declare const DiagramView: react.ForwardRefExoticComponent<DiagramViewProps & re
1021
875
  * `<SceneCanvas onClick>`. */
1022
876
  declare function useMirroredSelection(selection: Pick<SelectionApi, 'get' | 'set'>, selected: string | null | undefined, onSelect: ((id: string | null) => void) | undefined): () => void;
1023
877
 
1024
- export { ANCHOR_DERIVE_POSE, type ApplyLayoutOptions, BodySpec, CONNECT_ACTION_ID, type CanConnect, type ConnectActionOptions, DEFAULT_EDGE_STROKE, DIAGRAM_ANCHOR, DIAGRAM_EDGE, DIAGRAM_GROUP, DIAGRAM_LABEL, type DataEdge, type DataGroup, type DataNode, type DiagramAnchor, type DiagramContributionOptions, type DiagramData, type DiagramDerivePose, type DiagramEdge, DiagramGroup, type DiagramLabel, DiagramNode, DiagramNodeLike, DiagramNodeReader, type DiagramRouter, type DiagramSceneData, type DiagramSceneOptions, type DiagramShapeOptions, type DiagramSpec, type DiagramTextStyle, DiagramView, type DiagramViewApi, type DiagramViewProps, EASED_LAYOUTS, EDGE_DERIVE_PATH, type EasedProducerOptions, type EdgeEnd, type EdgeRouteOptions, type EdgeStyle, ForceOptions, type ForceProducerOptions, GRAB_PORT_ACTION_ID, GROUP_DERIVE_POSE, Graph, GraphSource, type GroupPoseOptions, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, type LabelPoseOptions, type LayeredRoute, type LayeredRouteInput, type LayoutActionOptions, LayoutDirection, LayoutFn, LayoutOptions, LayoutResult, type LiveLayout, type LiveLayoutCtx, type LiveLayoutFrame, type LiveLayoutProducer, MeasureRowText, type NodeMove, type NodeStyle, Outline, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, PortAnchor, type PortScratch, PortSpec, PortsOptions, ROUTERS, type RouteEdge, type RouteRequest, type Router, Row, type SelfLoop, type UseLiveLayoutOptions, anchorDerivePose, applyLayout, bezier, boxAround, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layeredRoutes, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, selfLoop, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
878
+ export { type ApplyLayoutOptions, CONNECT_ACTION_ID, type CanConnect, type ConnectActionOptions, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, DataEdge, type DiagramContributionOptions, type DiagramLayout, DiagramNodeLike, DiagramNodeReader, type DiagramShapeOptions, DiagramSpec, DiagramView, type DiagramViewApi, type DiagramViewProps, EASED_LAYOUTS, EDGE_DERIVE_PATH, type EasedProducerOptions, EdgeEnd, type EdgeRoute, type EdgeRouteOptions, ForceOptions, type ForceProducerOptions, GRAB_PORT_ACTION_ID, Graph, GraphNodeLike, GraphSource, LABEL_DERIVE_POSE, LAYOUTS, LAYOUT_ACTION_ID, type LabelPoseOptions, type LayeredRoute, type LayeredRouteInput, type LayoutActionOptions, LayoutDirection, LayoutFn, LayoutOptions, LayoutPlan, LayoutPlanner, LayoutPorts, LayoutResult, type LayoutRoutes, type LiveLayout, type LiveLayoutCtx, type LiveLayoutFrame, type LiveLayoutProducer, type NodeMove, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, type ParticipantPose, type ParticipantScene, type ParticipantSource, type PendingEdge, Port, type PortAffordanceOptions, type PortScratch, PortsOptions, ROUTERS, type RouteEdge, type RouteRequest, type RoutedScene, type Router, type SelfLoop, type UseLiveLayoutOptions, applyLayout, bezier, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramLayoutOf, diagramPorts, diagramShape, easedProducer, edgeDerivePath, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layeredRoutes, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, routeWrites, routesOf, sceneParticipants, selfLoop, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };