@graphty/graphty-element 2.6.1 → 3.0.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 (163) hide show
  1. package/dist/ai.js +3 -3
  2. package/dist/catalog.js +53 -54
  3. package/dist/chunks/{AiManager-4iQpsJW1.js → AiManager-Bhh0rR_p.js} +706 -655
  4. package/dist/chunks/GraphSession-DPoTaT3b.js +21175 -0
  5. package/dist/chunks/{GraphtyLogger-B_O67a6c.js → GraphtyLogger-BtcBJQPL.js} +1 -1
  6. package/dist/chunks/{VoiceInputAdapter-Cc6mHXTI.js → VoiceInputAdapter-CdLsJ_nG.js} +1 -1
  7. package/dist/chunks/{XRPivotCameraController-BLa89LXn.js → XRPivotCameraController-CiLJ9Gz6.js} +2 -2
  8. package/dist/chunks/algorithms-qij74zEN.js +6811 -0
  9. package/dist/chunks/{capability-check-Am2zliFj.js → capability-check-BbgTejS3.js} +1 -1
  10. package/dist/chunks/definePalette-BYt2Llxs.js +1333 -0
  11. package/dist/chunks/fields-5uVC1Pll.js +4999 -0
  12. package/dist/chunks/{format-detection-BHwrAVzW.js → format-detection-FKaDMshR.js} +1 -1
  13. package/dist/chunks/{index-BkBLbvui.js → index-Cc_6D9hV.js} +6034 -7127
  14. package/dist/chunks/interpolation-Dk206AhZ.js +105 -0
  15. package/dist/chunks/paletteRegistry-De3CGdst.js +357 -0
  16. package/dist/chunks/parse-SVp77JbE.js +669 -0
  17. package/dist/chunks/{pluginRegistry-Bs8bEkz9.js → pluginRegistry-Ddfl6Mv2.js} +27 -24
  18. package/dist/chunks/{registry-BdGvyZou.js → registry-CgMqldp4.js} +1 -1
  19. package/dist/chunks/{DataSource-BL2UzPff.js → sources-BbAJCDIH.js} +279 -108
  20. package/dist/commands.d.ts +128 -19
  21. package/dist/commands.js +49 -1
  22. package/dist/custom-elements.json +1 -1
  23. package/dist/extend.d.ts +10 -2
  24. package/dist/extend.js +64 -57
  25. package/dist/graphty-catalog.json +6 -3
  26. package/dist/graphty.bundle.js +267177 -240648
  27. package/dist/graphty.js +84 -78
  28. package/dist/index.d.ts +4 -0
  29. package/dist/logging.js +2 -2
  30. package/dist/schema.js +70 -71
  31. package/dist/session.d.ts +5 -6
  32. package/dist/session.js +40 -86
  33. package/dist/src/Edge.d.ts +31 -67
  34. package/dist/src/Graph.d.ts +335 -77
  35. package/dist/src/Node.d.ts +36 -3
  36. package/dist/src/NodeBehavior.d.ts +28 -0
  37. package/dist/src/Styles.d.ts +15 -4
  38. package/dist/src/acceleration/AccelerationController.d.ts +8 -0
  39. package/dist/src/acceleration/narrow.d.ts +9 -1
  40. package/dist/src/acceleration/types.d.ts +10 -0
  41. package/dist/src/ai/AiController.d.ts +12 -0
  42. package/dist/src/ai/AiManager.d.ts +7 -0
  43. package/dist/src/ai/commands/AlgorithmCommands.d.ts +1 -1
  44. package/dist/src/ai/commands/LayoutCommands.d.ts +1 -1
  45. package/dist/src/ai/commands/StyleCommands.d.ts +1 -1
  46. package/dist/src/ai/commands/types.d.ts +20 -1
  47. package/dist/src/algorithms/Algorithm.d.ts +21 -4
  48. package/dist/src/algorithms/BFSAlgorithm.d.ts +0 -9
  49. package/dist/src/algorithms/BellmanFordAlgorithm.d.ts +0 -14
  50. package/dist/src/algorithms/DFSAlgorithm.d.ts +1 -1
  51. package/dist/src/algorithms/FloydWarshallAlgorithm.d.ts +5 -0
  52. package/dist/src/algorithms/GirvanNewmanAlgorithm.d.ts +5 -0
  53. package/dist/src/algorithms/LeidenAlgorithm.d.ts +5 -0
  54. package/dist/src/algorithms/PageRankAlgorithm.d.ts +1 -1
  55. package/dist/src/algorithms/StronglyConnectedComponentsAlgorithm.d.ts +1 -1
  56. package/dist/src/algorithms/metrics/fields.d.ts +23 -1
  57. package/dist/src/algorithms/utils/graphUtils.d.ts +13 -1
  58. package/dist/src/catalog/paletteRegistry.d.ts +4 -4
  59. package/dist/src/catalog/registry.d.ts +3 -2
  60. package/dist/src/catalog/types.d.ts +18 -1
  61. package/dist/src/config/GraphStyle.d.ts +1 -1
  62. package/dist/src/config/StyleTemplate.d.ts +2 -2
  63. package/dist/src/config/xr-config-schema.d.ts +4 -4
  64. package/dist/src/data/CSVDataSource.d.ts +77 -22
  65. package/dist/src/data/ErrorAggregator.d.ts +5 -0
  66. package/dist/src/data/GEXFDataSource.d.ts +12 -61
  67. package/dist/src/data/GraphMLDataSource.d.ts +3 -44
  68. package/dist/src/data/GraphStore.d.ts +322 -15
  69. package/dist/src/data/JsonDataSource.d.ts +43 -1
  70. package/dist/src/data/graph-io-import.d.ts +89 -0
  71. package/dist/src/data/graph-io-records.d.ts +64 -0
  72. package/dist/src/data/lane.d.ts +23 -0
  73. package/dist/src/data/positions.d.ts +13 -0
  74. package/dist/src/data/seedPosition.d.ts +16 -0
  75. package/dist/src/errors/GraphtyError.d.ts +3 -1
  76. package/dist/src/errors/codes.d.ts +23 -0
  77. package/dist/src/events.d.ts +12 -0
  78. package/dist/src/graphty-element.d.ts +149 -54
  79. package/dist/src/input/types.d.ts +2 -0
  80. package/dist/src/layout/D3GraphLayoutEngine.d.ts +17 -3
  81. package/dist/src/layout/FixedLayoutEngine.d.ts +20 -4
  82. package/dist/src/layout/KamadaKawaiLayoutEngine.d.ts +6 -0
  83. package/dist/src/layout/LayoutEngine.d.ts +214 -116
  84. package/dist/src/layout/NGraphLayoutEngine.d.ts +10 -3
  85. package/dist/src/layout/SimulationLayoutEngine.d.ts +8 -3
  86. package/dist/src/managers/AlgorithmManager.d.ts +24 -5
  87. package/dist/src/managers/DataManager.d.ts +258 -181
  88. package/dist/src/managers/EventManager.d.ts +5 -2
  89. package/dist/src/managers/GraphContext.d.ts +7 -0
  90. package/dist/src/managers/InputManager.d.ts +11 -0
  91. package/dist/src/managers/LayoutManager.d.ts +129 -50
  92. package/dist/src/managers/RenderManager.d.ts +14 -1
  93. package/dist/src/managers/UpdateManager.d.ts +20 -0
  94. package/dist/src/screenshot/ScreenshotCapture.d.ts +1 -1
  95. package/dist/src/session/GraphSession.d.ts +83 -6
  96. package/dist/src/session/commands/algo.d.ts +169 -0
  97. package/dist/src/session/commands/config.d.ts +45 -0
  98. package/dist/src/session/commands/data.d.ts +178 -0
  99. package/dist/src/session/commands/doors.d.ts +93 -0
  100. package/dist/src/session/commands/index.d.ts +20 -0
  101. package/dist/src/session/commands/layout.d.ts +104 -0
  102. package/dist/src/session/commands/positions.d.ts +30 -0
  103. package/dist/src/session/commands/sets.d.ts +113 -0
  104. package/dist/src/session/commands/style.d.ts +92 -0
  105. package/dist/src/session/commands/view.d.ts +57 -0
  106. package/dist/src/session/commands/visibility.d.ts +41 -0
  107. package/dist/src/session/data.d.ts +131 -4
  108. package/dist/src/session/index.d.ts +1 -1
  109. package/dist/src/session/planning.d.ts +25 -8
  110. package/dist/src/session/project/Dispatcher.d.ts +905 -0
  111. package/dist/src/session/project/History.d.ts +382 -0
  112. package/dist/src/session/project/arrangement.d.ts +247 -0
  113. package/dist/src/session/project/derive.d.ts +132 -0
  114. package/dist/src/session/project/digest.d.ts +33 -0
  115. package/dist/src/session/project/draft.d.ts +194 -0
  116. package/dist/src/session/project/graphOps.d.ts +304 -0
  117. package/dist/src/session/project/ingest.d.ts +364 -0
  118. package/dist/src/session/project/state.d.ts +145 -0
  119. package/dist/src/session/project/strict.d.ts +68 -0
  120. package/dist/src/session/results/RunResult.d.ts +48 -0
  121. package/dist/src/session/results/statistics.d.ts +20 -0
  122. package/dist/src/session/runs/Run.d.ts +80 -4
  123. package/dist/src/session/runs/RunsApi.d.ts +23 -6
  124. package/dist/src/session/runs/types.d.ts +25 -6
  125. package/dist/src/session/scope/ElementMask.d.ts +14 -0
  126. package/dist/src/session/scope/ScopeApi.d.ts +3 -22
  127. package/dist/src/session/scope/spaces.d.ts +29 -0
  128. package/dist/src/session/sealed.d.ts +22 -0
  129. package/dist/src/session/selection/SelectionApi.d.ts +14 -4
  130. package/dist/src/session/sets/SetsApi.d.ts +13 -5
  131. package/dist/src/session/sets/store.d.ts +54 -53
  132. package/dist/src/session/sets/types.d.ts +5 -2
  133. package/dist/src/session/styles/Layer.d.ts +5 -0
  134. package/dist/src/session/styles/StylesApi.d.ts +68 -17
  135. package/dist/src/session/styles/autoApply.d.ts +64 -53
  136. package/dist/src/session/styles/index.d.ts +3 -3
  137. package/dist/src/session/styles/predicate.d.ts +7 -0
  138. package/dist/src/session/styles/repaint.d.ts +16 -1
  139. package/dist/src/session/styles/sources.d.ts +1 -1
  140. package/dist/src/session/types.d.ts +625 -54
  141. package/dist/src/session/visibility/VisibilityApi.d.ts +38 -18
  142. package/dist/src/session/visibility/filter.d.ts +10 -0
  143. package/dist/src/simple/defineAlgorithm.d.ts +28 -0
  144. package/dist/src/simple/defineLayout.d.ts +35 -0
  145. package/dist/src/simple/defineLogDestination.d.ts +31 -0
  146. package/dist/src/simple/definePalette.d.ts +26 -0
  147. package/dist/src/simple/definition.d.ts +106 -0
  148. package/dist/src/simple/options.d.ts +33 -0
  149. package/dist/src/simple/source.d.ts +49 -0
  150. package/dist/src/simple/types.d.ts +366 -0
  151. package/dist/src/simple/view.d.ts +107 -0
  152. package/dist/webgpu.js +2 -2
  153. package/package.json +10 -12
  154. package/dist/chunks/GraphSession-BhuHSXIo.js +0 -12819
  155. package/dist/chunks/GraphStyle-Cwr55SAE.js +0 -65
  156. package/dist/chunks/algorithms-BJ6DQMOe.js +0 -3777
  157. package/dist/chunks/detect-fyuVnlCT.js +0 -88
  158. package/dist/chunks/interpolation-DY-PNpqX.js +0 -43
  159. package/dist/chunks/optionsFromZod-CKMYSwTz.js +0 -3636
  160. package/dist/chunks/paletteRegistry-BCFSwJGK.js +0 -1196
  161. package/dist/chunks/parse-BMTqt4SS.js +0 -3658
  162. package/dist/src/data/csv-variant-detection.d.ts +0 -29
  163. package/dist/src/data/ingest.d.ts +0 -104
