@graphty/graphty-element 2.6.2 → 3.0.1

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
@@ -1,13 +1,14 @@
1
1
  /**
2
- * @file The kept-set slice: one keyed map of frozen records, written only through `put` and
3
- * `delete` inside a write group (design/sets/sets-design.md sections 3.1, 12.4, 13).
2
+ * @file The kept sets: the `sets` slice of project state, one keyed map of frozen records written
3
+ * only by the `set.*` commands through the session's dispatcher, and the state kept beside it
4
+ * (design/sets/sets-design.md sections 3.1, 12.4, 13; design/sets/undo-integration.md section 1).
4
5
  *
5
6
  * Beside the slice, never in it:
6
7
  *
7
8
  * - the ISSUED-ID REGISTER, every id ever minted. Monotonic: appended when a group commits,
8
9
  * never written by `put` or `delete`, never rewound. Minting skips it, the live ids and the ids
9
- * pending in the open group, so an id is never issued twice even when a group creates, removes
10
- * and re-creates one name.
10
+ * minted and not yet sealed, so an id is never issued twice even when a transaction creates,
11
+ * removes and re-creates one name. Undo, redo and a rollback never rewind it.
11
12
  * - the TOMBSTONES, `{ id, name, record? }` for each removed id, rewritten at every commit that
12
13
  * removes it. A tombstone is authoritative only while its id is absent from the slice. Its
13
14
  * record is kept while anything still names the id -- a reference to a removed set resolves
@@ -21,13 +22,14 @@
21
22
  * - the ORDER high-water mark: a new set's order is one past the highest order the store has
22
23
  * held, so a restored record can never share its order with a set created after it was removed.
23
24
  *
24
- * A committed group tells its listeners one {@link SetChange} per touched key, from the key's
25
- * before and after values; a group that throws is rolled back and tells nobody.
25
+ * Each change of the slice the dispatcher publishes -- a step recorded, undone or redone --
26
+ * tells the listeners one {@link SetChange} per changed key, from the key's before and after
27
+ * values; a rollback tells nobody.
26
28
  *
27
- * Only `session/sets/` may import this module (a static test enforces it): nothing else writes
28
- * set state.
29
+ * Only `session/sets/` may import this module (a static test enforces it).
29
30
  */
30
31
  import type { SetId } from "../../catalog/types";
32
+ import { Dispatcher } from "../project/Dispatcher";
31
33
  import { type RecordView } from "./prepare";
32
34
  import type { EdgeSeeds } from "./resolve";
33
35
  import type { ElementSet, SetChange } from "./types";
@@ -52,17 +54,34 @@ interface LogicalSets {
52
54
  /** Removed ids, oldest first, with their last name and, while anything named them, record. */
53
55
  readonly tombstones: readonly Tombstone[];
54
56
  }
