@x12i/diagrams 1.0.0 → 1.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.
@@ -0,0 +1,857 @@
1
+ /**
2
+ * The kit's own diagram document (spec section 10). It is versioned JSON, so
3
+ * the node canvas engine can change without breaking saved diagrams.
4
+ *
5
+ * Coordinates: `position` is the top-left corner of the node. For a child of a
6
+ * group or subflow (`parentId` set) it is relative to the parent's top-left
7
+ * corner, the same convention React Flow uses. Everything here is plain data:
8
+ * no React, no DOM.
9
+ */
10
+ declare const DIAGRAM_VERSION = 1;
11
+ /** The node library. Every node reads kit tokens. */
12
+ type NodeKind = "start" | "end" | "task" | "decision" | "group" | "note" | "image" | "entity" | "code" | "chart" | "agent" | "subflow";
13
+ /** Status colors match the rest of the kit: a status means the same in a badge, a chart and a node. */
14
+ type NodeStatus = "neutral" | "success" | "warning" | "danger" | "info";
15
+ type EdgeKind = "straight" | "bezier" | "step" | "smooth-step";
16
+ /**
17
+ * What the diagram models. It decides which structural rules `validateDiagram`
18
+ * and `checkConnection` enforce:
19
+ *
20
+ * - `graph` (default) and `flowchart`: any shape, loops are allowed.
21
+ * - `dag`: no cycles (workflows, data lineage).
22
+ * - `tree`: no cycles and at most one incoming edge per node (org charts).
23
+ */
24
+ type DiagramKind = "graph" | "flowchart" | "dag" | "tree";
25
+ interface Position {
26
+ x: number;
27
+ y: number;
28
+ }
29
+ interface DiagramNode {
30
+ id: string;
31
+ kind: NodeKind;
32
+ /** The text read aloud in the outline. Pass a translated string. */
33
+ label: string;
34
+ position: Position;
35
+ status?: NodeStatus;
36
+ /** Set on children of a group or subflow. */
37
+ parentId?: string;
38
+ width?: number;
39
+ height?: number;
40
+ /** Typed handles, for connection validation. */
41
+ ports?: Array<{
42
+ id: string;
43
+ direction: "in" | "out";
44
+ type?: string;
45
+ }>;
46
+ /** Free-form payload for custom node bodies, such as a ChartSpec for a chart node. */
47
+ data?: Record<string, unknown>;
48
+ /** A longer description, read after the label by screen readers. */
49
+ description?: string;
50
+ /** A collapsed group or subflow hides its children. */
51
+ collapsed?: boolean;
52
+ /** A locked node cannot be moved, resized, renamed or deleted in the editor. */
53
+ locked?: boolean;
54
+ }
55
+ interface DiagramEdge {
56
+ id: string;
57
+ source: string;
58
+ target: string;
59
+ sourcePort?: string;
60
+ targetPort?: string;
61
+ kind?: EdgeKind;
62
+ label?: string;
63
+ /** Draw arrowheads at both ends. */
64
+ bidirectional?: boolean;
65
+ animated?: boolean;
66
+ /** Draw the line dashed. Dashes carry meaning without color (for example "optional"). */
67
+ dashed?: boolean;
68
+ /** Tints the line with a status color, the same way a node status does. */
69
+ status?: NodeStatus;
70
+ }
71
+ interface DiagramDocument {
72
+ version: typeof DIAGRAM_VERSION;
73
+ /** Every diagram has a title that states what it shows. */
74
+ title: string;
75
+ nodes: DiagramNode[];
76
+ edges: DiagramEdge[];
77
+ /** Defaults to `graph`. */
78
+ kind?: DiagramKind;
79
+ /** A longer text alternative, read after the title. */
80
+ description?: string;
81
+ }
82
+ /** Every node kind, in palette order. */
83
+ declare const NODE_KINDS: readonly NodeKind[];
84
+ declare const NODE_STATUSES: readonly NodeStatus[];
85
+ declare const EDGE_KINDS: readonly EdgeKind[];
86
+ declare const DIAGRAM_KINDS: readonly DiagramKind[];
87
+ /** Kinds that can contain other nodes. */
88
+ declare function isContainerKind(kind: NodeKind): boolean;
89
+ /** Identity helper for authoring-time type checking. */
90
+ declare function defineDiagram(document: DiagramDocument): DiagramDocument;
91
+ /** An empty document with the current version. */
92
+ declare function createDiagram(title?: string, extra?: Partial<Omit<DiagramDocument, "version" | "title">>): DiagramDocument;
93
+
94
+ /** Lookup tables over a document. Build once, query many times. */
95
+ interface DiagramIndex {
96
+ nodesById: Map<string, DiagramNode>;
97
+ edgesById: Map<string, DiagramEdge>;
98
+ /** Edges whose endpoints both exist, grouped by source node id. */
99
+ outgoing: Map<string, DiagramEdge[]>;
100
+ incoming: Map<string, DiagramEdge[]>;
101
+ /** Child node ids by parent id, in document order. */
102
+ children: Map<string, string[]>;
103
+ }
104
+ /** Builds the lookup tables. Dangling edges are left out of `outgoing` and `incoming`. */
105
+ declare function buildIndex(doc: DiagramDocument): DiagramIndex;
106
+ /** The chain of ancestor ids, nearest first. Stops if the parent chain loops. */
107
+ declare function getAncestors(index: DiagramIndex, id: string): string[];
108
+ /** All descendants of a node, depth first, in document order. */
109
+ declare function getDescendants(index: DiagramIndex, id: string): string[];
110
+ /**
111
+ * Finds a directed cycle and returns its node ids in order (the first id is not
112
+ * repeated at the end), or `undefined` when the graph is acyclic. Self-loops
113
+ * count as cycles. Iterative, so a very long chain cannot overflow the stack.
114
+ */
115
+ declare function findCycle(doc: DiagramDocument, index?: DiagramIndex): string[] | undefined;
116
+ /** True when `to` can be reached from `from` by following edges forward. A node reaches itself. */
117
+ declare function hasPath(index: DiagramIndex, from: string, to: string): boolean;
118
+ /** Nodes with no incoming edge, in document order. */
119
+ declare function getRoots(doc: DiagramDocument, index?: DiagramIndex): string[];
120
+ /** Ids of nodes directly connected to `id` (either direction), without duplicates. */
121
+ declare function getNeighbors(index: DiagramIndex, id: string): string[];
122
+ /** Topological order of the nodes (Kahn). Nodes on a cycle come last, in document order. */
123
+ declare function topologicalOrder(doc: DiagramDocument, index?: DiagramIndex): string[];
124
+
125
+ type IssueSeverity = "error" | "warning";
126
+ type IssueCode = "unsupported-version" | "missing-title" | "empty-id" | "duplicate-node-id" | "duplicate-edge-id" | "invalid-node-kind" | "invalid-edge-kind" | "invalid-status" | "invalid-position" | "invalid-size" | "empty-label" | "dangling-source" | "dangling-target" | "unknown-source-port" | "unknown-target-port" | "source-port-direction" | "target-port-direction" | "port-type-mismatch" | "duplicate-port-id" | "duplicate-edge" | "self-loop" | "parent-missing" | "parent-self" | "parent-cycle" | "parent-not-container" | "cycle" | "multiple-parents";
127
+ /**
128
+ * One finding. `message` is English for developers and logs; show your own
129
+ * translated text for end users, keyed by `code`.
130
+ */
131
+ interface DiagramIssue {
132
+ code: IssueCode;
133
+ severity: IssueSeverity;
134
+ message: string;
135
+ /** Where in the document the problem is, for example `["edges", 2, "target"]`. */
136
+ path: Array<string | number>;
137
+ nodeId?: string;
138
+ edgeId?: string;
139
+ }
140
+ interface ValidationResult {
141
+ /** True when there are no errors. Warnings do not make a diagram invalid. */
142
+ valid: boolean;
143
+ issues: DiagramIssue[];
144
+ errors: DiagramIssue[];
145
+ warnings: DiagramIssue[];
146
+ }
147
+ interface ValidateOptions {
148
+ /** Overrides `doc.kind`. */
149
+ kind?: DiagramKind;
150
+ /** Report nodes with an empty label as warnings. Defaults to true. */
151
+ warnOnEmptyLabels?: boolean;
152
+ }
153
+ /**
154
+ * Checks a document and returns every problem it finds instead of stopping at
155
+ * the first: unique ids, edges that point at nothing, ports that do not exist
156
+ * or do not match, group (parent) structure, and, for `dag` and `tree`
157
+ * diagrams, cycles and multiple parents.
158
+ */
159
+ declare function validateDiagram(doc: DiagramDocument, options?: ValidateOptions): ValidationResult;
160
+
161
+ /** A proposed edge, before it has an id. */
162
+ interface DiagramConnection {
163
+ source: string;
164
+ target: string;
165
+ sourcePort?: string;
166
+ targetPort?: string;
167
+ }
168
+ /** Why a connection was refused. The canvas maps each code to a translated message. */
169
+ type ConnectionRejection = "unknown-source" | "unknown-target" | "self-loop" | "duplicate" | "locked" | "container-descendant" | "source-port-missing" | "target-port-missing" | "source-port-not-output" | "target-port-not-input" | "port-type-mismatch" | "cycle" | "multiple-parents" | "custom";
170
+ type ConnectionCheck = {
171
+ ok: true;
172
+ } | {
173
+ ok: false;
174
+ code: ConnectionRejection;
175
+ message: string;
176
+ };
177
+ interface ConnectionRules {
178
+ /** Allow an edge from a node to itself. Defaults to true, except for `dag` and `tree` diagrams. */
179
+ allowSelfLoops?: boolean;
180
+ /** Allow a second edge with the same endpoints and ports. Defaults to false. */
181
+ allowDuplicates?: boolean;
182
+ /** Refuse edges that would close a cycle. Defaults to true for `dag` and `tree` diagrams. */
183
+ acyclic?: boolean;
184
+ /** Refuse a second incoming edge. Defaults to true for `tree` diagrams. */
185
+ singleParent?: boolean;
186
+ }
187
+ /** The rules a diagram kind implies. Explicit rules override them. */
188
+ declare function resolveConnectionRules(kind: DiagramKind | undefined, rules?: ConnectionRules): Required<ConnectionRules>;
189
+ /** Port types are compatible when either is untyped or `any`, or when they are equal. */
190
+ declare function portTypesCompatible(a: string | undefined, b: string | undefined): boolean;
191
+ /**
192
+ * Checks a proposed edge against the diagram's rules: both nodes exist, ports
193
+ * exist, point the right way and have compatible types, and the structural
194
+ * rules of the diagram kind hold. The result carries a machine code; show a
195
+ * translated message for it.
196
+ */
197
+ declare function checkConnection(doc: DiagramDocument, connection: DiagramConnection, rules?: ConnectionRules, index?: DiagramIndex): ConnectionCheck;
198
+
199
+ /** A point in diagram space. Same shape as `Position`. */
200
+ type Point = Position;
201
+ interface Size {
202
+ width: number;
203
+ height: number;
204
+ }
205
+ interface Rect extends Point, Size {
206
+ }
207
+ type Side = "top" | "right" | "bottom" | "left";
208
+ /** Flow direction of a layout: top to bottom, bottom to top, left to right, right to left. */
209
+ type LayoutDirection = "TB" | "BT" | "LR" | "RL";
210
+ /** The diagram grid in diagram units. Layout and snapping round to it (spec: everything sits on the 8 px grid). */
211
+ declare const GRID_SIZE = 8;
212
+ /** Default node sizes by kind, in diagram units. All are multiples of the grid. */
213
+ declare const DEFAULT_NODE_SIZE: Readonly<Record<NodeKind, Size>>;
214
+ /** Height of a collapsed group: just its title bar. */
215
+ declare const COLLAPSED_HEIGHT = 48;
216
+ /** Approximate text metrics, used to size nodes and labels without a DOM. */
217
+ declare const TEXT_METRICS: {
218
+ readonly charWidth: 7;
219
+ readonly lineHeight: 20;
220
+ readonly nodePaddingX: 32;
221
+ readonly nodePaddingY: 24;
222
+ };
223
+ declare function estimateTextWidth(text: string, charWidth?: number): number;
224
+ /** Wraps text into lines that fit `maxWidth`, breaking at spaces and, for a very long word, anywhere. */
225
+ declare function wrapText(text: string, maxWidth: number, charWidth?: number): string[];
226
+ /**
227
+ * The size a node is drawn at: its own `width` and `height` when set, otherwise
228
+ * a default for its kind that grows to fit a long label (or the rows of an
229
+ * entity). The estimate is deterministic, so layout, the canvas and the static
230
+ * SVG all agree.
231
+ */
232
+ declare function getNodeSize(node: DiagramNode): Size;
233
+ declare function getNodeBounds(node: DiagramNode): Rect;
234
+ declare function rectCenter(rect: Rect): Point;
235
+ declare function unionRects(rects: Iterable<Rect>): Rect;
236
+ declare function rectsIntersect(a: Rect, b: Rect): boolean;
237
+ declare function rectContainsPoint(rect: Rect, point: Point): boolean;
238
+ /** Absolute (diagram space) top-left position of every node, resolving parent offsets. Loop-safe. */
239
+ declare function getAbsolutePositions(doc: DiagramDocument, index?: DiagramIndex): Map<string, Point>;
240
+ declare function getAbsolutePosition(doc: DiagramDocument, id: string): Point | undefined;
241
+ /** Absolute bounds of every node. */
242
+ declare function getAbsoluteBounds(doc: DiagramDocument, index?: DiagramIndex): Map<string, Rect>;
243
+ /** The rectangle that contains every node (absolute). An empty diagram gives a zero rectangle. */
244
+ declare function getDocumentBounds(doc: DiagramDocument): Rect;
245
+ declare function snapValue(value: number, grid?: number): number;
246
+ declare function snapPoint(point: Point, grid?: number): Point;
247
+ /** Which sides new edges leave from and arrive at for a flow direction. */
248
+ declare function directionSides(direction: LayoutDirection): {
249
+ source: Side;
250
+ target: Side;
251
+ };
252
+ /** In a right-to-left page, horizontal flows mirror: LR becomes RL. Vertical flows keep their direction. */
253
+ declare function mirrorDirection(direction: LayoutDirection, rtl: boolean): LayoutDirection;
254
+ declare function isHorizontal(direction: LayoutDirection): boolean;
255
+ interface PortPlacement {
256
+ /** Undefined for the default (unnamed) handle of a node without declared ports. */
257
+ id: string | undefined;
258
+ direction: "in" | "out";
259
+ side: Side;
260
+ type?: string;
261
+ /** Position along the side, 0 to 1, measured from the start of that side. */
262
+ offset: number;
263
+ }
264
+ /**
265
+ * Where a node's connection points sit. A node without declared ports gets one
266
+ * unnamed input and one unnamed output (a `start` node only an output, an `end`
267
+ * node only an input). Declared ports are spread evenly along their side. In a
268
+ * right-to-left page the spread along a horizontal edge mirrors.
269
+ */
270
+ declare function getPortPlacements(node: DiagramNode, direction?: LayoutDirection, rtl?: boolean): PortPlacement[];
271
+ /** The point on a rectangle's side, `offset` (0 to 1) along it. */
272
+ declare function sideAnchor(rect: Rect, side: Side, offset?: number): Point;
273
+ declare function sideVector(side: Side): Point;
274
+ interface EdgePathInput {
275
+ source: Point;
276
+ target: Point;
277
+ sourceSide: Side;
278
+ targetSide: Side;
279
+ kind?: EdgeKind;
280
+ /** How far an edge leaves a node before it turns. Defaults to 24. */
281
+ offset?: number;
282
+ /** Corner radius of `smooth-step` edges. Defaults to 8. */
283
+ radius?: number;
284
+ /** Bezier curvature, as React Flow defines it. Defaults to 0.25. */
285
+ curvature?: number;
286
+ /** Draw a loop that returns to the same node, `loopSize` units wide. */
287
+ loopSize?: number;
288
+ }
289
+ interface EdgePath {
290
+ /** SVG path data. */
291
+ path: string;
292
+ /** Where to put the label: the visual middle of the edge. */
293
+ label: Point;
294
+ /** The polyline (or control polygon for curves). Useful for bounds and hit tests. */
295
+ points: Point[];
296
+ }
297
+ /**
298
+ * The SVG path between two anchors. `step` and `smooth-step` run orthogonally
299
+ * and leave each node perpendicular to the side they attach to; `bezier` uses
300
+ * React Flow's control-point rule; `straight` is a line. The canvas and the
301
+ * static SVG both call this, so they draw the same edge.
302
+ */
303
+ declare function computeEdgePath(input: EdgePathInput): EdgePath;
304
+ interface GeometryOptions {
305
+ direction?: LayoutDirection;
306
+ rtl?: boolean;
307
+ /** Default edge kind for edges that do not set one. Defaults to `smooth-step`. */
308
+ edgeKind?: EdgeKind;
309
+ }
310
+ /** Precomputed lookups for drawing many edges of one document. */
311
+ interface GeometryContext {
312
+ doc: DiagramDocument;
313
+ index: DiagramIndex;
314
+ direction: LayoutDirection;
315
+ rtl: boolean;
316
+ edgeKind: EdgeKind;
317
+ /** Absolute bounds per node id. */
318
+ bounds: Map<string, Rect>;
319
+ }
320
+ declare function createGeometryContext(doc: DiagramDocument, options?: GeometryOptions): GeometryContext;
321
+ interface EdgeGeometry extends EdgePath {
322
+ source: Point;
323
+ target: Point;
324
+ sourceSide: Side;
325
+ targetSide: Side;
326
+ selfLoop: boolean;
327
+ }
328
+ /** Where an edge runs and how it is drawn. Returns undefined when an endpoint is missing. */
329
+ declare function getEdgeGeometry(context: GeometryContext, edge: DiagramEdge): EdgeGeometry | undefined;
330
+ interface ReadingOrderOptions {
331
+ direction?: LayoutDirection;
332
+ rtl?: boolean;
333
+ /** Nodes whose centers are this close along the flow axis share a row. Defaults to 24. */
334
+ tolerance?: number;
335
+ }
336
+ /**
337
+ * Node ids in reading order: top to bottom then start to end of the line for a
338
+ * vertical flow, or column by column for a horizontal one. A group comes
339
+ * before its children. This is the order the canvas puts nodes in the DOM, so
340
+ * Tab follows it.
341
+ */
342
+ declare function getReadingOrder(doc: DiagramDocument, options?: ReadingOrderOptions): string[];
343
+ type FocusDirection = "up" | "down" | "left" | "right";
344
+ /**
345
+ * The node to move to when an arrow key is pressed on `fromId`. Connected nodes
346
+ * in that direction win; if there is none, the nearest node of any kind in that
347
+ * direction is used. Returns undefined when nothing lies that way.
348
+ */
349
+ declare function findDirectionalNeighbor(doc: DiagramDocument, fromId: string, direction: FocusDirection, options?: {
350
+ candidates?: Iterable<string>;
351
+ }): string | undefined;
352
+
353
+ type MutationErrorCode = "duplicate-id" | "node-not-found" | "edge-not-found" | "parent-not-found" | "parent-not-container" | "invalid-connection" | "cannot-group";
354
+ /** Thrown when a mutation is asked to do something that would corrupt the document. */
355
+ declare class DiagramMutationError extends Error {
356
+ readonly code: MutationErrorCode;
357
+ /** For `invalid-connection`: the `ConnectionRejection` code. */
358
+ readonly detail?: string;
359
+ constructor(code: MutationErrorCode, message: string, detail?: string);
360
+ }
361
+ type NewNode = Omit<DiagramNode, "id" | "label" | "position"> & {
362
+ id?: string;
363
+ label?: string;
364
+ position?: Position;
365
+ };
366
+ /** The id `addNode` would give a new node of this kind. */
367
+ declare function nextNodeId(doc: DiagramDocument, prefix?: string): string;
368
+ /** The id `connect` would give a new edge. */
369
+ declare function nextEdgeId(doc: DiagramDocument, prefix?: string): string;
370
+ /** Builds a complete node with a fresh id, without adding it. */
371
+ declare function createNode(doc: DiagramDocument, input: NewNode): DiagramNode;
372
+ /** Adds a node. Throws `duplicate-id` if the id exists, or a parent error if `parentId` is not a container. */
373
+ declare function addNode(doc: DiagramDocument, input: NewNode): DiagramDocument;
374
+ type NodePatch = Partial<Omit<DiagramNode, "id">>;
375
+ /** Changes a node. The patch can be an object or a function of the current node. Throws `node-not-found`. */
376
+ declare function updateNode(doc: DiagramDocument, id: string, patch: NodePatch | ((node: DiagramNode) => NodePatch)): DiagramDocument;
377
+ interface RemoveOptions {
378
+ /**
379
+ * What happens to the children of a removed group. `release` (default) keeps
380
+ * them and moves them out to the group's parent without changing where they
381
+ * appear; `remove` deletes them too.
382
+ */
383
+ children?: "release" | "remove";
384
+ }
385
+ /** Removes nodes and every edge that touches them. Unknown ids are ignored. */
386
+ declare function removeNodes(doc: DiagramDocument, ids: Iterable<string>, options?: RemoveOptions): DiagramDocument;
387
+ /** Removes a node and its edges. Throws `node-not-found` when the id is unknown. */
388
+ declare function removeNode(doc: DiagramDocument, id: string, options?: RemoveOptions): DiagramDocument;
389
+ /** Moves nodes to the given positions (relative to their parents). Unknown ids are ignored. */
390
+ declare function moveNodes(doc: DiagramDocument, positions: Record<string, Position>): DiagramDocument;
391
+ /** Moves nodes by an offset. A group's children move with it, so they are not shifted twice. */
392
+ declare function translateNodes(doc: DiagramDocument, ids: Iterable<string>, dx: number, dy: number): DiagramDocument;
393
+ /** Sets a node's size. */
394
+ declare function resizeNode(doc: DiagramDocument, id: string, size: Size, position?: Position): DiagramDocument;
395
+ /**
396
+ * Moves a node into another group (or out to the top level with `undefined`)
397
+ * without changing where it appears.
398
+ */
399
+ declare function setNodeParent(doc: DiagramDocument, id: string, parentId: string | undefined): DiagramDocument;
400
+ type NewEdge = DiagramConnection & Partial<Omit<DiagramEdge, "source" | "target" | "sourcePort" | "targetPort">>;
401
+ interface ConnectOptions extends ConnectionRules {
402
+ /** Skip `checkConnection`. Defaults to true (validate). */
403
+ validate?: boolean;
404
+ }
405
+ /**
406
+ * Adds an edge. By default it runs `checkConnection` and throws
407
+ * `invalid-connection` (with the rejection code in `detail`) when the rules
408
+ * refuse it. Pass `{ validate: false }` to add it unconditionally.
409
+ */
410
+ declare function connect(doc: DiagramDocument, input: NewEdge, options?: ConnectOptions): DiagramDocument;
411
+ /**
412
+ * Removes an edge by id (throws `edge-not-found` if it does not exist), or all
413
+ * edges between two nodes when given `{ source, target }` (a no-op if none).
414
+ */
415
+ declare function disconnect(doc: DiagramDocument, target: string | DiagramConnection): DiagramDocument;
416
+ declare function removeEdges(doc: DiagramDocument, ids: Iterable<string>): DiagramDocument;
417
+ type EdgePatch = Partial<Omit<DiagramEdge, "id">>;
418
+ declare function updateEdge(doc: DiagramDocument, id: string, patch: EdgePatch): DiagramDocument;
419
+ interface GroupOptions {
420
+ id?: string;
421
+ label?: string;
422
+ /** `group` (default) or `subflow`. */
423
+ kind?: Extract<NodeKind, "group" | "subflow">;
424
+ /** Space between the group border and its content. Defaults to 32. */
425
+ padding?: number;
426
+ /** Space reserved for the group title. Defaults to 40. */
427
+ header?: number;
428
+ }
429
+ /**
430
+ * Wraps nodes in a new group sized to fit them. The nodes keep their place on
431
+ * the page. They must share one parent; a node whose ancestor is also selected
432
+ * is left alone because it moves with that ancestor. Throws `cannot-group`.
433
+ */
434
+ declare function groupNodes(doc: DiagramDocument, ids: Iterable<string>, options?: GroupOptions): DiagramDocument;
435
+ /** Dissolves a group: its children move out to the group's parent and the group is removed. */
436
+ declare function ungroupNode(doc: DiagramDocument, groupId: string): DiagramDocument;
437
+ interface DiagramFragment {
438
+ nodes: DiagramNode[];
439
+ edges: DiagramEdge[];
440
+ }
441
+ /**
442
+ * Copies nodes (with all their descendants) and the edges between them. Nodes
443
+ * whose parent is not part of the copy are stored with absolute positions and
444
+ * no parent, so the fragment can be pasted anywhere.
445
+ */
446
+ declare function extractFragment(doc: DiagramDocument, ids: Iterable<string>): DiagramFragment;
447
+ interface PasteOptions {
448
+ /** Shift applied to the pasted top-level nodes. Defaults to 24, 24. */
449
+ offset?: Position;
450
+ /** Paste inside this group. Pasted positions are then relative to it. */
451
+ parentId?: string;
452
+ }
453
+ interface PasteResult {
454
+ document: DiagramDocument;
455
+ /** Ids of every new node, including children. */
456
+ nodeIds: string[];
457
+ /** Ids of the new top-level nodes of the paste. */
458
+ rootIds: string[];
459
+ edgeIds: string[];
460
+ /** Old id to new id. */
461
+ idMap: Record<string, string>;
462
+ }
463
+ /** Adds a copy of a fragment with fresh ids. */
464
+ declare function pasteFragment(doc: DiagramDocument, fragment: DiagramFragment, options?: PasteOptions): PasteResult;
465
+ /** Copies nodes (and the edges between them) next to the originals. Copies stay in the same group. */
466
+ declare function duplicateNodes(doc: DiagramDocument, ids: Iterable<string>, options?: PasteOptions): PasteResult;
467
+ type Alignment = "left" | "center" | "right" | "top" | "middle" | "bottom";
468
+ /** Lines nodes up on one edge or center. Needs at least two nodes. */
469
+ declare function alignNodes(doc: DiagramDocument, ids: Iterable<string>, alignment: Alignment): DiagramDocument;
470
+ /** Spaces three or more nodes so the gaps between them are equal. The outer two stay put. */
471
+ declare function distributeNodes(doc: DiagramDocument, ids: Iterable<string>, axis: "horizontal" | "vertical"): DiagramDocument;
472
+
473
+ type LayoutAlgorithm = "layered" | "tree" | "radial" | "grid";
474
+ interface LayoutSpacing {
475
+ /** Gap between neighbors in the same rank, in diagram units. Defaults to 48. */
476
+ node?: number;
477
+ /** Gap between ranks (levels). Defaults to 80. */
478
+ rank?: number;
479
+ /** Padding between a group's border and its content. Defaults to 32. */
480
+ group?: number;
481
+ }
482
+ interface LayoutOptions {
483
+ /** `layered` (dagre, default), `tree` (d3-hierarchy), `radial` or `grid`. */
484
+ algorithm?: LayoutAlgorithm;
485
+ /** Flow direction. Defaults to `TB`. Radial layouts ignore it. */
486
+ direction?: LayoutDirection;
487
+ /** Mirror the layout for a right-to-left page: LR becomes RL, and vertical layouts flip left to right. */
488
+ rtl?: boolean;
489
+ /** Spacing in diagram units. A number sets the node gap. */
490
+ spacing?: LayoutSpacing | number;
491
+ /** `tree` and `radial`: the root node id. Defaults to the nodes with no incoming edge. */
492
+ root?: string;
493
+ /** `tree` and `radial`: `tidy` (default) or `cluster`, which puts every leaf on the same level. */
494
+ treeVariant?: "tidy" | "cluster";
495
+ /** `grid`: items per line. Defaults to the square root of the node count, rounded up. */
496
+ columns?: number;
497
+ /** `layered`: dagre's ranking algorithm. Defaults to `network-simplex`, or to the much faster `tight-tree` on levels with more than ~400 nodes and edges. */
498
+ ranker?: "network-simplex" | "tight-tree" | "longest-path";
499
+ /** Round positions and group sizes to this grid. Defaults to 8; 0 turns rounding off. */
500
+ grid?: number;
501
+ /** Space reserved for a group's title above its content. Defaults to 40. */
502
+ groupHeader?: number;
503
+ /** Where the top-left corner of the whole layout goes. Defaults to 0, 0. */
504
+ origin?: Position;
505
+ }
506
+ interface LayoutResult {
507
+ /** New `position` for every node (relative to its parent, as in the document). */
508
+ positions: Record<string, Position>;
509
+ /** New size for every group that was fitted around its children. */
510
+ sizes: Record<string, Size>;
511
+ /** Absolute bounds of the laid out diagram. */
512
+ bounds: Rect;
513
+ algorithm: LayoutAlgorithm;
514
+ /** The direction actually used, after right-to-left mirroring. */
515
+ direction: LayoutDirection;
516
+ }
517
+ /**
518
+ * Computes positions for every node. Deterministic: the same document and
519
+ * options always give the same result. Groups are laid out from the inside out:
520
+ * a group's children are arranged first, the group is sized to fit them, and
521
+ * then the group is placed as one box among its siblings. Edges that cross a
522
+ * group boundary are lifted to the level where both ends are visible.
523
+ *
524
+ * It does not change the document; pass the result to `applyLayout`.
525
+ */
526
+ declare function layoutDiagram(doc: DiagramDocument, options?: LayoutOptions): LayoutResult;
527
+ /** Writes a layout result into a document: new positions, and fitted sizes for groups. */
528
+ declare function applyLayout(doc: DiagramDocument, result: LayoutResult): DiagramDocument;
529
+ /** Lays out and applies in one step. */
530
+ declare function layoutedDiagram(doc: DiagramDocument, options?: LayoutOptions): DiagramDocument;
531
+
532
+ /**
533
+ * Undo and redo as a pure reducer, so it works in a React `useReducer`, in a
534
+ * store, or on a server. States are never mutated.
535
+ */
536
+ interface HistoryState<T> {
537
+ past: T[];
538
+ present: T;
539
+ future: T[];
540
+ /** The `coalesceKey` of the last `set`, so a run of similar changes becomes one undo step. */
541
+ lastKey?: string;
542
+ }
543
+ interface HistoryOptions {
544
+ /** The most undo steps to keep. Defaults to 100. */
545
+ limit?: number;
546
+ }
547
+ type HistoryAction<T> = {
548
+ type: "set";
549
+ value: T;
550
+ /**
551
+ * Changes with the same key in a row share one undo step, for example
552
+ * every keystroke while renaming a node.
553
+ */
554
+ coalesceKey?: string;
555
+ } | {
556
+ type: "undo";
557
+ } | {
558
+ type: "redo";
559
+ }
560
+ /** Replace the present and forget the past and future. */
561
+ | {
562
+ type: "reset";
563
+ value: T;
564
+ }
565
+ /** Forget the past and future, keeping the present. */
566
+ | {
567
+ type: "clear";
568
+ };
569
+ declare function createHistory<T>(initial: T): HistoryState<T>;
570
+ declare function canUndo<T>(state: HistoryState<T>): boolean;
571
+ declare function canRedo<T>(state: HistoryState<T>): boolean;
572
+ declare function historyReducer<T>(state: HistoryState<T>, action: HistoryAction<T>, options?: HistoryOptions): HistoryState<T>;
573
+
574
+ interface StatusPalette {
575
+ background: string;
576
+ foreground: string;
577
+ border: string;
578
+ }
579
+ /**
580
+ * The colors a static diagram uses. Every default is a `var(--kit-...)`
581
+ * reference, so the SVG follows the theme of the page it is placed in. To save
582
+ * a diagram as a stand-alone file, pass resolved colors (the React entry's
583
+ * `readDiagramPalette` reads them from the DOM).
584
+ */
585
+ interface DiagramPalette {
586
+ surface: string;
587
+ canvas: string;
588
+ text: string;
589
+ textMuted: string;
590
+ textInverse: string;
591
+ /** Node outlines. */
592
+ border: string;
593
+ /** Soft separators, such as the table header rule. */
594
+ borderSubtle: string;
595
+ /** Edges. Keeps 3:1 against the surface. */
596
+ edge: string;
597
+ accent: string;
598
+ accentSubtle: string;
599
+ onAccent: string;
600
+ inverse: string;
601
+ status: Record<NodeStatus, StatusPalette>;
602
+ fontSans: string;
603
+ fontMono: string;
604
+ }
605
+ type DiagramPaletteOverride = Partial<Omit<DiagramPalette, "status">> & {
606
+ status?: Partial<Record<NodeStatus, Partial<StatusPalette>>>;
607
+ };
608
+ declare const defaultPalette: DiagramPalette;
609
+ declare function resolvePalette(override?: DiagramPaletteOverride): DiagramPalette;
610
+ type SvgAttributes = Record<string, string | number | undefined>;
611
+ /**
612
+ * A tiny immutable tree of SVG elements. The string serializer and the React
613
+ * `StaticDiagram` both render from it, so they draw the same picture and
614
+ * nothing is ever injected as raw markup.
615
+ */
616
+ interface SvgElement {
617
+ tag: string;
618
+ attrs?: SvgAttributes;
619
+ /** CSS declarations, written as the `style` attribute. */
620
+ style?: Record<string, string>;
621
+ /** Child elements and text. Text is escaped when serialized. */
622
+ children?: Array<SvgElement | string>;
623
+ }
624
+ declare function escapeXml(text: string): string;
625
+ /** Serializes an element tree to an SVG string. Text and attribute values are escaped. */
626
+ declare function serializeSvg(element: SvgElement): string;
627
+ interface SvgOptions {
628
+ /** Flow direction, which decides where edges attach. Defaults to `TB`. */
629
+ direction?: LayoutDirection;
630
+ /** Right-to-left page: mirrors port order and aligns text to the right. */
631
+ rtl?: boolean;
632
+ /** Edge style for edges that do not set one. Defaults to `smooth-step`. */
633
+ edgeKind?: EdgeKind;
634
+ /** Space around the diagram, in diagram units. Defaults to 24. */
635
+ padding?: number;
636
+ palette?: DiagramPaletteOverride;
637
+ /** Prefix for generated element ids. Defaults to `kit-diagram`. Use a unique one when several diagrams share a page. */
638
+ idPrefix?: string;
639
+ /** Accessible title. Defaults to the document title. */
640
+ title?: string;
641
+ /** Accessible description. Defaults to the text outline of the diagram. */
642
+ description?: string;
643
+ /** Joins labels in the default description ("A to B"). Pass a translated word. */
644
+ connector?: string;
645
+ /** Language of the text, as a BCP 47 tag. */
646
+ lang?: string;
647
+ /** Width of the `<svg>` element. Omit to let it size from its container. */
648
+ width?: number | string;
649
+ height?: number | string;
650
+ /** Node ids to draw with the accent color: "one accent marks the path or node the diagram is about". */
651
+ highlight?: Iterable<string>;
652
+ /** Draw edge labels. Defaults to true. */
653
+ showEdgeLabels?: boolean;
654
+ /** Add the XML declaration, for saving as a `.svg` file. */
655
+ standalone?: boolean;
656
+ }
657
+ /**
658
+ * True for an image address a diagram may load: http(s) and raster `data:`
659
+ * URLs. Everything else (`javascript:`, `file:`, SVG data URLs, relative paths)
660
+ * is refused, because a diagram document is data that may come from anyone.
661
+ */
662
+ declare function isSafeImageUrl(value: unknown): value is string;
663
+ /** Builds the SVG element tree for a document. */
664
+ declare function diagramToSvgTree(doc: DiagramDocument, options?: SvgOptions): SvgElement;
665
+ /**
666
+ * Renders a diagram to an SVG string with no DOM and no React, so it works on a
667
+ * server, in a report or for print. Colors are `var(--kit-...)` references by
668
+ * default; pass `palette` for resolved colors when saving a stand-alone file.
669
+ */
670
+ declare function diagramToSvg(doc: DiagramDocument, options?: SvgOptions): string;
671
+
672
+ type ImportErrorCode = "invalid-json" | "not-an-object" | "missing-version" | "unsupported-version" | "missing-migration" | "invalid-structure";
673
+ /** Thrown by `importDiagram` when the input cannot become a document. */
674
+ declare class DiagramImportError extends Error {
675
+ readonly code: ImportErrorCode;
676
+ readonly issues: DiagramIssue[];
677
+ constructor(code: ImportErrorCode, message: string, issues?: DiagramIssue[]);
678
+ }
679
+ /** Upgrades the raw document by one version. It receives and returns plain JSON. */
680
+ type DiagramMigration = (raw: Record<string, unknown>) => Record<string, unknown>;
681
+ interface ImportOptions {
682
+ /**
683
+ * Migrations by the version they upgrade FROM: `{ 0: (raw) => ... }` turns a
684
+ * version 0 document into version 1. They run in order until the current
685
+ * version. The `version` field is advanced for you.
686
+ */
687
+ migrations?: Record<number, DiagramMigration>;
688
+ /**
689
+ * A single hook that runs after `migrations`, once per import, with the
690
+ * version the data started at. Use it for changes not tied to one version.
691
+ */
692
+ migrate?: (raw: Record<string, unknown>, fromVersion: number) => Record<string, unknown>;
693
+ /** Version to assume when the input has none. By default a missing version is an error. */
694
+ assumeVersion?: number;
695
+ /** Throw `invalid-structure` when validation finds errors. Defaults to false: errors are returned in `issues`. */
696
+ strict?: boolean;
697
+ }
698
+ interface ImportResult {
699
+ document: DiagramDocument;
700
+ /** Everything `validateDiagram` found. Warnings and, unless `strict`, errors. */
701
+ issues: DiagramIssue[];
702
+ /** The version the input had before migration, when it was migrated. */
703
+ migratedFrom?: number;
704
+ }
705
+ interface ExportJsonOptions {
706
+ /** Indentation. Defaults to 2; pass 0 for compact output. */
707
+ space?: number;
708
+ }
709
+ /** Serializes a document to JSON. The `version` field comes first. */
710
+ declare function exportDiagramJson(doc: DiagramDocument, options?: ExportJsonOptions): string;
711
+ /** Exports a stand-alone SVG file's text. Pass a resolved `palette`; `var(--kit-...)` colors only work inside a themed page. */
712
+ declare function exportDiagramSvg(doc: DiagramDocument, options?: SvgOptions): string;
713
+ /**
714
+ * Turns JSON text or a parsed object into a validated document. Old versions
715
+ * are migrated, unknown newer versions are refused, and the result is checked
716
+ * with `validateDiagram`. Round-trips with `exportDiagramJson`.
717
+ */
718
+ declare function importDiagram(input: string | unknown, options?: ImportOptions): ImportResult;
719
+
720
+ /** Values available to a message template, for example `{ label: "Review" }`. */
721
+ type MessageValues = Record<string, string | number>;
722
+ /**
723
+ * A translated string. Either a template with `{name}` placeholders, or a
724
+ * function for languages that need plurals or word order a template cannot
725
+ * express (use `Intl.PluralRules` inside it).
726
+ */
727
+ type Message = string | ((values: MessageValues) => string);
728
+ /** Fills `{name}` placeholders. An unknown placeholder is left as written. */
729
+ declare function formatMessage(message: Message, values?: MessageValues): string;
730
+ interface DiagramMessages {
731
+ canvas: {
732
+ /** Accessible name of the canvas. `{title}` */
733
+ label: Message;
734
+ /** Keyboard instructions for an editable canvas, read when it gets focus. */
735
+ instructions: Message;
736
+ /** Keyboard instructions for a read-only canvas. */
737
+ instructionsReadOnly: Message;
738
+ /** Shown instead of the nodes when the diagram is empty. */
739
+ empty: Message;
740
+ };
741
+ kinds: Record<NodeKind, Message>;
742
+ statuses: Record<NodeStatus, Message>;
743
+ node: {
744
+ /** `{label}`, `{kind}`, `{incoming}`, `{outgoing}` */
745
+ label: Message;
746
+ /** Appended when a node has a status other than neutral. `{status}` */
747
+ status: Message;
748
+ roleDescription: Message;
749
+ /** Accessible name of the inline editor. `{label}` */
750
+ editLabel: Message;
751
+ /** `{label}` */
752
+ collapse: Message;
753
+ /** `{label}` */
754
+ expand: Message;
755
+ /** Accessible name of a connection point. */
756
+ port: Message;
757
+ /** Shown on a collapsed group. `{count}` */
758
+ childCount: Message;
759
+ };
760
+ edge: {
761
+ /** `{source}`, `{target}` */
762
+ label: Message;
763
+ /** `{label}`, `{source}`, `{target}` */
764
+ labelWithText: Message;
765
+ roleDescription: Message;
766
+ /** Accessible name of the edge label editor. `{source}`, `{target}` */
767
+ editLabel: Message;
768
+ };
769
+ toolbar: {
770
+ label: Message;
771
+ addNode: Message;
772
+ palette: Message;
773
+ undo: Message;
774
+ redo: Message;
775
+ delete: Message;
776
+ duplicate: Message;
777
+ autoLayout: Message;
778
+ group: Message;
779
+ ungroup: Message;
780
+ zoomIn: Message;
781
+ zoomOut: Message;
782
+ fitView: Message;
783
+ showMiniMap: Message;
784
+ hideMiniMap: Message;
785
+ showGrid: Message;
786
+ hideGrid: Message;
787
+ controlsLabel: Message;
788
+ };
789
+ minimap: Message;
790
+ /** Spoken through a polite live region. */
791
+ announce: {
792
+ nodeAdded: Message;
793
+ nodeRemoved: Message;
794
+ itemsRemoved: Message;
795
+ connected: Message;
796
+ disconnected: Message;
797
+ moved: Message;
798
+ resized: Message;
799
+ renamed: Message;
800
+ selected: Message;
801
+ selectedMany: Message;
802
+ selectionCleared: Message;
803
+ undone: Message;
804
+ redone: Message;
805
+ nothingToUndo: Message;
806
+ nothingToRedo: Message;
807
+ layoutApplied: Message;
808
+ grouped: Message;
809
+ ungrouped: Message;
810
+ duplicated: Message;
811
+ copied: Message;
812
+ pasted: Message;
813
+ connectStarted: Message;
814
+ connectCancelled: Message;
815
+ locked: Message;
816
+ };
817
+ /** Why a connection was refused, by rejection code. */
818
+ connection: Record<ConnectionRejection, Message>;
819
+ outline: {
820
+ title: Message;
821
+ nodes: Message;
822
+ paths: Message;
823
+ /** Joins labels along a path: "A to B". */
824
+ connector: Message;
825
+ connectsTo: Message;
826
+ receivesFrom: Message;
827
+ noConnections: Message;
828
+ /** `{label}` */
829
+ goToNode: Message;
830
+ empty: Message;
831
+ /** `{status}` */
832
+ status: Message;
833
+ /** Shown before a group's children. `{label}` */
834
+ contains: Message;
835
+ /** Accessible name of the button that removes a connection. `{source}`, `{target}` */
836
+ disconnect: Message;
837
+ };
838
+ mermaid: {
839
+ loading: Message;
840
+ renderError: Message;
841
+ loadError: Message;
842
+ showSource: Message;
843
+ hideSource: Message;
844
+ sourceLabel: Message;
845
+ };
846
+ }
847
+ type DeepPartial<T> = {
848
+ [K in keyof T]?: T[K] extends Message ? T[K] : DeepPartial<T[K]>;
849
+ };
850
+ type DiagramMessagesOverride = DeepPartial<DiagramMessages>;
851
+ /** English strings. Copy this object, translate it, and pass it as `messages`. */
852
+ declare const defaultMessages: DiagramMessages;
853
+ /** Merges partial translations over the English defaults. Missing keys fall back to English. */
854
+ declare function resolveMessages(override?: DiagramMessagesOverride): DiagramMessages;
855
+
856
+ export { COLLAPSED_HEIGHT as C, EDGE_KINDS as E, GRID_SIZE as H, getReadingOrder as a$, NODE_KINDS as a0, NODE_STATUSES as a1, defaultPalette as aA, defineDiagram as aB, diagramToSvg as aC, diagramToSvgTree as aD, directionSides as aE, disconnect as aF, distributeNodes as aG, duplicateNodes as aH, escapeXml as aI, estimateTextWidth as aJ, exportDiagramJson as aK, exportDiagramSvg as aL, extractFragment as aM, findCycle as aN, findDirectionalNeighbor as aO, formatMessage as aP, getAbsoluteBounds as aQ, getAbsolutePosition as aR, getAbsolutePositions as aS, getAncestors as aT, getDescendants as aU, getDocumentBounds as aV, getEdgeGeometry as aW, getNeighbors as aX, getNodeBounds as aY, getNodeSize as aZ, getPortPlacements as a_, TEXT_METRICS as aj, addNode as am, alignNodes as an, applyLayout as ao, buildIndex as ap, canRedo as aq, canUndo as ar, checkConnection as as, computeEdgePath as at, connect as au, createDiagram as av, createGeometryContext as aw, createHistory as ax, createNode as ay, defaultMessages as az, getRoots as b0, groupNodes as b1, hasPath as b2, historyReducer as b3, importDiagram as b4, isContainerKind as b5, isHorizontal as b6, isSafeImageUrl as b7, layoutDiagram as b8, layoutedDiagram as b9, updateEdge as bA, updateNode as bB, validateDiagram as bC, wrapText as bD, mirrorDirection as ba, moveNodes as bb, nextEdgeId as bc, nextNodeId as bd, pasteFragment as be, portTypesCompatible as bf, rectCenter as bg, rectContainsPoint as bh, rectsIntersect as bi, removeEdges as bj, removeNode as bk, removeNodes as bl, resizeNode as bm, resolveConnectionRules as bn, resolveMessages as bo, resolvePalette as bp, serializeSvg as bq, setNodeParent as br, sideAnchor as bs, sideVector as bt, snapPoint as bu, snapValue as bv, topologicalOrder as bw, translateNodes as bx, ungroupNode as by, unionRects as bz, DEFAULT_NODE_SIZE as f, DIAGRAM_KINDS as g, DIAGRAM_VERSION as h, DiagramImportError as l, DiagramMutationError as s };
857
+ export type { MutationErrorCode as $, Alignment as A, EdgePathInput as B, DiagramDocument as D, ExportJsonOptions as F, FocusDirection as G, GeometryContext as I, GeometryOptions as J, GroupOptions as K, LayoutDirection as L, HistoryAction as M, NodeKind as N, HistoryOptions as O, HistoryState as P, ImportErrorCode as Q, ImportOptions as R, ImportResult as S, IssueCode as T, IssueSeverity as U, LayoutAlgorithm as V, LayoutOptions as W, LayoutResult as X, LayoutSpacing as Y, Message as Z, MessageValues as _, NodeStatus as a, NewEdge as a2, NewNode as a3, NodePatch as a4, PasteOptions as a5, PasteResult as a6, Point as a7, PortPlacement as a8, Position as a9, ReadingOrderOptions as aa, Rect as ab, RemoveOptions as ac, Side as ad, Size as ae, StatusPalette as af, SvgAttributes as ag, SvgElement as ah, SvgOptions as ai, ValidateOptions as ak, ValidationResult as al, ConnectOptions as b, ConnectionCheck as c, ConnectionRejection as d, ConnectionRules as e, DiagramConnection as i, DiagramEdge as j, DiagramFragment as k, DiagramIndex as m, DiagramIssue as n, DiagramKind as o, DiagramMessages as p, DiagramMessagesOverride as q, DiagramMigration as r, DiagramNode as t, DiagramPalette as u, DiagramPaletteOverride as v, EdgeGeometry as w, EdgeKind as x, EdgePatch as y, EdgePath as z };