@@ -45,5 +45,11 @@ export declare class KamadaKawaiLayout extends SimpleLayoutEngine {
45
45
  * reading: heavier means more strongly connected, means drawn closer together.
46
46
  */
47
47
  doLayout(): void;
48
+ /**
49
+ * The `dist` option as the matrix the layout reads, `n * n` distances row by row; a pair the
50
+ * record does not give is unreachable.
51
+ * @returns the matrix, or null for no option
52
+ */
53
+ private distances;
48
54
  }
49
55
  export {};
@@ -1,10 +1,13 @@
1
- import { type NodeMask } from "@graphty/graph-format";
1
+ import { type F32, type GraphSnapshot, type NodeMask } from "@graphty/graph-format";
2
+ import { type LayoutResult } from "@graphty/layout";
2
3
  import { z } from "zod/v4";
4
+ import { type RegisterOptions } from "../catalog/pluginRegistry";
3
5
  import type { AuthoredLayoutDescriptor } from "../catalog/types";
4
6
  import type { OptionsSchema } from "../config";
5
7
  import { ElementPositions } from "../data/positions";
6
8
  import type { Edge } from "../Edge";
7
- import type { Node, NodeIdType } from "../Node";
9
+ import type { Node } from "../Node";
10
+ import type { ReadonlyElementPositions } from "../session/types";
8
11
  export interface Position {
9
12
  x: number;
10
13
  y: number;
@@ -15,28 +18,6 @@ export interface EdgePosition {
15
18
  dst: Position;
16
19
  }
17
20
  type LayoutEngineClass = new (opts: object) => LayoutEngine;
18
- /**
19
- * The smallest weight either weighted layout is allowed to act on.
20
- *
21
- * A record may carry `weight: 0`, and both layout functions in `@graphty/layout` read a weight as
22
- * `getEdgeData(...) || 1`, which turns a deliberate zero into a FULL-strength edge -- the exact
23
- * opposite of what the author wrote, with nothing on screen to say so. Clamping here means zero
24
- * reads as "as close to nothing as the solver allows" in both engines: the weakest possible pull
25
- * in ForceAtlas2, the largest possible distance in Kamada-Kawai.
26
- */
27
- export declare const WEIGHT_EPSILON = 0.000001;
28
- /**
29
- * The key an ordered endpoint pair is filed under in a {@link LayoutEngine.pairWeights} map.
30
- *
31
- * It names a PAIR, not an edge: two parallel edges between the same two nodes share one key, and
32
- * that is deliberate -- see {@link LayoutEngine.pairWeights}. JSON is used rather than a separator
33
- * because a node id is a string or a number and may contain any character at all, so `a:b -> c`
34
- * and `a -> b:c` would collide under any punctuation, and `"1"` would collide with `1`.
35
- * @param source - the edge's source node id
36
- * @param target - the edge's target node id
37
- * @returns a key unique to that ordered pair
38
- */
39
- export declare function pairWeightKey(source: NodeIdType, target: NodeIdType): string;
40
21
  /**
41
22
  * What a layout engine class declares about itself, which is what the element and a picker read
42
23
  * without constructing one.
@@ -86,6 +67,37 @@ export interface LayoutEngineStatics {
86
67
  getZodOptionsSchema(): OptionsSchema;
87
68
  hasZodOptions(): boolean;
88
69
  }
70
+ /**
71
+ * The element's own reach into an engine's protected placement members: the hooks that apply the
72
+ * `positions` and `pins` slices, and the drag. No entry point exports it; a consumer places and
73
+ * pins nodes through `session.positions`.
74
+ */
75
+ export declare const layoutEngineInternals: {
76
+ /** See `LayoutEngine.setNodePosition`. */
77
+ setNodePosition(engine: LayoutEngine, n: Node, p: Position): void;
78
+ /** See `LayoutEngine.pin`. */
79
+ pin(engine: LayoutEngine, n: Node): void;
80
+ /** See `LayoutEngine.unpin`. */
81
+ unpin(engine: LayoutEngine, n: Node): void;
82
+ /** The engine's writable coordinate array; `LayoutEngine.nodePositions` is its read-only view. */
83
+ positions(engine: LayoutEngine): ElementPositions;
84
+ /** See `LayoutEngine.addNode`. */
85
+ addNode(engine: LayoutEngine, n: Node): void;
86
+ /** See `LayoutEngine.addEdge`. */
87
+ addEdge(engine: LayoutEngine, e: Edge): void;
88
+ /** See `LayoutEngine.addNodes`. */
89
+ addNodes(engine: LayoutEngine, nodes: Node[]): void;
90
+ /** See `LayoutEngine.addEdges`. */
91
+ addEdges(engine: LayoutEngine, edges: Edge[]): void;
92
+ /** See `LayoutEngine.removeNode`. */
93
+ removeNode(engine: LayoutEngine, n: Node): void;
94
+ /** See `LayoutEngine.removeEdge`. */
95
+ removeEdge(engine: LayoutEngine, e: Edge): void;
96
+ /** See `LayoutEngine.attachPositions`. */
97
+ attachPositions(engine: LayoutEngine, positions: ElementPositions): void;
98
+ /** See `LayoutEngine.edgeProblems`. */
99
+ edgeProblems(engine: LayoutEngine, drawn: ReadonlyMap<string, Edge>): string[];
100
+ };
89
101
  /**
90
102
  * Base class for all layout engines
91
103
  *
@@ -107,6 +119,15 @@ export interface LayoutEngineStatics {
107
119
  * is inexpressible and a frame allocates once per node. With the coordinates in one shared array,
108
120
  * a view reads them by index and allocates nothing, and a GPU layout can write into the same rows.
109
121
  */
122
+ /**
123
+ * The edges an engine holds against the edges drawn: each drawn edge held exactly once, and
124
+ * nothing held that is not drawn.
125
+ * @param held - What the engine holds.
126
+ * @param drawn - What the element draws.
127
+ * @returns One sentence per problem.
128
+ */
129
+ export declare function heldEdgeProblems(held: Iterable<Edge>, drawn: ReadonlyMap<string, Edge>): string[];
130
+ /** The base every layout engine extends: how the element adds, places, steps and removes. */
110
131
  export declare abstract class LayoutEngine {
111
132
  static type: string;
112
133
  static maxDimensions: number;
@@ -142,7 +163,7 @@ export declare abstract class LayoutEngine {
142
163
  * -- which is what happened to the element's own two force engines. The manager rebuilds from
143
164
  * the options it was given instead, and this is now the engine's own business.
144
165
  */
145
- config?: Record<string, unknown>;
166
+ protected config?: Record<string, unknown>;
146
167
  /**
147
168
  * NEW: Zod-based options schema for unified validation and UI metadata
148
169
  *
@@ -169,14 +190,21 @@ export declare abstract class LayoutEngine {
169
190
  /** How many rows {@link hold} covers; a row at or past it is newer than the hold, so held. */
170
191
  private holdRows;
171
192
  abstract init(): Promise<void>;
172
- abstract addNode(n: Node): void;
173
- abstract addEdge(e: Edge): void;
193
+ protected abstract addNode(n: Node): void;
194
+ protected abstract addEdge(e: Edge): void;
174
195
  abstract getNodePosition(n: Node): Position;
175
- abstract setNodePosition(n: Node, p: Position): void;
196
+ /**
197
+ * Place one node, as a drag or a restore does. Protected: the element reaches it through
198
+ * {@link layoutEngineInternals}, from the hooks that apply the `positions` and `pins` slices,
199
+ * so a caller cannot place a node without a step.
200
+ */
201
+ protected abstract setNodePosition(n: Node, p: Position): void;
176
202
  abstract getEdgePosition(e: Edge): EdgePosition;
177
203
  abstract step(): void;
178
- abstract pin(n: Node): void;
179
- abstract unpin(n: Node): void;
204
+ /** Hold a node where it is; protected for the reason {@link LayoutEngine.setNodePosition} is. */
205
+ protected abstract pin(n: Node): void;
206
+ /** Release a held node; protected for the reason {@link LayoutEngine.setNodePosition} is. */
207
+ protected abstract unpin(n: Node): void;
180
208
  abstract get nodes(): Iterable<Node>;
181
209
  abstract get edges(): Iterable<Edge>;
182
210
  abstract get isSettled(): boolean;
@@ -184,12 +212,12 @@ export declare abstract class LayoutEngine {
184
212
  * Add multiple nodes to the layout engine
185
213
  * @param nodes - Array of nodes to add
186
214
  */
187
- addNodes(nodes: Node[]): void;
215
+ protected addNodes(nodes: Node[]): void;
188
216
  /**
189
217
  * Add multiple edges to the layout engine
190
218
  * @param edges - Array of edges to add
191
219
  */
192
- addEdges(edges: Edge[]): void;
220
+ protected addEdges(edges: Edge[]): void;
193
221
  /**
194
222
  * Take a node out of the layout, before the element disposes the mesh that drew it.
195
223
  *
@@ -200,12 +228,12 @@ export declare abstract class LayoutEngine {
200
228
  * and everything that node references -- for as long as the engine lives.
201
229
  * @param _n - the node leaving the graph
202
230
  */
203
- removeNode(_n: Node): void;
231
+ protected removeNode(_n: Node): void;
204
232
  /**
205
233
  * The edge half of {@link LayoutEngine.removeNode}, with the same default and the same reason.
206
234
  * @param _e - the edge leaving the graph
207
235
  */
208
- removeEdge(_e: Edge): void;
236
+ protected removeEdge(_e: Edge): void;
209
237
  /**
210
238
  * Settle nodes that reached the graph after this layout was already running.
211
239
  *
@@ -222,18 +250,39 @@ export declare abstract class LayoutEngine {
222
250
  * Release whatever this engine holds. The element calls it when the reader switches layouts
223
251
  * and when the graph is torn down, and never uses the engine again afterwards.
224
252
  *
225
- * Declared with a do-nothing default for the same reason as {@link LayoutEngine.removeNode}:
253
+ * Declared with a do-nothing default for the same reason as `removeNode`:
226
254
  * it was duck-typed, undeclared and unimplemented by every engine here.
227
255
  */
228
256
  dispose(): void;
229
257
  /**
230
- * The array this engine publishes node coordinates into.
258
+ * Strict state: what is wrong with this engine's hold on the drawn edges, checked after every
259
+ * derivation pass. An engine that keeps a copy of the edges must hold every drawn edge once
260
+ * and nothing else, or a redraw asks it for a position it cannot give. Reads nothing lazily:
261
+ * the check must not change what it checks.
262
+ * @param drawn - The edges the element draws.
263
+ * @returns One sentence per problem; empty when there is none.
264
+ */
265
+ protected edgeProblems(drawn: ReadonlyMap<string, Edge>): string[];
266
+ /**
267
+ * The coordinates this engine publishes, read-only.
268
+ *
269
+ * Read-only because the array is the element's: a write here would move or pin a node with no
270
+ * undo step. The engine writes through `writeNodePosition`; a consumer
271
+ * places and pins nodes through `session.positions`.
272
+ * @returns the coordinates in use, read-only
273
+ */
274
+ get nodePositions(): ReadonlyElementPositions;
275
+ /** The read-only view {@link LayoutEngine.nodePositions} hands out; reads the array in use now. */
276
+ private readonly readonlyPositionArray;
277
+ /**
278
+ * The array this engine publishes node coordinates into, writable.
231
279
  *
232
280
  * Allocated on demand, so reading it is enough to make an engine that has never been handed an
233
- * element's array produce one of its own.
281
+ * element's array produce one of its own. The element reaches it through
282
+ * {@link layoutEngineInternals}.
234
283
  * @returns the position array in use
235
284
  */
236
- get nodePositions(): ElementPositions;
285
+ private get writablePositions();
237
286
  /**
238
287
  * Hand this engine the array it must publish into, and stop it adopting any other.
239
288
  *
@@ -242,7 +291,7 @@ export declare abstract class LayoutEngine {
242
291
  * one place and a re-freeze loses none of them.
243
292
  * @param positions - the element-owned array
244
293
  */
245
- attachPositions(positions: ElementPositions): void;
294
+ protected attachPositions(positions: ElementPositions): void;
246
295
  /**
247
296
  * Hold the nodes a scoped layout may not move, or release them all with null.
248
297
  *
@@ -277,6 +326,16 @@ export declare abstract class LayoutEngine {
277
326
  * an engine that already writes straight into the array overrides it to do nothing.
278
327
  */
279
328
  publishPositions(): void;
329
+ /**
330
+ * Take the coordinates in the position array as this engine's own, and stay at rest.
331
+ *
332
+ * Undo and redo write where the nodes were into the array and then call this, so the next
333
+ * drag, add or `setRunning(true)` starts from the restored arrangement instead of the one the
334
+ * engine was holding. The default hands every placed node back through
335
+ * `setNodePosition`, which is right for any engine that keeps coordinates of
336
+ * its own; an engine that can adopt the array in one pass overrides it.
337
+ */
338
+ loadArrangement(): void;
280
339
  /**
281
340
  * Read a node's published coordinates into an object the CALLER owns.
282
341
  *
@@ -335,51 +394,6 @@ export declare abstract class LayoutEngine {
335
394
  * @returns true when the row was written
336
395
  */
337
396
  protected writeNodePosition(n: Node, x: number, y: number, z: number, intent?: "layout" | "placement"): boolean;
338
- /**
339
- * The weight of every ordered endpoint pair this batch of edges covers, or null when the
340
- * graph's weights carry no information.
341
- *
342
- * WHY A PAIR AND NOT AN EDGE. `@graphty/layout`'s one weight channel is
343
- * `graph.getEdgeData(source, target, attr)`, which is asked by endpoint pair, and both layout
344
- * functions that read it write the answer into a matrix cell -- `A[i][j]` in ForceAtlas2,
345
- * `distances[s][t]` in Kamada-Kawai. There is no cell for a second edge between the same two
346
- * nodes, so parallel edges are SUMMED into one number rather than left to last-writer-wins,
347
- * where the order the file happened to list them in would decide the arrangement. Summing is
348
- * also what the element does when it simplifies a multigraph for an algorithm, so a graph's
349
- * weights mean the same thing to a layout and to a metric.
350
- *
351
- * READ ONCE PER LAYOUT COMPUTATION, not per frame and not per edge: the weights come from the
352
- * current snapshot's edge list, indexed by the same logical edge index `Edge.index` holds.
353
- *
354
- * NULL MEANS "DO NOT ATTACH A CALLBACK". graph-format stores an all-ones graph with no weight
355
- * column at all, and a graph whose every weight is 1 carries no information a layout could
356
- * arrange by -- so the caller leaves `getEdgeData` off the graph object entirely and the
357
- * arrangement is bit-identical to the one the same seed produced before weights existed.
358
- *
359
- * THIS IS A SLIGHTLY NARROWER QUESTION THAN `statistics().weighted`, deliberately. The status
360
- * chip asks whether the SNAPSHOT's weight column carries anything but ones; this asks it of
361
- * the edges this engine is actually about to arrange. They answer differently only when the
362
- * engine holds a strict subset of the graph's edges whose weights are all 1, and there the
363
- * narrower answer is the correct one: a layout cannot be moved by a weight on an edge it is
364
- * not laying out. A consumer who sees "weighted" on the status bar and an unmoved arrangement
365
- * is looking at that case.
366
- * @param edges - the edges this engine is about to lay out
367
- * @returns pair key (see {@link pairWeightKey}) to summed weight, or null
368
- */
369
- protected pairWeights(edges: readonly Edge[]): Map<string, number> | null;
370
- /**
371
- * Say once, per layout computation, how many pairs were clamped off zero.
372
- *
373
- * ONCE PER RUN AND NOT PER EDGE: a graph whose weights are all zero would otherwise produce
374
- * one line per edge, which buries every other message in the run it happened during. It is
375
- * reported at all because a clamp changes the picture -- a zero-weight edge is drawn as the
376
- * weakest connection the solver can express rather than as no connection -- and the record
377
- * that carried the zero is the reader's, not the element's, so they are the one who can fix
378
- * it.
379
- * @param layout - the layout name, for the message
380
- * @param weights - the pair weights about to be handed to the layout function
381
- */
382
- protected reportClampedWeights(layout: string, weights: ReadonlyMap<string, number>): void;
383
397
  /**
384
398
  * The array to use for this node: the one its own graph owns, unless a host attached one.
385
399
  *
@@ -411,12 +425,13 @@ export declare abstract class LayoutEngine {
411
425
  * are authored centrally in the layout catalogue where several engines may sit behind one
412
426
  * public name.
413
427
  * @param cls - The layout engine class.
428
+ * @param options - How to register it; `strict` refuses a different layout under a taken id.
414
429
  * @returns The same class, so a declaration can register itself in one expression.
415
430
  * @throws A `GraphtyError` with `E_BAD_COMMAND` when the class declares no `static type`, no
416
431
  * `static descriptor`, or a descriptor whose `id` disagrees with its `static type`; or with
417
432
  * `E_DUPLICATE_PLUGIN` when the name or the descriptor id is one the element itself ships.
418
433
  */
419
- static register<T extends LayoutEngineClass>(cls: T): T;
434
+ static register<T extends LayoutEngineClass>(cls: T, options?: RegisterOptions): T;
420
435
  /**
421
436
  * Get a layout engine instance by type
422
437
  * @param type - The layout engine type identifier
@@ -464,15 +479,41 @@ export declare const SimpleLayoutConfig: z.ZodObject<{
464
479
  }, z.core.$loose>;
465
480
  export type SimpleLayoutConfigType = z.infer<typeof SimpleLayoutConfig>;
466
481
  export type SimpleLayoutOpts = Partial<SimpleLayoutConfigType>;
482
+ /** The freeze a static engine is told about: the snapshot it replaced, the new one, and the renumbering. */
483
+ interface SnapshotReplacement {
484
+ readonly previous: GraphSnapshot | null;
485
+ readonly next: GraphSnapshot;
486
+ readonly report: {
487
+ readonly nodeRemap: Uint32Array | null;
488
+ };
489
+ }
467
490
  /**
468
- * Base class for simple static layout engines that compute positions synchronously
491
+ * Base class for static layout engines: an arrangement computed in one pass whenever the graph
492
+ * changes, rather than stepped frame by frame.
493
+ *
494
+ * TWO WAYS TO WRITE ONE. The element's own engines read the protected `graph` -- the
495
+ * element's undirected graph snapshot, whose row `i` is the node whose `index` is `i` -- and assign
496
+ * an index-based `@graphty/layout` result to the protected `result` in `doLayout`. An
497
+ * engine written before that existed fills the id-keyed {@link SimpleLayoutEngine.positions} record
498
+ * instead, and still works. Either way the base class scales the answer into the element's shared
499
+ * position array, never over a pinned row.
500
+ *
501
+ * AFTER AN ADD, EXISTING NODES STAY PUT. When the graph only grew -- a node added, with or without
502
+ * edges to the ones already drawn -- and no file is still loading, a re-run of an engine that
503
+ * reads `graph` places only the new nodes and leaves every existing one where it was. The
504
+ * existing coordinates are also offered to the layout as its start (`startPositions`), which is
505
+ * what lets Kamada-Kawai and ARF place a newcomer among its neighbours rather than from scratch.
469
506
  */
470
507
  export declare abstract class SimpleLayoutEngine extends LayoutEngine {
508
+ #private;
471
509
  static type: string;
472
510
  protected _nodes: Node[];
473
511
  protected _edges: Edge[];
474
512
  stale: boolean;
513
+ /** What an engine that does not read the protected `graph` computed, keyed by node id, in layout units. */
475
514
  positions: Record<string | number, number[]>;
515
+ /** What an engine that reads the protected `graph` computed: row `i` is row `i` of that graph. */
516
+ protected result: LayoutResult | null;
476
517
  scalingFactor: number;
477
518
  /**
478
519
  * Create a simple layout engine
@@ -501,14 +542,66 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
501
542
  * @param e - The edge to add
502
543
  */
503
544
  addEdge(e: Edge): void;
545
+ /**
546
+ * The graph this run arranges: the element's undirected snapshot, or, for an engine driven
547
+ * without an element, a graph built from the nodes and edges it was handed, in that order.
548
+ *
549
+ * Read it in `doLayout`. Reading it may freeze the element's graph, which is how a node added
550
+ * since the last run gets a row.
551
+ * @returns the undirected graph
552
+ */
553
+ protected get graph(): GraphSnapshot;
554
+ /**
555
+ * The coordinates to start this run from, in layout units, `dim` values per row of
556
+ * the protected `graph`: the element's current coordinates when the run follows an
557
+ * add, otherwise null. An unplaced row is NaN.
558
+ * @param dim - components per row
559
+ * @returns the start rows, or null
560
+ */
561
+ protected startPositions(dim: 2 | 3): F32 | null;
562
+ /**
563
+ * The row of a node named in an option, in the protected `graph`.
564
+ *
565
+ * A key of an options record is always a string, so a string that misses is tried again as the
566
+ * number it spells: `{ 1: [...] }` names the node whose id is the number 1.
567
+ * @param id - the node id, or a record key naming one
568
+ * @returns the row, or `INVALID_INDEX` when the graph has no such node
569
+ */
570
+ protected rowOfId(id: string | number): number;
571
+ /**
572
+ * {@link SimpleLayoutEngine.rowOfId} for an option that must name a node.
573
+ * @param id - the node id
574
+ * @param what - what the option names, for the message
575
+ * @returns the row
576
+ * @throws when the graph has no such node
577
+ */
578
+ protected requireRow(id: string | number, what: string): number;
579
+ /**
580
+ * Rows of an option that gives coordinates by node id, `dim` values per row of
581
+ * the protected `graph`; a node the record does not give is NaN.
582
+ * @param record - the coordinates by node id, in layout units, or null
583
+ * @param dim - components per row
584
+ * @returns the rows, or null for no record
585
+ */
586
+ protected rowsOfRecord(record: Record<string | number, number[]> | null, dim: 2 | 3): F32 | null;
587
+ /**
588
+ * Hear that the element's graph was frozen again.
589
+ *
590
+ * The layout is re-run at the next read. When the freeze only added to the graph this engine
591
+ * last arranged, and the data is not still loading, the re-run keeps every existing node where
592
+ * it is (see the class comment). A load is excluded because its chunks are one graph arriving,
593
+ * not a reader adding to a finished one: a circle whose first chunk was held would be drawn as
594
+ * two overlapping circles.
595
+ * @param change - the freeze
596
+ * @param loading - whether a load is still streaming records in
597
+ */
598
+ reload(change: SnapshotReplacement, loading: boolean): void;
504
599
  /**
505
600
  * Get the position of a node, computing layout if stale
506
601
  *
507
- * The coordinates come from the shared position array, which `SimpleLayoutEngine.refresh`
508
- * fills from `positions` as soon as the layout is recomputed. They are the same numbers the
509
- * record holds, rounded to the f32 the array stores -- so an arrangement never moves, but a
510
- * coordinate may differ in its last digit or two from the double the layout function returned.
511
- * A node with no row falls back to the record, which is every node in an engine driven by hand.
602
+ * The coordinates come from the shared position array, rounded to the f32 it stores. A node
603
+ * with no row there falls back to what the engine computed, which is every node in an engine
604
+ * driven by hand.
512
605
  * @param n - The node to get position for
513
606
  * @returns The node's position coordinates
514
607
  */
@@ -516,16 +609,14 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
516
609
  /**
517
610
  * Record where the reader has just put a node.
518
611
  *
519
- * A static layout recomputes every coordinate from scratch, so it has no per-node state a
520
- * placement could live in -- which is why this used to do nothing at all, and why a drag
521
- * under any of the fourteen static arrangements was discarded by the next `refresh()`. The
522
- * placement is written into the SHARED array instead, with `"placement"` intent so that it
523
- * lands even on a pinned row, and into the computed record so that a read which falls back to
524
- * the record (a node with no row of its own) answers the same.
612
+ * A static layout has no per-node state a placement could live in, so the placement is
613
+ * written into the SHARED array, with `"placement"` intent so that it lands even on a pinned
614
+ * row, and into what the engine computed, so that the next publish of the same answer does
615
+ * not put the node back.
525
616
  * @param n - the node that moved
526
617
  * @param p - where it moved to
527
618
  */
528
- setNodePosition(n: Node, p: Position): void;
619
+ protected setNodePosition(n: Node, p: Position): void;
529
620
  /**
530
621
  * Get the position of an edge based on its endpoints
531
622
  * @param e - The edge to get position for
@@ -547,12 +638,8 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
547
638
  *
548
639
  * WITHOUT THIS the engine holds the removed node -- and through it the node's Babylon mesh,
549
640
  * its data record and its endpoints -- for as long as the engine lives, and the frame loop
550
- * keeps walking it, so a node the reader deleted still draws at wherever it last was.
551
- *
552
- * The computed record is left alone and the layout is marked stale instead. Every layout
553
- * function here returns a WHOLE new record, which `doLayout` assigns over the old one, and
554
- * `refresh()` runs before any read -- so the removed node's entry is gone by the time anything
555
- * could read it, without this method having to reach into a keyed object by a computed name.
641
+ * keeps walking it, so a node the reader deleted still draws at wherever it last was. The
642
+ * layout is marked stale, so the next read recomputes it without the node.
556
643
  * @param n - the node leaving the graph
557
644
  */
558
645
  removeNode(n: Node): void;
@@ -566,16 +653,21 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
566
653
  *
567
654
  * A static layout has nothing of its own to hold still -- it recomputes every position from
568
655
  * scratch -- so the pin is kept by the element's position array instead, and
569
- * `writeNodePosition` refuses to move a pinned row. That is what makes a
570
- * pin mean something under all fourteen of these engines, none of which could hold one.
656
+ * `writeNodePosition` refuses to move a pinned row.
571
657
  */
572
- pin(): void;
658
+ protected pin(): void;
573
659
  /**
574
660
  * Unpin a node
575
661
  *
576
662
  * The element's position array holds the pin; see {@link SimpleLayoutEngine.pin}.
577
663
  */
578
- unpin(): void;
664
+ protected unpin(): void;
665
+ /**
666
+ * Keep the arrangement in the array instead of recomputing one: a static layout that was
667
+ * marked stale by the graph change an undo made would otherwise lay the graph out afresh at
668
+ * the next read and write over what was restored.
669
+ */
670
+ loadArrangement(): void;
579
671
  /**
580
672
  * Get all nodes in the layout
581
673
  * @returns Iterable of nodes
@@ -587,6 +679,7 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
587
679
  */
588
680
  get edges(): Iterable<Edge>;
589
681
  readonly isSettled = true;
682
+ /** Compute the layout: assign the protected `result` from `graph`, or fill `positions`. */
590
683
  abstract doLayout(): void;
591
684
  /**
592
685
  * Recompute the layout when it is stale, and publish what it produced.
@@ -597,23 +690,28 @@ export declare abstract class SimpleLayoutEngine extends LayoutEngine {
597
690
  */
598
691
  protected refresh(): void;
599
692
  /**
600
- * Write the computed record into the shared array, scaled to scene units.
693
+ * Write the computed answer into the shared array, scaled to scene units.
601
694
  *
602
- * A node the layout function returned nothing for is LEFT UNPLACED rather than published at
603
- * the origin: the two are indistinguishable once stored, and the origin is a place a reader
604
- * would draw at.
695
+ * A node the layout left unplaced (a NaN row, or no entry in `positions`) is LEFT UNPLACED
696
+ * rather than published at the origin: the two are indistinguishable once stored, and the
697
+ * origin is a place a reader would draw at.
605
698
  */
606
- private publishRecord;
699
+ private publishComputed;
607
700
  /**
608
- * A node's published row, or the computed record when it has no row of its own.
701
+ * A node's published row, or what the engine computed when it has no row of its own.
609
702
  *
610
703
  * The fallback is not a rare path: an engine driven directly -- by a test, or by a host that
611
704
  * keeps no graph -- has nodes whose index is `INVALID_INDEX`, and none of them is ever
612
705
  * published. Both branches produce the same arrangement; only the rounding differs.
613
706
  * @param n - the node, when the caller has one
614
- * @param id - the node's id, which is how the computed record is keyed
615
- * @returns a fresh coordinate triple
707
+ * @returns a fresh coordinate triple; the origin for a node nothing placed
616
708
  */
617
709
  private publishedOr;
618
710
  }
711
+ /**
712
+ * A dimension option as the index-based layouts take it.
713
+ * @param dim - the option, a number from a config
714
+ * @returns 3 for 3, otherwise 2
715
+ */
716
+ export declare function layoutDim(dim: number): 2 | 3;
619
717
  export {};
@@ -80,7 +80,7 @@ export declare class NGraphEngine extends LayoutEngine {
80
80
  * @param n - The node to set position for
81
81
  * @param newPos - The new position coordinates
82
82
  */
83
- setNodePosition(n: Node, newPos: Position): void;
83
+ protected setNodePosition(n: Node, newPos: Position): void;
84
84
  /**
85
85
  * Get the position of an edge based on its endpoint positions
86
86
  * @param e - The edge to get position for
@@ -101,12 +101,12 @@ export declare class NGraphEngine extends LayoutEngine {
101
101
  * Pin a node to its current position
102
102
  * @param n - The node to pin
103
103
  */
104
- pin(n: Node): void;
104
+ protected pin(n: Node): void;
105
105
  /**
106
106
  * Unpin a node to allow it to move freely
107
107
  * @param n - The node to unpin
108
108
  */
109
- unpin(n: Node): void;
109
+ protected unpin(n: Node): void;
110
110
  /**
111
111
  * Holds the nodes a scoped layout may not move, as pinned bodies. A node that is no longer
112
112
  * held is released unless the reader pinned it.
@@ -128,6 +128,13 @@ export declare class NGraphEngine extends LayoutEngine {
128
128
  * @param e - the edge leaving the graph
129
129
  */
130
130
  removeEdge(e: Edge): void;
131
+ /**
132
+ * Strict state: {@link LayoutEngine.edgeProblems}, and each edge its own link, which ngraph
133
+ * still holds.
134
+ * @param drawn - The edges the element draws.
135
+ * @returns One sentence per problem.
136
+ */
137
+ protected edgeProblems(drawn: ReadonlyMap<string, Edge>): string[];
131
138
  private _getMappedNode;
132
139
  private _getMappedEdge;
133
140
  }
@@ -347,6 +347,11 @@ export declare class SimulationLayoutEngine extends LayoutEngine {
347
347
  * ones have it, and a third party's may not -- so it is feature-tested rather than assumed.
348
348
  */
349
349
  reheat(): void;
350
+ /**
351
+ * Takes the element's array as the simulation's coordinates, through a reload of the graph it
352
+ * holds, and drops what batches already in flight read back.
353
+ */
354
+ loadArrangement(): void;
350
355
  /**
351
356
  * Holds a node still for the duration of a drag, without pinning it.
352
357
  *
@@ -371,12 +376,12 @@ export declare class SimulationLayoutEngine extends LayoutEngine {
371
376
  * Fixes a pinned node in the simulation. The store already holds the pin.
372
377
  * @param n - The node that was pinned.
373
378
  */
374
- pin(n: Node): void;
379
+ protected pin(n: Node): void;
375
380
  /**
376
381
  * Releases a node the simulation was holding fixed.
377
382
  * @param n - The node that was unpinned.
378
383
  */
379
- unpin(n: Node): void;
384
+ protected unpin(n: Node): void;
380
385
  /**
381
386
  * Holds the rows a scoped layout may not move, in the simulation's own fixed-node mask too.
382
387
  * @param mask - One bit per row to hold, or null to hold nothing.
@@ -388,7 +393,7 @@ export declare class SimulationLayoutEngine extends LayoutEngine {
388
393
  * @param n - The node that moved.
389
394
  * @param p - Where it moved to.
390
395
  */
391
- setNodePosition(n: Node, p: Position): void;
396
+ protected setNodePosition(n: Node, p: Position): void;
392
397
  /**
393
398
  * Reads a node's current coordinates, in scene units, out of the element's own position array.
394
399
  * @param n - The node to read.
@@ -1,5 +1,7 @@
1
1
  import type { BuiltInAlgorithmDescriptor } from "../catalog/algorithms";
2
2
  import type { Graph } from "../Graph";
3
+ import { type AlgoLegacyCommand } from "../session/commands/algo";
4
+ import type { UndoableContext } from "../session/project/Dispatcher";
3
5
  import type { RunExecutionContext, RunOutcome } from "../session/runs";
4
6
  import type { AlgorithmSpecificOptions } from "../utils/queue-migration";
5
7
  import type { EventManager } from "./EventManager";
@@ -51,14 +53,12 @@ export declare class AlgorithmManager implements Manager {
51
53
  */
52
54
  execute(context: RunExecutionContext, descriptor?: BuiltInAlgorithmDescriptor): Promise<RunOutcome>;
53
55
  /**
54
- * Run algorithms specified in the template configuration
55
- * Called during initialization if runAlgorithmsOnLoad is true
56
+ * Run algorithms specified in the template configuration, as one undoable step.
56
57
  * @param algorithms - Array of algorithm names in "namespace:type" format
57
58
  */
58
59
  runAlgorithmsFromTemplate(algorithms: string[]): Promise<void>;
59
60
  /**
60
- * Run a specific algorithm by its 1.10 registry address, publishing the result through side
61
- * effects and returning nothing.
61
+ * Run a specific algorithm by its 1.10 registry address, as one undoable step.
62
62
  *
63
63
  * This is the path a PLUGIN algorithm takes. A plugin registers itself under a
64
64
  * `namespace:type` and publishes no catalogue descriptor, so it cannot be started by key and
@@ -66,12 +66,31 @@ export declare class AlgorithmManager implements Manager {
66
66
  * to agree with. Everything this package ships goes through {@link AlgorithmManager.execute}
67
67
  * instead, reached from `graph.run` and `session.runs.start`.
68
68
  *
69
- * It goes when plugin algorithms publish descriptors of their own.
69
+ * It dispatches `algo.legacy`, which takes its turn on the queue, constructs the plugin with a
70
+ * facade of the graph and runs it: what it writes to node and edge records and
71
+ * `graphResults`, and every door it calls on the graph, are one step.
70
72
  * @param namespace - Algorithm namespace (e.g., "graphty")
71
73
  * @param type - Algorithm type (e.g., "dijkstra")
72
74
  * @param algorithmOptions - Optional algorithm-specific options (source, target, etc.)
73
75
  */
74
76
  runAlgorithm(namespace: string, type: string, algorithmOptions?: AlgorithmSpecificOptions): Promise<void>;
77
+ /**
78
+ * Carry out one `algo.legacy` the dispatcher has started: construct the plugin with a
79
+ * group-tagged facade of the graph, run it with the element's node and edge records read
80
+ * copy-on-write, and write what it wrote into the command's draft. Registered by the graph
81
+ * as its session's legacy service; nothing else calls it.
82
+ * @param command - The command.
83
+ * @param ctx - The command's context.
84
+ * @returns Settles once everything the plugin wrote is in the command's draft.
85
+ * @internal
86
+ */
87
+ runLegacy(command: AlgoLegacyCommand, ctx: UndoableContext): Promise<void>;
88
+ /**
89
+ * Dispatch one `algo.legacy`, announcing a failure on the graph's error channel.
90
+ * @param command - The command.
91
+ * @param dispatch - Where it goes.
92
+ */
93
+ private dispatchLegacy;
75
94
  /**
76
95
  * Check if an algorithm exists
77
96
  * @param namespace - Algorithm namespace