55
- /** The kept-set slice and the state beside it. */
57
+ /** The kept sets and the state beside them. */
56
58
  export declare class SetsStore implements RecordView {
57
- private readonly records;
59
+ /** The dispatcher whose `sets` slice holds the records and whose commands write them. */
60
+ readonly dispatcher: Dispatcher;
58
61
  private readonly issued;
62
+ /** Ids minted for a write not yet sealed; dropped once no group is open. */
63
+ private readonly pending;
59
64
  private readonly tombstones;
60
65
  private readonly listeners;
61
66
  private readonly commitListeners;
62
67
  private readonly seeds;
63
68
  private highestOrder;
64
- private group;
65
69
  private listed;
70
+ /** The records as the listeners were last told of them. */
71
+ private told;
72
+ /** The history position the listeners were last told at, to tell a restore's direction. */
73
+ private position;
74
+ /**
75
+ * Keep sets in a dispatcher's `sets` slice.
76
+ * @param dispatcher - The session's dispatcher; one of its own, over the set ops alone, when
77
+ * absent.
78
+ */
79
+ constructor(dispatcher?: Dispatcher);
80
+ /**
81
+ * The slice, live.
82
+ * @returns The records by id.
83
+ */
84
+ private get records();
66
85
  /**
67
86
  * One live record.
68
87
  * @param id - Its id.
@@ -75,12 +94,12 @@ export declare class SetsStore implements RecordView {
75
94
  */
76
95
  values(): Iterable<ElementSet>;
77
96
  /**
78
- * Every live record by order, ties by id: the same frozen array until a write.
97
+ * Every live record by order, ties by id: the same frozen array until the slice is written.
79
98
  * @returns The records.
80
99
  */
81
100
  list(): readonly ElementSet[];
82
101
  /**
83
- * Every id ever issued and committed.
102
+ * Every id ever issued and sealed.
84
103
  * @returns The register.
85
104
  */
86
105
  register(): ReadonlySet<SetId>;
@@ -111,9 +130,9 @@ export declare class SetsStore implements RecordView {
111
130
  forget(named: (id: SetId) => boolean): void;
112
131
  /**
113
132
  * Mint an id for a name: `set_<slug>`, then `_2`, `_3` and on, skipping the register, the
114
- * live ids and the ids already minted in the open group. Only inside a write group.
133
+ * live ids and the ids minted for writes not yet sealed.
115
134
  * @param name - The trimmed name.
116
- * @returns The id, pending until the group commits.
135
+ * @returns The id, issued once the step that writes it is sealed.
117
136
  */
118
137
  mint(name: string): SetId;
119
138
  /**
@@ -122,75 +141,57 @@ export declare class SetsStore implements RecordView {
122
141
  */
123
142
  nextOrder(): number;
124
143
  /**
125
- * Write one record. Only inside a write group.
126
- * @param record - A frozen record from `prepare`, or restored.
144
+ * Note a record a set op is about to write: its order is never given out again. Called by the
145
+ * set service, never rewound.
146
+ * @param record - The record.
127
147
  */
128
- put(record: ElementSet): void;
148
+ written(record: ElementSet): void;
129
149
  /**
130
- * Remove one record. Only inside a write group.
131
- * @param id - Its id.
150
+ * Keep an id in the register: the step that wrote it has been sealed.
151
+ * @param id - The id.
132
152
  */
133
- delete(id: SetId): void;
153
+ issue(id: SetId): void;
134
154
  /**
135
- * Run one write group: every `mint`, `put` and `delete` inside commits together, or, when
136
- * `write` throws, none of them does. A nested call joins the open group as a savepoint: when
137
- * it throws, its own writes and mints are undone and the group goes on.
138
- * @param write - The writes.
139
- * @param cause - What the listeners are told caused it, for an outermost group.
140
- * @returns What `write` returned.
155
+ * Tombstone a record about to be removed, newest last, so `sets.restore` can bring it back.
156
+ * @param record - The record.
141
157
  */
142
- transact<T>(write: () => T, cause?: SetChange["cause"]): T;
158
+ bury(record: ElementSet): void;
143
159
  /**
144
160
  * The slice as stored. Internal: the one serialiser a project file or an undo slice uses.
145
161
  * @returns The records, the register and the tombstones.
146
162
  */
147
163
  toLogicalRecords(): LogicalSets;
148
164
  /**
149
- * Load a stored slice into this empty store: every record validated in load mode and `put`,
150
- * the register and the tombstones restored. One write group, told as `load`. Internal.
165
+ * Load a stored slice into this empty store, as the baseline: every record validated in load
166
+ * mode, the register and the tombstones restored, and the listeners told once, as `load`.
167
+ * Internal.
151
168
  * @param stored - What {@link toLogicalRecords} returned, after any JSON round trip.
152
169
  *
153
170
  * A record's `createdFrom` of a kind this version does not know is kept as given and written
154
171
  * back unchanged, as an unknown definition kind is (design 12.5). A tombstone whose id is also
155
172
  * a live record is dropped: a tombstone speaks only for an absent id.
156
173
  * @throws `E_BAD_COMMAND` for a malformed slice, record or tombstone, or two records with one
157
- * id; an Error for a non-empty store.
174
+ * id; an Error for a non-empty store, or once the history has recorded a step.
158
175
  */
159
176
  loadLogicalRecords(stored: unknown): void;
160
177
  /**
161
- * Listen to committed changes: one call per touched key per group.
178
+ * Listen to committed changes: one call per changed key per change of the slice.
162
179
  * @param listener - The listener.
163
180
  * @returns A function that stops listening.
164
181
  */
165
182
  onChange(listener: (change: SetChange) => void): () => void;
166
183
  /**
167
- * Hear each committed group whole, before any per-key listener: the change notification
184
+ * Hear each change of the slice whole, before any per-key listener: the change notification
168
185
  * (`./notify`) re-resolves live sets here, ahead of `set:changed`.
169
186
  * @param listener - The listener, handed every change of the group.
170
187
  * @returns A function that stops listening.
171
188
  */
172
189
  onCommit(listener: (changes: readonly SetChange[]) => void): () => void;
173
190
  /**
174
- * The open group.
175
- * @param verb - What needs it, for the message.
176
- * @returns The group.
177
- * @throws An Error outside a write group.
178
- */
179
- private requireGroup;
180
- /**
181
- * Remember a key's value before the group first touched it.
182
- * @param id - The key.
183
- */
184
- private touch;
185
- /**
186
- * Commit a group: register its ids, tombstone what it removed, tell the listeners.
187
- * @param group - The group.
188
- */
189
- private commit;
190
- /**
191
- * Tombstone a removed record, newest last.
192
- * @param record - The record removed.
191
+ * Tell the listeners what changed since they were last told: the diff of the slice.
192
+ * @param cause - What changed it. A rollback puts back what nobody was told of, so it tells
193
+ * nobody.
193
194
  */
194
- private bury;
195
+ private tell;
195
196
  }
196
197
  export {};
@@ -39,8 +39,11 @@ export interface SetChange {
39
39
  readonly fields: readonly ("name" | "definition" | "order")[];
40
40
  /** The frozen record after the change; null after removal. */
41
41
  readonly set: ElementSet | null;
42
- /** What caused it. OPEN UNION: `command`, or `load` for a stored slice; undo and redo are added later. */
43
- readonly cause: "command" | "load";
42
+ /**
43
+ * What caused it. OPEN UNION: `command`, `load` for a stored slice, or `undo` and `redo` for a
44
+ * history call (a restore across several steps is told as the direction it moved).
45
+ */
46
+ readonly cause: "command" | "load" | "undo" | "redo";
44
47
  }
45
48
  /**
46
49
  * Whether a set still means what it meant, derived on read from what its definition reads and the
@@ -221,6 +221,11 @@ export interface CompiledLayer {
221
221
  readonly layer: Layer;
222
222
  /** Its selector, reduced to a predicate and the columns that predicate reads. */
223
223
  readonly selector: CompiledSelector;
224
+ /**
225
+ * Every path the layer reads, its selector's and its bindings', without repeats: what a
226
+ * change to a record's attributes has to name for the layer to paint anything differently.
227
+ */
228
+ readonly reads: readonly Path[];
224
229
  }
225
230
  /** Which edit asked for a repaint. */
226
231
  export type RepaintReason = "add" | "update" | "remove" | "move" | "sweep";
@@ -17,10 +17,10 @@
17
17
  * READING IS SYNCHRONOUS, WRITING IS A COMMAND. `list`, `get`, `validate`, `legend`, `explain`
18
18
  * and `toDocument` answer from what the session already holds and cost nothing. `add`, `update`,
19
19
  * `remove`, `move`, `removeBySource`, `encode`, `highlight`, `applyTemplate` and
20
- * `resolveToStatic` VALIDATE AND REPAINT, and a repaint is a pass over the elements a layer
21
- * matches -- so they are commands, not properties, and each returns a `Run`. That is what lets a
22
- * layer edit on a large graph report progress, take an `AbortSignal`, and be fired from a click
23
- * handler and forgotten without an unhandled rejection. Awaiting one gives the layer.
20
+ * `resolveToStatic` each dispatch a command (`style.patch`, `style.encode`, `style.template`),
21
+ * which is one undoable step in the session's history, and each returns a `Run` that can be
22
+ * fired from a click handler and forgotten without an unhandled rejection. Awaiting one gives the
23
+ * layer.
24
24
  *
25
25
  * ONE ANALYSIS LAYER PER RUN AND CHANNEL. `encode()` is the one path an analysis layer takes, and
26
26
  * it REPLACES the layer already painting that channel from that run rather than stacking a second
@@ -40,11 +40,20 @@
40
40
  * window. The door for "is this valid" before anything is committed is {@link StylesApi.validate},
41
41
  * which is synchronous, writes nothing, and reports every problem at once.
42
42
  *
43
- * THE MODEL MOVES ONLY AFTER THE PAINT SUCCEEDS. Each verb computes the stack it WOULD produce,
44
- * hands it to the repaint, and commits it only when the repaint resolves. A cancelled or failed
45
- * edit therefore leaves the list exactly as it was, rather than leaving the layer list saying one
46
- * thing and the screen showing another. The visible consequence is worth stating plainly: `add()`
47
- * followed immediately by `list()` does not show the new layer -- `await add()` does.
43
+ * THE STACK MOVES AT ONCE, AND THE PICTURE FOLLOWS IT. The stack is project state: the command
44
+ * writes the new stack when it is dispatched, so `add()` followed immediately by `list()` shows
45
+ * the new layer. The repaint then runs on the session's derivation lane, from the stack the
46
+ * picture shows to the stack state holds, whatever moved it -- an edit, an undo, a redo -- so
47
+ * restoring a stack restores the picture. Several edits made faster than a repaint are drawn by
48
+ * one pass. What a verb's `Run` means follows from that:
49
+ *
50
+ * - it settles once the pass that repaints the edit has run, and rejects when that repaint fails
51
+ * (the edit stays recorded, and undo takes it back);
52
+ * - a signal already aborted when the verb is called refuses the edit, and nothing is written;
53
+ * - `cancel()`, or an abort after the call, does NOT take the edit back: it has been recorded,
54
+ * and may have merged into a larger step (a colour picker's drag is one step), so undo is the
55
+ * way back. The run then resolves with the edit applied;
56
+ * - it is never listed in `history.pending` or in `runs`.
48
57
  *
49
58
  * AN ELEMENT-OWNED LAYER IS NOT THE CONSUMER'S. The base and selection layers are seeded at
50
59
  * construction with `source.by === "element"`, which makes them {@link Layer.locked}. Removing,
@@ -53,15 +62,18 @@
53
62
  * own comment admits breaks for a reader who calls their own layer "default".
54
63
  *
55
64
  * WHERE THE REPAINT PLUGS IN. `sources.repaint` is the seam, and nothing in this module
56
- * implements it: see {@link LayerRepaint} in `./Layer`. A session with no renderer hands none in
57
- * and paints nothing, which is not a degraded mode -- the stack is the session's and the paint is
58
- * the renderer's.
65
+ * implements it: see {@link LayerRepaint} in `./Layer`. This module registers the `styles` hook
66
+ * of the derivation lane, which hands the repaint the difference between two stacks
67
+ * (`stackChange` in `./repaint`). A session with no renderer hands none in and paints nothing,
68
+ * which is not a degraded mode -- the stack is the session's and the paint is the renderer's.
59
69
  *
60
70
  * Nothing here reaches Babylon.js, Lit or the DOM.
61
71
  */
62
72
  import type { Channel, EdgeId, LayerId, LayerSource, LayerSpec, NodeId, Path, Scope, StaticStyle, StyleDocument } from "../../catalog/types";
73
+ import { Dispatcher } from "../project/Dispatcher";
63
74
  import { type RunRef } from "../results/types";
64
75
  import { type EngineVersions, type ResolvedScope, type Run, type RunOptions, type RunQueue } from "../runs";
76
+ import type { HistoryCause } from "../types";
65
77
  import type { PreparedBinding } from "./encoding";
66
78
  import { type EncodingSource, type EncodingSpec } from "./EncodingSpec";
67
79
  import { type ExplainTarget, type StyleExplanation, type UnboundLayer } from "./explain";
@@ -343,6 +355,31 @@ export interface StylesApi {
343
355
  * @returns The document, bottom first.
344
356
  */
345
357
  toDocument(): StyleDocument;
358
+ /**
359
+ * Choose the palette a colour binding uses when it names none, one per palette kind: a
360
+ * binding on groups takes the categorical default, one on amounts the sequential default, and
361
+ * one with a `midpoint` the diverging default. A kind left out keeps its current default.
362
+ *
363
+ * RESOLVED WHEN A LAYER IS WRITTEN: a binding that names no palette records the default's id,
364
+ * so a saved document always names a concrete palette. The call therefore belongs before the
365
+ * layers are added. A later call leaves the layers that took the previous default as they are
366
+ * and writes a warning naming them; with `reapply: true` it re-resolves those layers instead.
367
+ * A layer that names its palette is never touched.
368
+ * @param palettes - The palette id per kind. Each must name a palette of that kind.
369
+ * @param options - How a late call treats the layers already written.
370
+ * @param options.reapply - True re-resolves the layers that took the previous default.
371
+ * @throws `E_UNKNOWN_PALETTE` for an id no palette answers to, `E_BAD_COMMAND` for a palette
372
+ * of the wrong kind or a slot that is not a palette kind.
373
+ */
374
+ setDefaultPalettes(palettes: DefaultPalettes, options?: {
375
+ readonly reapply?: boolean;
376
+ }): void;
377
+ }
378
+ /** The palette a colour binding naming none uses, per palette kind. */
379
+ export interface DefaultPalettes {
380
+ readonly categorical?: string;
381
+ readonly sequential?: string;
382
+ readonly diverging?: string;
346
383
  }
347
384
  /**
348
385
  * The style stack as the session holds it: the consumer surface, plus the compiled form.
@@ -380,7 +417,11 @@ export interface StyleChange {
380
417
  readonly reason: RepaintReason;
381
418
  /** The layers it touched, bottom first. */
382
419
  readonly layers: readonly LayerId[];
383
- /** How much was repainted, or null when no renderer is bound to this session. */
420
+ /**
421
+ * How much the pass that drew this change repainted, or null when no renderer is bound to
422
+ * this session. When one pass covers several edits (a colour picker's drag, a held undo),
423
+ * each edit's change carries that pass's whole report.
424
+ */
384
425
  readonly painted: RepaintReport | null;
385
426
  /**
386
427
  * The paths the changed layers read that nothing in this session answers.
@@ -390,6 +431,11 @@ export interface StyleChange {
390
431
  * paints nothing instead of showing a confident empty screen.
391
432
  */
392
433
  readonly unresolvedPaths: readonly Path[];
434
+ /**
435
+ * What moved the stack: an edit (`"command"`), or an undo, a redo, a restore or a rollback,
436
+ * which each publish one change per step they passed.
437
+ */
438
+ readonly cause: HistoryCause;
393
439
  }
394
440
  /** Everything the style stack is built from. */
395
441
  export interface StylesSources {
@@ -476,11 +522,16 @@ export interface StylesSources {
476
522
  */
477
523
  readonly repaint?: LayerRepaint;
478
524
  /**
479
- * The queue an edit takes its turn in. Absent builds a sequential one of its own, which is
480
- * right for a headless session and wrong for a rendered graph -- a rendered graph hands in
481
- * the element's own operation queue so a repaint does not interleave with a load.
525
+ * The queue the session's runs take their turn in, which `settled()` waits on because a run
526
+ * paints its suggested layers when it finishes. An edit does not take a turn in it: it is
527
+ * written when it is dispatched.
482
528
  */
483
529
  readonly queue?: RunQueue;
530
+ /**
531
+ * The one path every change to project state takes, and the history it records. The stack
532
+ * lives in its `styles` slice. Absent, the stack gets a dispatcher of its own.
533
+ */
534
+ readonly dispatcher?: Dispatcher;
484
535
  /**
485
536
  * Resolve a scope specification, so an edit can record what it looked at.
486
537
  *
@@ -525,7 +576,7 @@ export declare const DEFAULT_HIGHLIGHT: {
525
576
  /**
526
577
  * Build the style stack one session holds.
527
578
  * @param sources - What a selector compiles against, the element's own layers, the scales, the
528
- * repaint seam, the queue and the change hook.
579
+ * repaint seam, the dispatcher and the change hook.
529
580
  * @returns The stack, including the compiled form a renderer reads.
530
581
  * @throws A `GraphtyError` with code `E_INTERNAL` when one of the element's own layers is
531
582
  * malformed, which is a bug in the element rather than in the call.
@@ -10,7 +10,9 @@
10
10
  * THE RULES, ALL SIX OF THEM:
11
11
  *
12
12
  * - **A run paints on its FIRST completion, and never again.** A re-run keeps its id, so it keeps
13
- * its layers too -- repainting would stack a second copy on the one already bound to it.
13
+ * its layers too -- repainting would stack a second copy on the one already bound to it. Whether
14
+ * a run has had that moment is kept on its entry in the `runs` slice (`RunEntry.painted`), so an
15
+ * undo that takes the run away takes the moment with it, and a redo brings both back.
14
16
  * - **A layer somebody wrote by hand wins.** If an authored layer already drives the channel a
15
17
  * suggestion would paint, the suggestion is dropped rather than painted over the decision. The
16
18
  * element's own base layers are not authored and do not suppress anything, or nothing would ever
@@ -21,53 +23,40 @@
21
23
  * the one that would have ended up on top -- so the picture is the same as running the six by
22
24
  * hand, without the five dead layers.
23
25
  * - **An explicit `encode()` replaces the derived layer rather than stacking on it.** That rule is
24
- * `styles.encode()`'s own, which is exactly why this policy applies suggestions THROUGH the same
25
- * verb a consumer calls instead of adding layers by another door: a stranger who runs an
26
- * algorithm and then colours by it gets one layer and one legend block, in the place the derived
27
- * layer already had.
26
+ * `styles.encode()`'s own, which is exactly why a suggestion is applied as the same command a
27
+ * consumer's call dispatches (see {@link suggestionCommand}) instead of adding layers by another
28
+ * door: a stranger who runs an algorithm and then colours by it gets one layer and one legend
29
+ * block, in the place the derived layer already had.
28
30
  * - **A highlight is exclusive**, likewise enforced by `styles.highlight()`: a second route or
29
31
  * chosen set replaces the first.
30
32
  * - **`{ style: false }` opts out**, and a run that failed, was cancelled or has nothing per
31
33
  * element to paint suggests nothing in the first place.
32
34
  *
35
+ * WHERE THE LAYERS GO. This policy only decides; the runs API plans what it decides into the run's
36
+ * own step, in the synchronous tail that records the run, so the run, its result and its layers are
37
+ * one step and one undo (design/undo/undo-design.md section 4.7). A batch's decisions are held and
38
+ * planned into the batch's step when it is released.
39
+ *
33
40
  * WHAT THIS DOES NOT DO. It never removes a layer, never disables one, and never paints an element
34
41
  * a run measured nothing about: what a suggestion becomes is decided by {@link suggestStyles} and
35
- * applied by the two verbs, both of which scope the layer to the elements carrying that run's
36
- * value. Dimming the rest is a reader's choice and is not made here.
42
+ * applied as an encoding or a highlight, both of which scope the layer to the elements carrying
43
+ * that run's value. Dimming the rest is a reader's choice and is not made here.
37
44
  *
38
45
  * Nothing here reaches Babylon.js, Lit or the DOM.
39
46
  */
40
47
  import type { RunId } from "../../catalog/types";
48
+ import type { StyleCommand } from "../commands/style";
41
49
  import type { RunStatus, RunStyle } from "../runs/types";
42
- import type { EncodingRun, EncodingSpec } from "./EncodingSpec";
50
+ import { type StyleSuggestion } from "./derive";
51
+ import type { EncodingRun } from "./EncodingSpec";
43
52
  import type { Layer } from "./Layer";
44
- import type { HighlightSpec } from "./StylesApi";
45
- /**
46
- * The stack, as the policy needs it: what is in it, and the two verbs that bind a run to it.
47
- *
48
- * Narrow on purpose. A policy that held the whole styles API could add, move and remove layers,
49
- * and the reason it applies a suggestion through `encode()` rather than through `add()` is that
50
- * `encode()` carries the replacement rule and the generated selector. Taking only the two verbs is
51
- * what makes going around them impossible rather than merely discouraged.
52
- */
53
+ /** The stack, as the policy reads it: what is in it, so an authored layer can win. */
53
54
  export interface AutoApplyStyles {
54
55
  /**
55
56
  * Every layer in the stack, bottom first.
56
57
  * @returns The layers.
57
58
  */
58
59
  list(): readonly Layer[];
59
- /**
60
- * Paint a run's measurement onto a channel.
61
- * @param spec - The run, the channel and the taste.
62
- * @returns A run that resolves with the layer.
63
- */
64
- encode(spec: EncodingSpec): PromiseLike<unknown>;
65
- /**
66
- * Paint the elements a run chose.
67
- * @param spec - The run, the field that says which elements it chose, and what they look like.
68
- * @returns A run that resolves with the layers it added.
69
- */
70
- highlight(spec: HighlightSpec): PromiseLike<unknown>;
71
60
  }
72
61
  /**
73
62
  * What the policy needs to know about a finished run: what it suggests, plus whether to listen.
@@ -94,43 +83,65 @@ export interface AutoApplySources {
94
83
  /**
95
84
  * Called when applying a suggestion was refused.
96
85
  *
97
- * Optional, and the reason it exists at all is that the defect this whole system replaces was
98
- * silent: a style that failed to apply left the picture looking like an answer. A host that
99
- * wires this can say so; a host that does not still never has a refusal reach a consumer's
100
- * click handler, because applying is fire-and-forget.
86
+ * The defect this whole system replaces was silent: a style that failed to apply left the
87
+ * picture looking like an answer. The run is still recorded without the refused layer, and
88
+ * the refusal arrives here instead of at a caller who did not ask for the layer.
101
89
  * @param runId - The run whose suggestion was refused.
102
90
  * @param error - Why.
103
91
  */
104
92
  readonly onProblem?: (runId: RunId, error: unknown) => void;
105
93
  }
106
- /**
107
- * The policy, as whoever finishes a run calls it.
108
- *
109
- * Three verbs, and the middle two are a pair: everything between `hold()` and `release()` is one
110
- * piece of work as far as painting is concerned.
111
- */
94
+ /** A batch's suggestions, held until the batch is released. */
95
+ export interface PaintHold {
96
+ /**
97
+ * Stop holding.
98
+ * @returns What was held, one suggestion per channel, keeping the member that would have
99
+ * ended up on top, minus what an authored layer already drives; empty on a second release.
100
+ */
101
+ release(): readonly StyleSuggestion[];
102
+ }
103
+ /** What the policy decided about one finished run. */
104
+ interface PaintDecision {
105
+ /** Whether the run has now had its first-completion moment: `RunEntry.painted`. */
106
+ readonly painted: boolean;
107
+ /** What to plan into the run's step now; empty when held or when there is nothing to paint. */
108
+ readonly paint: readonly StyleSuggestion[];
109
+ }
110
+ /** The policy, as whoever records a finished run calls it. */
112
111
  export interface AutoApplyPolicy {
113
112
  /**
114
- * A run reached the end of its life. Paint it, if this is the moment to.
113
+ * A run reached the end of its life. Decide whether this is the moment it paints.
115
114
  * @param run - The run that finished.
115
+ * @param painted - Whether it had its moment already: the `painted` flag of the entry its
116
+ * record replaces.
117
+ * @param hold - The batch holding it, whose release paints it instead.
118
+ * @returns The decision.
116
119
  */
117
- completed(run: AutoApplyRun): void;
118
- /** Hold painting until the matching {@link AutoApplyPolicy.release}. Nests. */
119
- hold(): void;
120
- /** Release one hold, painting what was held once the last one is released. */
121
- release(): void;
120
+ completed(run: AutoApplyRun, painted: boolean, hold?: PaintHold): PaintDecision;
122
121
  /**
123
- * Forget that a run has already painted, because the session no longer holds it.
124
- *
125
- * Starting the same work again after it was removed is a first completion again: the layers
126
- * that painted it went with it, so "never again" would leave a run that nothing can draw.
127
- * @param runId - The run that was removed.
122
+ * Hold a batch's painting until the hold is released.
123
+ * @returns The hold.
128
124
  */
129
- forget(runId: RunId): void;
125
+ hold(): PaintHold;
126
+ /**
127
+ * Report a suggestion the stack refused.
128
+ * @param runId - The run.
129
+ * @param error - Why.
130
+ */
131
+ refused(runId: RunId, error: unknown): void;
130
132
  }
131
133
  /**
132
- * Build the policy one session applies its runs' styling through.
133
- * @param sources - The stack to paint into, and where a refusal is reported.
134
+ * The command a suggestion is applied as: the one `styles.encode()` or `styles.highlight()`
135
+ * dispatches for the same specification, so the replacement and exclusivity rules of those verbs
136
+ * hold for it too.
137
+ * @param suggestion - The suggestion.
138
+ * @returns The style command.
139
+ */
140
+ export declare function suggestionCommand(suggestion: StyleSuggestion): StyleCommand;
141
+ /**
142
+ * Build the policy one session decides its runs' styling with.
143
+ * @param sources - The stack to read, and where a refusal is reported.
134
144
  * @returns The policy.
135
145
  */
136
146
  export declare function createAutoApplyPolicy(sources: AutoApplySources): AutoApplyPolicy;
147
+ export {};
@@ -19,16 +19,16 @@
19
19
  *
20
20
  * Nothing in this module's import graph reaches Babylon.js, Lit or the DOM.
21
21
  */
22
- export type { AutoApplyPolicy, AutoApplyRun, AutoApplySources, AutoApplyStyles, } from "./autoApply";
22
+ export type { AutoApplyPolicy, AutoApplyRun, AutoApplySources, AutoApplyStyles } from "./autoApply";
23
23
  export { createAutoApplyPolicy } from "./autoApply";
24
24
  export type { EncodingSuggestion, HighlightSuggestion, StyleSuggestion } from "./derive";
25
25
  export { suggestStyles } from "./derive";
26
26
  export type { EncodingRun, EncodingSource, EncodingSpec } from "./EncodingSpec";
27
- export type { ChannelExplanation, ExplainTarget, StyleContribution, StyleExplanation, UnboundLayer, } from "./explain";
27
+ export type { ChannelExplanation, ExplainTarget, StyleContribution, StyleExplanation, UnboundLayer } from "./explain";
28
28
  export type { CompiledLayer, Layer, LayerEdit, LayerPosition, LayerProblem, LayerRepaint, PathDirectory, RepaintContext, RepaintReason, RepaintReport, RepaintRequest, ValidationResult, } from "./Layer";
29
29
  export type { FieldWords, LegendBlock, LegendSwatch } from "./legend";
30
30
  export { quotePath } from "./predicate";
31
31
  export type { ElementPaint } from "./repaint";
32
32
  export type { Selector } from "./selector";
33
- export type { ElementLayerSpec, HighlightSpec, SessionStylesApi, StyleChange, StylesApi, StylesSources, TemplateOptions, TemplateReport, } from "./StylesApi";
33
+ export type { DefaultPalettes, ElementLayerSpec, HighlightSpec, SessionStylesApi, StyleChange, StylesApi, StylesSources, TemplateOptions, TemplateReport, } from "./StylesApi";
34
34
  export { createStylesApi } from "./StylesApi";
@@ -226,6 +226,13 @@ export interface CompiledSelector {
226
226
  */
227
227
  readonly problem?: () => string | undefined;
228
228
  }
229
+ /**
230
+ * JMESPath equality: deep, and structural for lists and objects.
231
+ * @param left - One value.
232
+ * @param right - The other.
233
+ * @returns Whether JMESPath considers them equal.
234
+ */
235
+ export declare function deepEquals(left: unknown, right: unknown): boolean;
229
236
  /**
230
237
  * Write a column key as a path an expression selector reads back as the same key.
231
238
  *
@@ -57,7 +57,7 @@
57
57
  import type { GraphtyErrorCode, LayerId, Path } from "../../catalog/types";
58
58
  import { type ChannelValues } from "./channels";
59
59
  import { type PreparedBinding } from "./encoding";
60
- import type { CompiledLayer, LayerRepaint, RepaintContext, RepaintReport } from "./Layer";
60
+ import type { CompiledLayer, LayerRepaint, RepaintContext, RepaintReport, RepaintRequest } from "./Layer";
61
61
  import { type SelectorSource, type SelectorTarget } from "./predicate";
62
62
  import { type ScaleRegistry } from "./scales";
63
63
  /**
@@ -319,6 +319,21 @@ export interface ElementIndices {
319
319
  /** Edge indices. */
320
320
  readonly edge: ArrayLike<number>;
321
321
  }
322
+ /**
323
+ * What the `styles` hook of the derivation lane hands the repaint: the difference between the
324
+ * stack the picture shows and the stack project state holds, whatever moved it -- an edit, an
325
+ * undo, a redo, a rollback, or several of them folded into one pass.
326
+ *
327
+ * A layer is the same layer when it is the same compiled object: an update replaces it, so the
328
+ * old and new objects under one id are an edit from one to the other; a layer only in one of the
329
+ * two stacks was added or removed. A layer kept by identity whose place among the kept layers
330
+ * changed was moved, and is marked with itself on both sides, so the repaint visits the elements
331
+ * it matches.
332
+ * @param previous - The stack the picture shows, bottom first.
333
+ * @param next - The stack to show.
334
+ * @returns The request; no edits when the two stacks are the same.
335
+ */
336
+ export declare function stackChange(previous: readonly CompiledLayer[], next: readonly CompiledLayer[]): RepaintRequest;
322
337
  /**
323
338
  * Build the repaint one session paints through.
324
339
  *
@@ -171,7 +171,7 @@ export declare function edgeEndpointOf(graph: GraphSnapshot, index: number, key:
171
171
  * @example
172
172
  * ```ts
173
173
  * const elements = createSelectorSource({
174
- * snapshot: () => session.snapshot(),
174
+ * snapshot: () => store.getSnapshot(),
175
175
  * results: (id) => session.runs.get(id)?.result,
176
176
  * records,
177
177
  * });