@graphysdk/viz-engine 0.0.1-experimental.7 → 0.0.1-plugins.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,44 +1,17 @@
1
- /**
2
- * `@graphysdk/viz-engine` — a grammar-of-graphics engine that compiles a declarative spec into a
3
- * render-ready `CompiledSpec`. Framework-agnostic: no DOM, no React.
4
- *
5
- * Rebuilding a renderer — read these first, in order:
6
- * 1. {@link Compiler} / {@link createCompiler} — the compile lifecycle and the apply→recompile loop.
7
- * 2. {@link CompiledSpec} — the render-ready projection you paint (and the compile/render contract).
8
- * 3. {@link LayoutCompiler} — pixel layout (panel/axes/header rects) plus final axis-tick selection.
9
- * 4. {@link Command} — serializable spec mutations for interactive edits.
10
- *
11
- * The contract in one breath: position scales emit normalized `[0,1]` (x 0=left…1=right, y 0=bottom…1=top,
12
- * so SVG renderers invert y as `1 - y`); visual scales emit final values (color strings, px sizes); the
13
- * engine emits format *descriptors* ({@link ValueFormat}) the renderer turns into locale strings; the
14
- * renderer owns pixel layout, theme, and formatting. Resolve layers and scales by id, never by index.
15
- *
16
- * @packageDocumentation
17
- */
18
-
19
- import { Area } from 'd3-shape';
20
- import { CurveFactory } from 'd3-shape';
21
1
  import { internal } from 'arquero';
22
- import { Line } from 'd3-shape';
23
- import { Translator } from '@graphysdk/i18n';
24
2
 
25
- /** Maps each visual channel (x, y, color, size, ...) to a data column or constant value. */
26
3
  export declare interface AesMapping {
27
4
  x?: AestheticValue;
28
5
  y?: AestheticValue;
29
6
  label?: AestheticValue;
30
7
  color?: AestheticValue;
31
8
  size?: AestheticValue;
32
- /** Opacity channel (0–1). */
33
9
  alpha?: AestheticValue;
34
- /** Splits marks into series (separate lines/areas) without assigning a visual encoding. */
35
10
  group?: AestheticValue;
36
11
  strokeWidth?: AestheticValue;
37
- /** Dash-pattern channel (solid, dashed, dotted, ...). */
38
12
  lineType?: AestheticValue;
39
13
  }
40
14
 
41
- /** Name of a single visual channel that can be mapped, such as `'x'` or `'color'`. */
42
15
  export declare type AestheticKey = keyof AesMapping;
43
16
 
44
17
  /**
@@ -85,6 +58,18 @@ declare type AggregationInput = Record<VariableName, {
85
58
  aggregation: AggregationFunction;
86
59
  }>;
87
60
 
61
+ /**
62
+ * Where an annotation anchored to an observation sits, in normalised panel space `[0, 1]²`, plus the
63
+ * render discriminant the annotation renderer dispatches on. A geom computes this from an observation
64
+ * under a coord system (`Geom.resolveAnchorPosition`); the annotations compiler resolves anchors through
65
+ * the definition rather than branching on geom name.
66
+ */
67
+ declare interface AnchorPosition {
68
+ x: number;
69
+ y: number;
70
+ geom: 'bar' | 'line' | 'polar-bar';
71
+ }
72
+
88
73
  /**
89
74
  * Bounds of the segment a stack total anchors to, in normalised `[0,1]` panel space (origin
90
75
  * bottom-left, y grows up). `direction` records which side of the stack the anchor came from.
@@ -97,41 +82,81 @@ declare interface AnchorSegment {
97
82
  direction: 'positive' | 'negative';
98
83
  }
99
84
 
100
- /** Angular sweep of a polar arc, in radians clockwise from 12 o'clock. */
101
85
  export declare interface AngleExtent {
102
86
  startAngle: NumericDataValue;
103
87
  endAngle: NumericDataValue;
104
88
  }
105
89
 
106
- /** Anchors an annotation to a data point by row position, column, and/or category value. */
90
+ /**
91
+ * The compile-half definition of a custom annotation kind (ADR-035).
92
+ *
93
+ * It carries **no compile logic** — coordinate resolution is generic for every kind — and exists only
94
+ * so the registration-typed builder (`createGraphyBuilder({ annotations })`) can type `annotation.<kind>({
95
+ * params })` from `TParams`, merge `defaultParams`, and enforce the optional coordinate arity. The
96
+ * render-half `draw` lives in the renderer and binds to this definition by import (`defineAnnotationRenderer`).
97
+ */
98
+ /** Optional coordinate-count guardrail enforced by the builder; unbounded when omitted. */
99
+ declare interface AnnotationArity {
100
+ min?: number;
101
+ max?: number;
102
+ }
103
+
104
+ /**
105
+ * A single coordinate for a custom annotation (ADR-035). One of three whole-coordinate modes — a
106
+ * data-domain value scaled through the chart's scales, a raw [0,1] unit fraction of the panel, or a
107
+ * snap to an existing observation. Per-axis mixing (x in one mode, y in another) is a deliberate
108
+ * non-goal until a consumer needs it.
109
+ */
110
+ export declare type AnnotationCoordinateInput = {
111
+ data: {
112
+ x: DataValue;
113
+ y: DataValue;
114
+ };
115
+ } | {
116
+ unit: {
117
+ x: number;
118
+ y: number;
119
+ };
120
+ } | {
121
+ observation: ObservationAnchorInput;
122
+ };
123
+
124
+ /** A resolved coordinate — the observation anchor's `layerIndex` normalised to a `layerId`. */
125
+ declare type AnnotationCoordinateSpec = {
126
+ data: {
127
+ x: DataValue;
128
+ y: DataValue;
129
+ };
130
+ } | {
131
+ unit: {
132
+ x: number;
133
+ y: number;
134
+ };
135
+ } | {
136
+ observation: ObservationAnchor;
137
+ };
138
+
107
139
  declare interface AnnotationDataPoint {
108
140
  rowIndex?: number;
109
141
  columnKey?: string;
110
142
  rowValue?: DataValue;
111
143
  }
112
144
 
113
- /**
114
- * Attach annotations to a spec. Multiple `annotations(...)` items append within
115
- * each group (like `geom` layers and `highlight`s), they don't replace.
116
- *
117
- * @example
118
- * pipe(
119
- * createSpec({ x: 'quarter', y: 'revenue' }),
120
- * geom.bar(),
121
- * scale.x(),
122
- * scale.y(),
123
- * annotations({
124
- * differenceArrows: [
125
- * {
126
- * start: { anchorValue: 'Q1', groupValue: null },
127
- * end: { anchorValue: 'Q3', groupValue: null },
128
- * label: 'relative-difference',
129
- * },
130
- * ],
131
- * }),
132
- * );
133
- */
134
- export declare const annotations: (input: AnnotationsInput) => AnnotationsItem;
145
+ declare interface AnnotationDef<TParams extends object = object, TType extends string = string> {
146
+ type: TType;
147
+ /** Carrier that lets the builder recover `TParams` and merge defaults before a param reaches `draw`. */
148
+ defaultParams: TParams;
149
+ coordinates?: AnnotationArity;
150
+ }
151
+
152
+ /** Pipeable spec item produced by the registration-typed `annotation.<kind>(...)` builder. */
153
+ declare interface AnnotationItem {
154
+ type: 'annotation';
155
+ annotation: CustomAnnotationInput;
156
+ }
157
+
158
+ /** Recovers an annotation definition's params type — carried structurally by its `defaultParams`. */
159
+ declare type AnnotationParamsOf<Definition> = Definition extends AnnotationDef<infer TParams> ? TParams : never;
135
160
 
136
161
  /**
137
162
  * Compiles annotations into a render-ready format.
@@ -144,18 +169,19 @@ export declare const annotations: (input: AnnotationsInput) => AnnotationsItem;
144
169
  * stage passes them through into dedicated `Compiled<Kind>` types.
145
170
  */
146
171
  declare class AnnotationsCompiler extends Stage<AnnotationsCompilerInput, CompiledAnnotations> {
172
+ private readonly geomCompiler;
173
+ constructor(geomCompiler: GeomCompiler);
147
174
  protected dependencies(input: AnnotationsCompilerInput): readonly unknown[];
148
175
  protected run(input: AnnotationsCompilerInput): CompiledAnnotations;
149
176
  }
150
177
 
151
- /** Inputs the annotations compiler needs: the spec plus the already-compiled layers and coord system it projects against. */
152
178
  declare interface AnnotationsCompilerInput {
153
179
  annotations: AnnotationsSpec;
154
180
  layers: readonly CompiledLayer[];
155
181
  coordSystem: CoordSystem;
182
+ scales: CompiledScales;
156
183
  }
157
184
 
158
- /** All annotations attached to a chart, as user-facing input. Every group is optional. */
159
185
  export declare interface AnnotationsInput {
160
186
  differenceArrows?: DifferenceArrowInput[];
161
187
  shapes?: ShapeInput[];
@@ -164,15 +190,9 @@ export declare interface AnnotationsInput {
164
190
  stickers?: StickerAnnotationInput[];
165
191
  pinnedNumbers?: PinnedNumberAnnotationInput[];
166
192
  comments?: CommentAnnotationInput[];
193
+ custom?: CustomAnnotationInput[];
167
194
  }
168
195
 
169
- /** Annotations as a pipeable spec item. Produced by {@link annotations}. */
170
- export declare interface AnnotationsItem {
171
- type: 'annotations';
172
- annotations: AnnotationsInput;
173
- }
174
-
175
- /** Resolved annotations for a chart, with every group present and all fields defaulted. */
176
196
  export declare interface AnnotationsSpec {
177
197
  differenceArrows: DifferenceArrowSpec[];
178
198
  shapes: ShapeSpec[];
@@ -181,16 +201,16 @@ export declare interface AnnotationsSpec {
181
201
  stickers: StickerAnnotationSpec[];
182
202
  pinnedNumbers: PinnedNumberAnnotationSpec[];
183
203
  comments: CommentAnnotationSpec[];
204
+ custom: CustomAnnotationSpec[];
184
205
  }
185
206
 
207
+ /** Whether a custom annotation paints behind the geoms (background) or on top (foreground). */
208
+ export declare type AnnotationZOrder = 'background' | 'foreground';
209
+
186
210
  declare interface Appearance {
187
- /** Id of the color palette to apply to all series. */
188
211
  paletteId?: string;
189
- /** Per-series style overrides, keyed by series id. */
190
212
  seriesStyles?: Record<string, SeriesStyle>;
191
- /** Colors all bars the same instead of giving each category its own palette color. */
192
213
  useSingleColorForBars?: boolean;
193
- /** Tints the chart background with the palette instead of leaving it plain. */
194
214
  backgroundModifier?: 'none' | 'tint';
195
215
  border?: Partial<{
196
216
  style: 'none' | 'custom' | 'tinted' | 'gradient' | 'preset' | 'grey';
@@ -202,27 +222,17 @@ declare interface Appearance {
202
222
  heading: GraphTextStyle;
203
223
  body: GraphTextStyle;
204
224
  }>;
205
- /** One of the {@link CHART_TEXT_SCALES} multipliers applied to all text sizes. */
206
225
  textScale?: number;
207
- /** How non-highlighted series are de-emphasized when one series is highlighted. */
208
226
  highlightStyle?: 'grey' | 'fade-color';
209
227
  isLogoHidden?: boolean;
210
228
  numberFormat?: Partial<{
211
- /** Fixed number of decimal places, or 'auto' to choose per value. */
212
229
  decimalPlaces: 'auto' | number;
213
- /** Large-number suffix: none, automatic, or a forced thousands/millions/billions unit. */
214
230
  abbreviation: 'none' | 'auto' | 'k' | 'm' | 'b';
215
231
  }>;
216
232
  showTooltips?: boolean;
217
233
  animateTransitions?: boolean;
218
234
  }
219
235
 
220
- /**
221
- * Resolved appearance config the renderer reads for chrome. `background` and `border` are narrowed to
222
- * their compiled variants — see {@link BackgroundConfig} and {@link BorderConfig} for how the renderer
223
- * materializes each `'tinted'`/`'gradient'`/`'preset'` case. The inherited `textScale` must be applied
224
- * at both measurement and CSS render time.
225
- */
226
236
  export declare type AppearanceConfig = Omit<AppearanceSpec, 'background' | 'border'> & {
227
237
  background: BackgroundConfig;
228
238
  border: BorderConfig;
@@ -236,10 +246,6 @@ export declare interface AppearanceSpec {
236
246
  /**
237
247
  * Multiplier applied to every text element. The renderer sets a CSS
238
248
  * variable; em-based theme tokens scale automatically.
239
- *
240
- * Renderer contract: apply this at BOTH text measurement and CSS render time.
241
- * The engine assumes the measured sizes it receives already include the
242
- * multiplier, so layout will be wrong if it is applied to only one of the two.
243
249
  * @default 1
244
250
  */
245
251
  textScale: number;
@@ -260,7 +266,6 @@ export declare interface AppearanceSpec {
260
266
  */
261
267
  cornerRadius: number;
262
268
  /**
263
- * How non-matched observations are de-emphasised when a highlight is active.
264
269
  * @default 'dim'
265
270
  */
266
271
  highlightStyle: HighlightStyle;
@@ -268,36 +273,32 @@ export declare interface AppearanceSpec {
268
273
 
269
274
  declare function area(options?: GeomOptions<'area'>): LayerInputOf<'area'>;
270
275
 
276
+ /**
277
+ * Represents a series of observations as a filled area. Multiple areas will be stacked on top of each other.
278
+ *
279
+ * Like the line geom, if the x variable is numeric or temporal, the data will be sorted by x.
280
+ */
281
+ declare class AreaGeom extends Geom {
282
+ readonly type: "area";
283
+ readonly requiredAesthetics: AestheticKey[];
284
+ readonly positionChannels: readonly PositionChannel[];
285
+ readonly swatchShape: SwatchShape;
286
+ readonly highlightStrategy = "overlay-anchor";
287
+ readonly spatialKind = "buckets";
288
+ readonly directLabelPositions: readonly ["identity", "stack", "dodge", "fill"];
289
+ readonly supportsPerGroupHeadline: boolean;
290
+ compile({ data, mapping }: GeomCompilerInput): CompiledGeom;
291
+ }
292
+
271
293
  /**
272
294
  * Area-specific parameters (same rendering knobs as line, but fills under the curve)
273
295
  */
274
296
  export declare interface AreaGeomParams {
275
- /**
276
- * Outline stroke width in pixels. `'auto'` reads the per-observation
277
- * `strokeWidth` channel (`getStrokeWidth`), falling back to the geom default.
278
- */
279
297
  lineWidth: number | 'auto';
280
- /**
281
- * Interpolation method between points — a d3-shape curve family (`'linear'` ⇒
282
- * `curveLinear`, `'catmull-rom'` ⇒ `curveCatmullRom`).
283
- * @default 'linear'
284
- */
285
298
  interpolate: InterpolateType;
286
- /**
287
- * How to handle missing (NULL/undefined) values, as for line: `'zero'`
288
- * pre-substituted by the compiler, `'gap'` breaks the path at a null, and
289
- * `'connect'` drops null rows before pathing.
290
- * @default 'gap'
291
- */
292
299
  missingValues: MissingValuesType;
293
300
  }
294
301
 
295
- export declare interface AreaPathGenerators {
296
- lineGenerator: Line<Observation>;
297
- areaGenerator: Area<Observation>;
298
- }
299
-
300
- /** One end of a freeform arrow, positioned as a fraction of the plot rect (0..1) so it re-flows on resize. */
301
302
  export declare interface ArrowEndpoint {
302
303
  /** 0..1 of plot width. */
303
304
  x: number;
@@ -305,17 +306,13 @@ export declare interface ArrowEndpoint {
305
306
  y: number;
306
307
  }
307
308
 
308
- /** Whether an arrow end carries an arrowhead. */
309
309
  export declare type ArrowheadStyle = 'none' | 'line-arrow';
310
310
 
311
- /** Whether an arrow's line is drawn solid or dashed. */
312
311
  export declare type ArrowLineStyle = 'solid' | 'dashed';
313
312
 
314
- /** Preset stroke weight for an arrow annotation. */
315
313
  export declare type ArrowThickness = 'thin' | 'medium' | 'thick';
316
314
 
317
315
  declare interface AverageLine {
318
- /** Column whose mean value the average line is drawn at. */
319
316
  columnKey: string;
320
317
  }
321
318
 
@@ -331,9 +328,7 @@ export declare interface AverageLineSymbol {
331
328
  declare interface Axes {
332
329
  x?: AxisOptions;
333
330
  y?: AxisOptions;
334
- /** Secondary (right-hand) y-axis; only its label is configurable. */
335
331
  y2?: Pick<AxisOptions, 'label'>;
336
- /** Splits series across a primary and secondary y-axis. */
337
332
  hasDualYAxis?: boolean;
338
333
  showGridLines?: boolean;
339
334
  }
@@ -387,14 +382,10 @@ declare interface AxisMapping {
387
382
  declare interface AxisOptions {
388
383
  label?: string;
389
384
  isHidden?: boolean;
390
- /** Flips the axis direction so values run from high to low. */
391
385
  isReversed?: boolean;
392
386
  scaleType?: 'linear' | 'logarithmic';
393
- /** Forces the lower bound of the axis domain instead of deriving it from the data. */
394
387
  min?: number;
395
- /** Forces the upper bound of the axis domain instead of deriving it from the data. */
396
388
  max?: number;
397
- /** Whether to show ticks at every step ('auto') or only at the domain edges ('edges'). */
398
389
  tickDisplayMode?: 'auto' | 'edges';
399
390
  }
400
391
 
@@ -407,13 +398,7 @@ declare type AxisPosition = 'left' | 'right' | 'top' | 'bottom';
407
398
  export declare interface AxisTick {
408
399
  /** Raw value in data space (number, Date, or string) */
409
400
  value: DataValue;
410
- /**
411
- * Tick position in the same normalized space the geoms use. Normalized to [0,1]: x is 0=left…1=right,
412
- * y is 0=bottom…1=top (data-up). SVG / top-origin renderers invert y as `1 - y`.
413
- *
414
- * For discrete scales this is the band CENTER; the band spans `position ± bandwidth/2`
415
- * (see {@link CompiledAxisGuide.bandwidth}).
416
- */
401
+ /** Normalized position in [0,1] space — used for placement */
417
402
  position: number;
418
403
  }
419
404
 
@@ -434,16 +419,6 @@ declare interface AxisTicksConfig {
434
419
  mode: AxisLabelMode;
435
420
  }
436
421
 
437
- /**
438
- * Resolved background fill (compiled from `BackgroundSpec`). The variant is decided, but
439
- * `'theme'` carries no `color` — the renderer narrows (`'color' in background`) before reading it.
440
- *
441
- * Renderer materialization by variant:
442
- * - `'theme'`: paint the active theme's graph-background token (no `color` on this variant).
443
- * - `'solid'`: paint `color` as-is (a CSS color string, possibly a theme token; `'transparent'` = none).
444
- * - `'tinted'`: `color` is the resolved anchor; mix it with the theme background, lightening or
445
- * darkening per the active {@link GraphTheme}.
446
- */
447
422
  export declare type BackgroundConfig = {
448
423
  type: 'theme';
449
424
  } | {
@@ -474,19 +449,45 @@ export declare type BackgroundSpec = {
474
449
  declare function bar(options?: GeomOptions<'bar'>): LayerInputOf<'bar'>;
475
450
 
476
451
  /**
477
- * Which side of the value scale's domain a bar's baseline (its zero origin) falls beyond.
452
+ * Represents each observation as a rectangular bar.
478
453
  *
479
- * - `'min'`: the baseline sits below the domain, so bars grow from the clipped lower edge
480
- * (the usual case a positive `domainMin`).
481
- * - `'max'`: the baseline sits above the domain.
482
- * - `null`: the baseline is in view, so bars start at their true origin.
483
- */
484
- export declare type BarBaselineClip = 'min' | 'max' | null;
454
+ * Neither `x` nor `y` are required because:
455
+ * - `x` is not needed if a polar coordinate system is used
456
+ * - `y` may be computed by a stat (e.g. `count`)
457
+ */
458
+ declare class BarGeom extends Geom {
459
+ readonly type: "bar";
460
+ readonly positionChannels: readonly PositionChannel[];
461
+ readonly swatchShape: SwatchShape;
462
+ readonly highlightStrategy = "observation-rerender";
463
+ readonly spatialKind = "rects";
464
+ readonly directLabelPositions: readonly ["stack", "fill"];
465
+ readonly gridPolicies: {
466
+ cartesian: {
467
+ hideGridX: boolean;
468
+ };
469
+ flip: {
470
+ hideGridX: boolean;
471
+ };
472
+ polar: {
473
+ hideGridX: boolean;
474
+ hideGridY: boolean;
475
+ };
476
+ };
477
+ readonly emitsGrandTotal: boolean;
478
+ readonly emitsStackTotals: boolean;
479
+ readonly supportsPerGroupHeadline: boolean;
480
+ compile({ data, mapping }: GeomCompilerInput): CompiledGeom;
481
+ /**
482
+ * Cartesian: the top-edge midpoint of the bar (the right-edge midpoint when flipped). Polar (pie
483
+ * slice): the slice midpoint projected from `(angle, radius)` into the panel's `[0, 1]²` frame —
484
+ * `xMin`/`xMax` are start/end angles (radians) and `yMin`/`yMax` are inner/outer radii.
485
+ */
486
+ resolveAnchorPosition(observation: Observation, coordSystem: CoordSystem): AnchorPosition | null;
487
+ }
485
488
 
486
489
  /**
487
- * Bar/Column-specific parameters — intentionally empty. A bar's geometry comes
488
- * entirely from the position columns (band edges plus bar length); corner radius
489
- * and column grouping are renderer-owned styling, not spec params.
490
+ * Bar/Column-specific parameters
490
491
  */
491
492
  declare type BarGeomParams = Record<string, never>;
492
493
 
@@ -509,7 +510,6 @@ declare interface BaseCoordParams {
509
510
  }
510
511
 
511
512
  declare interface BaseGeomOptions<T extends GeomParams> {
512
- /** Layer-local aesthetic mapping, merged over the spec-level mapping. */
513
513
  aes?: AesMapping;
514
514
  stat?: StatName | StatInput;
515
515
  position?: PositionType;
@@ -532,17 +532,6 @@ export declare const BORDER_PRESET_GRADIENTS: Record<BorderPreset, string>;
532
532
  /** Named gradient presets available to `border.type === 'preset'`. */
533
533
  export declare const BORDER_PRESETS: readonly ["lilac", "neon_pink", "blackberry", "sun", "iceland", "sunset", "ultraviolet", "purple", "ice_cream", "mint", "cool", "fresh"];
534
534
 
535
- /**
536
- * Resolved border ring (compiled from `BorderSpec`), painted inside the chart bounds. `'none'` carries
537
- * no `color`/`width`; narrow on `type` before reading either.
538
- *
539
- * Renderer materialization by variant:
540
- * - `'solid'`: fill the ring with `color` as-is.
541
- * - `'tinted'`: fill with `color` lightened/darkened for the active {@link GraphTheme}.
542
- * - `'gradient'`: fill with a 90° linear gradient from `color` to a theme-adjusted stop.
543
- * - `'preset'`: fill with `BORDER_PRESET_GRADIENTS[preset]` used verbatim as the CSS background (the
544
- * preset already encodes its own colors and angle, theme-independent).
545
- */
546
535
  export declare type BorderConfig = {
547
536
  type: 'none';
548
537
  } | {
@@ -563,7 +552,6 @@ export declare type BorderConfig = {
563
552
  width: number;
564
553
  };
565
554
 
566
- /** Name of a built-in gradient available to `border.type === 'preset'`. */
567
555
  export declare type BorderPreset = (typeof BORDER_PRESETS)[number];
568
556
 
569
557
  /**
@@ -602,7 +590,6 @@ export declare type BorderSpec = {
602
590
  width: number;
603
591
  };
604
592
 
605
- /** A width/height pair in pixels, for elements whose position is tracked separately or not yet known. */
606
593
  export declare type BoxSize = {
607
594
  width: number;
608
595
  height: number;
@@ -612,11 +599,9 @@ export declare type BoxSize = {
612
599
  * For each average-line rule in a graph with a categorical color grouping, returns the swatch
613
600
  * shape + color the renderer should paint inside the rule's label pill.
614
601
  *
615
- * Pass the SOURCE `spec.layers` (not the compiled layers) so each layer's original `stat`,
616
- * `transforms` and `mapping` are available without needing extra fields on `CompiledLayer`, and the
617
- * compiled `color` scale (`scales.color`). The returned map is keyed by RULE-layer id (the id is
618
- * preserved through compilation): look up a rule by its own id; a missing entry means draw no
619
- * symbol (e.g. no categorical color grouping, or the rule's series didn't resolve to a color).
602
+ * Reads the rule-matching fields (`stat`, `transforms`, `mapping`) from the source `layers` specs,
603
+ * and the source layer's swatch shape from its `CompiledLayer` (matched by id, which is preserved
604
+ * through compilation). The returned map is keyed by layer id.
620
605
  *
621
606
  * Two shapes of rule layer are handled:
622
607
  * - **Filtered**: the rule carries an `eq`-filter. The color-scale lookup value is the filter's
@@ -624,7 +609,7 @@ export declare type BoxSize = {
624
609
  * - **Unfiltered**: no filter; the rule's `mapping.y` IS the lookup value. The source layer is
625
610
  * the one whose `y` aesthetic is bound to the same variable.
626
611
  */
627
- export declare function buildAverageLineSymbols(layers: readonly LayerSpec[], colorScale: CompiledScale | undefined, coordSystem: CoordSystem): ReadonlyMap<string, AverageLineSymbol>;
612
+ export declare function buildAverageLineSymbols(layers: readonly LayerSpec[], compiledLayers: readonly CompiledLayer[], colorScale: CompiledScale | undefined, coordSystem: CoordSystem): ReadonlyMap<string, AverageLineSymbol>;
628
613
 
629
614
  /**
630
615
  * Pure projection of `(layers, coordSystem, axes, formatter context, panelRect, measureDataLabel)`
@@ -653,11 +638,8 @@ export declare interface BuildDataLabelsContentInput {
653
638
  }
654
639
 
655
640
  /**
656
- * Builds a CSS font shorthand string from a FontSpec, in the order `"<style> <weight> <size>px
657
- * <family>"` (e.g. `"normal 500 12px 'Inter'"`). A single family is quoted so names with spaces stay
658
- * one token; a family already containing a comma is treated as a ready fallback list and passed
659
- * through unquoted. A named weight (e.g. `'bold'`) is emitted as-is — the browser canvas resolves it —
660
- * rather than being mapped to a number; omitted weight/style fall back to the defaults.
641
+ * Builds a CSS font shorthand string from a FontSpec.
642
+ * Always quotes the family name to safely handle names with spaces.
661
643
  *
662
644
  * Renderer-agnostic: usable by browser (OffscreenCanvas) and backend
663
645
  * (@napi-rs/canvas) measurers to set `ctx.font` before `ctx.measureText()`.
@@ -676,17 +658,10 @@ declare interface BuildLayerYValueFormatterInput {
676
658
  }
677
659
 
678
660
  /**
679
- * Builds the SVG arc path for a polar bar (pie / donut slice).
680
- *
681
- * Returns `null` for an empty slice (no angular span or no radius) that d3 cannot render.
682
- */
683
- export declare const buildPolarBarArcPath: (input: PolarBarArcInput) => string | null;
684
-
685
- /**
686
- * Pure projection of `(compiled, hover)` into the tooltip's `{ header, rows }`. Pure (no I/O, no
687
- * mutation) — safe to memoize on its input.
688
- *
689
- * Returns `null` when there is no primary hit; use that as the tooltip show/hide signal.
661
+ * Renders `(compiled, hover)` into a chart's semantic content the single source every surface
662
+ * (tooltip, screen reader, accessible table) reads, so content never forks per surface. Each
663
+ * observation's value flows through the layer's semantic map: the raw reading projected on demand
664
+ * from the declared y encoding, localized here at display time via the encoding's format.
690
665
  *
691
666
  * Row order is layer-declaration order across layers, color-scale domain order within a layer
692
667
  * (= the legend's display order). The primary hit is emphasized in place — never reordered to
@@ -694,30 +669,45 @@ export declare const buildPolarBarArcPath: (input: PolarBarArcInput) => string |
694
669
  */
695
670
  export declare const buildTooltipContent: ({ layers, coordSystem, scales, guides, numberFormat, parsingLocale, hover, formattingLocale, }: BuildTooltipContentInput) => TooltipContent | null;
696
671
 
697
- /**
698
- * Everything `buildTooltipContent` needs to turn the current hover into tooltip rows. Feed the
699
- * same compiled-spec slices used to build the `HoverEngine` (`layers`, `coordSystem`) so labels and
700
- * hits stay aligned, plus the spec's `scales`, `guides`, and config formatting fields.
701
- */
702
672
  export declare interface BuildTooltipContentInput {
703
- /** Compiled layers — the same array passed to the `HoverEngine`. Source of per-series swatch/label data. */
704
673
  layers: readonly CompiledLayer[];
705
- /** Compiled coord system — the same one passed to the `HoverEngine` (drives swatch shape; gates the header). */
706
674
  coordSystem: CoordSystem;
707
- /** `CompiledSpec.scales`. The `color` scale's domain fixes legend (display) row order within a layer. */
708
675
  scales: CompiledScales;
709
- /** `CompiledSpec.guides`. Supplies axis titles, color-group labels, and the `ValueFormat` descriptors. */
710
676
  guides: CompiledGuides;
711
- /** From `config.numberFormat`. Number-formatting options threaded into every value formatter. */
712
677
  numberFormat: NumberFormatConfig;
713
- /** From `config.parsingLocale`. Interprets raw values and is the display-locale fallback. */
714
678
  parsingLocale: Locale;
715
- /** The `HoverState` returned by `HoverEngine.query()` for the current pointer. */
716
679
  hover: HoverState;
717
- /** Overrides `parsingLocale` for display only (`locale = formattingLocale ?? parsingLocale`). */
718
680
  formattingLocale?: Locale;
719
681
  }
720
682
 
683
+ /** The built-in names as a runtime set, so the resolver can route a custom name to the generic path. */
684
+ export declare const BUILTIN_GEOM_NAMES: ReadonlySet<GeomName>;
685
+
686
+ /**
687
+ * Discriminated union of the built-in layer inputs, keyed on `geom`. The per-geom `params` typing is
688
+ * derived from this via `Extract`, so it stays a closed union the open identity never pollutes.
689
+ */
690
+ declare type BuiltinLayerInput = {
691
+ [G in GeomName]: LayerInputOf<G>;
692
+ }[GeomName];
693
+
694
+ /**
695
+ * Discriminated union of the built-in resolved layer specs, keyed on `geom`. All properties are fully
696
+ * resolved — no optionals. The `Extract`-based per-geom params views read from this closed union.
697
+ */
698
+ declare type BuiltinLayerSpec = {
699
+ [G in GeomName]: LayerSpecOf<G>;
700
+ }[GeomName];
701
+
702
+ /**
703
+ * The resolved `params` type for a geom identity: a built-in's typed params (read from the closed
704
+ * `BuiltinLayerSpec`, which the open identity never pollutes) or opaque params for a custom geom whose
705
+ * concrete type a render plugin supplies via the `TParams` slot of {@link CompiledLayerFor}.
706
+ */
707
+ declare type BuiltinParams<G extends string> = G extends GeomName ? Extract<BuiltinLayerSpec, {
708
+ geom: G;
709
+ }>['params'] : Record<string, unknown>;
710
+
721
711
  /**
722
712
  * Caching decorator for any TextMeasurer implementation.
723
713
  *
@@ -760,8 +750,6 @@ export declare interface CartesianCoordSystem {
760
750
  * rise, X ticks on the horizontal axis); `'y'` for `coord.flip()` (bars extend, Y ticks on the
761
751
  * horizontal axis). Consumers that need to branch on flip read this; the runtime `coord/axes`
762
752
  * helpers turn it into main/cross accessors so the branch lives in one place.
763
- * When `'y'`, the x-columns carry the measure / cross-axis extent and the y-columns carry the
764
- * main-axis band, so geoms swap which reader feeds which pixel axis. See {@link MainAxis}.
765
753
  */
766
754
  mainAxis: MainAxis;
767
755
  /** Axis orientation metadata for the guide compiler */
@@ -787,6 +775,12 @@ declare interface CategoricalValueFormat {
787
775
  type: 'text';
788
776
  }
789
777
 
778
+ /**
779
+ * The spatial axis a position channel binds to. Selects the scale (`x` → the x scale, `y` → the
780
+ * primary or secondary y scale) and is the axis flip swaps and polar projects.
781
+ */
782
+ declare type ChannelAxis = 'x' | 'y';
783
+
790
784
  /**
791
785
  * One discrete colour group: its domain value, mapped colour, per-value swatch shape, and the
792
786
  * index of the first layer that contributed the value (its "owning" layer in legend/headline
@@ -823,7 +817,6 @@ declare interface ColorScaleMethods {
823
817
  }
824
818
 
825
819
  declare interface ComboOptions {
826
- /** Geometry used for the bar-like series alongside line series in a combo chart. */
827
820
  comboType?: 'grouped-bars' | 'stacked-bars' | 'lines';
828
821
  }
829
822
 
@@ -842,14 +835,7 @@ export declare interface Command<TParams extends Record<string, unknown> = Recor
842
835
  readonly params: TParams;
843
836
  /**
844
837
  * Execute the command against a spec.
845
- * Returns the new spec and a revert command that can undo this change, or `null` when the
846
- * command is a no-op (e.g. the target is missing or the value is unchanged).
847
- *
848
- * Dispatch loop a renderer runs to reflect a command in the view: apply it to the live
849
- * `CompiledSpec.spec`, and on a non-`null` result recompile the returned spec —
850
- * `const result = command.apply(compiled.spec); if (!result) return; recompile({ spec: result.spec })`.
851
- * A `null` result skips the recompile (and any notify). Always apply against `CompiledSpec.spec`,
852
- * the canonical live spec.
838
+ * Returns the new spec and a revert command that can undo this change.
853
839
  */
854
840
  apply: (spec: Spec) => CommandApplyResult | null;
855
841
  }
@@ -865,17 +851,6 @@ export declare interface CommandApplyResult {
865
851
  readonly revert: Command;
866
852
  }
867
853
 
868
- /**
869
- * Descriptor that knows how to deserialize a specific command type.
870
- * Each concrete command co-locates its descriptor alongside the command class.
871
- *
872
- * Serialization is handled uniformly by the registry via `Command.params`.
873
- */
874
- declare interface CommandDescriptor<TParams extends Record<string, unknown> = Record<string, unknown>> {
875
- readonly type: string;
876
- deserialize: (params: TParams, metadata: CommandMetadata) => Command;
877
- }
878
-
879
854
  /**
880
855
  * Unique identifier for commands.
881
856
  */
@@ -895,33 +870,6 @@ export declare interface CommandMetadata {
895
870
  readonly author: string;
896
871
  }
897
872
 
898
- /**
899
- * Central registry mapping command types to their serialization descriptors.
900
- */
901
- export declare class CommandRegistry {
902
- private readonly descriptors;
903
- /**
904
- * Register a command descriptor. Throws if the type is already registered.
905
- */
906
- register<TParams extends Record<string, unknown>>(descriptor: CommandDescriptor<TParams>): void;
907
- /**
908
- * Serialize a command to its wire format.
909
- */
910
- serialize(command: Command): SerializedCommand;
911
- /**
912
- * Deserialize a command from its wire format.
913
- */
914
- deserialize(data: SerializedCommand): Command;
915
- /**
916
- * Get all registered command type names.
917
- */
918
- getRegisteredTypes(): string[];
919
- private getDescriptor;
920
- }
921
-
922
- /** Default singleton registry instance. */
923
- export declare const commandRegistry: CommandRegistry;
924
-
925
873
  /**
926
874
  * Event types emitted by CommandStackManager.
927
875
  */
@@ -1033,9 +981,7 @@ declare interface CommandStackOptions {
1033
981
  export declare interface CommandStackSnapshot {
1034
982
  readonly canUndo: boolean;
1035
983
  readonly canRedo: boolean;
1036
- /** Description of the command that an undo would reverse, for labeling UI; null when nothing to undo. */
1037
984
  readonly undoDescription: string | null;
1038
- /** Description of the command that a redo would re-apply; null when nothing to redo. */
1039
985
  readonly redoDescription: string | null;
1040
986
  }
1041
987
 
@@ -1059,14 +1005,6 @@ declare interface CommentAnnotationSpec {
1059
1005
  /** Comparison operators for declarative filtering. */
1060
1006
  declare type ComparisonOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte';
1061
1007
 
1062
- /**
1063
- * All annotations of a chart after compilation, grouped by kind and ready for the renderer.
1064
- *
1065
- * Paint order (back to front): background shapes → geoms → data-labels → difference-arrows →
1066
- * foreground shapes → freeform-arrows → text-annotations. Note that `shapes` are split by their
1067
- * `zOrder` into two passes that bracket the geom layer (see {@link ShapeZOrder}); stickers, pinned
1068
- * numbers and comments are marker overlays painted above text-annotations.
1069
- */
1070
1008
  export declare interface CompiledAnnotations {
1071
1009
  differenceArrows: CompiledDifferenceArrow[];
1072
1010
  shapes: CompiledShape[];
@@ -1075,6 +1013,7 @@ export declare interface CompiledAnnotations {
1075
1013
  stickers: CompiledStickerAnnotation[];
1076
1014
  pinnedNumbers: CompiledPinnedNumberAnnotation[];
1077
1015
  comments: CompiledCommentAnnotation[];
1016
+ custom: CompiledCustomAnnotation[];
1078
1017
  }
1079
1018
 
1080
1019
  /**
@@ -1087,34 +1026,21 @@ export declare interface CompiledAxisGuide {
1087
1026
  scaleAestheticKey: ScaledAestheticKey;
1088
1027
  /** The aesthetic this axis serves (always 'x' or 'y') */
1089
1028
  aesthetic: AestheticKey;
1090
- /**
1091
- * Axis placement. Already reflects any flip — drive placement off this, do NOT re-derive from the coord
1092
- * system's `mainAxis`.
1093
- */
1029
+ /** Axis placement */
1094
1030
  position: AxisPosition;
1095
1031
  /** Title text, null means no title */
1096
1032
  label: string | null;
1097
- /**
1098
- * Gates the whole axis region (line + ticks + labels). Orthogonal to `ticksVisible` / `gridVisible` and to
1099
- * {@link CompiledPanel.border}.
1100
- */
1033
+ /** Whether the axis (line + ticks + labels) is visible */
1101
1034
  isVisible: boolean;
1102
- /**
1103
- * Candidate tick sets, sorted by ascending count — these are RAW candidates, not the final selection.
1104
- * Final selection happens in `LayoutCompiler` (two phases: pick the densest candidate whose labels fit), and
1105
- * the renderer paints the resulting `FormattedAxis.ticks` verbatim — it must not re-select candidates here.
1106
- */
1035
+ /** Candidate tick sets, sorted by ascending count. Runtime picks the densest one that fits. */
1107
1036
  tickCandidates: AxisTickCandidate[];
1108
- /**
1109
- * Resolved format descriptor shared by all ticks on this axis. Already applied during tick selection: the
1110
- * renderer paints `FormattedAxis.ticks[].formattedLabel` as-is and must NOT re-apply this `ValueFormat`.
1111
- */
1037
+ /** Resolved format descriptor shared by all ticks on this axis */
1112
1038
  valueFormat: ValueFormat;
1113
- /** Tick mode from config. Already honored by the compiler when building candidates — don't re-filter ticks. */
1039
+ /** Tick mode from config */
1114
1040
  tickMode: AxisLabelMode;
1115
- /** Whether tick marks are visible. Labels still show when this is false (independent of `isVisible`). */
1041
+ /** Whether tick marks are visible */
1116
1042
  ticksVisible: boolean;
1117
- /** Whether grid lines are visible at tick positions. Grid is drawn separately from the axis region. */
1043
+ /** Whether grid lines are visible at tick positions */
1118
1044
  gridVisible: boolean;
1119
1045
  /** Scale type — the renderer uses this to select a formatting strategy */
1120
1046
  scaleType: ScaleType;
@@ -1135,6 +1061,19 @@ declare type CompiledConfig = Omit<ConfigSpec, 'appearance'> & {
1135
1061
  appearance: AppearanceConfig;
1136
1062
  };
1137
1063
 
1064
+ /**
1065
+ * Compile-time projection of a custom (registered) annotation. `type` resolves the render-side `draw`;
1066
+ * `targets` are the resolved coordinates; `params` is opaque (typed at authoring time); `zOrder` is the
1067
+ * per-instance override, or `null` to defer to the renderer's declared default.
1068
+ */
1069
+ export declare interface CompiledCustomAnnotation {
1070
+ id: string;
1071
+ type: string;
1072
+ params: Record<string, unknown>;
1073
+ zOrder: AnnotationZOrder | null;
1074
+ targets: ResolvedTarget[];
1075
+ }
1076
+
1138
1077
  /**
1139
1078
  * A difference arrow with both endpoints resolved to normalized [0, 1]² coordinates.
1140
1079
  */
@@ -1150,9 +1089,7 @@ export declare interface CompiledDifferenceArrow {
1150
1089
  */
1151
1090
  color: string | null;
1152
1091
  size: DifferenceArrowSize;
1153
- /** Which quantity the arrow's label reports (absolute, relative, or proportion of the two endpoints). */
1154
1092
  label: DifferenceArrowLabelKind;
1155
- /** Where the label sits along the arrow, as a fraction from start (0) to end (1). */
1156
1093
  labelCrossPosition: number;
1157
1094
  }
1158
1095
 
@@ -1163,13 +1100,11 @@ export declare interface CompiledFreeformArrow {
1163
1100
  id: string;
1164
1101
  start: ArrowEndpoint;
1165
1102
  end: ArrowEndpoint;
1166
- /** Line color, or `null` to fall back to the theme default. */
1167
1103
  color: string | null;
1168
1104
  thickness: ArrowThickness;
1169
1105
  startArrowheadStyle: ArrowheadStyle;
1170
1106
  endArrowheadStyle: ArrowheadStyle;
1171
1107
  lineStyle: ArrowLineStyle;
1172
- /** Whether to draw the playful 'sticker' arrow styling instead of a plain line. */
1173
1108
  hasStickerStyle: boolean;
1174
1109
  }
1175
1110
 
@@ -1178,6 +1113,13 @@ declare interface CompiledGeom {
1178
1113
  data: Dataset;
1179
1114
  /** Any mapping overrides produced by the geom */
1180
1115
  mapping: AesMapping;
1116
+ /**
1117
+ * Extra single-observation tooltip rows this geom contributes (e.g. OHLC). The compiler derives
1118
+ * each row's display format from its column and the renderer materialises the values for the
1119
+ * hovered observation. Omit when the geom adds no detail rows; the standard one-row-per-series
1120
+ * tooltip applies.
1121
+ */
1122
+ tooltipRows?: GeomTooltipRow[];
1181
1123
  }
1182
1124
 
1183
1125
  declare interface CompiledGrandTotalHeadline {
@@ -1228,20 +1170,10 @@ export declare interface CompiledIdentityScale extends CompiledScaleBase {
1228
1170
  * Render-ready layer with resolved mappings and transformed data.
1229
1171
  */
1230
1172
  export declare interface CompiledLayer {
1231
- /**
1232
- * Stable identity carried over from `LayerSpec.id`. Preserved across recompiles (commands keep
1233
- * it too), so renderers root per-mark React keys on it — marks morph instead of remounting — and
1234
- * join hover by matching `HoverHit.layerId === layer.id`.
1235
- */
1173
+ /** Stable identity carried over from `LayerSpec.id`. Preserved across recompiles. */
1236
1174
  id: string;
1237
- /**
1238
- * This layer's transformed, render-ready observations, in render order. Within a series the rows
1239
- * are pre-ordered along the main axis, so a line / area path can be drawn as-is with no re-sort.
1240
- * Connected geoms (line, area, polar arc) partition these by `GROUP_VARIABLES.group`
1241
- * (`data.groupBy(...)`) — one mark per group; per-mark geoms (bar, point) iterate `data` directly.
1242
- */
1243
1175
  data: Dataset;
1244
- geom: GeomName;
1176
+ geom: GeomIdentity;
1245
1177
  /** Final mapping after merging root + layer + stat + geom overrides */
1246
1178
  mapping: AesMapping;
1247
1179
  position: PositionType;
@@ -1261,27 +1193,45 @@ export declare interface CompiledLayer {
1261
1193
  * highlights compile stage when applicable highlights match (empty otherwise).
1262
1194
  */
1263
1195
  highlight: CompiledLayerHighlight | null;
1196
+ /**
1197
+ * The layer's geometry-agnostic meaning: the ordered encodings each observation carries (raw
1198
+ * value + format descriptor). Pure data — values stay in `data` and are projected on demand.
1199
+ */
1200
+ semanticMap: SemanticMapDescriptor;
1201
+ /**
1202
+ * The layer's geometry-agnostic hit-test declaration: the spatial structure its marks present,
1203
+ * so the runtime builds the matching index without branching on geom name. Pure data.
1204
+ */
1205
+ spatialMap: SpatialMapDescriptor;
1206
+ /**
1207
+ * The legend/tooltip mark for this layer — the geom's declared cartesian-natural swatch shape.
1208
+ * Pure serialisable data; a polar coord refines a `square` mark to a `slice` at read time.
1209
+ */
1210
+ swatchShape: SwatchShape;
1211
+ /**
1212
+ * `true` when the layer's compile threw or produced invalid output and it was quarantined to a
1213
+ * sentinel that carries no data. The runtime renders a failed-layer placeholder for it; sibling
1214
+ * layers and shared scale domains are unaffected because a sentinel contributes nothing.
1215
+ */
1216
+ failed: boolean;
1264
1217
  }
1265
1218
 
1266
1219
  /**
1267
1220
  * `CompiledLayer` narrowed to a specific `geom`. Lets call sites that already know the geom
1268
1221
  * (geom renderers dispatched off `layer.geom`) take a typed `params` directly, eliminating the
1269
- * `as ...GeomParams` cast that was needed when `params` was the full param union.
1222
+ * `as ...GeomParams` cast that was needed when `params` was the full param union. A custom geom's
1223
+ * render plugin passes `TParams` explicitly (its definition's params type); built-in call sites
1224
+ * default it from the geom name.
1270
1225
  */
1271
- export declare type CompiledLayerFor<G extends GeomName> = Omit<CompiledLayer, 'geom' | 'params'> & {
1226
+ export declare type CompiledLayerFor<G extends string, TParams = BuiltinParams<G>> = Omit<CompiledLayer, 'geom' | 'params'> & {
1272
1227
  geom: G;
1273
- params: Extract<LayerSpec, {
1274
- geom: G;
1275
- }>['params'];
1228
+ params: TParams;
1276
1229
  };
1277
1230
 
1278
1231
  /**
1279
1232
  * Per-layer highlight state on `CompiledLayer.highlight`. Bundles the geom's composition
1280
1233
  * strategy (set at layer compile time, never changes after) with the composition output
1281
1234
  * (rewritten by the highlights compile stage when applicable highlights match).
1282
- *
1283
- * This is the **static** highlight (from the spec). A live hover transiently supersedes it: while a
1284
- * pointer is over the chart, render the hover result (see `HoverState`) in place of this.
1285
1235
  */
1286
1236
  export declare interface CompiledLayerHighlight {
1287
1237
  /** How this layer composes highlight matches above its base render. */
@@ -1290,13 +1240,8 @@ export declare interface CompiledLayerHighlight {
1290
1240
  composition: HighlightComposition;
1291
1241
  }
1292
1242
 
1293
- /** A render-ready legend: its resolved placement plus the items it lists, possibly merged across aesthetics. */
1294
1243
  export declare interface CompiledLegendGuide {
1295
- /**
1296
- * Visual SCALE keys this legend represents (may be merged): one or more of `color` / `size` / `alpha` /
1297
- * `strokeWidth` / `lineType` — match those exact tokens. When this includes `'size'` the legend is a BUBBLE
1298
- * legend: paint each item as a circle sized by {@link LegendItemVisual.size} (pixels) rather than a swatch.
1299
- */
1244
+ /** Which aesthetics this legend represents (may be merged) */
1300
1245
  aesthetics: AestheticKey[];
1301
1246
  /** Title text, null means no title */
1302
1247
  title: string | null;
@@ -1308,12 +1253,7 @@ export declare interface CompiledLegendGuide {
1308
1253
  items: LegendItem[];
1309
1254
  }
1310
1255
 
1311
- /** The plotting area's frame, telling the renderer whether to draw a border around the panel. */
1312
1256
  export declare interface CompiledPanel {
1313
- /**
1314
- * Panel-rect outline. Drawn alongside the grid lines and independent of axis visibility — an axis can be
1315
- * hidden while this border still shows (and vice versa).
1316
- */
1317
1257
  border: {
1318
1258
  isVisible: boolean;
1319
1259
  };
@@ -1346,55 +1286,30 @@ declare interface CompiledPositionAdjuster {
1346
1286
  */
1347
1287
  export declare interface CompiledPositionScale<Input = DataValue> extends CompiledScaleBase<Input> {
1348
1288
  kind: 'position';
1349
- /**
1350
- * Maps a data value to its normalized position.
1351
- * Normalized to [0,1]: x is 0=left…1=right, y is 0=bottom…1=top (data-up). SVG / top-origin
1352
- * renderers invert y as `1 - y`.
1353
- * Continuous unclamped scales extrapolate outside [0,1] for out-of-domain inputs, so [0,1] is the
1354
- * in-domain range, not a hard guarantee.
1355
- */
1356
1289
  map: (value: Input) => number;
1357
- /**
1358
- * Band width as a fraction of the panel's main-axis extent, for discrete position scales. This is
1359
- * the full category band; a per-observation xMin/xMax extent may be a narrower dodged sub-band
1360
- * inside it. Stays keyed to aesthetic x even when the coord is flipped. Null for continuous /
1361
- * datetime; null or ≤0 means derive the extent per observation instead.
1362
- */
1290
+ /** Band width in [0,1] space for discrete position scales. Null for continuous/datetime. */
1363
1291
  bandwidth: number | null;
1364
1292
  }
1365
1293
 
1366
1294
  declare type CompiledPositionScaleOptions<Input = DataValue> = Omit<CompiledPositionScale<Input>, 'kind' | 'aesthetic' | 'spec'>;
1367
1295
 
1368
- /** Any compiled scale, discriminated by `kind` into position, visual, or identity. */
1369
1296
  export declare type CompiledScale<Input = DataValue> = CompiledPositionScale<Input> | CompiledVisualScale<Input> | CompiledIdentityScale;
1370
1297
 
1371
- /** Fields shared by every compiled scale, regardless of `kind`. */
1372
1298
  declare interface CompiledScaleBase<Input = DataValue> {
1373
- /** The aesthetic this scale drives (e.g. 'x', 'color'). */
1374
1299
  aesthetic: AestheticKey;
1375
- /**
1376
- * The scale's resolved input domain, in domain order.
1377
- * Iterate and `map(value)` over this to enumerate legend / palette swatches.
1378
- */
1379
1300
  domain: Input[];
1380
- /** The spec this scale was compiled from. */
1381
1301
  spec: ScaleSpec;
1382
- /** Produces axis/legend tick values; see {@link GenerateTicksOptions} for the supported variants. */
1383
1302
  generateTicks: (options?: GenerateTicksOptions) => Input[];
1384
1303
  }
1385
1304
 
1386
- /** All compiled scales for a plot, keyed by the aesthetic each one drives. Absent keys are unmapped. */
1387
1305
  export declare type CompiledScales = Partial<Record<ScaledAestheticKey, CompiledScale>>;
1388
1306
 
1389
1307
  /**
1390
- * Compile-time projection of a freeform rectangle. `x`/`y`/`width`/`height` are normalized
1391
- * `[0, 1]` of the panel with a top-left origin (no y-flip) — unlike observation anchors, which
1392
- * are data-space.
1308
+ * Compile-time projection of a freeform rectangle.
1393
1309
  */
1394
1310
  export declare interface CompiledShape {
1395
1311
  id: string;
1396
1312
  kind: ShapeKind;
1397
- /** Whether the shape draws behind the geoms (background) or over them (foreground). */
1398
1313
  zOrder: ShapeZOrder;
1399
1314
  x: number;
1400
1315
  y: number;
@@ -1403,30 +1318,13 @@ export declare interface CompiledShape {
1403
1318
  fillColor: string;
1404
1319
  fillOpacity: number;
1405
1320
  strokeWidth: number;
1406
- /** Stroke color, or `null` for no border. */
1407
1321
  strokeColor: string | null;
1408
1322
  }
1409
1323
 
1410
1324
  /**
1411
- * Fully compiled spec, ready for the renderer — the output of {@link Compiler.compile} and the input a
1412
- * renderer paints from. Coords/scales/guides/config/annotations are resolved descriptors; geom data
1413
- * carries its visual values in normalized `[0,1]` position space (y up). See {@link Compiler} for the
1414
- * cross-cutting conventions and {@link LayoutCompiler} for turning this into pixel rects.
1415
- *
1416
- * Not serializable: scales hold live `map` closures (see {@link CompiledPositionScale}). Keep a
1417
- * `CompiledSpec` in memory only — to persist or snapshot, store {@link CompiledSpec.spec} (or the
1418
- * original {@link CompilerInput}) and recompile.
1419
- *
1420
- * Reference stability: each compile returns a fresh top-level object, but a nested slice whose inputs
1421
- * did not change keeps its prior reference (per-stage memoization). Renderers can therefore subscribe
1422
- * by reference identity on individual slices (`layers`, `scales`, …) to skip unchanged work.
1325
+ * Fully compiled spec, ready for the renderer.
1423
1326
  */
1424
1327
  export declare interface CompiledSpec {
1425
- /**
1426
- * The resolved spec this output was compiled from, retained so callers can recompile or inspect the
1427
- * source. This is the canonical live {@link Spec} to apply {@link Command}s against — feed a
1428
- * command's resulting spec back through {@link Compiler.recompile}.
1429
- */
1430
1328
  spec: Spec;
1431
1329
  coordSystem: CoordSystem;
1432
1330
  layers: CompiledLayer[];
@@ -1453,11 +1351,7 @@ declare interface CompiledStickerAnnotation {
1453
1351
  }
1454
1352
 
1455
1353
  /**
1456
- * Compile-time projection of a text annotation. `x`/`y`/`width` are normalized `[0, 1]` of the
1457
- * panel with a top-left origin (no y-flip), like `CompiledShape` — panel fractions, not pixels.
1458
- *
1459
- * There is deliberately no height: it is content-intrinsic. The renderer lays out the text within
1460
- * `width` and extends the box down to the panel's bottom edge.
1354
+ * Compile-time projection of a text annotation.
1461
1355
  */
1462
1356
  export declare interface CompiledTextAnnotation {
1463
1357
  id: string;
@@ -1465,9 +1359,7 @@ export declare interface CompiledTextAnnotation {
1465
1359
  x: number;
1466
1360
  y: number;
1467
1361
  width: number;
1468
- /** Fill behind the text, or `null` for no background. */
1469
1362
  backgroundColor: string | null;
1470
- /** Whether the background is faded into the plot or fully opaque. */
1471
1363
  backgroundColorStyle: TextAnnotationBackgroundColorStyle;
1472
1364
  }
1473
1365
 
@@ -1477,45 +1369,13 @@ export declare interface CompiledTextAnnotation {
1477
1369
  */
1478
1370
  export declare interface CompiledVisualScale<Input = DataValue> extends CompiledScaleBase<Input> {
1479
1371
  kind: 'visual';
1480
- /**
1481
- * Maps a data value to a concrete visual output (color string, pixel size, opacity, …).
1482
- * Already applied per observation by the visual mapper — read the resolved value via the matching
1483
- * value reader. Call `map` only for out-of-band values like legend swatches, enumerating the
1484
- * inputs via {@link CompiledScaleBase.domain}.
1485
- */
1486
1372
  map: (value: Input) => DataValue;
1487
1373
  }
1488
1374
 
1489
1375
  declare type CompiledVisualScaleOptions<Input = DataValue> = Omit<CompiledVisualScale<Input>, 'kind' | 'aesthetic' | 'spec'>;
1490
1376
 
1491
1377
  /**
1492
- * The grammar-of-graphics engine. Orchestrates the compilation of a spec into render-ready output.
1493
- *
1494
- * Start here. The full pipeline a renderer drives is:
1495
- * `{@link CompilerInput} → {@link createCompiler}() → {@link CompiledSpec} → {@link Command}.apply →
1496
- * {@link Compiler.recompile}`. {@link Compiler.compile} resolves the input and runs every stage;
1497
- * {@link Compiler.recompile} re-runs the stages against an already-resolved {@link Spec} (the live
1498
- * {@link CompiledSpec.spec}) — that is how a dispatched command reaches the view.
1499
- *
1500
- * Conventions that hold across every compiled output (so a renderer can be built against this package
1501
- * alone):
1502
- * - Positions are normalized to `[0,1]` with **y up**: x runs 0=left…1=right, y runs 0=bottom…1=top.
1503
- * A top-origin renderer (SVG, canvas) must invert y as `1 - y`. Continuous unclamped scales may
1504
- * extrapolate outside `[0,1]` for out-of-domain inputs.
1505
- * - The engine emits **descriptors, not strings**: values carry a {@link ValueFormat} and the renderer
1506
- * formats them at paint time (locale lives renderer-side). Likewise scales are callable `map`
1507
- * functions, never D3 config for the renderer to rebuild.
1508
- * - Pixel layout is a separate step the engine owns too: feed a {@link CompiledSpec} (or its slices)
1509
- * through {@link LayoutCompiler} to get the plot/panel/axis rects; geoms paint in `[0,1]` inside the
1510
- * panel rect. Renderers do not reimplement the grid.
1511
- * - Resolve cross-references **by id**: a layer's scale comes from {@link CompiledSpec.scales} keyed by
1512
- * aesthetic, its axis from {@link CompiledSpec.guides}; marks key off the layer id.
1513
- *
1514
- * Statefulness: a `Compiler` retains the last dataset seen by {@link Compiler.compile}/{@link Compiler.recompile}
1515
- * and reuses it to derive guide and legend labels. Call {@link Compiler.compile} (which takes data) at
1516
- * least once before any {@link Compiler.recompile}({ spec }), otherwise label derivation has no dataset.
1517
- * Per-stage memoization and dataset retention are instance-scoped, so create one `Compiler` per graph
1518
- * and reuse it across recompiles rather than building a fresh one per frame.
1378
+ * The main compiler. It orchestrates the compilation of a spec into a render-ready output.
1519
1379
  *
1520
1380
  * Each layer is processed through the pipeline. Coords and scales are compiled separately as they apply to
1521
1381
  * the entire plot.
@@ -1540,15 +1400,6 @@ export declare class Compiler {
1540
1400
  private lastData;
1541
1401
  private readonly stages;
1542
1402
  constructor(dataCompiler: DataCompiler, specCompiler: SpecResolver, transformCompiler: TransformCompiler, constantMappingCompiler: ConstantMappingCompiler, layerValidationCheck: LayerValidationCheck, layerCompiler: LayerCompiler, scaleCompiler: ScaleCompiler, coordCompiler: CoordCompiler, positionMapperCompiler: PositionMapperCompiler, visualMapperCompiler: VisualMapperCompiler, guideCompiler: GuideCompiler, summariseCompiler: SummariseCompiler, highlightsCompiler: HighlightsCompiler, configCompiler: ConfigCompiler, annotationsCompiler: AnnotationsCompiler);
1543
- /**
1544
- * Resolve `input` (a low-level spec or a high-level GraphConfig) against `data` and run the full
1545
- * pipeline, producing a render-ready {@link CompiledSpec}. The primary entry point for consumers.
1546
- *
1547
- * Routing: reach for `compile` whenever the input or the {@link RendererContext} changes — including
1548
- * a theme or palette switch, since `ctx` is consumed **only** here ({@link Compiler.recompile} takes
1549
- * no `ctx`). For a data-only or command-driven update prefer {@link Compiler.recompile}, which keeps
1550
- * layer ids stable so id-keyed renderers can transition marks instead of remounting them.
1551
- */
1552
1403
  compile({ input, data, ctx }: {
1553
1404
  input: CompilerInput;
1554
1405
  data: Data;
@@ -1557,15 +1408,6 @@ export declare class Compiler {
1557
1408
  /**
1558
1409
  * Run the rendering pipeline against an already-resolved {@link Spec}.
1559
1410
  *
1560
- * This is the second half of the command dispatch loop. To reflect a {@link Command} in the view:
1561
- * `const result = command.apply(compiled.spec); if (!result) return; recompile({ spec: result.spec })`.
1562
- * A `null` apply result is a no-op — skip the recompile. {@link CompiledSpec.spec} is the canonical
1563
- * live {@link Spec} to apply commands against; pass the new spec straight back here. Using a
1564
- * {@link CommandStackManager} is optional — the minimal path is just apply-then-recompile.
1565
- *
1566
- * Requires a prior {@link Compiler.compile} (or recompile-with-data): the retained dataset is what
1567
- * guide and legend labels are derived from.
1568
- *
1569
1411
  * When `data` is provided, it is parsed and spliced onto `spec` before the pipeline
1570
1412
  * runs — useful for swapping the dataset without re-resolving the spec, so layer ids
1571
1413
  * survive and id-keyed renderers can transition marks instead of unmounting them.
@@ -1610,31 +1452,22 @@ export declare type CompilerCacheStats = Record<CompilerStageName, MemoStats>;
1610
1452
  */
1611
1453
  export declare type CompilerInput = SpecInput | GraphConfig;
1612
1454
 
1613
- /** Identifier of a single compile pipeline stage, used to key the per-stage cache counters. */
1614
1455
  declare type CompilerStageName = (typeof COMPILER_STAGE_KEYS)[number];
1615
1456
 
1616
- /** A difference arrow ready to paint: SVG paths, label text and box styling, all in panel-local pixel space. */
1617
1457
  export declare interface ComputedDifferenceArrow {
1618
1458
  id: string;
1619
1459
  /** SVG path for the arrow body. Coordinates are in panel-local pixel space. */
1620
1460
  linePath: string;
1621
1461
  /** SVG path for the arrowhead. */
1622
1462
  arrowheadPath: string;
1623
- /**
1624
- * Pixel position of the label box CENTER. The engine does not size the box: measure `labelText`
1625
- * yourself, then box width = measured width + `2·labelPaddingX` and box height =
1626
- * `labelLineHeight + 2·labelPaddingY`.
1627
- */
1463
+ /** Pixel position for the label box midpoint. */
1628
1464
  labelPosition: PointPosition;
1629
1465
  labelText: string;
1630
1466
  /** Resolved color for the arrow. `null` lets the renderer fall back to a neutral default. */
1631
1467
  color: string | null;
1632
1468
  /** Stroke width derived from the arrow size. */
1633
1469
  strokeWidth: number;
1634
- /**
1635
- * Label box line height in pixels. Doubles as the label's font size (paint the text at this size
1636
- * with `labelFontWeight`); the box height is `labelLineHeight + 2·labelPaddingY`.
1637
- */
1470
+ /** Label box line height in pixels. */
1638
1471
  labelLineHeight: number;
1639
1472
  /** Label box horizontal padding (left + right). */
1640
1473
  labelPaddingX: number;
@@ -1646,15 +1479,6 @@ export declare interface ComputedDifferenceArrow {
1646
1479
  labelFontWeight: number;
1647
1480
  }
1648
1481
 
1649
- /**
1650
- * A freeform arrow ready to paint: SVG paths plus stroke/dash styling, all in panel-local pixel space.
1651
- *
1652
- * Sticker rendering (`hasStickerStyle`): paint the union of the three path strings (`linePath`,
1653
- * `startArrowheadPath`, `endArrowheadPath`) as one shape with a light outline and a drop shadow. The
1654
- * outline and shadow extend past the geometry, so the drop-shadow filter region must be inflated on
1655
- * every side by `arrowheadExtent + strokeWidth + outlineWidth + blurRadius` to avoid clipping at the
1656
- * arrowheads (the last two are renderer-owned).
1657
- */
1658
1482
  export declare interface ComputedFreeformArrow {
1659
1483
  id: string;
1660
1484
  /** SVG path for the arrow body, in panel-local pixels. */
@@ -1663,7 +1487,7 @@ export declare interface ComputedFreeformArrow {
1663
1487
  startArrowheadPath: string;
1664
1488
  /** SVG path for the end arrowhead or '' when there is none. */
1665
1489
  endArrowheadPath: string;
1666
- /** Resolved color, or `null` to fall back to the theme `defaultAnnotationArrowStroke` token. */
1490
+ /** Resolved color or `null` to fall back to theme default. */
1667
1491
  color: string | null;
1668
1492
  strokeWidth: number;
1669
1493
  /** SVG `stroke-dasharray` or `null` for a solid line. */
@@ -1693,14 +1517,9 @@ export declare const computeDifferenceArrow: ({ arrow, mainAxis, panelWidth, pan
1693
1517
 
1694
1518
  declare interface ComputeDifferenceArrowParams {
1695
1519
  arrow: CompiledDifferenceArrow;
1696
- /**
1697
- * Chart orientation — `CartesianCoordSystem.mainAxis`. When `'y'` the arrow geometry is flipped to
1698
- * run horizontally. Pass `'x'` for a polar coord system.
1699
- */
1700
1520
  mainAxis: 'x' | 'y';
1701
1521
  panelWidth: number;
1702
1522
  panelHeight: number;
1703
- /** Graph-wide text scale, applied to the label so it sizes with the rest of the chart. */
1704
1523
  textScale: number;
1705
1524
  locale: Locale;
1706
1525
  }
@@ -1714,9 +1533,7 @@ export declare const computeDirectLabelsLayout: <TPayload>({ items, containerHei
1714
1533
 
1715
1534
  declare interface ComputeDirectLabelsLayoutInput<TPayload> {
1716
1535
  items: ReadonlyArray<DirectLabelInput<TPayload>>;
1717
- /** Pixel height of the area labels are placed within; positions are clamped to `[0, containerHeight]`. */
1718
1536
  containerHeight: number;
1719
- /** Pixel height of a single label, used to detect and resolve vertical overlap. */
1720
1537
  labelHeight: number;
1721
1538
  }
1722
1539
 
@@ -1726,12 +1543,14 @@ declare interface ComputeDirectLabelsLayoutInput<TPayload> {
1726
1543
  export declare const computeFreeformArrow: ({ arrow, panelWidth, panelHeight, }: ComputeFreeformArrowParams) => ComputedFreeformArrow | null;
1727
1544
 
1728
1545
  declare interface ComputeFreeformArrowParams {
1729
- arrow: CompiledFreeformArrow;
1546
+ arrow: FreeformArrowSpec;
1730
1547
  panelWidth: number;
1731
1548
  panelHeight: number;
1732
1549
  }
1733
1550
 
1734
- /** Wraps partial config options into a tagged `ConfigItem` for inclusion in a spec. */
1551
+ /**
1552
+ * Create a custom configuration
1553
+ */
1735
1554
  export declare function config(options: ConfigInput): ConfigItem;
1736
1555
 
1737
1556
  /**
@@ -1742,7 +1561,6 @@ declare class ConfigCompiler extends Stage<ConfigCompilerInput, CompiledConfig>
1742
1561
  protected run(input: ConfigCompilerInput): CompiledConfig;
1743
1562
  }
1744
1563
 
1745
- /** Inputs the config compiler needs: the config spec plus the compiled scales it resolves color references against. */
1746
1564
  declare interface ConfigCompilerInput {
1747
1565
  config: ConfigSpec;
1748
1566
  scales: CompiledScales;
@@ -1766,13 +1584,6 @@ declare interface ConfigItem {
1766
1584
  * All fields are required and always populated after resolution.
1767
1585
  */
1768
1586
  export declare interface ConfigSpec {
1769
- /**
1770
- * Locale used to interpret source values AND, by default, to format display
1771
- * output (axis labels, tooltips, numbers). Pass `formattingLocale` to a
1772
- * `format*` helper to override display only — it resolves
1773
- * `formattingLocale ?? parsingLocale`. The `duration` format is always
1774
- * English regardless of locale.
1775
- */
1776
1587
  parsingLocale: Locale;
1777
1588
  legend: LegendConfig;
1778
1589
  axes: AxesConfig;
@@ -1830,7 +1641,6 @@ declare interface Content {
1830
1641
  isSubtitleHidden?: boolean;
1831
1642
  caption?: string | RichTextContent;
1832
1643
  isCaptionHidden?: boolean;
1833
- /** Attribution shown in the footer; optional label text and link target. */
1834
1644
  source?: Partial<{
1835
1645
  label: string;
1836
1646
  url: string;
@@ -1942,7 +1752,6 @@ declare type ContinuousScaleSpec = Required<ContinuousScaleInput>;
1942
1752
  */
1943
1753
  export declare function convertSpecToInput(spec: Spec): SpecInput;
1944
1754
 
1945
- /** Builders for the chart's coordinate system. Pass the result as the spec's `coord` to choose cartesian, flipped, or polar. */
1946
1755
  export declare const coord: {
1947
1756
  /**
1948
1757
  * Standard cartesian (x-y) coordinate system. This is the default if no coord is specified.
@@ -1987,23 +1796,21 @@ declare class CoordCompiler {
1987
1796
  declare type CoordInput = CartesianCoordInput | FlipCoordInput | PolarCoordInput;
1988
1797
 
1989
1798
  /**
1990
- * Built-in coordinate system implementations keyed by {@link CoordType}.
1799
+ * Built-in coordinate system implementations keyed by {@link CoordType}. `flip` and `polar` read each
1800
+ * layer geom's position-channel manifest via {@link GeomRegistry} to decide which columns to project.
1991
1801
  */
1992
1802
  declare class CoordRegistry extends Registry<CoordType, CoordStrategy> {
1993
- constructor();
1803
+ constructor(geomRegistry: GeomRegistry);
1994
1804
  }
1995
1805
 
1996
- /** What a {@link CoordStrategy} receives during the setup pass — the coord spec plus the plot's scales and compiled layers. */
1997
1806
  declare interface CoordSetupInput {
1998
1807
  coordSpec: CoordSpec;
1999
1808
  scales: ScaleSpec[];
2000
1809
  layers: CompiledLayer[];
2001
1810
  }
2002
1811
 
2003
- /** Output of the setup pass: the resolved coordinate system plus scales the coord system may have adjusted. */
2004
1812
  declare type CoordSetupResult = {
2005
1813
  coordSystem: CoordSystem;
2006
- /** Scales after any coord-imposed adjustments (e.g. `flip` reversing an axis). */
2007
1814
  scales: ScaleSpec[];
2008
1815
  };
2009
1816
 
@@ -2036,13 +1843,9 @@ declare interface CoordStrategy {
2036
1843
  /**
2037
1844
  * Render-ready coordinate system (discriminated union).
2038
1845
  * Discriminates on geometric paradigm: cartesian plane vs polar projection.
2039
- *
2040
- * A mark's geometry is the `(geom, coordSystem.type)` pair, not the geom alone: the same geom
2041
- * renders differently per coord — a bar is a rect in cartesian and an arc in polar.
2042
1846
  */
2043
1847
  export declare type CoordSystem = CartesianCoordSystem | PolarCoordSystem;
2044
1848
 
2045
- /** What a {@link CoordStrategy} receives during the transform pass — the coord spec and position-mapped layers. */
2046
1849
  declare interface CoordTransformInput {
2047
1850
  coordSpec: CoordSpec;
2048
1851
  layers: CompiledLayer[];
@@ -2066,57 +1869,46 @@ declare interface CountStatSpec {
2066
1869
  type: 'count';
2067
1870
  }
2068
1871
 
2069
- /** Reader for the raw value behind a layer's `alpha` mapping. */
2070
- export declare const createAlphaValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
2071
-
2072
- /**
2073
- * Builds the d3 area + top-line generators for an area layer.
2074
- *
2075
- * The `mainAxis === 'y'` branch is structural: d3-area exposes two separate APIs —
2076
- * `.x(main).y0(crossMin).y1(crossMax)` for vertical fills and `.y(main).x0(crossMin).x1(crossMax)`
2077
- * for horizontal fills — there is no orientation-agnostic setter. After `coord.flip()` has run,
2078
- * `POSITION_VARIABLES.x` holds the measure (fill bound) and `POSITION_VARIABLES.y` the band
2079
- * center, so the flipped branch reads `xMin`/`xMax` as the fill bounds and `y` as the main-axis
2080
- * position.
2081
- */
2082
- export declare const createAreaPathGenerators: (coordSystem: CartesianCoordSystem, params: AreaGeomParams) => AreaPathGenerators;
1872
+ export declare const createAlphaValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2083
1873
 
2084
- /** Reader for the raw value behind a layer's `color` mapping. */
2085
- export declare const createColorValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1874
+ export declare const createColorValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2086
1875
 
2087
1876
  /**
2088
- * Builds a {@link Compiler} with all built-in stages and registries wired together. The standard
2089
- * entry point for consumers; use this rather than constructing `Compiler` by hand.
2090
- *
2091
- * The returned {@link Compiler} is the head of the pipeline
2092
- * (`{@link CompilerInput} → createCompiler() → {@link CompiledSpec} → {@link Command}.apply →
2093
- * {@link Compiler.recompile}`) — see {@link Compiler} for the conventions a renderer relies on.
2094
- * Because the compiler is stateful (retains the last dataset, memoizes per stage), create one
2095
- * instance per graph and reuse it across recompiles instead of calling `createCompiler` each frame.
1877
+ * Builds a compiler instance. Pass `geoms` to register custom (or override built-in) geom
1878
+ * definitions per-instance there is no global registry to mutate, so injected geoms never bleed
1879
+ * across instances. An injected geom whose `type` matches a built-in overrides it (last write wins).
2096
1880
  */
2097
- export declare const createCompiler: () => Compiler;
1881
+ export declare const createCompiler: (opts?: {
1882
+ geoms?: readonly Geom[];
1883
+ }) => Compiler;
2098
1884
 
2099
1885
  /**
2100
- * Fresh empty highlight state for a layer of the given geom, or `null` when the geom opts
2101
- * out of highlighting (no entry in `HIGHLIGHT_STRATEGY_BY_GEOM`).
1886
+ * Fresh empty highlight state for the given composition strategy, or `null` when the strategy is
1887
+ * `null` (the geom opts out of highlighting). Callers resolve the strategy from the geom definition.
2102
1888
  */
2103
- export declare function createEmptyHighlight(geom: GeomName): CompiledLayerHighlight | null;
2104
-
2105
- /** Reader for the raw value behind a layer's `group` mapping. */
2106
- export declare const createGroupValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1889
+ export declare function createEmptyHighlight(strategy: HighlightStrategy | null): CompiledLayerHighlight | null;
2107
1890
 
2108
1891
  /**
2109
- * The single structural model of a headline item's rows and segments. Both the measurer (which sizes
2110
- * each segment without a DOM) and the JSX (which paints them) read this, so a layout change — a new
2111
- * row, a reordered segment lands in one place and the two can't drift. Styling and font pixels are
2112
- * resolved by each consumer from the roles; this model carries only structure, text, and gaps.
1892
+ * Builds a Graphy authoring surface for a set of custom geoms and/or annotations: a `geom` builder that
1893
+ * merges the built-in methods with one method per registered custom geom, an `annotation` builder with
1894
+ * one method per registered annotation kind, plus the standard `createSpec`. The 90% case stays the
1895
+ * plain `import { geom, createSpec }`; reach for this only when authoring custom geoms (decision 8) or
1896
+ * custom annotations (ADR-035). Registration is per-instance — geoms are injected to
1897
+ * `createCompiler({ geoms })`; annotations need no compile-side registry (coordinate resolution is
1898
+ * generic), only the render plugin via `<GraphProvider annotationPlugins={[...]}>`.
2113
1899
  */
2114
- export declare const createHeadlineItemRows: (item: FormattedHeadlineItem) => HeadlineRow[];
1900
+ export declare function createGraphyBuilder<const Geoms extends readonly Geom[] = readonly [], const Annotations extends readonly AnnotationDef[] = readonly []>(options: {
1901
+ geoms?: Geoms;
1902
+ annotations?: Annotations;
1903
+ }): {
1904
+ geom: typeof geom & CustomGeomBuilders<Geoms>;
1905
+ annotation: CustomAnnotationBuilders<Annotations>;
1906
+ createSpec: typeof createSpec;
1907
+ };
2115
1908
 
2116
- /** Reader for the raw value behind a layer's `label` mapping. */
2117
- export declare const createLabelValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1909
+ export declare const createGroupValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2118
1910
 
2119
- export declare const createLinePathGenerator: (params: LineGeomParams) => Line<Observation>;
1911
+ export declare const createLabelValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2120
1912
 
2121
1913
  /**
2122
1914
  * Builds a per-observation reader for an `AestheticValue`:
@@ -2126,20 +1918,12 @@ export declare const createLinePathGenerator: (params: LineGeomParams) => Line<O
2126
1918
  * so downstream code can rely on a typed `DataValue`.
2127
1919
  * - `undefined` / unknown variable → `null`.
2128
1920
  */
2129
- export declare const createRawValueReader: (data: Dataset, aestheticValue: AestheticValue | undefined) => RawValueReader;
1921
+ export declare const createRawValueReader: (data: Dataset, aestheticValue: AestheticValue | undefined) => ((observation: Observation) => DataValue);
2130
1922
 
2131
- /**
2132
- * Per-observation reader for the segment-y value of a compiled layer, in original data units.
2133
- *
2134
- * When the layer is stacked, the y position columns already hold cumulative band bounds (draw
2135
- * segments directly) — so for a label or tooltip showing the segment's own value, use this reader,
2136
- * not {@link getY} (which is normalized to `[0, 1]`). It auto-selects `yRaw` for stacked layers and
2137
- * the user's y mapping otherwise.
2138
- */
1923
+ /** Per-observation reader for the segment-y value of a compiled layer. */
2139
1924
  export declare function createSegmentYReader(layer: CompiledLayer): (observation: Observation) => DataValue;
2140
1925
 
2141
- /** Reader for the raw value behind a layer's `size` mapping. */
2142
- export declare const createSizeValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1926
+ export declare const createSizeValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2143
1927
 
2144
1928
  /**
2145
1929
  * Create a new spec, optionally piping spec items in one call. Data is passed separately
@@ -2159,58 +1943,117 @@ export declare const createSizeValueReader: (data: Dataset, mapping: AesMapping)
2159
1943
  */
2160
1944
  export declare function createSpec(...items: Array<AesMapping | SpecItem>): SpecInput;
2161
1945
 
2162
- export declare const createStableKeyGenerator: (data: Dataset, mapping: AesMapping, layerId: string) => ((observation: Observation) => string);
2163
-
2164
- /** Reader for the raw value behind a layer's `strokeWidth` mapping. */
2165
- export declare const createStrokeWidthValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1946
+ export declare const createStrokeWidthValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2166
1947
 
2167
1948
  /**
2168
- * The single descriptor string entry point: turns a compiler-emitted {@link ValueFormat} (plus a
2169
- * locale and number-format config) into a reusable {@link ValueFormatter}. A renderer materializing a
2170
- * `valueFormat` it pulled off a compiled guide/legend/headline goes through here rather than
2171
- * branching on `type` itself.
2172
- *
2173
- * Dispatches on `valueFormat.type` and throws on an unknown kind (so a newly added format surfaces
2174
- * loudly instead of silently formatting wrong). A `lookup` formatter needs the per-call `Observation`
2175
- * to pick its case — see {@link ValueFormatter}; every other kind ignores the observation.
1949
+ * Creates a value formatter for a given value format.
2176
1950
  */
2177
1951
  export declare const createValueFormatter: (params: ValueFormatterFactoryParams) => ValueFormatter;
2178
1952
 
2179
- /** Reader for the raw value behind a layer's `x` mapping. */
2180
- export declare const createXValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1953
+ export declare const createXValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2181
1954
 
2182
- /** Reader for the raw value behind a layer's `y` mapping. */
2183
- export declare const createYValueReader: (data: Dataset, mapping: AesMapping) => RawValueReader;
1955
+ export declare const createYValueReader: (data: Dataset, mapping: AesMapping) => (observation: Observation) => DataValue;
2184
1956
 
2185
1957
  /** Three letter ISO string representing the currency */
2186
- export declare type CurrencyIso = 'aed' | 'aud' | 'bdt' | 'bhd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'cop' | 'czk' | 'dkk' | 'egp' | 'eur' | 'gbp' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'mxn' | 'myr' | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'qar' | 'ron' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' | 'try' | 'twd' | 'usd' | 'vnd' | 'zar';
1958
+ declare type CurrencyIso = 'aed' | 'aud' | 'bdt' | 'bhd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'cop' | 'czk' | 'dkk' | 'egp' | 'eur' | 'gbp' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'mxn' | 'myr' | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'qar' | 'ron' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' | 'try' | 'twd' | 'usd' | 'vnd' | 'zar';
2187
1959
 
2188
1960
  declare interface CurrencyValueFormat {
2189
1961
  type: 'currency';
2190
1962
  iso: CurrencyIso;
2191
1963
  }
2192
1964
 
2193
- /** A single named color slot within a custom palette supplied by the renderer. */
2194
- export declare type CustomPaletteColor = {
1965
+ /**
1966
+ * One builder method per registered custom annotation, keyed by its `type` and typed from its
1967
+ * definition — so `annotation.calloutBox(...)` exists because `calloutBox` was registered, with params
1968
+ * checked against the definition's `TParams`. Returns a pipeable {@link AnnotationItem}.
1969
+ */
1970
+ declare type CustomAnnotationBuilders<Annotations extends readonly AnnotationDef[]> = {
1971
+ [Definition in Annotations[number] as Definition['type']]: (options: CustomAnnotationOptions<AnnotationParamsOf<Definition>>) => AnnotationItem;
1972
+ };
1973
+
1974
+ /**
1975
+ * A custom (registered) annotation instance. `type` names the registered kind (resolves the render-side
1976
+ * `draw`); `coordinates` resolve to targets at compile time; `params` is opaque at the spec level — the
1977
+ * registration-typed builder types it from the annotation definition. `zOrder` overrides the kind's
1978
+ * declared default.
1979
+ */
1980
+ export declare interface CustomAnnotationInput {
1981
+ id?: string;
1982
+ type: string;
1983
+ coordinates: AnnotationCoordinateInput[];
1984
+ params?: Record<string, unknown>;
1985
+ zOrder?: AnnotationZOrder;
1986
+ }
1987
+
1988
+ /** Options for authoring a custom annotation through the registration-typed builder (ADR-035). */
1989
+ declare interface CustomAnnotationOptions<TParams extends object> {
1990
+ coordinates: AnnotationCoordinateInput[];
1991
+ params?: Partial<TParams>;
1992
+ zOrder?: AnnotationZOrder;
1993
+ id?: string;
1994
+ }
1995
+
1996
+ export declare interface CustomAnnotationSpec {
2195
1997
  id: string;
2196
- hex: string;
2197
- name?: string;
1998
+ type: string;
1999
+ coordinates: AnnotationCoordinateSpec[];
2000
+ params: Record<string, unknown>;
2001
+ /** `null` defers to the renderer's declared default zOrder. */
2002
+ zOrder: AnnotationZOrder | null;
2003
+ }
2004
+
2005
+ /**
2006
+ * One builder method per registered custom geom, keyed by its `type` and typed from its definition —
2007
+ * so `geom.candlestick(...)` exists because `candlestick` was registered, with params checked against
2008
+ * the geom's `TParams` (decision 8).
2009
+ */
2010
+ declare type CustomGeomBuilders<Geoms extends readonly Geom[]> = {
2011
+ [Definition in Geoms[number] as Definition['type'] & string]: (options?: CustomGeomOptions<ParamsOf<Definition>, MappableAes<Definition>>) => LayerInput;
2198
2012
  };
2199
2013
 
2014
+ /**
2015
+ * Options for authoring a custom geom's layer through the registration-typed builder. Mirrors the
2016
+ * built-in geom options, but `params` is typed from the registered definition's `TParams` and `aes`
2017
+ * is constrained to the geom's declared mapping keys (`TAes`) — an undeclared aesthetic is rejected.
2018
+ */
2019
+ declare interface CustomGeomOptions<TParams extends object, TAes extends string> {
2020
+ aes?: Partial<Record<TAes, AestheticValue>>;
2021
+ stat?: StatName | StatInput;
2022
+ position?: PositionType;
2023
+ yScaleType?: YScaleType;
2024
+ params?: Partial<TParams>;
2025
+ transforms?: TransformInput[];
2026
+ interactive?: boolean;
2027
+ dataLabels?: DataLabelsInput;
2028
+ }
2029
+
2030
+ /**
2031
+ * A layer input for a custom-registered geom whose `type` is not a built-in name. `params` is opaque
2032
+ * at the spec level; the registration-typed builder is where it is typed from the geom definition.
2033
+ */
2034
+ declare interface CustomLayerInput extends LayerInputBase {
2035
+ geom: string;
2036
+ params?: Record<string, unknown>;
2037
+ }
2038
+
2039
+ /** A resolved layer spec for a custom-registered geom — the generic resolver's output shape. */
2040
+ declare interface CustomLayerSpec extends LayerSpecBase {
2041
+ geom: string;
2042
+ params: Record<string, unknown>;
2043
+ }
2044
+
2200
2045
  declare type CustomPaletteConfig = {
2201
2046
  type: 'custom';
2202
2047
  id: string;
2203
2048
  colors: string[];
2204
2049
  };
2205
2050
 
2206
- /** Reference to a user-registered custom palette by id, resolved against the palette registry. */
2207
2051
  declare type CustomPaletteInput = {
2208
2052
  type: 'custom';
2209
2053
  id: string;
2210
2054
  };
2211
2055
 
2212
- /** Renderer-owned custom palettes, keyed by `paletteId`, that a GraphConfig may reference by id. */
2213
- export declare type CustomPalettesInput = Record<string, CustomPaletteColor[]>;
2056
+ export declare type CustomPalettesInput = Record<string, string[]>;
2214
2057
 
2215
2058
  /**
2216
2059
  * Data to visualize. Structured as a table.
@@ -2264,9 +2107,7 @@ export declare type DataLabelPosition = 'inside' | 'outside';
2264
2107
 
2265
2108
  declare interface DataLabels {
2266
2109
  showDataLabels?: boolean;
2267
- /** Whether labels show raw values or each point's share of its stack/total. */
2268
2110
  dataLabelFormat?: 'absolute' | 'percentage';
2269
- /** Shows the summed total above each stack in stacked charts. */
2270
2111
  showStackTotals?: boolean;
2271
2112
  showCategoryLabels?: boolean;
2272
2113
  }
@@ -2302,16 +2143,12 @@ export declare interface DataLabelsConfig {
2302
2143
  labelSource: AestheticValue;
2303
2144
  }
2304
2145
 
2305
- /**
2306
- * Fully-derived data-labels content. Renderers map straight over `labels` and paint each as given —
2307
- * the engine resolves no overlaps between labels.
2308
- */
2146
+ /** Fully-derived data-labels content. Renderers map straight over `labels`. */
2309
2147
  export declare interface DataLabelsContent {
2310
2148
  /** Flat list, in layer-declaration order; per-observation labels precede stack totals within a layer. */
2311
2149
  labels: PlacedDataLabel[];
2312
2150
  }
2313
2151
 
2314
- /** User-facing data-labels options; any omitted field falls back to its resolved default. */
2315
2152
  export declare type DataLabelsInput = DeepPartial<Omit<DataLabelsConfig, 'labelSource'>>;
2316
2153
 
2317
2154
  /**
@@ -2325,10 +2162,6 @@ export declare type DataLabelTarget = 'observation' | 'aggregate';
2325
2162
  /**
2326
2163
  * Renderer-supplied measurer that knows which font to apply for each `DataLabelKind`. Lets
2327
2164
  * placement strategies measure text without the engine ever holding a `FontSpec`.
2328
- *
2329
- * Must return the FINAL plate size — text metrics plus the renderer's own padding. The engine
2330
- * treats the returned `width`/`height` as the plate dimensions (`PlacedDataLabel.width`/`height`)
2331
- * and does not add padding of its own.
2332
2165
  */
2333
2166
  export declare type DataLabelTextMeasurer = (kind: DataLabelKind, text: string) => MeasuredText;
2334
2167
 
@@ -2390,7 +2223,7 @@ export declare class Dataset {
2390
2223
  * Derives a new variable based on existing variables, using a table expression. If `valueFormat`
2391
2224
  * is omitted, a type-based default is used.
2392
2225
  */
2393
- deriveVariable(variable: VariableName, type: DataType, expression: (observation: Observation) => DataValue, valueFormat?: ValueFormat): Dataset;
2226
+ deriveVariable(variable: VariableName, type: DataType, expression: (observation: Observation, rowIndex: number) => DataValue, valueFormat?: ValueFormat): Dataset;
2394
2227
  /**
2395
2228
  * Renames a variable.
2396
2229
  */
@@ -2611,21 +2444,18 @@ declare type DeepPartial<T> = {
2611
2444
  [K in keyof T]?: T[K] extends Array<infer U> ? Array<DeepPartial<U>> : NonNullable<T[K]> extends object ? DeepPartial<NonNullable<T[K]>> : T[K];
2612
2445
  };
2613
2446
 
2447
+ export declare const DEFAULT_COLOR_PALETTE: string[];
2448
+
2614
2449
  /**
2615
2450
  * Default font style.
2616
2451
  */
2617
2452
  export declare const DEFAULT_FONT_STYLE = "normal";
2618
2453
 
2619
2454
  /**
2620
- * Numeric weight used when a `FontSpec` omits `weight`. A from-scratch measurer must apply the same
2621
- * default to produce identical metrics (and identical cache keys).
2455
+ * Default font weight.
2622
2456
  */
2623
2457
  export declare const DEFAULT_FONT_WEIGHT = 500;
2624
2458
 
2625
- /** Stroke width used when a line/area layer leaves `lineWidth` on `'auto'` and the observation carries none. */
2626
- export declare const DEFAULT_LINE_WIDTH = 2;
2627
-
2628
- /** Locale used when a consumer does not specify one. */
2629
2459
  export declare const DEFAULT_LOCALE: Locale;
2630
2460
 
2631
2461
  declare type DefaultPaletteConfig = {
@@ -2645,7 +2475,7 @@ declare interface DifferenceArrowDimensions {
2645
2475
  curveRadius: number;
2646
2476
  /** Stroke width of the arrow. */
2647
2477
  strokeWidth: number;
2648
- /** Line height of the label in pixels. Also the label's font size; box height = `labelLineHeight + 2·labelPaddingY`. */
2478
+ /** Line height of the label in pixels. */
2649
2479
  labelLineHeight: number;
2650
2480
  /** Horizontal label padding (left + right). */
2651
2481
  labelPaddingX: number;
@@ -2662,25 +2492,17 @@ declare interface DifferenceArrowDimensions {
2662
2492
  * are defaulted by the resolver.
2663
2493
  */
2664
2494
  export declare interface DifferenceArrowInput {
2665
- /** Stable id; generated by the resolver when omitted. */
2666
2495
  id?: string;
2667
- /** Observation the arrow's tail points from. */
2668
2496
  start: ObservationAnchorInput;
2669
- /** Observation the arrow's head points to. */
2670
2497
  end: ObservationAnchorInput;
2671
- /** What the arrow's label measures (raw gap, relative change, or share). */
2672
2498
  label: DifferenceArrowLabelKind;
2673
- /** null falls back to a theme default. */
2674
2499
  color?: string | null;
2675
2500
  size?: DifferenceArrowSize;
2676
- /** Offset of the label across the arrow, as a fraction of the arrow's length. */
2677
2501
  labelCrossPosition?: number;
2678
2502
  }
2679
2503
 
2680
- /** What a difference arrow's label measures: the raw gap, the relative change, or one value as a share of the other. */
2681
2504
  export declare type DifferenceArrowLabelKind = 'absolute-difference' | 'relative-difference' | 'proportion';
2682
2505
 
2683
- /** Preset visual scale for a difference arrow. */
2684
2506
  export declare type DifferenceArrowSize = 'small' | 'medium' | 'large';
2685
2507
 
2686
2508
  /**
@@ -2696,25 +2518,18 @@ export declare interface DifferenceArrowSpec {
2696
2518
  labelCrossPosition: number;
2697
2519
  }
2698
2520
 
2699
- /** One label to place, given by its data anchor as a normalized Y in `[0, 1]` (1 = top). */
2700
2521
  export declare interface DirectLabelInput<TPayload> {
2701
2522
  normalizedY: number;
2702
- /** Renderer-defined data round-tripped onto the resulting placement. */
2703
2523
  payload: TPayload;
2704
2524
  }
2705
2525
 
2706
- /** A label's resolved position after overlap resolution, plus its original anchor for drawing a connector. */
2707
2526
  export declare interface DirectLabelPlacement<TPayload> {
2708
- /** Vertical centre of the label after repulsion, in top-down pixel space. */
2709
2527
  y: number;
2710
- /** Pixel Y of the original data anchor — the start of any connector line back to the data. */
2711
2528
  anchorY: number;
2712
2529
  payload: TPayload;
2713
2530
  }
2714
2531
 
2715
- /** Result of laying out direct labels. `hasOverlap` flags whether any labels had to be nudged apart. */
2716
2532
  export declare interface DirectLabelsLayout<TPayload> {
2717
- /** Placements in ascending Y order. Items whose final Y falls outside the rect are omitted. */
2718
2533
  placements: Array<DirectLabelPlacement<TPayload>>;
2719
2534
  hasOverlap: boolean;
2720
2535
  }
@@ -2755,25 +2570,15 @@ declare type DiscreteScaleOptions<RangeValue extends number | string = number |
2755
2570
  declare type DiscreteScaleSpec = Required<DiscreteScaleInput>;
2756
2571
 
2757
2572
  /** A value format with no inner lookups. Lookup cases and fallbacks are constrained to this so a `lookup` cannot nest another `lookup` at the type level. */
2758
- export declare type ExplicitValueFormat = TemporalValueFormat | NumericValueFormat | CurrencyValueFormat | CategoricalValueFormat;
2573
+ declare type ExplicitValueFormat = TemporalValueFormat | NumericValueFormat | CurrencyValueFormat | CategoricalValueFormat;
2759
2574
 
2760
- /**
2761
- * Measured heights of content regions (header: title+subtitle, footer: caption). The caller measures
2762
- * the already-rendered header/footer DOM and feeds these back; only the HEIGHTS are read (widths are
2763
- * ignored, since those regions span the full width). Expect a zero-size first pass before the DOM has
2764
- * mounted, then a second compile once real heights are known.
2765
- */
2575
+ /** Measured heights of content regions (header: title+subtitle, footer: caption). */
2766
2576
  export declare interface ExternalMeasurements {
2767
2577
  headerSize: BoxSize;
2768
2578
  footerSize: BoxSize;
2769
2579
  }
2770
2580
 
2771
- /**
2772
- * Flattens a title, subtitle, or caption to plain text by concatenating every leaf `text` node with
2773
- * no separators. Block boundaries (paragraphs, list items, line breaks) are intentionally dropped —
2774
- * the result feeds text measurement and static fallbacks, not display, so it must not be shown as the
2775
- * formatted heading.
2776
- */
2581
+ /** Flattens a title, subtitle, or caption to plain text for measurement and static renderers. */
2777
2582
  export declare const extractPlainText: (content: TextContent) => string;
2778
2583
 
2779
2584
  declare function filter(options: FilterOptions): FilterTransformInput;
@@ -2802,10 +2607,6 @@ declare interface FilterTransformInput {
2802
2607
  * Lookups go through `scaleAestheticKey`, not `aesthetic`: `ySecondary` shares
2803
2608
  * `aesthetic === 'y'` with the primary y axis, so matching on aesthetic would return the wrong
2804
2609
  * axis for layers with `yScaleType: 'secondary'`.
2805
- *
2806
- * Pair with `resolveYScaleAesthetic` (scale.constants.ts) to find a layer's axis:
2807
- * `findAxisGuide(guides, resolveYScaleAesthetic(layer.yScaleType))` resolves a secondary-y layer to
2808
- * the `ySecondary` guide.
2809
2610
  */
2810
2611
  export declare function findAxisGuide(guides: CompiledGuides, scaleAestheticKey: ScaledAestheticKey): CompiledAxisGuide | null;
2811
2612
 
@@ -2830,50 +2631,30 @@ declare interface FlipCoordSpec {
2830
2631
  params: BaseCoordParams;
2831
2632
  }
2832
2633
 
2833
- /**
2834
- * The font a `TextMeasurer` measures against. Both `family` and `size` are required: a
2835
- * measurer backed by canvas `measureText` (or `buildFontString`) needs a concrete family and
2836
- * pixel size to return real metrics, so an incomplete spec silently yields wrong measurements.
2837
- */
2838
2634
  export declare interface FontSpec {
2839
- /** CSS font family (e.g. `'Inter'`). Required — there is no fallback family. */
2840
2635
  family: string;
2841
- /**
2842
- * Final font size in pixels. Named `size`, not `fontSize`. The caller has already applied text-scale
2843
- * and any em → px conversion, so a measurer (and the renderer) must use this as-is — do not re-scale.
2844
- */
2845
2636
  size: number;
2846
2637
  weight?: NamedWeightKey | number;
2847
2638
  style?: 'normal' | 'italic' | 'oblique';
2848
2639
  }
2849
2640
 
2850
2641
  /**
2851
- * Composes a compiled headline guide into final display strings: locale-formatted figures, the
2852
- * English aggregate prefix, the series/measure name, the trend percentage, and main-axis labels.
2853
- * Every field on the result is render-verbatim (see {@link FormattedHeadlineItem}); the renderer adds
2854
- * only the arrow/colour implied by the trend `direction`. Returns null when there is no headline.
2642
+ * Composes a compiled headline guide into display strings: locale-formatted figures, the English
2643
+ * aggregate prefix, the series/measure name, the trend percentage, and main-axis labels.
2855
2644
  */
2856
- export declare const formatHeadline: ({ headline, show, numberFormat, parsingLocale, formattingLocale, t, }: FormatHeadlineInput) => FormattedHeadline | null;
2645
+ export declare const formatHeadline: ({ headline, show, numberFormat, parsingLocale, formattingLocale, }: FormatHeadlineInput) => FormattedHeadline | null;
2857
2646
 
2858
- /** Inputs to {@link formatHeadline}. `formattingLocale` overrides `parsingLocale` for display when the two differ. */
2859
2647
  export declare interface FormatHeadlineInput {
2860
2648
  headline: CompiledHeadlineGuide | null;
2861
2649
  /** Aggregate mode, read from `config.headline.show` — it isn't duplicated onto the guide. */
2862
2650
  show: HeadlineShow;
2863
2651
  numberFormat: NumberFormatConfig;
2864
2652
  parsingLocale: Locale;
2865
- /**
2866
- * Display-locale override: `locale = formattingLocale ?? parsingLocale`. The supported set is small
2867
- * ('en-GB', 'en-US', 'ar', 'pt-PT'); a `duration` figure always formats in English regardless.
2868
- */
2869
2653
  formattingLocale?: Locale;
2870
- t: Translator;
2871
2654
  }
2872
2655
 
2873
2656
  /**
2874
- * Formats all legend guides by attaching a `formattedLabel` to each item. Item order is preserved
2875
- * from the compiled guide — canonical scale-domain order — and consumers must keep it (tooltips and
2876
- * swatch lists read top-to-bottom in this order).
2657
+ * Formats all legend guides by attaching a `formattedLabel` to each item.
2877
2658
  */
2878
2659
  export declare const formatLegends: ({ legends, numberFormat, parsingLocale, formattingLocale, }: FormatLegendsInput) => FormattedLegend[];
2879
2660
 
@@ -2881,18 +2662,12 @@ declare interface FormatLegendsInput {
2881
2662
  legends: readonly CompiledLegendGuide[];
2882
2663
  numberFormat: NumberFormatConfig;
2883
2664
  parsingLocale: Locale;
2884
- /**
2885
- * Display-locale override: `locale = formattingLocale ?? parsingLocale`. The supported set is small
2886
- * ('en-GB', 'en-US', 'ar', 'pt-PT'); a `duration` label always formats in English regardless.
2887
- */
2888
2665
  formattingLocale?: Locale;
2889
2666
  }
2890
2667
 
2891
2668
  /**
2892
- * Formats a rule layer's underlying numeric value with the same value formatter as the axis the rule
2893
- * anchors to: y-rules use the y-axis; x-rules use x. Takes the rule's own `CompiledLayerFor<'rule'>`
2894
- * and returns ONLY the formatted value — or null when the rule has no observation or a null value.
2895
- * The caller composes it with the rule's label text (this helper does not).
2669
+ * Formats a rule's underlying numeric value with the same value formatter as the axis the rule
2670
+ * anchors to: y-rules use the y-axis; x-rules use x.
2896
2671
  */
2897
2672
  export declare const formatRuleValue: ({ guides, numberFormat, layer, locale }: FormatRuleValueInput) => string | null;
2898
2673
 
@@ -2903,17 +2678,7 @@ declare interface FormatRuleValueInput {
2903
2678
  locale: Locale;
2904
2679
  }
2905
2680
 
2906
- /**
2907
- * An axis guide with each tick's label composed into a display string, plus rotation and truncation
2908
- * hints. Produced by `LayoutCompiler.compile()`, which runs the final two-phase tick selection
2909
- * (picking the densest candidate set whose labels fit) — so by the time a renderer sees this the
2910
- * ticks are settled. Do not re-select candidates or re-apply a `ValueFormat`.
2911
- */
2912
2681
  export declare interface FormattedAxis extends Omit<CompiledAxisGuide, 'tickCandidates'> {
2913
- /**
2914
- * The chosen ticks, each carrying its composed display string in `formattedLabel`. The label is
2915
- * already locale-formatted — render it verbatim.
2916
- */
2917
2682
  ticks: Array<AxisTick & {
2918
2683
  formattedLabel: string;
2919
2684
  }>;
@@ -2923,10 +2688,6 @@ export declare interface FormattedAxis extends Omit<CompiledAxisGuide, 'tickCand
2923
2688
  labelMaxWidthPx: number | null;
2924
2689
  }
2925
2690
 
2926
- /**
2927
- * A headline showing a single grand-total number, used for polar charts. `value` is a bare final
2928
- * display string — no prefix, label, or swatch accompanies it; render it verbatim.
2929
- */
2930
2691
  export declare interface FormattedGrandTotalHeadline {
2931
2692
  kind: 'grandTotal';
2932
2693
  value: string;
@@ -2938,21 +2699,15 @@ export declare interface FormattedGrandTotalHeadline {
2938
2699
  */
2939
2700
  export declare type FormattedHeadline = FormattedPerGroupHeadline | FormattedGrandTotalHeadline;
2940
2701
 
2941
- /** A headline's trend versus a reference observation, formatted for display. */
2942
2702
  export declare interface FormattedHeadlineComparison {
2943
- /** Movement direction — the only place the sign lives; drives the renderer's arrow and colour. */
2703
+ /** Movement direction — drives the renderer's arrow and colour. */
2944
2704
  direction: HeadlineTrendDirection;
2945
- /** Unsigned magnitude of the variation as a locale percentage, e.g. "20%" (sign comes from `direction`). */
2705
+ /** Absolute variation as a locale percentage, e.g. "20%". */
2946
2706
  percentage: string;
2947
2707
  /** Reference observation label, e.g. "from Feb 2025". */
2948
2708
  reference: string;
2949
2709
  }
2950
2710
 
2951
- /**
2952
- * A single headline figure: its name, formatted value, observation label and optional trend
2953
- * comparison. Every string field is final and display-ready (English-only — the aggregate prefix,
2954
- * name, figure, and trend are already composed); render them verbatim, do not recompose or relocalize.
2955
- */
2956
2711
  export declare interface FormattedHeadlineItem {
2957
2712
  /** Group swatch, passed through from the compiled item (present only at ≥2 groups). */
2958
2713
  swatch: HeadlineGroupSwatch | null;
@@ -2966,15 +2721,12 @@ export declare interface FormattedHeadlineItem {
2966
2721
  comparison: FormattedHeadlineComparison | null;
2967
2722
  }
2968
2723
 
2969
- /** A legend guide with each item's label composed into a display string. */
2970
2724
  export declare interface FormattedLegend extends Omit<CompiledLegendGuide, 'items'> {
2971
- /** Legend entries, each carrying its composed display string in `formattedLabel`. */
2972
2725
  items: Array<LegendItem & {
2973
2726
  formattedLabel: string;
2974
2727
  }>;
2975
2728
  }
2976
2729
 
2977
- /** A headline showing one labelled figure per group (e.g. one per series). */
2978
2730
  export declare interface FormattedPerGroupHeadline {
2979
2731
  kind: 'perGroup';
2980
2732
  items: FormattedHeadlineItem[];
@@ -2987,9 +2739,7 @@ export declare interface FormattedPerGroupHeadline {
2987
2739
  */
2988
2740
  export declare interface FreeformArrowInput {
2989
2741
  id?: string;
2990
- /** Tail endpoint. */
2991
2742
  start: ArrowEndpoint;
2992
- /** Head endpoint. */
2993
2743
  end: ArrowEndpoint;
2994
2744
  /** null falls back to the theme `defaultAnnotationArrowStroke`. */
2995
2745
  color?: string | null;
@@ -2997,11 +2747,9 @@ export declare interface FreeformArrowInput {
2997
2747
  startArrowheadStyle?: ArrowheadStyle;
2998
2748
  endArrowheadStyle?: ArrowheadStyle;
2999
2749
  lineStyle?: ArrowLineStyle;
3000
- /** Render with a raised, outlined sticker-like appearance. */
3001
2750
  hasStickerStyle?: boolean;
3002
2751
  }
3003
2752
 
3004
- /** Resolved freeform arrow with all optional fields defaulted. */
3005
2753
  export declare interface FreeformArrowSpec {
3006
2754
  id: string;
3007
2755
  start: ArrowEndpoint;
@@ -3032,14 +2780,113 @@ declare type GenerateTicksOptions = {
3032
2780
 
3033
2781
  /**
3034
2782
  * Base class for geoms that turn observations into visual marks (points, bars, lines etc).
2783
+ *
2784
+ * `TParams` is the geom's parameter type — the single source of truth the registration-typed builder
2785
+ * reads to type `geom.<name>({ params })` and to default missing params. Built-ins leave it at the
2786
+ * empty default and keep their typed params through the spec builder's static surface; a custom geom
2787
+ * names its params type and declares matching {@link defaultParams}.
3035
2788
  */
3036
- declare abstract class Geom {
3037
- readonly requiredAesthetics: AestheticKey[];
3038
- abstract readonly type: GeomName;
2789
+ declare abstract class Geom<TParams extends object = object> {
2790
+ /**
2791
+ * The aesthetics an author must map for this geom. Built-ins list closed aesthetic keys
2792
+ * (`['x','y']`); a custom geom may also list open channel names (a box plot's `min`/`q1`/…) that it
2793
+ * binds to scales through its {@link positionChannels}, so the registration-typed builder accepts and
2794
+ * types them as `aes` keys instead of forcing the bindings into `params`.
2795
+ */
2796
+ readonly requiredAesthetics: readonly string[];
2797
+ /**
2798
+ * The params merged in by the registration-typed builder before a missing key would reach the geom.
2799
+ * The default is empty; a custom geom overrides it with its definition's defaults, which doubles as
2800
+ * the carrier that lets the builder recover `TParams` from the registered instance.
2801
+ */
2802
+ readonly defaultParams: TParams;
2803
+ /**
2804
+ * The position channels this geom produces. The position mapper and coord projection iterate this
2805
+ * manifest instead of a hardcoded column set, so a geom's geometry is described by what it declares.
2806
+ */
2807
+ readonly positionChannels: readonly PositionChannel[];
2808
+ /**
2809
+ * The visual aesthetics this geom encodes (color, size, …). The visual mapper and legend iterate
2810
+ * this declaration instead of a hardcoded set, so a geom's visual surface is described by what it
2811
+ * declares. The default is the full vocabulary; a geom narrows it to the aesthetics it actually
2812
+ * paints, and a custom aesthetic extends it (decision 10).
2813
+ */
2814
+ readonly visualAesthetics: readonly ScaledVisualAestheticKey[];
2815
+ /**
2816
+ * The swatch shape that evokes this geom's on-canvas mark in the legend and tooltip — its
2817
+ * cartesian-natural shape. A polar coord refines a `square` mark to a `slice` (pie/donut wedge)
2818
+ * at read time. Decoupled from `GeomName` so guides paint by what a geom declares, not its name.
2819
+ */
2820
+ readonly swatchShape: SwatchShape;
2821
+ /**
2822
+ * How this geom composes highlight matches above its base render, or `null` to opt out of
2823
+ * highlighting. Read at layer compile and stamped onto `CompiledLayer.highlight.strategy`.
2824
+ * Declared per geom so core resolves it from the definition, not a geom-keyed lookup.
2825
+ */
2826
+ readonly highlightStrategy: HighlightStrategy | null;
2827
+ /**
2828
+ * The spatial structure this geom's marks present for hit-testing — its cartesian-natural kind.
2829
+ * Stamped onto `CompiledLayer.spatialMap`; a polar coord refines it to `arcs`. Declared per geom
2830
+ * so the runtime builds the matching index from data, not by branching on the geom name.
2831
+ */
2832
+ readonly spatialKind: SpatialIndexKind;
2833
+ /**
2834
+ * Positions for which this geom supports direct (inline) series labels in the legend. Empty when
2835
+ * the geom never shows them. The legend reads this from the definition, not a geom-keyed lookup.
2836
+ */
2837
+ readonly directLabelPositions: readonly PositionType[];
2838
+ /**
2839
+ * Grid/border visibility overrides this geom requests per coord type (e.g. a bar hides the
2840
+ * categorical-axis grid). Empty when the geom imposes none. The axis guide reads these.
2841
+ */
2842
+ readonly gridPolicies: Partial<Record<CoordType, GridPolicy>>;
2843
+ /**
2844
+ * How this geom derives each observation's stable identity key: `'index'` (position by row, so
2845
+ * marks morph smoothly on enter/exit) or `'fields'` (x value plus resolved series). The identity
2846
+ * compiler reads this from the definition, not a geom-name check.
2847
+ */
2848
+ readonly identityKeyStrategy: 'index' | 'fields';
2849
+ /**
2850
+ * The coord types under which this geom has meaningful semantics. The layer validator rejects a
2851
+ * layer whose coord is absent from this set (e.g. a rule has no polar interpretation). Declared per
2852
+ * geom so the validator resolves support from the definition, not a geom-name check.
2853
+ */
2854
+ readonly supportedCoordTypes: readonly CoordType[];
2855
+ /**
2856
+ * Whether a non-stacked layer of this geom carries a layer-wide grand total (the signed sum of `y`,
2857
+ * the pie/donut headline figure). The summarise stage runs the grand-total summariser only for geoms
2858
+ * that declare this, so eligibility lives on the definition rather than a geom-name gate.
2859
+ */
2860
+ readonly emitsGrandTotal: boolean;
2861
+ /**
2862
+ * Whether a stacked layer of this geom carries per-x stack totals (the share-of-stack denominator).
2863
+ * The summarise stage runs the stack-totals summariser only for geoms that declare this, so a stacked
2864
+ * area (which writes the same interval columns) is excluded by declaration, not by a geom-name gate.
2865
+ */
2866
+ readonly emitsStackTotals: boolean;
2867
+ /**
2868
+ * Whether a layer of this geom contributes a per-group headline figure. The headline guide builds a
2869
+ * per-group strip only when an eligible geom is present, reading this from the definition rather than
2870
+ * a membership list of geom names (so a reference-line geom, which is an annotation, opts out).
2871
+ */
2872
+ readonly supportsPerGroupHeadline: boolean;
2873
+ abstract readonly type: GeomIdentity;
2874
+ /**
2875
+ * Where an annotation anchored to the given observation sits, in normalised panel space, or `null`
2876
+ * when this geom does not support anchoring (the default). The annotations compiler resolves anchors
2877
+ * through this method rather than branching on geom name; the returned `geom` discriminant tells the
2878
+ * renderer how to place the annotation.
2879
+ */
2880
+ resolveAnchorPosition(_observation: Observation, _coordSystem: CoordSystem): AnchorPosition | null;
3039
2881
  abstract compile(input: GeomCompilerInput): CompiledGeom;
2882
+ /**
2883
+ * Validates the layer's mapping against invariants specific to this geom (e.g. a rule needs exactly
2884
+ * one numeric axis). Returns the issues found; omit the method when the geom imposes no mapping
2885
+ * invariant. The layer validator dispatches here instead of branching on geom name.
2886
+ */
2887
+ validateMapping?(input: GeomMappingValidationInput): ValidationIssue[];
3040
2888
  }
3041
2889
 
3042
- /** Factories for the geometry layers a chart can draw (point, line, area, bar, rule). */
3043
2890
  export declare const geom: {
3044
2891
  point: typeof point;
3045
2892
  line: typeof line;
@@ -3048,13 +2895,55 @@ export declare const geom: {
3048
2895
  rule: typeof rule;
3049
2896
  };
3050
2897
 
2898
+ /**
2899
+ * The built-in geometric marks. The single source for both the {@link GeomName} type and the runtime
2900
+ * {@link BUILTIN_GEOM_NAMES} set used to tell a built-in name from a custom registration.
2901
+ *
2902
+ * - `'point'` — Scatter-style dot marks
2903
+ * - `'line'` — Connected line marks
2904
+ * - `'area'` — Filled area marks
2905
+ * - `'bar'` — Rectangular bar marks
2906
+ * - `'rule'` — Horizontal or vertical reference line at a constant value
2907
+ */
2908
+ export declare const GEOM_NAMES: readonly ["point", "line", "area", "bar", "rule"];
2909
+
3051
2910
  /**
3052
2911
  * Resolves a geom by name and delegates compilation.
3053
2912
  */
3054
2913
  declare class GeomCompiler {
3055
2914
  private readonly registry;
3056
2915
  constructor(registry: GeomRegistry);
3057
- compile(geomName: GeomName, input: GeomCompilerInput): CompiledGeom;
2916
+ compile(geomName: GeomIdentity, input: GeomCompilerInput): CompiledGeom;
2917
+ /**
2918
+ * The geom's declared position-channel manifest — the contract the position adjuster gate reads to
2919
+ * decide whether an adjustment applies to this geom.
2920
+ */
2921
+ getPositionChannels(geomName: GeomIdentity): readonly PositionChannel[];
2922
+ /**
2923
+ * The geom's declared swatch shape — the legend/tooltip mark the guides paint for it. The coord
2924
+ * refinement (a polar `square` becomes a `slice`) is applied by the reader, not here.
2925
+ */
2926
+ getSwatchShape(geomName: GeomIdentity): SwatchShape;
2927
+ /** The geom's declared spatial-index kind — the hit-test structure its marks present. */
2928
+ getSpatialKind(geomName: GeomIdentity): SpatialIndexKind;
2929
+ /** The visual aesthetics this geom encodes — the set the visual mapper and legend iterate. */
2930
+ getVisualAesthetics(geomName: GeomIdentity): readonly ScaledVisualAestheticKey[];
2931
+ /** The geom's declared highlight composition strategy, or `null` when it opts out of highlighting. */
2932
+ getHighlightStrategy(geomName: GeomIdentity): HighlightStrategy | null;
2933
+ /** The positions for which the geom supports direct (inline) legend labels. */
2934
+ getDirectLabelPositions(geomName: GeomIdentity): readonly PositionType[];
2935
+ /** The grid/border visibility overrides the geom requests per coord type. */
2936
+ getGridPolicies(geomName: GeomIdentity): Partial<Record<CoordType, GridPolicy>>;
2937
+ /** How the geom derives its observation identity key — `'index'` or `'fields'`. */
2938
+ getIdentityKeyStrategy(geomName: GeomIdentity): 'index' | 'fields';
2939
+ /** Whether a non-stacked layer of this geom carries a layer-wide grand total. */
2940
+ emitsGrandTotal(geomName: GeomIdentity): boolean;
2941
+ /** Whether a stacked layer of this geom carries per-x stack totals. */
2942
+ emitsStackTotals(geomName: GeomIdentity): boolean;
2943
+ /** Whether a layer of this geom contributes a per-group headline figure. */
2944
+ supportsPerGroupHeadline(geomName: GeomIdentity): boolean;
2945
+ /** Where an annotation anchored to the observation sits, or `null` when the geom does not anchor. */
2946
+ resolveAnchorPosition(geomName: GeomIdentity, observation: Observation, coordSystem: CoordSystem): AnchorPosition | null;
3058
2947
  }
3059
2948
 
3060
2949
  declare interface GeomCompilerInput {
@@ -3067,18 +2956,29 @@ declare interface GeomCompilerInput {
3067
2956
  }
3068
2957
 
3069
2958
  /**
3070
- * The type of geometric mark used to represent data in a layer.
3071
- *
3072
- * - `'point'` Scatter-style dot marks
3073
- * - `'line'`Connected line marks
3074
- * - `'area'` — Filled area marks
3075
- * - `'bar'` Rectangular bar marks
3076
- * - `'rule'` — Horizontal or vertical reference line at a constant value
3077
- *
3078
- * A mark's geometry is the `(geom, coordSystem.type)` pair, not geom alone a `bar` is a rect in
3079
- * cartesian and an arc in polar so renderers dispatch on the pair.
2959
+ * Open geom identity: the built-in vocabulary plus any custom registration's `type`. The layer types,
2960
+ * the compiled layer, and the geom registry key on this so a registered custom geom is a first-class
2961
+ * mark; the built-in literals stay for autocomplete. Core never *decides* on the name (the decisions
2962
+ * live on the geom definition) this is purely the identity a layer carries.
2963
+ */
2964
+ export declare type GeomIdentity = GeomName | (string & {});
2965
+
2966
+ /**
2967
+ * Input to a geom's mapping validation. The validator resolves the stat-computed aesthetics and the
2968
+ * effective mapping, so the geom only expresses its own invariant (e.g. a rule needs exactly one
2969
+ * numeric axis) without reaching for a registry.
3080
2970
  */
3081
- export declare type GeomName = 'point' | 'line' | 'area' | 'bar' | 'rule';
2971
+ declare interface GeomMappingValidationInput {
2972
+ /** Layer id, for issue attribution. */
2973
+ layerId: string;
2974
+ /** The effective mapping: spec mapping merged with the layer's. */
2975
+ mapping: AesMapping;
2976
+ /** Aesthetics a stat computes at compile time, so a missing literal there is not an error. */
2977
+ computedVariables: ReadonlySet<AestheticKey>;
2978
+ }
2979
+
2980
+ /** A built-in geom's name — the default vocabulary the spec builder offers out of the box. */
2981
+ export declare type GeomName = (typeof GEOM_NAMES)[number];
3082
2982
 
3083
2983
  declare type GeomOptions<G extends GeomName> = BaseGeomOptions<GeomParamsMap[G]>;
3084
2984
 
@@ -3096,64 +2996,42 @@ declare interface GeomParamsMap {
3096
2996
  }
3097
2997
 
3098
2998
  /**
3099
- * Built-in geom implementations keyed by {@link GeomName}.
2999
+ * Geom implementations keyed by their open `type` — the built-ins by default, with any injected
3000
+ * custom geoms overlaid last-write-wins. The key is `string`, not the closed `GeomName`, so a custom
3001
+ * registration is a first-class entry.
3100
3002
  */
3101
- declare class GeomRegistry extends Registry<GeomName, Geom> {
3102
- constructor();
3003
+ declare class GeomRegistry extends Registry<string, Geom> {
3004
+ constructor(opts?: {
3005
+ geoms?: readonly Geom[];
3006
+ });
3103
3007
  }
3104
3008
 
3105
3009
  /**
3106
- * Reads the resolved alpha (opacity) value from an observation, in `[0, 1]`.
3107
- *
3108
- * `null` / `undefined` unmapped — apply the geom default. A series (line / area) shares one
3109
- * resolved value across its rows; read it from the first observation.
3010
+ * One extra tooltip row a geom contributes for the hovered observation a named reading of a data
3011
+ * column the standard one-row-per-series tooltip would not surface on its own. An OHLC candle, for
3012
+ * instance, declares four (open/high/low/close); the compiler derives each row's display format from
3013
+ * the column and the renderer materialises the values for the observation under the cursor. Pure
3014
+ * data: the `label` is static text and the `variable` names a column, so the rows ride in the
3015
+ * serialisable compiled spec.
3110
3016
  */
3017
+ declare interface GeomTooltipRow {
3018
+ /** The row's label (e.g. "Open"). Static text the geom supplies. */
3019
+ label: string;
3020
+ /** The data column whose per-observation value the row displays. */
3021
+ variable: VariableName;
3022
+ }
3023
+
3024
+ /** Reads the resolved alpha (opacity) value from an observation. */
3111
3025
  export declare function getAlpha(observation: Observation): NumericDataValue;
3112
3026
 
3113
- /**
3114
- * Reads a polar arc's angular sweep. In polar layers the x position columns are repurposed as
3115
- * angles: absolute radians, `startAngle` already applied, clockwise from 12 o'clock — matching the
3116
- * polar transform, `HoverHit`'s polar convention, and d3-shape `arc()`. Pass the values through
3117
- * unmodified. Either field `null` ⇒ skip the arc.
3118
- */
3119
3027
  export declare function getAngleExtent(observation: Observation): AngleExtent;
3120
3028
 
3121
- /**
3122
- * Reports whether a bar geom's baseline falls outside the value scale's visible domain. When it
3123
- * does, bar lengths are truncated at the panel edge and no longer encode their values to scale, so
3124
- * renderers can fade the clipped edge to signal the truncation.
3125
- *
3126
- * Returns `null` for non-continuous or reversed value scales, where a single clipped edge is not
3127
- * well defined.
3128
- */
3129
- export declare const getBarBaselineClip: (valueScale: CompiledScale | undefined) => BarBaselineClip;
3130
-
3131
- /**
3132
- * Normalized data-space bounds for a bar observation in [0, 1]² with origin at the top-left
3133
- * (matches SVG conventions used by overlay renderers). Negative values flip the rect upside-down
3134
- * so callers don't need to know whether the bar grows up or down from its baseline.
3135
- *
3136
- * `mainAxis` selects which data axis separates bars (bar length runs along the cross axis). For
3137
- * `'x'` bars grow vertically; for `'y'` (flipped) they grow horizontally and the cross-axis
3138
- * extent can be negative for negative values.
3139
- */
3140
- export declare const getBarRectBounds: (mainAxis: MainAxis, observation: Observation) => Rect | null;
3141
-
3142
- /**
3143
- * Reads the resolved color string from an observation.
3144
- *
3145
- * `undefined` ⇒ unmapped — the renderer supplies its geom default fill. A series (line / area)
3146
- * shares one resolved value across its rows; read it from the first observation.
3147
- */
3029
+ /** Reads the resolved color string from an observation. */
3148
3030
  export declare function getColor(observation: Observation): string | undefined;
3149
3031
 
3150
3032
  /** Reads the coordinate lying on the cross axis of the coord system. */
3151
3033
  export declare function getCrossAxisCoordinate(mainAxis: MainAxis, point: XYPoint): number;
3152
3034
 
3153
- export declare const getCurve: (interpolate: InterpolateType) => CurveFactory;
3154
-
3155
- export declare const getDashArray: (lineType: LineStyleType) => string | undefined;
3156
-
3157
3035
  /**
3158
3036
  * Returns pixel dimensions for a difference arrow at the requested size.
3159
3037
  *
@@ -3162,65 +3040,30 @@ export declare const getDashArray: (lineType: LineStyleType) => string | undefin
3162
3040
  */
3163
3041
  export declare const getDifferenceArrowDimensions: (size: DifferenceArrowSize, textScale: number) => DifferenceArrowDimensions;
3164
3042
 
3165
- /**
3166
- * Reads the series-grouping key written onto an observation during compilation.
3167
- *
3168
- * Connected geoms (line, area, polar arc) must partition observations by this key —
3169
- * `data.groupBy(GROUP_VARIABLES.group)` — and emit one mark per group; per-mark geoms (bar, point)
3170
- * iterate `data` directly. Visual channels are constant within a group, so read them from the
3171
- * first observation.
3172
- */
3173
3043
  export declare const getGroup: (observation: Observation) => CategoricalDataValue;
3174
3044
 
3175
- export declare function getHoverGuideLineProps(coordSystem: CartesianCoordSystem, primary: XYPoint): HoverGuideLineProps;
3176
-
3177
- export declare function getHoverGuideRectProps(coordSystem: CartesianCoordSystem, scales: CompiledSpec['scales'], primary: HoverHit): HoverGuideRectProps | null;
3045
+ /**
3046
+ * Reads the stable identity key the compiler emits for each observation. Surfaces that need a
3047
+ * mark's identity (animation data-join, linked selection, reading order) read this one key rather
3048
+ * than reconstructing their own.
3049
+ */
3050
+ export declare const getIdentityKey: (observation: Observation) => string;
3178
3051
 
3179
3052
  /**
3180
3053
  * Reads the resolved line type (stroke style) from an observation.
3181
3054
  * Falls back to `'solid'` when no `lineType` variable was derived.
3182
- *
3183
- * A series (line / area) shares one resolved value across its rows; read it from the first
3184
- * observation.
3185
3055
  */
3186
3056
  export declare function getLineType(observation: Observation): LineStyleType;
3187
3057
 
3188
3058
  /** Reads the coordinate lying on the main (independent) axis of the coord system. */
3189
3059
  export declare function getMainAxisCoordinate(mainAxis: MainAxis, point: XYPoint): number;
3190
3060
 
3191
- /**
3192
- * Resolves a `FontSpec` weight to its numeric CSS value, the form a text-measurement cache key needs.
3193
- * A number passes through; a named weight maps via {@link NAMED_WEIGHTS}; an omitted weight falls back
3194
- * to {@link DEFAULT_FONT_WEIGHT}. A from-scratch measurer must resolve weights the same way to share
3195
- * cache keys with the engine.
3196
- */
3197
- export declare const getNumericWeight: (weight: NamedWeightKey | number | undefined) => number;
3198
-
3199
- /**
3200
- * Reads a polar arc's radial extent. In polar layers the y position columns are repurposed as
3201
- * radii: fractions (`[0, 1]`) of the outer radius. The renderer maps these into a unit circle and
3202
- * lets the browser scale it to pixels (no manual multiply by a panel radius). Either field `null`
3203
- * ⇒ skip the arc.
3204
- */
3205
3061
  export declare function getRadiusExtent(observation: Observation): RadiusExtent;
3206
3062
 
3207
- /**
3208
- * Reads the resolved size value from an observation — a nominal diameter (points halve it for the
3209
- * marker radius).
3210
- *
3211
- * `null` / `undefined` ⇒ unmapped — apply the geom default. A series (line / area) shares one
3212
- * resolved value across its rows; read it from the first observation.
3213
- */
3063
+ /** Reads the resolved size value from an observation. */
3214
3064
  export declare function getSize(observation: Observation): NumericDataValue;
3215
3065
 
3216
- export declare const getStablePolarBarKeyGenerator: () => ((observation: Observation, index: number) => string);
3217
-
3218
- /**
3219
- * Reads the resolved stroke width value from an observation, in pixels.
3220
- *
3221
- * `null` / `undefined` ⇒ unmapped — apply the geom default. A series (line / area) shares one
3222
- * resolved value across its rows; read it from the first observation.
3223
- */
3066
+ /** Reads the resolved stroke width value from an observation. */
3224
3067
  export declare function getStrokeWidth(observation: Observation): NumericDataValue;
3225
3068
 
3226
3069
  declare interface GetValuesOptions {
@@ -3232,71 +3075,35 @@ declare interface GetValuesOptions {
3232
3075
  distinct?: boolean;
3233
3076
  }
3234
3077
 
3235
- /**
3236
- * Reads the normalized x position (band center) from an observation.
3237
- *
3238
- * Normalized to [0,1]: x is 0=left…1=right, y is 0=bottom…1=top (data-up). SVG / top-origin
3239
- * renderers invert y as `1 - y`.
3240
- */
3078
+ /** Reads the normalized x position from an observation. */
3241
3079
  export declare function getX(observation: Observation): NumericDataValue;
3242
3080
 
3243
- /**
3244
- * Reads the normalized xMax (right band edge) from an observation.
3245
- *
3246
- * Written only for extent-bearing geoms (bars, stacked / range area); returns `null` otherwise —
3247
- * fall back to {@link getX} (the band center). Per geom: bar = rect band edge + bar length,
3248
- * area = cross-axis fill bound; point / line leave it unused.
3249
- */
3081
+ /** Reads the normalized xMax (right band edge) from an observation. */
3250
3082
  export declare function getXMax(observation: Observation): NumericDataValue;
3251
3083
 
3252
- /**
3253
- * Reads the normalized xMin (left band edge) from an observation.
3254
- *
3255
- * Written only for extent-bearing geoms (bars, stacked / range area); returns `null` otherwise —
3256
- * fall back to {@link getX} (the band center). Per geom: bar = rect band edge, area = cross-axis
3257
- * fill bound; point / line leave it unused.
3258
- */
3084
+ /** Reads the normalized xMin (left band edge) from an observation. */
3259
3085
  export declare function getXMin(observation: Observation): NumericDataValue;
3260
3086
 
3261
- /**
3262
- * Reads the normalized y position (band center) from an observation.
3263
- *
3264
- * Normalized to [0,1]: x is 0=left…1=right, y is 0=bottom…1=top (data-up). SVG / top-origin
3265
- * renderers invert y as `1 - y`.
3266
- */
3087
+ /** Reads the normalized y position from an observation. */
3267
3088
  export declare function getY(observation: Observation): NumericDataValue;
3268
3089
 
3269
- /**
3270
- * Reads the normalized yMax (upper extent) from an observation.
3271
- *
3272
- * Written only for extent-bearing geoms (bars, stacked / range area); returns `null` otherwise —
3273
- * fall back to {@link getY} (the band center). Per geom: bar = rect band edge + bar length,
3274
- * area = cross-axis fill bound; point / line leave it unused.
3275
- */
3090
+ /** Reads the normalized yMax (upper extent) from an observation. */
3276
3091
  export declare function getYMax(observation: Observation): NumericDataValue;
3277
3092
 
3278
- /**
3279
- * Reads the normalized yMin (lower extent) from an observation.
3280
- *
3281
- * Written only for extent-bearing geoms (bars, stacked / range area); returns `null` otherwise —
3282
- * fall back to {@link getY} (the band center). Per geom: bar = rect band edge, area = cross-axis
3283
- * fill bound; point / line leave it unused.
3284
- */
3093
+ /** Reads the normalized yMin (lower extent) from an observation. */
3285
3094
  export declare function getYMin(observation: Observation): NumericDataValue;
3286
3095
 
3287
3096
  /**
3288
3097
  * Reads the segment value written by stacking position adjusters.
3289
3098
  *
3290
- * Survives the position-mapper untouched, so renderers can recover original-unit values
3099
+ * Survives theposition-mapper untouched, so renderers can recover original-unit values
3291
3100
  * for stacked segments after `mapping.y` has been rewritten to the cumulative band top.
3292
3101
  */
3293
3102
  export declare function getYRaw(observation: Observation): NumericDataValue;
3294
3103
 
3295
3104
  /** Goal line with a target value. */
3296
3105
  declare interface GoalLine {
3297
- /** Value on the measure axis where the line is drawn. */
3298
3106
  target: number;
3299
- /** Category value at which to anchor an optional marker on the line. */
3300
3107
  marker?: DataValue;
3301
3108
  label?: string;
3302
3109
  }
@@ -3306,10 +3113,8 @@ declare type GraphAnnotation = GraphStickerAnnotation | GraphTooltipAnnotation |
3306
3113
  declare interface GraphArrowAnnotation {
3307
3114
  id: string;
3308
3115
  type: 'arrow';
3309
- /** Tail position as fractions (0-1) of the plot width and height. */
3310
3116
  startX: number;
3311
3117
  startY: number;
3312
- /** Head position as fractions (0-1) of the plot width and height. */
3313
3118
  endX: number;
3314
3119
  endY: number;
3315
3120
  color?: string;
@@ -3317,22 +3122,15 @@ declare interface GraphArrowAnnotation {
3317
3122
  startArrowheadStyle: 'none' | 'line-arrow';
3318
3123
  lineStyle: 'solid' | 'dashed';
3319
3124
  endArrowheadStyle: 'none' | 'line-arrow';
3320
- /** Adds a white outline so the arrow reads as a sticker on top of the chart. */
3321
3125
  hasStickerStyle: boolean;
3322
3126
  }
3323
3127
 
3324
- /**
3325
- * The full set of options a consumer passes to configure a chart's type, styling, axes, and content.
3326
- * Every field is optional, so omitted settings fall back to engine defaults.
3327
- */
3328
3128
  export declare interface GraphConfig {
3329
3129
  type?: GraphType;
3330
- /** Geometry-specific settings, unioned across all chart types. */
3331
3130
  options?: Options;
3332
3131
  axes?: Axes;
3333
3132
  legend?: Legend;
3334
3133
  appearance?: Appearance;
3335
- /** Ad-hoc overrides of individual theme tokens, plus a shortcut for the chart background. */
3336
3134
  themeOverrides?: {
3337
3135
  [key: string]: unknown;
3338
3136
  graphBackground?: string;
@@ -3347,33 +3145,25 @@ export declare interface GraphConfig {
3347
3145
  declare interface GraphDifferenceArrowAnnotation {
3348
3146
  id: string;
3349
3147
  type: 'difference-arrow';
3350
- /** How the gap between the two points is expressed in the label. */
3351
3148
  show: 'absolute-difference' | 'relative-difference' | 'proportion';
3352
3149
  start: AnnotationDataPoint;
3353
3150
  end: AnnotationDataPoint;
3354
3151
  color?: string;
3355
3152
  size: 'medium' | 'small' | 'large';
3356
- /** Position of the label along the arrow as a fraction (0-1) from start to end. */
3357
3153
  labelPosition?: number;
3358
3154
  }
3359
3155
 
3360
3156
  declare type GraphHighlightAnnotation = {
3361
3157
  id: string;
3362
3158
  type: 'highlight';
3363
- /** Scope of the emphasis: a single point, a whole series, or all points at one x value. */
3364
3159
  highlight: 'data-point' | 'series' | 'x-value';
3365
3160
  } & AnnotationDataPoint;
3366
3161
 
3367
3162
  /** Output of the layout computation. */
3368
3163
  export declare interface GraphLayout {
3369
- /** The full graphical area: panel + axes + axis labels, excluding header and footer. */
3164
+ /** The full graphical area (axes + panel + axis labels), excluding header and footer. */
3370
3165
  plot: Rect;
3371
- /**
3372
- * The panel area where geom layers render i.e. the data rectangle inside the axes. Strictly nested
3373
- * inside {@link GraphLayout.plot}. Geoms paint here in normalized `[0,1]` data space with y inverted
3374
- * (data y=0 sits at the panel bottom), so map a data point to `panel.x + x * panel.width` and
3375
- * `panel.y + (1 - y) * panel.height`.
3376
- */
3166
+ /** The panel area where geom layers render i.e. the data rectangle inside the axes. */
3377
3167
  panel: Rect;
3378
3168
  /** Rects for axis regions (ticks + tick labels), keyed by edge. */
3379
3169
  axes: Partial<Record<LayoutEdge, Rect>>;
@@ -3393,16 +3183,12 @@ declare interface GraphShapeAnnotation {
3393
3183
  id: string;
3394
3184
  type: 'shape';
3395
3185
  shape: 'rectangle';
3396
- /** Whether the shape is drawn behind or in front of the plotted data. */
3397
3186
  layer: 'belowPlot' | 'abovePlot';
3398
- /** Top-left position as fractions (0-1) of the plot width and height. */
3399
3187
  x: number;
3400
3188
  y: number;
3401
- /** Size as fractions (0-1) of the plot width and height. */
3402
3189
  width: number;
3403
3190
  height: number;
3404
3191
  fillColor: string;
3405
- /** Fill opacity from 0 (transparent) to 1 (opaque). */
3406
3192
  fillOpacity: number;
3407
3193
  strokeWidth: number;
3408
3194
  }
@@ -3417,35 +3203,24 @@ declare interface GraphTextAnnotation {
3417
3203
  id: string;
3418
3204
  type: 'text';
3419
3205
  content: RichTextContent;
3420
- /** Left position as a fraction (0-1) of the plot width. */
3421
3206
  x: number;
3422
- /** Top position as a fraction (0-1) of the plot height. */
3423
3207
  y: number;
3424
- /** Box width as a fraction (0-1) of the plot width; text wraps within it. */
3425
3208
  width: number;
3426
3209
  backgroundColor?: string;
3427
- /** Whether the background is semi-transparent ('fade') or fully opaque. */
3428
3210
  backgroundColorStyle?: 'fade' | 'opaque';
3429
3211
  }
3430
3212
 
3431
3213
  declare interface GraphTextStyle {
3432
- /** Id of a font registered with the engine; resolved to a concrete family at compile time. */
3433
3214
  fontId?: string;
3434
3215
  color?: string;
3435
3216
  }
3436
3217
 
3437
- /**
3438
- * Active light or dark mode. Selects the full theme token set used when resolving theme-dependent
3439
- * colors, and sets the direction tints and gradients adjust in (lighten on dark, darken on light) —
3440
- * see the `'tinted'`/`'gradient'` variants of {@link BackgroundConfig} and {@link BorderConfig}.
3441
- */
3442
3218
  export declare type GraphTheme = 'light' | 'dark';
3443
3219
 
3444
3220
  declare type GraphTooltipAnnotation = {
3445
3221
  id: string;
3446
3222
  type: 'tooltip';
3447
- /** Tooltip body as TipTap rich-text content; omit or `null` for a pinned-number annotation (no caption). */
3448
- caption?: RichTextContent | null;
3223
+ caption: unknown;
3449
3224
  } & AnnotationDataPoint;
3450
3225
 
3451
3226
  /** Type of graph to use for the data. */
@@ -3456,16 +3231,19 @@ declare type GraphyPaletteConfig = {
3456
3231
  variant?: GraphyPaletteVariant;
3457
3232
  };
3458
3233
 
3459
- /** `waterfall` swaps in the positive/negative/total colors used by waterfall charts. */
3460
3234
  declare type GraphyPaletteVariant = 'default' | 'waterfall';
3461
3235
 
3462
3236
  /**
3463
- * Internal column name holding each observation's series-grouping key.
3464
- *
3465
- * Connected geoms (line, area, polar arc) partition observations on `group`
3466
- * (`data.groupBy(GROUP_VARIABLES.group)`) to emit one mark per series; per-mark geoms (bar, point)
3467
- * iterate the dataset directly. Read via `getGroup`.
3237
+ * Grid/border visibility a geom requests per coord type — a bar hides the categorical-axis grid,
3238
+ * for instance. A geom declares these so the axis guide resolves grid policy from the definition
3239
+ * instead of a geom-keyed lookup. Every field absent means the geom imposes no policy.
3468
3240
  */
3241
+ declare interface GridPolicy {
3242
+ hideGridX?: boolean;
3243
+ hideGridY?: boolean;
3244
+ hideBorder?: boolean;
3245
+ }
3246
+
3469
3247
  export declare const GROUP_VARIABLES: {
3470
3248
  readonly group: string;
3471
3249
  };
@@ -3518,6 +3296,8 @@ declare type GroupedVariableNames = {
3518
3296
  * influences axis placement.
3519
3297
  */
3520
3298
  declare class GuideCompiler extends Stage<GuideCompilerInput, CompiledGuides> {
3299
+ private readonly geomCompiler;
3300
+ constructor(geomCompiler: GeomCompiler);
3521
3301
  protected dependencies(input: GuideCompilerInput): readonly unknown[];
3522
3302
  protected run(input: GuideCompilerInput): CompiledGuides;
3523
3303
  }
@@ -3542,24 +3322,8 @@ declare interface GuideCompilerInput {
3542
3322
  */
3543
3323
  declare type GuideConfig = Pick<ConfigSpec, 'axes' | 'legend' | 'headline' | 'panel'>;
3544
3324
 
3545
- /** Geometric shape an axis traces: a straight line, a full circle, or a spoke from the centre. */
3546
3325
  declare type GuideGeometry = 'linear' | 'circular' | 'radial';
3547
3326
 
3548
- /** Gap between the swatch and its group label, in pixels. */
3549
- export declare const HEADLINE_SWATCH_GAP = 4;
3550
-
3551
- /** Side length of the colour swatch in a headline group row, in pixels. */
3552
- export declare const HEADLINE_SWATCH_SIZE = 12;
3553
-
3554
- /** Gap between trend segments (arrow, percentage, reference), in pixels. */
3555
- export declare const HEADLINE_TREND_GAP = 4;
3556
-
3557
- /** Side length of the trend arrow icon, in pixels. */
3558
- export declare const HEADLINE_TREND_ICON_SIZE = 12;
3559
-
3560
- /** Gap between the value figure and its observation label, in pixels. */
3561
- export declare const HEADLINE_VALUE_LABEL_GAP = 4;
3562
-
3563
3327
  /**
3564
3328
  * Comparison reference for trend indicator
3565
3329
  * - 'previous': Compare to preceding data point
@@ -3604,9 +3368,6 @@ declare interface HeadlineConfig {
3604
3368
  position: HeadlinePosition;
3605
3369
  }
3606
3370
 
3607
- /** Which font token a text segment renders at: the large figure or the smaller chrome. */
3608
- export declare type HeadlineFontRole = 'value' | 'label';
3609
-
3610
3371
  declare interface HeadlineGroupSwatch {
3611
3372
  color: string;
3612
3373
  shape: SwatchShape;
@@ -3633,24 +3394,14 @@ declare interface HeadlineItem {
3633
3394
  comparison: HeadlineComparison | null;
3634
3395
  }
3635
3396
 
3636
- /**
3637
- * What the layout needs to measure, size, and place a headline. Passing this only RESERVES the headline
3638
- * band during compile — it does not place the headline. To paint it, the renderer must call
3639
- * {@link resolveHeadlinePlacement} with the resolved {@link GraphLayout} for the paint rect, size, and
3640
- * visible-item count.
3641
- */
3397
+ /** What the layout needs to measure, size, and place a headline. */
3642
3398
  export declare interface HeadlineLayoutInput {
3643
- /** The headline content to render, already resolved into a strip or grand-total shape. */
3644
3399
  formatted: FormattedHeadline;
3645
3400
  /** Configured size token; `'auto'` resolves to the largest that fits. */
3646
3401
  size: HeadlineSize;
3647
3402
  /** Configured polar placement; only consulted for a grand total. */
3648
3403
  position: HeadlinePosition;
3649
- /**
3650
- * Donut hole radius as a fraction of the outer radius; the caller builds it from the coord system as
3651
- * `polar ? coordSystem.innerRadius : 0`. A centred grand total with `innerRadius > 0` overlays the
3652
- * donut hole instead of reserving a strip band.
3653
- */
3404
+ /** Donut hole radius as a fraction of the outer radius; `0` for non-polar. */
3654
3405
  innerRadius: number;
3655
3406
  }
3656
3407
 
@@ -3666,9 +3417,7 @@ export declare interface HeadlineMeasurer {
3666
3417
  }
3667
3418
 
3668
3419
  declare interface HeadlineNumbers {
3669
- /** Which aggregate to surface as the big headline figure. */
3670
3420
  show?: 'current' | 'average' | 'total' | 'conversion' | 'none';
3671
- /** Baseline the headline is compared against to compute the change indicator. */
3672
3421
  compareWith?: 'previous' | 'first' | 'none';
3673
3422
  size?: 'auto' | 'small' | 'medium' | 'large';
3674
3423
  }
@@ -3702,32 +3451,6 @@ export declare interface HeadlinePlacement {
3702
3451
  */
3703
3452
  export declare type HeadlinePosition = 'above' | 'center';
3704
3453
 
3705
- export declare interface HeadlineRow {
3706
- role: HeadlineRowRole;
3707
- gap: number;
3708
- segments: HeadlineSegment[];
3709
- ariaLabel?: string;
3710
- }
3711
-
3712
- /** Which row a segment stacks into, aligned with the renderer's CSS class names. */
3713
- export declare type HeadlineRowRole = 'valueRow' | 'trendRow' | 'groupRow';
3714
-
3715
- export declare type HeadlineSegment = {
3716
- kind: 'text';
3717
- textRole: HeadlineTextRole;
3718
- fontRole: HeadlineFontRole;
3719
- text: string;
3720
- trendDirection?: HeadlineTrendDirection;
3721
- } | {
3722
- kind: 'swatch';
3723
- swatch: HeadlineSwatch;
3724
- size: number;
3725
- } | {
3726
- kind: 'trendArrow';
3727
- direction: HeadlineTrendDirection;
3728
- size: number;
3729
- };
3730
-
3731
3454
  /**
3732
3455
  * Display mode for headline numbers
3733
3456
  * - 'total': Sum of all values
@@ -3747,16 +3470,6 @@ export declare type HeadlineShow = 'total' | 'average' | 'current' | 'conversion
3747
3470
  */
3748
3471
  export declare type HeadlineSize = 'auto' | 'small' | 'medium' | 'large';
3749
3472
 
3750
- declare type HeadlineSwatch = NonNullable<FormattedHeadlineItem['swatch']>;
3751
-
3752
- /** Which styled span a text segment maps to (drives its CSS class and colour). */
3753
- export declare type HeadlineTextRole = 'value' | 'observation' | 'label' | 'trendPercentage' | 'trendReference';
3754
-
3755
- /**
3756
- * Direction of a headline's trend, and the SOLE source of the comparison's sign — drive both the
3757
- * arrow and the colour off it, since `percentage` is unsigned. `flat` is neutral, so render it
3758
- * without a good/bad colour or a directional arrow.
3759
- */
3760
3473
  export declare type HeadlineTrendDirection = 'up' | 'down' | 'flat';
3761
3474
 
3762
3475
  /**
@@ -3783,18 +3496,6 @@ export declare class HeuristicTextMeasurer implements TextMeasurer {
3783
3496
  */
3784
3497
  export declare function highlight(predicate: Predicate, options?: HighlightBuilderOptions): HighlightInput;
3785
3498
 
3786
- /**
3787
- * Source of truth for which composition strategy each geom uses, or `null` for geoms that
3788
- * opt out of highlighting entirely.
3789
- */
3790
- declare const HIGHLIGHT_STRATEGY_BY_GEOM: {
3791
- readonly bar: "observation-rerender";
3792
- readonly rule: null;
3793
- readonly line: "overlay-anchor";
3794
- readonly area: "overlay-anchor";
3795
- readonly point: "overlay-anchor";
3796
- };
3797
-
3798
3499
  declare interface HighlightBuilderOptions {
3799
3500
  id?: string;
3800
3501
  scope?: HighlightScope;
@@ -3803,15 +3504,11 @@ declare interface HighlightBuilderOptions {
3803
3504
 
3804
3505
  /** Per-layer side-channel produced by the highlights compile stage. */
3805
3506
  export declare interface HighlightComposition {
3806
- /** True when the base render should be wrapped in a dim group (so matched marks stand out). */
3507
+ /** True when the base render should be wrapped in a dim group. */
3807
3508
  isDimmed: boolean;
3808
- /**
3809
- * A real sub-`CompiledLayer` whose dataset is mask-filtered to just the matched observations
3810
- * (everything else identical to the source layer). Feed it back through the same geom renderer to
3811
- * draw the highlighted marks at full strength; `null` when no re-render pass applies.
3812
- */
3509
+ /** Sub-layer holding only matched observations, or `null` when no matched re-render pass applies. */
3813
3510
  matchedLayer: CompiledLayer | null;
3814
- /** Observations to paint as overlay markers under `'overlay-anchor'`. Empty for other strategies. */
3511
+ /** Observations that qualify for the overlay-marker pass. Empty when the strategy doesn't use it. */
3815
3512
  overlayCandidates: HighlightOverlayCandidate[];
3816
3513
  }
3817
3514
 
@@ -3828,13 +3525,9 @@ export declare interface HighlightInput {
3828
3525
  layerIndex?: number;
3829
3526
  }
3830
3527
 
3831
- /** Observation that qualifies for an overlay marker (`'overlay-anchor'` dot + value label). */
3528
+ /** Observation that qualifies for an overlay marker, along with its source index. */
3832
3529
  export declare interface HighlightOverlayCandidate {
3833
3530
  observation: Observation;
3834
- /**
3835
- * Row index into the **source** layer's dataset. Use only as a stable React key for the overlay;
3836
- * it is not a coordinate or a handle into the (mask-filtered) `matchedLayer`.
3837
- */
3838
3531
  observationIndex: number;
3839
3532
  }
3840
3533
 
@@ -3888,21 +3581,28 @@ export declare interface HighlightSpec {
3888
3581
  }
3889
3582
 
3890
3583
  /**
3891
- * How a geom composes highlight matches above its base render. Looked up per geom in
3892
- * `HIGHLIGHT_STRATEGY_BY_GEOM` and stamped onto `CompiledLayer.highlight.strategy` by
3893
- * the layer compiler. Tells the renderer how to consume {@link HighlightComposition}:
3584
+ * How a geom composes highlight matches above its base render. Declared per geom on the geom
3585
+ * definition and stamped onto `CompiledLayer.highlight.strategy` by the layer compiler.
3894
3586
  *
3895
- * - `'observation-rerender'`: re-render `composition.matchedLayer` through the same geom renderer
3896
- * (on top of the dimmed base), no overlays. Used by per-observation surface geoms (bar, rule).
3897
- * - `'overlay-anchor'`: series-scope matches still go through the `matchedLayer` re-render pass,
3898
- * but data-point / x-value matches surface instead as a dot + value label per
3899
- * `composition.overlayCandidates` entry, placed at the geom's own anchor for that observation.
3900
- * Used by line, area, point.
3587
+ * - `'observation-rerender'`: matched observations re-render through the same plugin against a
3588
+ * filtered sub-dataset. Used by per-observation surface geoms (bar, rule).
3589
+ * - `'overlay-anchor'`: series-scope matches go through the matched re-render pass;
3590
+ * data-point / x-value-scope matches surface as a dot + value label at the plugin's
3591
+ * reported anchor. Used by line, area, point.
3901
3592
  */
3902
3593
  export declare type HighlightStrategy = 'observation-rerender' | 'overlay-anchor';
3903
3594
 
3904
- /** The fixed mapping from geom to highlight strategy, for code that needs to look up a geom's strategy by name. */
3905
- export declare type HighlightStrategyByGeom = typeof HIGHLIGHT_STRATEGY_BY_GEOM;
3595
+ /**
3596
+ * Per-geom highlight strategy as a type map, derived from the geom definitions so the renderer's
3597
+ * plugin contract requires `getOverlayAnchor` for exactly the `'overlay-anchor'` geoms.
3598
+ */
3599
+ export declare type HighlightStrategyByGeom = {
3600
+ bar: BarGeom['highlightStrategy'];
3601
+ line: LineGeom['highlightStrategy'];
3602
+ area: AreaGeom['highlightStrategy'];
3603
+ point: PointGeom['highlightStrategy'];
3604
+ rule: RuleGeom['highlightStrategy'];
3605
+ };
3906
3606
 
3907
3607
  /**
3908
3608
  * Chart-global dimming mode for non-matched observations when at least one
@@ -3941,6 +3641,13 @@ export declare class HoverEngine {
3941
3641
  * detection nor group/related, so they are visual decoration only as far as hover is concerned.
3942
3642
  */
3943
3643
  private nonInteractiveLayerIds;
3644
+ /**
3645
+ * Render-side hit-testers registered per layer for `render-hit-test` (Tier-C) layers, keyed by
3646
+ * `CompiledLayer.id`. The renderer owns this map and injects it via {@link setHitTesters}; the
3647
+ * engine holds the live reference so a plugin mounting or updating its tester is visible at the
3648
+ * next `query()` without a re-index. Empty for charts with no Tier-C geom.
3649
+ */
3650
+ private hitTesters;
3944
3651
  constructor({ layers, coordSystem }: HoverEngineInput);
3945
3652
  /**
3946
3653
  * Diff-aware re-index. Layers whose `data`, `geom`, `position` or the graph's coord-system
@@ -3956,10 +3663,6 @@ export declare class HoverEngine {
3956
3663
  /**
3957
3664
  * Renderers call this on mount and on resize. Viewport is set-once, not per-query.
3958
3665
  *
3959
- * Pass the plot **panel** rect (the geom-drawing area), the same rect the pointer is normalized
3960
- * against before `query` — these must agree or hit-testing skews. Only the aspect ratio is used
3961
- * (it corrects the scatter points-2D index); the absolute pixels are not retained.
3962
- *
3963
3666
  * On every call, viewport-dependent indexes are asked to re-prep themselves for the new
3964
3667
  * viewport (`reindexPoints2DForAspect` is idempotent on aspect match, so unchanged-aspect
3965
3668
  * calls are cheap). The renderer is expected to debounce resize events so the underlying
@@ -3967,14 +3670,25 @@ export declare class HoverEngine {
3967
3670
  */
3968
3671
  setViewport(viewport: HoverViewport): void;
3969
3672
  /**
3970
- * Synchronous query. The `cursor` is the pointer normalized to `[0, 1]` against the **same rect
3971
- * passed to `setViewport`** (the plot panel) x left-to-right, y in data-space (0 bottom, 1 top),
3972
- * so a top-origin renderer flips Y before calling (see {@link HoverCursor}).
3973
- *
3974
- * Returns the same `HoverState` reference while the primary stays on the same `(layerId,
3975
- * pointIndex)`, so subscribers using identity-based equality can short-circuit intra-geom cursor
3976
- * moves without re-rendering. Cache invalidates on `update()`. Warm-start seeds are written after
3977
- * every match so the next call walks a minimum number of Delaunay edges.
3673
+ * Registers the render-side hit-testers for `render-hit-test` layers. The renderer holds a live,
3674
+ * mutable map (a plugin writes its tester on mount); the engine keeps the reference, so updates
3675
+ * are seen at the next `query()` without re-indexing. Pure runtime injection nothing here rides
3676
+ * in the compiled spec.
3677
+ */
3678
+ setHitTesters(hitTesters: ReadonlyMap<string, PanelHitTester>): void;
3679
+ /**
3680
+ * Resolves a `HoverState` for a known observation identity on a `render-hit-test` layer the push
3681
+ * path for geoms that own their own pointer surface (a live-simulation layer whose interactive
3682
+ * overlay intercepts pointer events before the central capture layer sees them). The geom already
3683
+ * knows which mark is under the cursor and supplies its key; the engine turns it into the same
3684
+ * unified `HoverState` the pull path produces, so the central tooltip and a11y read one source.
3685
+ */
3686
+ hoverByKey(layerId: string, key: string): HoverState;
3687
+ /**
3688
+ * Synchronous query. Returns the same `HoverState` reference while the primary stays on the
3689
+ * same `(layerId, pointIndex)`, so subscribers using identity-based equality can short-circuit
3690
+ * intra-geom cursor moves without re-rendering. Cache invalidates on `update()`. Warm-start
3691
+ * seeds are written after every match so the next call walks a minimum number of Delaunay edges.
3978
3692
  */
3979
3693
  query(cursor: HoverCursor): HoverState;
3980
3694
  private invalidateCache;
@@ -3990,22 +3704,6 @@ export declare interface HoverEngineInput {
3990
3704
  coordSystem: CoordSystem;
3991
3705
  }
3992
3706
 
3993
- /** SVG `<line>` endpoints for the continuous-composition rule. */
3994
- declare interface HoverGuideLineProps {
3995
- x1: string | number;
3996
- x2: string | number;
3997
- y1: string | number;
3998
- y2: string | number;
3999
- }
4000
-
4001
- /** SVG `<rect>` attributes for the bar-composition band rect. */
4002
- declare interface HoverGuideRectProps {
4003
- x: string | number;
4004
- y: string | number;
4005
- width: string | number;
4006
- height: string | number;
4007
- }
4008
-
4009
3707
  /**
4010
3708
  * A single hit returned by the hover engine.
4011
3709
  *
@@ -4029,12 +3727,9 @@ export declare interface HoverHit {
4029
3727
  layerId: string;
4030
3728
  pointIndex: number;
4031
3729
  /**
4032
- * Paint coordinates for an overlay marker (e.g. a hover dot) at this hit. Normalized panel-local.
4033
- * Cartesian: `[0, 1]²` in data-space (y=0 at the bottom, y=1 at the top — matching the compiler's
4034
- * `POSITION_VARIABLES.y`); convert to panel pixels as `xPixel = x * panel.width`,
4035
- * `yPixel = (1 - y) * panel.height` (invert y for top-origin renderers). Polar: `(angle in radians
4036
- * clockwise from 12 o'clock, radius in [0, 1])` — place via the same angle/radius transform the
4037
- * arcs use (center = panel center, outer radius = `min(panel.w, panel.h) / 2`).
3730
+ * Normalized panel-local position. Cartesian: `[0, 1]²` in data-space (y=0 at the bottom,
3731
+ * y=1 at the top — matching the compiler's `POSITION_VARIABLES.y`). Polar: `(angle in radians
3732
+ * clockwise from 12 o'clock, radius in [0, 1])`.
4038
3733
  */
4039
3734
  x: number;
4040
3735
  y: number;
@@ -4042,37 +3737,20 @@ export declare interface HoverHit {
4042
3737
  observation: Observation;
4043
3738
  }
4044
3739
 
4045
- /**
4046
- * Output of `HoverEngine.query()`. Hits are partitioned into three roles. To paint, recombine
4047
- * **per layer**: the primary's own layer renders `primary + group + (related.get(layerId) ?? [])`;
4048
- * every other layer renders only `related.get(layerId) ?? []`. Tooltips list these in legend
4049
- * (color scale-domain) order, with the primary emphasized in place rather than hoisted to the top.
4050
- */
3740
+ /** Output of `HoverEngine.query()`. */
4051
3741
  export declare interface HoverState {
4052
- /** The single hit directly under the cursor (nearest/contained geom), or `null` when over no geom. */
4053
3742
  primary: HoverHit | null;
4054
- /**
4055
- * Companion hits in the **primary's own layer** that share its main-axis position: the stacked
4056
- * segments of the hovered column, or the dodged-bar siblings in the same band. Empty for point
4057
- * and arc layers (each mark stands alone). Render alongside the primary in its layer; do not
4058
- * surface them in any other layer.
4059
- */
4060
3743
  group: HoverHit[];
4061
3744
  /**
4062
- * Companion hits in **other** layers at the same main-axis position (e.g. every line/area/bar
4063
- * crossing the hovered x), bucketed by `CompiledLayer.id` so a per-layer consumer reads its own
4064
- * hits in O(1) (`related.get(layer.id) ?? []`) instead of filtering a flat array on every render.
4065
- * Layers with no companions are absent from the map — callers fall back to an empty array on miss.
3745
+ * Cross-layer and same-layer companion hits, bucketed by `CompiledLayer.id` so a per-layer
3746
+ * consumer reads its own hits in O(1) (`related.get(layer.id) ?? []`) instead of filtering
3747
+ * a flat array on every render. Layers with no companions are absent from the map callers
3748
+ * fall back to an empty array on miss.
4066
3749
  */
4067
3750
  related: ReadonlyMap<string, HoverHit[]>;
4068
3751
  }
4069
3752
 
4070
- /**
4071
- * Viewport facts the engine needs for aspect-corrected hit testing. Pass the plot **panel** rect
4072
- * (where geoms paint), not the whole chart — `HoverEngine.query` normalizes the pointer against
4073
- * this same rect. Only the aspect ratio (`width / height`) is consumed: it corrects the points-2D
4074
- * (scatter) nearest-neighbour index so Euclidean distance matches on-screen pixel distance.
4075
- */
3753
+ /** Viewport facts the engine needs for aspect-corrected hit testing. */
4076
3754
  export declare interface HoverViewport {
4077
3755
  /** Panel width in pixels. */
4078
3756
  width: number;
@@ -4111,27 +3789,22 @@ declare type InferredScaleOptions = ContinuousScaleOptions | DiscreteScaleOption
4111
3789
  /**
4112
3790
  * Curve interpolation method for lines and areas.
4113
3791
  *
4114
- * - `'linear'` — Straight segments between points. Maps to d3-shape `curveLinear`.
4115
- * - `'catmull-rom'` — Smooth spline through points. Maps to d3-shape `curveCatmullRom`.
3792
+ * - `'linear'` — Straight segments between points
3793
+ * - `'catmull-rom'` — Smooth spline through points
4116
3794
  */
4117
3795
  export declare type InterpolateType = 'linear' | 'catmull-rom';
4118
3796
 
3797
+ /** Whether a geom identity is one of the built-in marks (vs. a custom registration). */
3798
+ export declare function isBuiltinGeomName(name: string): name is GeomName;
3799
+
4119
3800
  /** Narrows a coord system to the cartesian variant. */
4120
3801
  export declare function isCartesian(coordSystem: CoordSystem): coordSystem is CartesianCoordSystem;
4121
3802
 
4122
- export declare const isCategoricalValueFormat: (valueFormat: ValueFormat) => valueFormat is CategoricalValueFormat;
4123
-
4124
3803
  /**
4125
3804
  * Current state of the process-wide caching flag.
4126
3805
  */
4127
3806
  export declare const isCompilerCachingEnabled: () => boolean;
4128
3807
 
4129
- export declare const isCurrencyValueFormat: (valueFormat: ValueFormat) => valueFormat is CurrencyValueFormat;
4130
-
4131
- export declare const isDateWithoutYearValueFormat: (valueFormat: ValueFormat) => valueFormat is TemporalValueFormat & {
4132
- type: "month" | "day_month" | "weekly_date_range";
4133
- };
4134
-
4135
3808
  /**
4136
3809
  * Type guard that checks if a value is neither null nor undefined.
4137
3810
  */
@@ -4139,10 +3812,6 @@ export declare function isDefined<T>(value: T | null | undefined): value is T;
4139
3812
 
4140
3813
  /**
4141
3814
  * True when `input` is a {@link GraphConfig} rather than a {@link SpecInput}.
4142
- *
4143
- * Discriminates purely on the presence of `layers`: a {@link SpecInput} always carries a `layers`
4144
- * array and a {@link GraphConfig} never has a top-level `layers`, so its absence identifies a
4145
- * GraphConfig.
4146
3815
  */
4147
3816
  export declare const isGraphConfig: (input: SpecInput | GraphConfig) => input is GraphConfig;
4148
3817
 
@@ -4152,34 +3821,10 @@ export declare const isGraphConfig: (input: SpecInput | GraphConfig) => input is
4152
3821
  *
4153
3822
  * if (isLayerOf(layer, 'line')) renderLine(layer); // layer is CompiledLayerFor<'line'>
4154
3823
  */
4155
- export declare function isLayerOf<G extends GeomName>(layer: CompiledLayer, geom: G): layer is CompiledLayerFor<G>;
3824
+ export declare function isLayerOf<G extends string>(layer: CompiledLayer, geom: G): layer is CompiledLayerFor<G>;
4156
3825
 
4157
- export declare const isLookupValueFormat: (valueFormat: ValueFormat) => valueFormat is LookupValueFormat;
4158
-
4159
- export declare const isNumericValueFormat: (valueFormat: ValueFormat) => valueFormat is NumericValueFormat | CurrencyValueFormat;
4160
-
4161
- /**
4162
- * Whether a string is safe to assign to an `href`. Allows http, https, and
4163
- * mailto; rejects javascript:, data:, vbscript:, and any other scheme that
4164
- * could execute code or load arbitrary content.
4165
- *
4166
- * Leading control characters (NUL, tab, newline, etc.) are stripped before
4167
- * the scheme check because the browser ignores them when parsing href, so
4168
- * `\tjavascript:alert(1)` would otherwise sneak past a naive prefix test.
4169
- */
4170
- export declare const isSafeUrl: (input: unknown) => input is string;
4171
-
4172
- /**
4173
- * True for positions that accumulate values along an axis (`'stack'` and `'fill'`).
4174
- *
4175
- * When true, the y position columns already hold cumulative band bounds — the renderer draws each
4176
- * segment directly between them. Recover a segment's own value (for labels / tooltips) via
4177
- * `getYRaw`, or in original data units via `createSegmentYReader`.
4178
- */
4179
3826
  export declare function isStackedPosition(position: PositionType): boolean;
4180
3827
 
4181
- export declare const isTemporalValueFormat: (valueFormat: ValueFormat) => valueFormat is TemporalValueFormat;
4182
-
4183
3828
  /**
4184
3829
  * Snapshot of the most recent compile/recompile call returned by {@link Compiler.getLastCompile}.
4185
3830
  * `stages` holds the per-stage hit/miss delta incurred *by that call* (not cumulative);
@@ -4219,24 +3864,17 @@ declare interface LayerCompilerInput {
4219
3864
  }
4220
3865
 
4221
3866
  /**
4222
- * Discriminated union of all layer inputs, keyed on `geom`.
3867
+ * Any layer input: a built-in (typed per geom) or a custom registration (open identity, opaque params).
4223
3868
  * This is the user-facing type — fields are optional and will be resolved with defaults.
4224
3869
  */
4225
- declare type LayerInput = {
4226
- [G in GeomName]: LayerInputOf<G>;
4227
- }[GeomName];
3870
+ declare type LayerInput = BuiltinLayerInput | CustomLayerInput;
4228
3871
 
4229
3872
  declare interface LayerInputBase {
4230
3873
  type: 'layer';
4231
- /** Stable identifier; auto-assigned during resolution when omitted. */
4232
3874
  id?: string;
4233
- /** Layer-local aesthetic mapping, merged over the spec-level mapping. */
4234
3875
  mapping?: AesMapping;
4235
- /** Statistical transform applied to this layer (e.g. count, mean, smooth). @default 'identity' */
4236
3876
  stat?: StatName | StatInput;
4237
- /** How overlapping marks are arranged (stack, dodge, fill, identity). */
4238
3877
  position?: PositionType;
4239
- /** Which y scale this layer binds to — the primary or secondary axis. */
4240
3878
  yScaleType?: YScaleType;
4241
3879
  dataLabels?: DataLabelsInput;
4242
3880
  /**
@@ -4258,12 +3896,9 @@ declare type LayerInputOf<G extends GeomName> = LayerInputBase & {
4258
3896
  };
4259
3897
 
4260
3898
  /**
4261
- * Discriminated union of all resolved layer specs, keyed on `geom`.
4262
- * All properties are fully resolved — no optionals.
3899
+ * Any resolved layer spec: a built-in or a custom registration. All properties are fully resolved.
4263
3900
  */
4264
- declare type LayerSpec = {
4265
- [G in GeomName]: LayerSpecOf<G>;
4266
- }[GeomName];
3901
+ declare type LayerSpec = BuiltinLayerSpec | CustomLayerSpec;
4267
3902
 
4268
3903
  declare interface LayerSpecBase {
4269
3904
  type: 'layer';
@@ -4282,7 +3917,6 @@ declare type LayerSpecOf<G extends GeomName> = LayerSpecBase & {
4282
3917
  params: GeomParamsMap[G];
4283
3918
  };
4284
3919
 
4285
- /** Optional per-layer aggregates the summariser emits for label rendering. Fields are present only when the layer's geometry calls for them. */
4286
3920
  export declare interface LayerSummary {
4287
3921
  /** Per-x stack totals — one entry per x. */
4288
3922
  stackTotals?: StackTotalEntry[];
@@ -4312,7 +3946,7 @@ declare interface LayerValidationCheckInput {
4312
3946
 
4313
3947
  declare interface LayerValidationInput {
4314
3948
  layerId: string;
4315
- geom: GeomName;
3949
+ geom: GeomIdentity;
4316
3950
  stat: StatSpec;
4317
3951
  /** `spec.mapping` merged with `layer.mapping` */
4318
3952
  effectiveMapping: AesMapping;
@@ -4347,31 +3981,23 @@ declare class LayerValidator {
4347
3981
  */
4348
3982
  private validateVariableExistence;
4349
3983
  /**
4350
- * Rule layers need exactly one numeric ValueMapping on `x` or `y`, unless a stat produces the
4351
- * value at compile time (e.g. `stat.mean()` populates `y`).
3984
+ * Runs the geom's own mapping invariant (a rule needs exactly one numeric axis, say). The invariant
3985
+ * lives on the geom definition, so the validator dispatches to it rather than naming a geom; geoms
3986
+ * without a `validateMapping` hook impose none.
4352
3987
  */
4353
- private validateRuleMapping;
3988
+ private validateGeomMapping;
4354
3989
  /**
4355
- * In the current version, rule layers don't have meaningful semantics under polar coords (pie / donut).
3990
+ * Rejects a layer whose coord is absent from the geom's declared `supportedCoordTypes` (e.g. a rule
3991
+ * has no meaning under polar pie/donut coords). The supported set lives on the geom definition, so
3992
+ * the validator resolves support from there rather than naming a geom.
4356
3993
  */
4357
- private validateRuleCoord;
3994
+ private validateCoordSupport;
4358
3995
  }
4359
3996
 
4360
- /**
4361
- * Default outer padding around the whole chart, in pixels. Used when {@link buildLayoutGrid} is called
4362
- * without an explicit `padding` (the renderer threads a resolved theme token in to override it). Once
4363
- * resolved it is baked into every {@link GraphLayout} rect (rects start at this inset), so don't re-add
4364
- * it when painting — the constant is exported only so callers can reconcile against the container edge.
4365
- * The per-region gaps below are intentionally internal; trust the returned rects rather than
4366
- * reproducing the spacing.
4367
- */
3997
+ /** Outer padding around the whole chart, in pixels. */
4368
3998
  export declare const LAYOUT_PADDING = 24;
4369
3999
 
4370
4000
  /**
4371
- * The public layout entry point — `new LayoutCompiler(measurer).compile(input)`. Renderers consume the
4372
- * returned {@link GraphLayout} rects and {@link LayoutCompileResult.formattedAxes}; they do not
4373
- * reimplement the grid or re-run tick selection.
4374
- *
4375
4001
  * Compiles layout geometry and final axis ticks. Output rects are in container coordinates.
4376
4002
  *
4377
4003
  * Horizontal axis edge height is candidate-independent (~line-height), so `panel.height` is
@@ -4420,44 +4046,23 @@ export declare class LayoutCompiler {
4420
4046
  */
4421
4047
  export declare interface LayoutCompileResult {
4422
4048
  layout: GraphLayout;
4423
- /**
4424
- * The axes with their final ticks already chosen and formatted by the compiler — paint each
4425
- * `ticks[].formattedLabel` as-is. Tick selection runs as part of layout (it depends on the resolved
4426
- * panel size), so do not re-run candidate selection or re-apply a value format in the renderer.
4427
- */
4428
4049
  formattedAxes: FormattedAxis[];
4429
4050
  }
4430
4051
 
4431
4052
  /** Input for the layout compiler. */
4432
4053
  export declare interface LayoutCompilerInput {
4433
4054
  axes: readonly CompiledAxisGuide[];
4434
- /** Locale the source data was parsed with; the fallback when no formatting locale is given. */
4435
4055
  parsingLocale: Locale;
4436
4056
  numberFormat: NumberFormatConfig;
4437
4057
  formattedLegends: FormattedLegend[];
4438
- /** Total pixel area available to the whole chart. */
4439
4058
  containerSize: BoxSize;
4440
4059
  externalMeasurements: ExternalMeasurements;
4441
4060
  /** The headline to measure and place; absent = no headline. */
4442
4061
  headline?: HeadlineLayoutInput;
4443
- /** Locale used to format tick/label text; falls back to `parsingLocale` when absent. */
4444
4062
  formattingLocale?: Locale;
4445
- /** Annotations that may overflow the panel and need extra edge padding reserved. */
4446
4063
  annotations?: CompiledAnnotations;
4447
- /** Coord system, consulted only to decide whether annotation overflow applies (cartesian only). */
4448
4064
  coordSystem?: CoordSystem;
4449
- /**
4450
- * Must equal the renderer's actual text zoom. It only affects layout when difference-arrow
4451
- * annotations exist on a cartesian coord, where it scales their label measurements to reserve the
4452
- * right panel-edge padding; otherwise it is inert.
4453
- */
4454
4065
  textScale?: number;
4455
- /**
4456
- * Outer padding around the whole chart, in pixels, applied uniformly on all four sides. Defaults to
4457
- * {@link LAYOUT_PADDING} when omitted. Renderers thread a resolved theme token here so a consumer can
4458
- * tighten or loosen the frame (e.g. a borderless, recolored chart that wants less surrounding space).
4459
- */
4460
- padding?: number;
4461
4066
  }
4462
4067
 
4463
4068
  /** Positions where axes/labels/legends can be placed around the panel. */
@@ -4465,20 +4070,13 @@ export declare type LayoutEdge = 'top' | 'right' | 'bottom' | 'left';
4465
4070
 
4466
4071
  /** Strategy for measuring chart element sizes in pixels. */
4467
4072
  export declare interface LayoutMeasurer extends HeadlineMeasurer {
4468
- /**
4469
- * Returns the axis band thickness (height for top/bottom, width for left/right): the tick-label
4470
- * extent plus the renderer's own tick marks and offsets. Honour `labelRotation` and the
4471
- * `labelMaxWidthPx` truncation cap so the reserved band matches what is actually painted.
4472
- */
4073
+ /** Returns the pixel size for an axis (height for top/bottom, width for left/right). */
4473
4074
  measureAxis: (axis: FormattedAxis) => number;
4474
4075
  /** Returns the pixel size for an axis title (height for top/bottom, width for left/right). */
4475
4076
  measureAxisLabel: (axis: FormattedAxis) => number;
4476
4077
  /** Returns the pixel size for a legend (height for top/bottom, width for left/right). */
4477
4078
  measureLegend: (legend: FormattedLegend) => number;
4478
- /**
4479
- * Returns the size of a single tick label. This drives final tick density, so measure with the exact
4480
- * font the ticks are painted in; selection reads only `width`/`height` from the result.
4481
- */
4079
+ /** Returns the size of a single tick label. */
4482
4080
  measureTickLabel: (label: string) => MeasuredText;
4483
4081
  /** Returns the size of a difference-arrow label rendered at the given size. */
4484
4082
  measureDifferenceArrowLabel: (text: string, size: DifferenceArrowSize) => MeasuredText;
@@ -4521,7 +4119,6 @@ declare type LegendConfigInput = Partial<LegendConfig>;
4521
4119
  */
4522
4120
  declare type LegendDisplay = 'pill' | 'direct' | 'auto';
4523
4121
 
4524
- /** One entry in a legend: a domain value paired with the visual values that represent it. */
4525
4122
  export declare interface LegendItem {
4526
4123
  /** Raw data value (e.g., "Apples") */
4527
4124
  value: DataValue;
@@ -4529,17 +4126,12 @@ export declare interface LegendItem {
4529
4126
  label: string | null;
4530
4127
  /** Mapped visual values per aesthetic (e.g., { color: '#ff0000' }). */
4531
4128
  visual: LegendItemVisual;
4532
- /**
4533
- * Normalized y position in [0,1], 1 = top (matches {@link DirectLabelInput.normalizedY}). Null when display
4534
- * is not 'direct' or no endpoint found.
4535
- */
4129
+ /** Normalized y position in [0,1]. Null when display is not 'direct' or no endpoint found. */
4536
4130
  normalizedY: number | null;
4537
4131
  /**
4538
4132
  * Visual signature of the geom this item describes. Per-item because a
4539
4133
  * single merged legend can span layers of different geoms (e.g. a combo
4540
- * chart's bar series and line series share one legend). Resolved via the
4541
- * same {@link SwatchShape} mapping that drives the headline and rule pills,
4542
- * so a series paints one consistent mark everywhere.
4134
+ * chart's bar series and line series share one legend).
4543
4135
  */
4544
4136
  swatchShape: SwatchShape;
4545
4137
  /**
@@ -4554,14 +4146,9 @@ export declare interface LegendItem {
4554
4146
  */
4555
4147
  declare interface LegendItemVisual {
4556
4148
  color?: string;
4557
- /**
4558
- * Symbol diameter in PIXELS, present on bubble legends (see {@link CompiledLegendGuide.aesthetics}). Render a
4559
- * sized circle rather than a swatch; skip the item when this is non-finite or ≤ 0.
4560
- */
4561
4149
  size?: DataValue;
4562
4150
  alpha?: DataValue;
4563
4151
  strokeWidth?: DataValue;
4564
- /** Stroke style for `line` / `area` swatches only (solid/dashed/dotted). Other `swatchShape`s ignore it. */
4565
4152
  lineType?: LineStyleType;
4566
4153
  }
4567
4154
 
@@ -4569,27 +4156,37 @@ declare type LegendPosition = 'auto' | 'right' | 'left' | 'top' | 'bottom' | 'no
4569
4156
 
4570
4157
  declare function line(options?: GeomOptions<'line'>): LayerInputOf<'line'>;
4571
4158
 
4159
+ /**
4160
+ * Represents a series of points connected by a line.
4161
+ *
4162
+ * If the x variable is numeric or temporal, the data will be sorted by x (this is to ensure the line is connected in the correct order).
4163
+ */
4164
+ declare class LineGeom extends Geom {
4165
+ readonly type: "line";
4166
+ readonly requiredAesthetics: AestheticKey[];
4167
+ readonly positionChannels: readonly PositionChannel[];
4168
+ readonly swatchShape: SwatchShape;
4169
+ readonly highlightStrategy = "overlay-anchor";
4170
+ readonly spatialKind = "buckets";
4171
+ readonly directLabelPositions: readonly ["identity", "stack", "dodge", "fill"];
4172
+ readonly supportsPerGroupHeadline: boolean;
4173
+ compile(input: GeomCompilerInput): CompiledGeom;
4174
+ /** The observation's exact `(x, y)`. A line has no polar anchor. */
4175
+ resolveAnchorPosition(observation: Observation, coordSystem: CoordSystem): AnchorPosition | null;
4176
+ }
4177
+
4572
4178
  /**
4573
4179
  * Line-specific parameters
4574
4180
  */
4575
4181
  export declare interface LineGeomParams {
4576
- /**
4577
- * Stroke width in pixels. `'auto'` reads the per-observation `strokeWidth`
4578
- * channel (`getStrokeWidth`) and falls back to the geom default when unmapped.
4579
- */
4580
4182
  lineWidth: number | 'auto';
4581
4183
  /**
4582
- * Interpolation method to use for the line. Names a d3-shape curve family:
4583
- * `'linear'` ⇒ `curveLinear`, `'catmull-rom'` ⇒ `curveCatmullRom`.
4184
+ * Interpolation method to use for the line.
4584
4185
  * @default 'linear'
4585
4186
  */
4586
4187
  interpolate: InterpolateType;
4587
4188
  /**
4588
- * How to handle missing (NULL/undefined) values:
4589
- * - `'zero'`: nulls arrive already substituted with zero by the compiler —
4590
- * render normally, no special handling.
4591
- * - `'gap'`: break the path wherever x or y is null (d3 `defined()`).
4592
- * - `'connect'`: drop null rows before pathing so the line spans the gap.
4189
+ * How to handle missing (NULL/undefined) values.
4593
4190
  * @default 'gap'
4594
4191
  */
4595
4192
  missingValues: MissingValuesType;
@@ -4599,7 +4196,6 @@ declare interface LineOptions {
4599
4196
  isSmoothLine?: boolean;
4600
4197
  lineThickness?: number | 'auto';
4601
4198
  showPoints?: boolean;
4602
- /** How gaps in the data are drawn: leave a gap, connect across, or treat as zero. */
4603
4199
  missingValues?: 'gap' | 'connect' | 'zero';
4604
4200
  }
4605
4201
 
@@ -4612,16 +4208,9 @@ declare interface LineOptions {
4612
4208
  */
4613
4209
  export declare type LineStyleType = 'solid' | 'dashed' | 'dotted';
4614
4210
 
4615
- /**
4616
- * One of the BCP-47 locale strings the engine supports for number and date
4617
- * formatting. Used both to parse source values and as the display fallback (see
4618
- * `ConfigSpec.parsingLocale`); a `format*` helper's `formattingLocale` param
4619
- * overrides display. The supported set is deliberately small (see `LOCALES`),
4620
- * and `duration` always formats in English regardless of the locale.
4621
- */
4622
4211
  export declare type Locale = (typeof LOCALES)[number];
4623
4212
 
4624
- /** The full set of supported BCP-47 locale strings. */
4213
+ /** A BCP-47 string representing a supported locale. */
4625
4214
  declare const LOCALES: readonly ["en-GB", "en-US", "ar", "pt-PT"];
4626
4215
 
4627
4216
  /** Logical composition of any predicate. */
@@ -4637,7 +4226,7 @@ export declare type LogicalPredicate = {
4637
4226
  * A value format that switches on a peer variable's value. Produced by transforms whose output is
4638
4227
  * structurally observation-dependent (see `reshapeFromWideToLong`).
4639
4228
  */
4640
- export declare interface LookupValueFormat {
4229
+ declare interface LookupValueFormat {
4641
4230
  type: 'lookup';
4642
4231
  /** Peer variable whose stringified value selects the case. */
4643
4232
  byVariable: VariableName;
@@ -4647,13 +4236,16 @@ export declare interface LookupValueFormat {
4647
4236
  fallback: ExplicitValueFormat;
4648
4237
  }
4649
4238
 
4239
+ /** The data-space axis a `CartesianCoordSystem` uses as the main (independent) axis. */
4240
+ export declare type MainAxis = 'x' | 'y';
4241
+
4650
4242
  /**
4651
- * The data-space axis a `CartesianCoordSystem` uses as the main (independent) axis.
4652
- * When `mainAxis === 'y'` (flip), the position column roles swap: the x-columns (`getX`/`getXMin`/
4653
- * `getXMax`) carry the measure / cross-axis extent and the y-columns carry the main-axis band
4654
- * position. Geoms branch on this to decide which reader feeds which pixel axis.
4243
+ * The aesthetic keys a custom geom's layer mapping may carry — recovered structurally from the
4244
+ * definition's declared manifest (`requiredAesthetics` + `visualAesthetics`), plus `group` (series is
4245
+ * universal). An author writing `geom.<name>({ aes })` is autocompleted to these and an undeclared key
4246
+ * is rejected, the mapping analog of how `ParamsOf` types `params`.
4655
4247
  */
4656
- export declare type MainAxis = 'x' | 'y';
4248
+ declare type MappableAes<Definition extends Geom> = Definition['requiredAesthetics'][number] | Definition['visualAesthetics'][number] | 'group';
4657
4249
 
4658
4250
  /**
4659
4251
  * Create a pipeable mapping spec item.
@@ -4685,13 +4277,6 @@ declare interface MeanStatSpec {
4685
4277
  type: 'mean';
4686
4278
  }
4687
4279
 
4688
- /**
4689
- * Pixel dimensions of a measured string, in CSS pixels. `height === ascent + descent`, where ascent
4690
- * and descent come from the font box (canvas `fontBoundingBoxAscent`/`fontBoundingBoxDescent`), not
4691
- * the glyph box — so the height reflects the font's line metrics and is stable across strings rather
4692
- * than tracking the actual glyphs drawn. `width` is the advance width (the pen advance), not the
4693
- * tight ink bounding box.
4694
- */
4695
4280
  export declare interface MeasuredText {
4696
4281
  width: number;
4697
4282
  height: number;
@@ -4710,19 +4295,14 @@ export declare interface MemoStats {
4710
4295
  /**
4711
4296
  * Strategy for handling null/undefined values in lines and areas.
4712
4297
  *
4713
- * - `'zero'` — Replace missing values with zero. Pre-substituted by the compiler, so the renderer
4714
- * sees no nulls and paths normally.
4715
- * - `'gap'` — Leave a visible gap where values are missing. The renderer breaks the path at any
4716
- * null x / y (e.g. d3's `defined()`).
4717
- * - `'connect'` — Skip missing values and connect adjacent valid points. The renderer drops null
4718
- * rows before pathing.
4298
+ * - `'zero'` — Replace missing values with zero
4299
+ * - `'gap'` Leave a visible gap where values are missing
4300
+ * - `'connect'` — Skip missing values and connect adjacent valid points
4719
4301
  */
4720
4302
  export declare type MissingValuesType = 'zero' | 'gap' | 'connect';
4721
4303
 
4722
- /** The hues available as a base for monochrome palettes, in pick order. */
4723
4304
  export declare const MONO_BASES: readonly ["grey", "red", "orange", "yellow", "green", "cyan", "blue", "purple", "pink"];
4724
4305
 
4725
- /** One of the base hues a monochrome palette can be built from. */
4726
4306
  export declare type MonoPaletteBase = (typeof MONO_BASES)[number];
4727
4307
 
4728
4308
  declare type MonoPaletteConfig = {
@@ -4731,17 +4311,14 @@ declare type MonoPaletteConfig = {
4731
4311
  variant?: MonoPaletteVariant;
4732
4312
  };
4733
4313
 
4734
- /** Tints the single-hue ramp for use on light vs dark backgrounds. */
4735
4314
  declare type MonoPaletteVariant = 'light' | 'dark';
4736
4315
 
4737
4316
  /**
4738
- * Named font weights mapped to their numeric values — the canonical table `getNumericWeight` resolves
4739
- * a named `FontSpec.weight` against. A from-scratch measurer must reproduce these exact mappings to
4740
- * match the engine's text-measurement cache keys.
4317
+ * Named font weights mapped to their numeric values.
4741
4318
  *
4742
4319
  * See: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/font-weight#common_weight_name_mapping
4743
4320
  */
4744
- export declare const NAMED_WEIGHTS: {
4321
+ declare const NAMED_WEIGHTS: {
4745
4322
  readonly thin: 100;
4746
4323
  readonly hairline: 100;
4747
4324
  readonly extraLight: 200;
@@ -4761,12 +4338,10 @@ export declare const NAMED_WEIGHTS: {
4761
4338
  readonly ultraBlack: 950;
4762
4339
  };
4763
4340
 
4764
- export declare type NamedWeightKey = keyof typeof NAMED_WEIGHTS;
4341
+ declare type NamedWeightKey = keyof typeof NAMED_WEIGHTS;
4765
4342
 
4766
- /** The hues available as a base for neon palettes, in pick order. */
4767
4343
  export declare const NEON_BASES: readonly ["cyan", "pink", "purple", "red", "orange", "yellow", "green", "blue"];
4768
4344
 
4769
- /** One of the base hues a neon palette can be built from. */
4770
4345
  export declare type NeonPaletteBase = (typeof NEON_BASES)[number];
4771
4346
 
4772
4347
  declare type NeonPaletteConfig = {
@@ -4775,7 +4350,6 @@ declare type NeonPaletteConfig = {
4775
4350
  variant?: NeonPaletteVariant;
4776
4351
  };
4777
4352
 
4778
- /** `waterfall` swaps in the positive/negative/total colors used by waterfall charts. */
4779
4353
  declare type NeonPaletteVariant = 'default' | 'waterfall';
4780
4354
 
4781
4355
  /**
@@ -4809,15 +4383,11 @@ export declare interface NumberFormatConfig {
4809
4383
  */
4810
4384
  decimalSeparator?: string;
4811
4385
  /**
4812
- * Prefix to prepend (e.g., '$', '€'). No value formatter reads this — it is
4813
- * renderer-applied (currently inert in this package); a rebuild must prepend
4814
- * it to the formatted string itself.
4386
+ * Prefix to prepend (e.g., '$', '€').
4815
4387
  */
4816
4388
  prefix?: string;
4817
4389
  /**
4818
- * Suffix to append (e.g., '%', ' units'). No value formatter reads this — it
4819
- * is renderer-applied (currently inert in this package); a rebuild must append
4820
- * it to the formatted string itself.
4390
+ * Suffix to append (e.g., '%', ' units').
4821
4391
  */
4822
4392
  suffix?: string;
4823
4393
  }
@@ -4859,55 +4429,26 @@ export declare interface ObservationAnchorInput {
4859
4429
 
4860
4430
  declare type Options = Partial<LineOptions & BarOptions & ScatterOptions & ComboOptions & PieOptions & TableOptions>;
4861
4431
 
4862
- /** Resolved palette selector; a custom palette carries its looked-up `colors`. */
4863
4432
  declare type PaletteConfig = DefaultPaletteConfig | GraphyPaletteConfig | PastelPaletteConfig | NeonPaletteConfig | MonoPaletteConfig | CustomPaletteConfig;
4864
4433
 
4865
- /** Palette selector accepted from users; a custom palette is referenced by `id` only. */
4866
4434
  declare type PaletteConfigInput = DefaultPaletteConfig | GraphyPaletteConfig | PastelPaletteConfig | NeonPaletteConfig | MonoPaletteConfig | CustomPaletteInput;
4867
4435
 
4868
- /** Resolved palette overrides: group number (1-indexed) to a concrete hex color. */
4869
- export declare type PaletteOverrides = Record<number, string>;
4870
-
4871
- /**
4872
- * Sparse override map. Keys are group numbers (1-indexed), values are either a raw
4873
- * hex value, or a color id to look up in the active custom palette.
4874
- *
4875
- * Indexes that are not specified fallback to the palette default.
4876
- *
4877
- * If both are set, `hex` wins. If `id` is set but not found in the active custom palette,
4878
- * the override is ignored.
4879
- */
4880
- export declare type PaletteOverridesInput = Record<number, {
4881
- hex?: string;
4882
- id?: string;
4883
- }>;
4884
-
4885
- /** User-facing palette color scale; defaults to the active theme palette when `palette` is omitted. */
4886
4436
  declare interface PaletteScaleInput {
4887
4437
  type: 'scale';
4888
- /** Which channel this scale drives — only color channels accept palettes. */
4889
4438
  scaledAesthetic: ScaledAestheticKey;
4890
4439
  scaleType: 'palette';
4891
- /** Named or custom palette to draw group colors from. */
4892
4440
  palette?: PaletteConfigInput;
4893
- /** Per-group color overrides on top of the chosen palette. */
4894
- overrides?: PaletteOverridesInput;
4895
4441
  }
4896
4442
 
4897
4443
  declare interface PaletteScaleOptions {
4898
- /** Named or custom palette to draw group colors from. */
4899
4444
  palette?: PaletteConfigInput;
4900
- /** Per-group color overrides on top of the chosen palette. */
4901
- overrides?: PaletteOverridesInput;
4902
4445
  }
4903
4446
 
4904
- /** Resolved palette color scale with a concrete palette config. */
4905
4447
  declare interface PaletteScaleSpec {
4906
4448
  type: 'scale';
4907
4449
  scaledAesthetic: ScaledAestheticKey;
4908
4450
  scaleType: 'palette';
4909
4451
  palette: PaletteConfig;
4910
- overrides?: PaletteOverrides;
4911
4452
  }
4912
4453
 
4913
4454
  /**
@@ -4919,12 +4460,31 @@ declare interface PanelConfig {
4919
4460
  };
4920
4461
  }
4921
4462
 
4463
+ /**
4464
+ * A render-side hit-test a Tier-C geom registers for a `render-hit-test` layer. The engine calls it
4465
+ * with the cursor in panel `[0, 1]` space using a **top-left origin (y-down)** — the same frame the
4466
+ * geom paints in (unit-space SVG / `toPercent`), so the geom can test against its own rendered
4467
+ * geometry without re-flipping. It returns the declared identity `key` of the observation under the
4468
+ * cursor, or `null` for a miss; the engine resolves that key to a `HoverHit` and `HoverState`.
4469
+ *
4470
+ * The closure is injected per-instance through the renderer and never rides in the compiled spec —
4471
+ * only the `render-hit-test` kind does (the serialisable boundary, ADR-005 / ADR-033 decision 4).
4472
+ */
4473
+ export declare type PanelHitTester = (cursor: {
4474
+ x: number;
4475
+ y: number;
4476
+ }) => {
4477
+ key: string;
4478
+ } | null;
4479
+
4480
+ /** Recovers a geom definition's params type — carried structurally by its `defaultParams`. */
4481
+ declare type ParamsOf<Definition> = Definition extends Geom<infer TParams> ? TParams : never;
4482
+
4922
4483
  declare type PastelPaletteConfig = {
4923
4484
  type: 'pastel';
4924
4485
  variant?: PastelPaletteVariant;
4925
4486
  };
4926
4487
 
4927
- /** `waterfall` swaps in the positive/negative/total colors used by waterfall charts. */
4928
4488
  declare type PastelPaletteVariant = 'default' | 'waterfall';
4929
4489
 
4930
4490
  /**
@@ -4951,7 +4511,6 @@ declare abstract class PerLayerStage<Input extends {
4951
4511
  }
4952
4512
 
4953
4513
  declare interface PieOptions {
4954
- /** Where the aggregate total is shown: inside the ring (donut) or outside the chart. */
4955
4514
  pieTotalPosition?: 'center' | 'outside';
4956
4515
  }
4957
4516
 
@@ -4978,12 +4537,8 @@ export declare function pipe(spec: SpecInput, ...items: SpecItem[]): SpecInput;
4978
4537
  /**
4979
4538
  * One placed label, ready for a renderer to paint. Coordinates are in panel-pixel space
4980
4539
  * with origin at the panel's top-left — renderers translate into their own frame.
4981
- *
4982
- * Labels are placed independently, with NO cross-label overlap resolution — paint each as given.
4983
- * (Contrast `computeDirectLabelsLayout`, which de-collides line-end labels against each other.)
4984
4540
  */
4985
4541
  export declare interface PlacedDataLabel {
4986
- /** Id of the layer this label belongs to, so the renderer can group/style by source layer. */
4987
4542
  layerId: string;
4988
4543
  /**
4989
4544
  * Stable key — derived from data identity (X value + group), not array position. Per-observation
@@ -4994,7 +4549,7 @@ export declare interface PlacedDataLabel {
4994
4549
  x: number;
4995
4550
  y: number;
4996
4551
  text: string;
4997
- /** Box width/height in pixels — the plate size returned by `measureDataLabel` (text + renderer padding). (x, y) is the box centre. */
4552
+ /** Box width/height in pixels — measured text plus the engine's padding. (x, y) is the box centre. */
4998
4553
  width: number;
4999
4554
  height: number;
5000
4555
  /** When true, paint the label rotated -90° around `(x, y)`. */
@@ -5007,11 +4562,25 @@ export declare interface PlacedDataLabel {
5007
4562
 
5008
4563
  declare function point(options?: GeomOptions<'point'>): LayerInputOf<'point'>;
5009
4564
 
4565
+ /**
4566
+ * Represents each observation as a point (e.g. for scatter plots).
4567
+ */
4568
+ declare class PointGeom extends Geom {
4569
+ readonly type: "point";
4570
+ readonly requiredAesthetics: AestheticKey[];
4571
+ readonly positionChannels: readonly PositionChannel[];
4572
+ readonly swatchShape: SwatchShape;
4573
+ readonly highlightStrategy = "overlay-anchor";
4574
+ readonly spatialKind = "points";
4575
+ readonly identityKeyStrategy = "index";
4576
+ readonly supportsPerGroupHeadline: boolean;
4577
+ compile({ data }: GeomCompilerInput): CompiledGeom;
4578
+ }
4579
+
5010
4580
  /**
5011
4581
  * Point-specific parameters
5012
4582
  */
5013
4583
  declare interface PointGeomParams {
5014
- /** Marker diameter in pixels. */
5015
4584
  size: number;
5016
4585
  }
5017
4586
 
@@ -5020,13 +4589,6 @@ declare interface PointPosition {
5020
4589
  y: number;
5021
4590
  }
5022
4591
 
5023
- export declare interface PolarBarArcInput {
5024
- startAngle: number;
5025
- endAngle: number;
5026
- innerRadius: number;
5027
- outerRadius: number;
5028
- }
5029
-
5030
4592
  declare interface PolarCoordInput {
5031
4593
  type: 'coord';
5032
4594
  coordType: 'polar';
@@ -5062,25 +4624,15 @@ declare interface PolarCoordSpec {
5062
4624
  * Angular params (theta, startAngle) are consumed by the compiler during coordTransform.
5063
4625
  * `innerRadius` is also exposed here so the renderer can recover the donut hole geometry
5064
4626
  * (e.g. to place a centred headline) without reaching into per-observation radii.
5065
- *
5066
- * Polar layers repurpose the position columns: x-columns carry angles, y-columns carry radii.
5067
- * Angles are absolute radians (`startAngle` already applied, clockwise — matches d3-shape `arc()`),
5068
- * passed through unmodified. Radii are [0,1] fractions of the outer radius, mapped into a unit
5069
- * circle whose center is the panel center and whose radius is `min(panel.w, panel.h) / 2`.
5070
4627
  */
5071
4628
  export declare interface PolarCoordSystem {
5072
4629
  type: 'polar';
5073
4630
  /** Axis orientation metadata for the guide compiler */
5074
4631
  axisMapping: AxisMapping;
5075
- /**
5076
- * Donut hole radius as a fraction of the outer radius (0-1). 0 for a full pie.
5077
- * This is the same fraction the radial transform uses as the per-arc RadiusExtent.innerRadius
5078
- * floor, so it is recoverable from any arc.
5079
- */
4632
+ /** Donut hole radius as a fraction of the outer radius (0-1). 0 for a full pie. */
5080
4633
  innerRadius: number;
5081
4634
  }
5082
4635
 
5083
- /** Internal column names holding each observation's compiled positions (normalized to `[0, 1]`). */
5084
4636
  export declare const POSITION_VARIABLES: {
5085
4637
  readonly x: string;
5086
4638
  readonly y: string;
@@ -5096,16 +4648,26 @@ export declare const POSITION_VARIABLES: {
5096
4648
  */
5097
4649
  declare abstract class PositionAdjuster {
5098
4650
  abstract readonly type: PositionType;
4651
+ /**
4652
+ * The single position channel this adjuster reads to compute the adjustment, expressed in the
4653
+ * geom-agnostic channel vocabulary. A geom is adjuster-compatible only when its manifest declares
4654
+ * this channel; otherwise the adjustment is meaningless and the compiler no-ops it. Returns `null`
4655
+ * for a topology-free adjuster (identity), which applies to any geom.
4656
+ */
4657
+ getConsumedChannel(_axis: ChannelAxis): PositionChannel | null;
5099
4658
  abstract adjust(input: PositionAdjusterCompilerInput): CompiledPositionAdjuster;
5100
4659
  }
5101
4660
 
5102
4661
  /**
5103
- * Resolves a position adjustment by name and delegates to the appropriate implementation.
4662
+ * Resolves a position adjustment by name and delegates to the appropriate implementation, gating on
4663
+ * the consuming geom's position-channel manifest: an adjuster applies only when the geom declares the
4664
+ * channel it consumes. An incompatible pairing (e.g. stacking a geom with no value point) no-ops —
4665
+ * the adjustment is meaningless for that geom, not an error.
5104
4666
  */
5105
4667
  declare class PositionAdjusterCompiler {
5106
4668
  private readonly registry;
5107
4669
  constructor(registry: PositionAdjusterRegistry);
5108
- adjust(positionName: PositionType, input: PositionAdjusterCompilerInput): CompiledPositionAdjuster;
4670
+ adjust(positionName: PositionType, geomChannels: readonly PositionChannel[], input: PositionAdjusterCompilerInput): CompiledPositionAdjuster;
5109
4671
  }
5110
4672
 
5111
4673
  declare interface PositionAdjusterCompilerInput {
@@ -5113,6 +4675,11 @@ declare interface PositionAdjusterCompilerInput {
5113
4675
  data: Dataset;
5114
4676
  /** The effective mapping for the layer */
5115
4677
  mapping: AesMapping;
4678
+ /**
4679
+ * The axis stack/fill accumulate along (the value axis). Defaults to `y`. dodge ignores it — it
4680
+ * always splits the band axis.
4681
+ */
4682
+ axis?: ChannelAxis;
5116
4683
  }
5117
4684
 
5118
4685
  /**
@@ -5151,23 +4718,26 @@ declare interface PositionalScaleMethods {
5151
4718
  }
5152
4719
 
5153
4720
  /**
5154
- * A position variable mapper that applies position scaling for a single concern
5155
- * (e.g. primary x, band extents, y baseline extents).
5156
- *
5157
- * Returns the dataset with any new positional variables added,
5158
- * or the same dataset unchanged if this mapper does not apply.
4721
+ * One position channel a geom declares: where it sits ({@link ChannelAxis}, {@link PositionChannelRole}),
4722
+ * how its raw column maps to a position ({@link PositionValueKind}), and an open identity its column
4723
+ * derives from. A geom's channels are the manifest the position mapper and coord projection iterate
4724
+ * instead of hardcoding the column set.
5159
4725
  */
5160
- declare interface PositionMapper {
5161
- map: (input: PositionMapperInput) => Dataset;
4726
+ declare type PositionChannel = RolePositionChannel | ScalarPositionChannel;
4727
+
4728
+ declare interface PositionChannelBase {
4729
+ axis: ChannelAxis;
4730
+ valueKind: PositionValueKind;
5162
4731
  }
5163
4732
 
5164
4733
  /**
5165
- * Applies position scales to raw data values, producing scale-normalized `x` and `y` variables
5166
- * ([0,1]) on each layer's dataset.
4734
+ * Applies position scales to raw data values, producing scale-normalized position variables ([0,1])
4735
+ * on each layer's dataset. The variables produced are whatever the layer's geom declares in its
4736
+ * channel manifest, mapped by {@link mapPositionChannels}.
5167
4737
  */
5168
4738
  declare class PositionMapperCompiler extends PerLayerStage<PositionMapperCompilerInput> {
5169
- private readonly mappers;
5170
- constructor(mappers?: PositionMapper[]);
4739
+ private readonly geomRegistry;
4740
+ constructor(geomRegistry: GeomRegistry);
5171
4741
  protected dependencies(input: PositionMapperCompilerInput): readonly unknown[];
5172
4742
  protected compileLayer(layer: CompiledLayer, input: PositionMapperCompilerInput): CompiledLayer;
5173
4743
  }
@@ -5177,15 +4747,6 @@ declare interface PositionMapperCompilerInput {
5177
4747
  scales: PositionScalesSlice;
5178
4748
  }
5179
4749
 
5180
- /**
5181
- * Context passed to each column mapper during position mapping.
5182
- */
5183
- declare interface PositionMapperInput {
5184
- data: Dataset;
5185
- layer: CompiledLayer;
5186
- getPositionScale: (scaleAestheticKey: ScaledAestheticKey) => CompiledPositionScale | null;
5187
- }
5188
-
5189
4750
  /**
5190
4751
  * Subset of {@link CompiledScales} the position mapper reads. Narrowed so visual-scale changes
5191
4752
  * don't enter this stage's cache dependency surface.
@@ -5202,18 +4763,17 @@ declare type PositionScalesSlice = Pick<CompiledScales, 'x' | 'y' | 'ySecondary'
5202
4763
  */
5203
4764
  export declare type PositionType = 'stack' | 'dodge' | 'identity' | 'fill';
5204
4765
 
5205
- /** Any highlight match condition: a field test or a logical combination of them. */
5206
- export declare type Predicate = VariablePredicate | LogicalPredicate;
5207
-
5208
4766
  /**
5209
- * Namespaces a variable name so the compiler's own columns can't collide with user data columns.
5210
- * The prefix uses control characters that real datasets won't contain.
4767
+ * How a channel's raw column becomes a scaled position.
4768
+ * - `value` the column holds data-domain values mapped directly through the scale.
4769
+ * - `bandOffset` — the column holds fractional offsets around the scaled axis point, sized by the
4770
+ * scale's bandwidth (a bar's left/right edges relative to its band centre).
5211
4771
  */
5212
- export declare const prefixInternalVariable: (name: string) => string;
4772
+ declare type PositionValueKind = 'value' | 'bandOffset';
5213
4773
 
5214
- export declare const prepareAreaObservations: (observations: Observation[], missingValues: AreaGeomParams["missingValues"]) => Observation[];
4774
+ export declare type Predicate = VariablePredicate | LogicalPredicate;
5215
4775
 
5216
- export declare const prepareLineObservations: (observations: Observation[], missingValues: LineGeomParams["missingValues"]) => Observation[];
4776
+ export declare const prefixInternalVariable: (name: string) => string;
5217
4777
 
5218
4778
  declare interface QuantitativeScaleMethods {
5219
4779
  /**
@@ -5233,24 +4793,12 @@ declare interface QuantitativeScaleMethods {
5233
4793
  identity: (options?: IdentityScaleOptions) => IdentityScaleInput;
5234
4794
  }
5235
4795
 
5236
- /** Radial extent of a polar arc, as a fraction (`[0, 1]`) of the outer radius. */
5237
4796
  export declare interface RadiusExtent {
5238
4797
  innerRadius: NumericDataValue;
5239
4798
  outerRadius: NumericDataValue;
5240
4799
  }
5241
4800
 
5242
- /** Resolves one observation's raw, type-filtered value for an aesthetic. */
5243
- declare type RawValueReader = (observation: Observation) => DataValue;
5244
-
5245
- export declare function readXExtent(primary: HoverHit): number | null;
5246
-
5247
- export declare function readYExtent(primary: HoverHit): number | null;
5248
-
5249
- /**
5250
- * A rectangle in pixel coordinates, origin at top-left. Every rect on a {@link GraphLayout} is measured
5251
- * from the chart CONTAINER top-left (with {@link LAYOUT_PADDING} already included), never panel- or
5252
- * plot-local — paint against these absolute coordinates without re-offsetting.
5253
- */
4801
+ /** A rectangle in pixel coordinates, origin at top-left. */
5254
4802
  export declare interface Rect {
5255
4803
  x: number;
5256
4804
  y: number;
@@ -5293,12 +4841,9 @@ declare class Registry<K extends string, T> {
5293
4841
  }
5294
4842
 
5295
4843
  /**
5296
- * Render-time context used for GraphConfig conversion. Consumed only by {@link Compiler.compile}; a
5297
- * theme or palette change therefore requires a fresh `compile` (it cannot be applied via
5298
- * {@link Compiler.recompile}, which takes no `ctx`).
4844
+ * Render-time context used for GraphConfig conversion.
5299
4845
  */
5300
4846
  export declare interface RendererContext {
5301
- /** Theme used to resolve theme-dependent colors; defaults to light when omitted. */
5302
4847
  theme?: GraphTheme;
5303
4848
  /** Renderer-owned palette catalog, keyed by `paletteId`. */
5304
4849
  customPalettes?: CustomPalettesInput;
@@ -5362,25 +4907,29 @@ export declare type ResolvedLegendDisplay = Exclude<LegendDisplay, 'auto'>;
5362
4907
  export declare type ResolvedLegendPosition = Exclude<LegendPosition, 'auto' | 'none'>;
5363
4908
 
5364
4909
  /**
5365
- * An observation's anchor projected into normalized panel space `[0, 1]²`. Data-space origin
5366
- * (y=0 at the bottom, matching `POSITION_VARIABLES.y`), so a top-origin renderer flips `y`
5367
- * before painting unlike `CompiledShape`/`CompiledTextAnnotation`, which are top-left.
4910
+ * An observation's anchor projected into normalized panel space `[0, 1]²`, with the raw measure and
4911
+ * resolved color the renderer needs. The `{ x, y, geom }` triple is the {@link AnchorPosition} the
4912
+ * owning geom computes; this adds the per-observation facts the annotation chrome reads.
5368
4913
  */
5369
- export declare interface ResolvedObservationAnchor {
5370
- x: number;
5371
- y: number;
5372
- /**
5373
- * The geom kind the anchored observation belongs to. Advisory: lets the renderer offset a
5374
- * marker-style annotation (sticker, pinned number, comment) to sit on that mark's shape. NOT used
5375
- * by `computeDifferenceArrow` — its endpoint gaps come from `getDifferenceArrowDimensions`.
5376
- */
5377
- geom: 'bar' | 'line' | 'polar-bar';
4914
+ export declare interface ResolvedObservationAnchor extends AnchorPosition {
5378
4915
  /** Raw value of the y-aesthetic (or x-aesthetic when flipped), used by label formatting. */
5379
4916
  measurementValue: number;
5380
4917
  /** Resolved color for this observation, if any. */
5381
4918
  color: string | undefined;
5382
4919
  }
5383
4920
 
4921
+ /**
4922
+ * A custom annotation's coordinate resolved to normalized panel space, in **top-left [0,1]** — the
4923
+ * space the draw function paints in. `observation` is attached only for a snap-to-observation
4924
+ * coordinate (so the draw can read the bound row's columns); unit/data targets are synthetic points.
4925
+ */
4926
+ export declare interface ResolvedTarget {
4927
+ x: number;
4928
+ y: number;
4929
+ mode: 'data' | 'unit' | 'observation';
4930
+ observation?: Observation;
4931
+ }
4932
+
5384
4933
  /**
5385
4934
  * Decides where, at what size, and how much of a headline paints, once the layout is resolved. A strip
5386
4935
  * headline takes the reserved band at its container-fit size, then drops trailing items that overflow
@@ -5391,32 +4940,20 @@ export declare interface ResolvedObservationAnchor {
5391
4940
  */
5392
4941
  export declare const resolveHeadlinePlacement: ({ headline, layout, containerSize, measurer, itemGap, }: ResolveHeadlinePlacementInput) => HeadlinePlacement | null;
5393
4942
 
5394
- /** What {@link resolveHeadlinePlacement} needs: the headline, the resolved layout it places into, and the renderer's measurer. */
5395
4943
  export declare interface ResolveHeadlinePlacementInput {
5396
4944
  headline: HeadlineLayoutInput;
5397
4945
  layout: GraphLayout;
5398
4946
  containerSize: BoxSize;
5399
- /** Measures the headline as the renderer actually paints it; a mismatch miscounts the visible items. */
5400
4947
  measurer: HeadlineMeasurer;
5401
- /**
5402
- * Gap the renderer lays out between adjacent strip items; governs how many fit the band. Keep it
5403
- * consistent with the real paint gap, alongside `measurer`, or the visible-item count drifts.
5404
- */
4948
+ /** Gap the renderer lays out between adjacent strip items; governs how many fit the band. */
5405
4949
  itemGap: number;
5406
4950
  }
5407
4951
 
5408
4952
  /** Source of a layer's segment y value: `yRaw` when stacked, user's y mapping otherwise. */
5409
4953
  export declare function resolveSegmentYSource(position: PositionType, mapping: AesMapping): AestheticValue;
5410
4954
 
5411
- export declare const resolveStrokeWidth: (observation: Observation, params: AreaGeomParams | LineGeomParams) => number;
5412
-
5413
4955
  /**
5414
4956
  * Resolves the Y scale aesthetic based on the layer's `yScaleType` axis assignment.
5415
- *
5416
- * `CompiledScales` is keyed by scale aesthetic (`ySecondary` is a key, not an aesthetic), so to get
5417
- * a layer's y scale read `scales[resolveYScaleAesthetic(layer.yScaleType)]`. For its axis, pass the
5418
- * same resolved key to `findAxisGuide` (find-guide.ts): the two compose for secondary-axis lookup,
5419
- * so a `secondary` layer resolves to the `ySecondary` guide, not `y`.
5420
4957
  */
5421
4958
  export declare function resolveYScaleAesthetic(yScaleType: YScaleType): ScaledAestheticKey;
5422
4959
 
@@ -5426,20 +4963,7 @@ export declare function resolveYScaleAesthetic(yScaleType: YScaleType): ScaledAe
5426
4963
  */
5427
4964
  export declare const RESTING_HOVER_STATE: HoverState;
5428
4965
 
5429
- /**
5430
- * TipTap-compatible rich text node (no tiptap dependency).
5431
- *
5432
- * Renderer contract — the vocabulary a text renderer must handle. Anything not
5433
- * listed falls through to a `<span>` carrying the node's own marks/attrs.
5434
- *
5435
- * Block `type`s (snake_case and camelCase accepted): `doc`, `paragraph`,
5436
- * `heading`, `blockquote`, `bulletList`/`bullet_list`, `orderedList`/
5437
- * `ordered_list`, `listItem`/`list_item`, `hardBreak`/`hard_break`, `text`.
5438
- *
5439
- * Mark `type`s (some carry synonyms): `bold`/`strong`, `italic`/`em`,
5440
- * `underline`, `strike`, `code`, `link`, `textStyle`. The `link` mark reads
5441
- * `attrs.href` and the renderer URL-safety-checks it before emitting an anchor.
5442
- */
4966
+ /** TipTap-compatible rich text node (no tiptap dependency). */
5443
4967
  export declare interface RichTextContent {
5444
4968
  type?: string;
5445
4969
  content?: RichTextContent[];
@@ -5448,31 +4972,48 @@ export declare interface RichTextContent {
5448
4972
  type: string;
5449
4973
  attrs?: Record<string, unknown>;
5450
4974
  }>;
5451
- /**
5452
- * Per-node attributes the renderer recognizes: `heading.level` (1–3),
5453
- * `paragraph.textAlign`, and on the `textStyle` mark `color`, `font` (a font
5454
- * id), and `fontSize` — a number read as `n/10` em. Unrecognized keys are
5455
- * ignored.
5456
- */
5457
4975
  attrs?: Record<string, unknown>;
5458
4976
  }
5459
4977
 
4978
+ /**
4979
+ * A structural channel — the per-axis anchor (`point`) or one end of an interval (`lower`/`upper`).
4980
+ * Its {@link PositionChannel.name | name} defaults to the role, so the canonical position columns
4981
+ * (`x`, `xMin`, `yMax`, …) are preserved and a geom declares `{ axis, role, valueKind }` with no name.
4982
+ */
4983
+ declare interface RolePositionChannel extends PositionChannelBase {
4984
+ role: 'point' | 'lower' | 'upper';
4985
+ /** Open identity override; defaults to {@link role}. Built-ins omit it to keep canonical columns. */
4986
+ name?: string;
4987
+ }
4988
+
5460
4989
  declare function rule(options?: GeomOptions<'rule'>): LayerInputOf<'rule'>;
5461
4990
 
5462
4991
  /**
5463
- * Rule-specific parameters.
4992
+ * Reference line at a numeric value, supplied as a constant mapping or read from a stat-output
4993
+ * variable (e.g. `stat.mean()`). Emits a 1-observation dataset on a synthetic variable.
5464
4994
  *
5465
- * A rule is a single reference line. The renderer reads one observation —
5466
- * `data.getFirst()` — via `getX`/`getY`. Orientation: horizontal when the layer
5467
- * maps `y` (a constant-y line spanning the panel width), vertical otherwise;
5468
- * under a flipped coord system the orientation inverts with the axes.
4995
+ * The other axis is cleared so inherited mappings don't reach the position mapper.
4996
+ */
4997
+ declare class RuleGeom extends Geom {
4998
+ readonly type: "rule";
4999
+ readonly positionChannels: readonly PositionChannel[];
5000
+ readonly highlightStrategy: null;
5001
+ readonly supportedCoordTypes: readonly CoordType[];
5002
+ compile({ data: inputData, mapping }: GeomCompilerInput): CompiledGeom;
5003
+ /**
5004
+ * A rule needs exactly one numeric value on `x` or `y` (or a stat that computes one at compile
5005
+ * time, e.g. `stat.mean()` on `y`) — it draws a single reference line on one axis.
5006
+ */
5007
+ validateMapping({ layerId, mapping, computedVariables }: GeomMappingValidationInput): ValidationIssue[];
5008
+ }
5009
+
5010
+ /**
5011
+ * Rule-specific parameters.
5469
5012
  */
5470
5013
  export declare interface RuleGeomParams {
5471
5014
  /** Stroke color; falls back to a theme token. */
5472
5015
  color?: string;
5473
- /** Stroke width in pixels. */
5474
5016
  strokeWidth: number;
5475
- /** Dash pattern of the line (solid, dashed, dotted, ...). */
5476
5017
  lineType: LineStyleType;
5477
5018
  /** Optional inline text label rendered alongside the line. */
5478
5019
  label?: string;
@@ -5484,6 +5025,26 @@ export declare interface RuleGeomParams {
5484
5025
  */
5485
5026
  export declare type RuleLabelPosition = 'start' | 'end';
5486
5027
 
5028
+ /**
5029
+ * A standalone scaled value on an axis, identified by an open {@link ScalarPositionChannel.name | name}.
5030
+ * Always `value`-kind — a lone value has no band to offset against. Its column is namespaced from the
5031
+ * name, so two geoms' scalars never collide and neither freezes an internal column (the decision-9
5032
+ * litmus): a box plot declares `q1`/`median`/`q3`, an error bar its CI bounds, and the position mapper
5033
+ * scales each through the axis scale exactly as it scales an interval bound.
5034
+ */
5035
+ declare interface ScalarPositionChannel extends PositionChannelBase {
5036
+ role: 'scalar';
5037
+ valueKind: 'value';
5038
+ name: string;
5039
+ /**
5040
+ * The mapping key this channel sources its raw values from. When set, the position mapper reads
5041
+ * `mapping[aes]`'s column and scales it in place — so the value is authored as `aes` (a box plot's
5042
+ * `q1`), not hand-copied from `params`. When omitted, the channel reads the column the geom wrote
5043
+ * under {@link variableFor}`(axis, name)` (a geom that computes the value itself).
5044
+ */
5045
+ aes?: string;
5046
+ }
5047
+
5487
5048
  declare abstract class Scale {
5488
5049
  /** Data types this scale accepts. */
5489
5050
  abstract readonly compatibleDataTypes: readonly DataType[];
@@ -5596,10 +5157,8 @@ declare interface ScaleCompilerInput {
5596
5157
  */
5597
5158
  export declare type ScaledAestheticKey = ScaledPositionAestheticKey | ScaledVisualAestheticKey;
5598
5159
 
5599
- /** Scale keys whose output is a spatial coordinate. `ySecondary` is the optional second y axis. */
5600
5160
  declare type ScaledPositionAestheticKey = 'x' | 'y' | 'ySecondary';
5601
5161
 
5602
- /** Scale keys whose output is a visual channel rather than a position. */
5603
5162
  declare type ScaledVisualAestheticKey = 'color' | 'size' | 'alpha' | 'strokeWidth' | 'lineType';
5604
5163
 
5605
5164
  /**
@@ -5633,7 +5192,6 @@ declare type ScaleTransformType = 'linear' | 'log' | 'sqrt';
5633
5192
  * - `'discrete'` — Discrete categorical values
5634
5193
  * - `'datetime'` — Date/time range
5635
5194
  * - `'identity'` — Pass-through, values used as-is
5636
- * - `'palette'` — Discrete values mapped through a named color palette
5637
5195
  */
5638
5196
  declare type ScaleType = 'continuous' | 'discrete' | 'datetime' | 'identity' | 'palette';
5639
5197
 
@@ -5641,13 +5199,47 @@ declare interface ScatterOptions {
5641
5199
  pointSize?: number | 'auto';
5642
5200
  }
5643
5201
 
5202
+ /**
5203
+ * One geom-declared detail row: a label, the column its value is read from, and the format to display
5204
+ * that value with (derived from the column, never formatted at compile time). Resolved once per layer
5205
+ * from the geom's {@link GeomTooltipRow} declaration; values stay in `data` and are read on demand.
5206
+ */
5207
+ declare interface SemanticDetailRowDescriptor {
5208
+ label: string;
5209
+ variable: VariableName;
5210
+ valueFormat: ValueFormat;
5211
+ }
5212
+
5213
+ /**
5214
+ * One encoding in a layer's semantic map: an aesthetic, the variable its value is read from, and
5215
+ * the format to display that value with. The format is usually the value variable's own, but the
5216
+ * two can differ — a stacked y reads its segment magnitude from a column that defaults to decimal
5217
+ * while displaying it with the y variable's preserved format. Resolved once per layer — the
5218
+ * descriptor is O(aesthetics), pure data, and never holds per-observation values, so it rides in
5219
+ * the compiled spec without duplicating columns.
5220
+ */
5221
+ declare interface SemanticEncodingDescriptor {
5222
+ aesthetic: AestheticKey;
5223
+ variable: VariableName;
5224
+ valueFormat: ValueFormat;
5225
+ }
5226
+
5227
+ /** A layer's geometry-agnostic meaning, declared as the ordered set of encodings its marks carry. */
5228
+ declare interface SemanticMapDescriptor {
5229
+ encodings: SemanticEncodingDescriptor[];
5230
+ /**
5231
+ * Extra single-observation rows a geom contributes to the tooltip (e.g. OHLC), in declaration
5232
+ * order. Empty for geoms that add none, in which case the standard one-row-per-series content
5233
+ * applies.
5234
+ */
5235
+ detailRows: SemanticDetailRowDescriptor[];
5236
+ }
5237
+
5644
5238
  /**
5645
5239
  * Serialized representation of a command for wire transport and persistence.
5646
5240
  * Only forward commands are serialized — inverses are recomputed at execution time.
5647
- * Round-trip a command with {@link commandRegistry}'s `serialize`/`deserialize`.
5648
5241
  */
5649
5242
  export declare interface SerializedCommand {
5650
- /** Command type discriminator used to pick the right descriptor when deserializing. */
5651
5243
  readonly type: string;
5652
5244
  readonly params: Record<string, unknown>;
5653
5245
  readonly metadata: CommandMetadata;
@@ -5655,9 +5247,7 @@ export declare interface SerializedCommand {
5655
5247
 
5656
5248
  /** Config for styling a specific series. */
5657
5249
  declare interface SeriesStyle {
5658
- /** Id of a slot in the active palette; takes precedence over the palette's default assignment. */
5659
5250
  paletteColorId?: string;
5660
- /** Explicit color that overrides any palette slot for this series. */
5661
5251
  customColor?: string;
5662
5252
  fillStyle?: 'solid' | 'hatched';
5663
5253
  lineStyle?: 'solid' | 'dashed' | 'dotted';
@@ -5702,7 +5292,6 @@ export declare class SetContentCaptionCommand implements Command<SetContentCapti
5702
5292
  }
5703
5293
 
5704
5294
  declare type SetContentCaptionParams = {
5705
- /** New caption, or `null` to clear it. */
5706
5295
  caption: TextContent | null;
5707
5296
  };
5708
5297
 
@@ -5721,7 +5310,6 @@ export declare class SetContentSubtitleCommand implements Command<SetContentSubt
5721
5310
  }
5722
5311
 
5723
5312
  declare type SetContentSubtitleParams = {
5724
- /** New subtitle, or `null` to clear it. */
5725
5313
  subtitle: TextContent | null;
5726
5314
  };
5727
5315
 
@@ -5741,7 +5329,6 @@ export declare class SetContentTitleCommand implements Command<SetContentTitlePa
5741
5329
  }
5742
5330
 
5743
5331
  declare type SetContentTitleParams = {
5744
- /** New title, or `null` to clear it. */
5745
5332
  title: TextContent | null;
5746
5333
  };
5747
5334
 
@@ -5775,7 +5362,6 @@ export declare class SetLineWidthCommand implements Command<SetLineWidthParams>
5775
5362
  }
5776
5363
 
5777
5364
  declare type SetLineWidthParams = {
5778
- /** Layer to target; when omitted, the first line/area layer is used. */
5779
5365
  layerId?: string;
5780
5366
  lineWidth: LineGeomParams['lineWidth'];
5781
5367
  };
@@ -5794,11 +5380,8 @@ export declare class SetScaleDomainCommand implements Command<SetScaleDomainPara
5794
5380
  }
5795
5381
 
5796
5382
  declare type SetScaleDomainParams = {
5797
- /** Which scale to change, identified by the aesthetic it drives (e.g. x, y). */
5798
5383
  scaledAesthetic: ScaledAestheticKey;
5799
- /** New lower bound; omit to leave the existing minimum unchanged. */
5800
5384
  domainMin?: ContinuousScaleSpec['domainMin'];
5801
- /** New upper bound; omit to leave the existing maximum unchanged. */
5802
5385
  domainMax?: ContinuousScaleSpec['domainMax'];
5803
5386
  };
5804
5387
 
@@ -5810,28 +5393,20 @@ declare type SetScaleDomainParams = {
5810
5393
  export declare interface ShapeInput {
5811
5394
  id?: string;
5812
5395
  kind?: ShapeKind;
5813
- /** Draw beneath the geoms (background) or on top (foreground). */
5814
5396
  zOrder?: ShapeZOrder;
5815
- /** Left edge, as a fraction of plot width (0..1). */
5816
5397
  x: number;
5817
- /** Top edge, as a fraction of plot height (0..1). */
5818
5398
  y: number;
5819
- /** Width, as a fraction of plot width (0..1). */
5820
5399
  width: number;
5821
- /** Height, as a fraction of plot height (0..1). */
5822
5400
  height: number;
5823
5401
  fillColor?: string;
5824
- /** Fill alpha, 0 (transparent) to 1 (opaque). */
5825
5402
  fillOpacity?: number;
5826
5403
  strokeWidth?: number;
5827
5404
  /** null falls back to the theme `defaultAnnotationShapeStroke`. */
5828
5405
  strokeColor?: string | null;
5829
5406
  }
5830
5407
 
5831
- /** The geometry a shape annotation draws. */
5832
5408
  export declare type ShapeKind = 'rectangle';
5833
5409
 
5834
- /** Resolved shape annotation with all optional fields defaulted. */
5835
5410
  export declare interface ShapeSpec {
5836
5411
  id: string;
5837
5412
  kind: ShapeKind;
@@ -5847,9 +5422,7 @@ export declare interface ShapeSpec {
5847
5422
  }
5848
5423
 
5849
5424
  /**
5850
- * Whether the shape renders beneath the geoms (background) or on top (foreground). This is not a
5851
- * sort key within a single pass: the renderer paints `background` shapes, then the geom layer, then
5852
- * `foreground` shapes — two separate passes bracketing the geoms (see `CompiledAnnotations`).
5425
+ * Whether the shape renders beneath the geoms (background) or on top (foreground).
5853
5426
  */
5854
5427
  export declare type ShapeZOrder = 'background' | 'foreground';
5855
5428
 
@@ -5878,9 +5451,7 @@ export declare type SmoothMethod = 'linear' | 'loess' | 'exponential' | 'logarit
5878
5451
  declare interface SmoothStatInput {
5879
5452
  type: 'smooth';
5880
5453
  method: SmoothMethod;
5881
- /** Polynomial order — only meaningful when `method: 'polynomial'`. */
5882
5454
  order?: number;
5883
- /** LOESS bandwidth — only meaningful when `method: 'loess'`. */
5884
5455
  bandwidth?: number;
5885
5456
  }
5886
5457
 
@@ -5920,6 +5491,29 @@ export declare interface SourceContent {
5920
5491
  url?: string;
5921
5492
  }
5922
5493
 
5494
+ /**
5495
+ * The kind of spatial structure a geom presents for hit-testing. Each value selects one of the
5496
+ * runtime's index builders, so the descriptor lets the engine dispatch on declared data instead
5497
+ * of branching on the geom name.
5498
+ *
5499
+ * `render-hit-test` is the Tier-C escape hatch: the geom's geometry comes from a layout algorithm,
5500
+ * not from scales, so the compiler cannot build a spatial index from position columns. The geom
5501
+ * instead provides a render-side hit-test function (injected per-instance through the renderer),
5502
+ * and the engine resolves the observation it returns against the declared identity key. Only the
5503
+ * kind rides in the compiled spec — the closure never crosses the serialisable boundary.
5504
+ */
5505
+ declare type SpatialIndexKind = 'buckets' | 'rects' | 'points' | 'arcs' | 'noop' | 'render-hit-test';
5506
+
5507
+ /**
5508
+ * A layer's geometry-agnostic hit-test declaration. Pure serialisable data riding in the compiled
5509
+ * spec: it names the spatial structure the marks present so the runtime can build the matching
5510
+ * index without knowing which geom produced it. A geom declares its cartesian-natural kind on its
5511
+ * definition; a polar coord refines it to `arcs` during the coord transform.
5512
+ */
5513
+ declare interface SpatialMapDescriptor {
5514
+ kind: SpatialIndexKind;
5515
+ }
5516
+
5923
5517
  /**
5924
5518
  * Fully resolved spec — all fields populated, defaults applied, inferred types resolved.
5925
5519
  * This is what the compilation pipeline consumes.
@@ -5974,16 +5568,12 @@ export declare interface SpecInput {
5974
5568
  config: ConfigInput;
5975
5569
  }
5976
5570
 
5977
- declare type SpecItem = LayerInput | ScaleInput | CoordInput | ConfigItem | TransformInput | MappingItem | HighlightInput | AnnotationsItem;
5571
+ declare type SpecItem = LayerInput | ScaleInput | CoordInput | ConfigItem | TransformInput | MappingItem | HighlightInput | AnnotationItem;
5978
5572
 
5979
5573
  /**
5980
5574
  * Compiles a raw compiler input or graph config into a resolved Spec.
5981
5575
  */
5982
5576
  export declare class SpecResolver {
5983
- /**
5984
- * Resolve user input into a fully defaulted {@link Spec}. Accepts either a viz-engine
5985
- * `SpecInput` or a legacy `GraphConfig`, converting the latter before resolution.
5986
- */
5987
5577
  compile({ input, dataset, ctx }: {
5988
5578
  input: CompilerInput;
5989
5579
  dataset: Dataset;
@@ -5991,7 +5581,6 @@ export declare class SpecResolver {
5991
5581
  }): Spec;
5992
5582
  }
5993
5583
 
5994
- /** A stack's total at one x position, with where its label should be anchored. Drives stack-total labels. */
5995
5584
  export declare interface StackTotalEntry {
5996
5585
  /** Serialised x value for stable join keys across observations. */
5997
5586
  xKey: string;
@@ -6045,7 +5634,6 @@ declare abstract class Stat {
6045
5634
  protected abstract computeStat(input: StatCompilerInput): CompiledStat;
6046
5635
  }
6047
5636
 
6048
- /** Factories for the statistical transforms a layer can apply (identity, count, smooth, mean). */
6049
5637
  export declare const stat: {
6050
5638
  identity: typeof identity;
6051
5639
  count: typeof count;
@@ -6126,8 +5714,14 @@ declare type StickerId = 'rocket' | 'clapping-hands' | 'thumbs-up' | 'thumbs-dow
6126
5714
 
6127
5715
  /**
6128
5716
  * Computes per-layer aggregates (stack totals, grand totals) consumed directly by the renderer.
5717
+ *
5718
+ * Which geoms carry each aggregate is declared on the geom definition and resolved through the
5719
+ * injected {@link GeomCompiler}, so the stage dispatches by declaration rather than naming a geom;
5720
+ * the summariser strategies stay geom-agnostic computations.
6129
5721
  */
6130
5722
  declare class SummariseCompiler extends PerLayerStage<SummariseCompilerInput> {
5723
+ private readonly geomCompiler;
5724
+ constructor(geomCompiler: GeomCompiler);
6131
5725
  protected dependencies(): readonly unknown[];
6132
5726
  protected compileLayer(layer: CompiledLayer): CompiledLayer;
6133
5727
  }
@@ -6137,38 +5731,29 @@ declare interface SummariseCompilerInput {
6137
5731
  }
6138
5732
 
6139
5733
  /**
6140
- * Visual signature of a geom, decoupled from `GeomName` because legends and tooltips don't care
6141
- * about the geom's spec-level identity only what shape best evokes its on-canvas mark.
5734
+ * Visual signature of a geom's mark, decoupled from `GeomName` because legends and tooltips care
5735
+ * only about the shape that best evokes the on-canvas mark, not the geom's spec-level identity.
6142
5736
  *
6143
- * - `bar` + cartesian `square`
6144
- * - `bar` + polar `slice` (pie / donut wedge)
6145
- * - `line` → `line` (a horizontal stroke)
6146
- * - `area` → `area` (filled region with a stroke accent)
6147
- * - `point` → `circle`
6148
- *
6149
- * One resolver feeds the legend, the headline strip, and rule pills, so a given series shows the same mark in
6150
- * every surface. A combo legend still carries mixed shapes — the value is resolved per series, not per chart.
5737
+ * - `square` a filled rect (bar)
5738
+ * - `line` → a horizontal stroke
5739
+ * - `area` → a filled region with a stroke accent
5740
+ * - `circle` → a point
5741
+ * - `slice` → a pie / donut wedge (a `square` mark refined under polar coords)
6151
5742
  */
6152
5743
  export declare type SwatchShape = 'square' | 'line' | 'circle' | 'area' | 'slice';
6153
5744
 
6154
5745
  declare type Table = internal.ColumnTable;
6155
5746
 
6156
5747
  declare interface TableOptions {
6157
- /** Relative widths per column, keyed by column key; values are normalized into fractions. */
6158
5748
  tableColumnRatios?: Record<string, number>;
6159
5749
  }
6160
5750
 
6161
5751
  declare interface TemporalValueFormat {
6162
5752
  type: 'datetime' | 'time' | 'date' | 'year' | 'quarter' | 'month_year' | 'month' | 'weekly_date_range_with_year' | 'weekly_date_range' | 'day_month';
6163
- /**
6164
- * Source-parsing metadata: the template the values were originally parsed from (e.g. 'dd-mm-yyyy').
6165
- * It is NOT a formatting instruction and is not consumed when materializing output — the renderer
6166
- * picks the display shape from `type` alone, not from this field.
6167
- */
5753
+ /** Template string representing how values of this type have been formatted. ie. dd-mm-yyyy */
6168
5754
  dateFormat?: string;
6169
5755
  }
6170
5756
 
6171
- /** How a text annotation's background fill is applied: faded into the plot or fully opaque. */
6172
5757
  export declare type TextAnnotationBackgroundColorStyle = 'fade' | 'opaque';
6173
5758
 
6174
5759
  /**
@@ -6177,7 +5762,6 @@ export declare type TextAnnotationBackgroundColorStyle = 'fade' | 'opaque';
6177
5762
  */
6178
5763
  export declare interface TextAnnotationInput {
6179
5764
  id?: string;
6180
- /** Rich-text body to render. */
6181
5765
  content: RichTextContent;
6182
5766
  /** 0..1 of plot width — top-left corner. */
6183
5767
  x: number;
@@ -6187,11 +5771,9 @@ export declare interface TextAnnotationInput {
6187
5771
  width: number;
6188
5772
  /** null falls back to a transparent background. */
6189
5773
  backgroundColor?: string | null;
6190
- /** Whether the background fill fades into the plot or is fully opaque. */
6191
5774
  backgroundColorStyle?: TextAnnotationBackgroundColorStyle;
6192
5775
  }
6193
5776
 
6194
- /** Resolved text annotation with all optional fields defaulted. */
6195
5777
  export declare interface TextAnnotationSpec {
6196
5778
  id: string;
6197
5779
  content: RichTextContent;
@@ -6205,34 +5787,26 @@ export declare interface TextAnnotationSpec {
6205
5787
  /** A text value — plain string or structured rich text. */
6206
5788
  export declare type TextContent = string | RichTextContent;
6207
5789
 
6208
- /**
6209
- * Measures rendered text dimensions so the layout engine can size labels without a real DOM.
6210
- * Swap implementations (canvas-backed, heuristic) to fit the runtime environment.
6211
- *
6212
- * Contract: `measureText` must be deterministic for a given `(text, FontSpec)` — the engine caches
6213
- * results keyed on that pair and assumes repeat calls agree. It must measure against fonts that are
6214
- * already loaded; measuring before the family is ready would cache fallback-font metrics and skew
6215
- * every subsequent layout for that key.
6216
- */
6217
5790
  export declare interface TextMeasurer {
6218
5791
  measureText: (text: string, font: FontSpec) => MeasuredText;
6219
5792
  }
6220
5793
 
6221
- /** Fully-derived tooltip content. The popover renders directly from this. */
5794
+ /** A chart's semantic content for the hovered observations, in reading order. */
6222
5795
  export declare interface TooltipContent {
6223
- /** Formatted main-axis value of the primary's observation. `null` for polar. */
5796
+ /** Localized main-axis value of the primary's observation. `null` for polar. */
6224
5797
  header: string | null;
6225
5798
  rows: TooltipRow[];
6226
5799
  }
6227
5800
 
6228
5801
  /**
6229
- * One row in the chart tooltip popover. Pure projection of a `HoverHit` against the layer's
6230
- * compiled scales.
5802
+ * One row of a chart's semantic content: a single observation's meaning, resolved and localized for
5803
+ * display. Surface-agnostic — the tooltip renders it directly, and the accessible surfaces (screen
5804
+ * reader, data table) derive from the same rows so content never forks per surface.
6231
5805
  */
6232
5806
  export declare interface TooltipRow {
6233
5807
  /**
6234
5808
  * Resolved color string applied as the row's swatch fill/stroke. `null` only when the chart
6235
- * has no color scale at all — the popover suppresses the swatch cell in that edge case.
5809
+ * has no color scale at all — surfaces suppress the swatch cell in that edge case.
6236
5810
  */
6237
5811
  swatchColor: string | null;
6238
5812
  /** Visual signature of the row's source geom. Drives the swatch shape. */
@@ -6241,7 +5815,7 @@ export declare interface TooltipRow {
6241
5815
  swatchLineType: LineStyleType;
6242
5816
  /** Row label — color value (multi-series) or layer's Y-axis title (single-series). */
6243
5817
  label: string;
6244
- /** Formatted Y reading for this hit. */
5818
+ /** Localized Y reading for this observation. */
6245
5819
  value: string;
6246
5820
  /** Styling hint: the row whose hit `=== primary`. Never re-orders. */
6247
5821
  isPrimary: boolean;
@@ -6249,23 +5823,6 @@ export declare interface TooltipRow {
6249
5823
  key: string;
6250
5824
  }
6251
5825
 
6252
- /** Formats a normalized [0,1] value as a CSS percentage string for SVG positioning. */
6253
- export declare const toPercent: (value: number) => string;
6254
-
6255
- /**
6256
- * Converts a normalized [0,1] x-coordinate to viewBox coordinate (identity transform).
6257
- * Used inside nested SVGs with viewBox="0 0 1 1".
6258
- */
6259
- export declare function toViewBoxX(normalized: number): number;
6260
-
6261
- /**
6262
- * Converts a normalized [0,1] y-coordinate to viewBox coordinate (Y-inverted).
6263
- * SVG y=0 is at the top, but data y=0 is at the bottom, so we invert.
6264
- * Used inside nested SVGs with viewBox="0 0 1 1".
6265
- */
6266
- export declare function toViewBoxY(normalized: number): number;
6267
-
6268
- /** Factories for data transforms applied before charting (reshape, filter, sort, aggregate, constant). */
6269
5826
  export declare const transform: {
6270
5827
  reshape: typeof reshape;
6271
5828
  filter: typeof filter;
@@ -6331,7 +5888,7 @@ declare interface UndoRedoResult {
6331
5888
  * Each validation stage (e.g. {@link LayerValidator}, the pre-pass in {@link ScaleCompiler}) collects
6332
5889
  * {@link ValidationIssue}s and throws a single {@link SpecValidationError} at the end of its stage.
6333
5890
  */
6334
- declare type ValidationCode = 'UNKNOWN_VARIABLE' | 'INCOMPATIBLE_TYPE' | 'MISSING_AESTHETIC' | 'INVALID_RULE_MAPPING' | 'INVALID_RULE_COORD';
5891
+ declare type ValidationCode = 'UNKNOWN_VARIABLE' | 'INCOMPATIBLE_TYPE' | 'MISSING_AESTHETIC' | 'INVALID_RULE_MAPPING' | 'UNSUPPORTED_COORD';
6335
5892
 
6336
5893
  declare interface ValidationIssue {
6337
5894
  code: ValidationCode;
@@ -6340,37 +5897,7 @@ declare interface ValidationIssue {
6340
5897
  aesthetic?: string;
6341
5898
  }
6342
5899
 
6343
- /**
6344
- * The compiler-emitted descriptor of how a raw data value should be turned into a display string.
6345
- * The engine never formats values itself; it tags each guide/legend/headline figure with a
6346
- * `ValueFormat`, and the renderer materializes it via `createValueFormatter` (a descriptor is inert
6347
- * until paired with a locale and number-format config). It surfaces on compiled guides
6348
- * (`CompiledAxisGuide.valueFormat`, legend/headline items), so a renderer holds these values and must
6349
- * be able to name and switch on them.
6350
- *
6351
- * The `type` discriminant selects the formatter. Rendered examples (en-US, default number config):
6352
- * - `currency` — '$1,234.50' (narrow currency symbol from `iso`, 2 decimals).
6353
- * - `decimal` — '1,234.5' (locale grouping; decimals/abbreviation from number-format config).
6354
- * - `integer` — '1,235' (no fraction digits).
6355
- * - `percentage` — '12%' (value is a fraction: 0.12 → '12%').
6356
- * - `duration` — '1h 5m' (value is milliseconds; always English, never localized).
6357
- * - `text` — 'North' (categorical value passed through unchanged).
6358
- * - `date` — 'Jan 5, 2025'.
6359
- * - `datetime` — 'Jan 5, 2025 • 14:30:00' (comma between date and time replaced by a middot).
6360
- * - `time` — '14:30'.
6361
- * - `year` — '2025'.
6362
- * - `quarter` — 'Q1 2025'.
6363
- * - `month` — 'January' (no year).
6364
- * - `month_year` — 'Jan 2025'.
6365
- * - `day_month` — 'January 5' (no year).
6366
- * - `weekly_date_range` — 'January 5 – 11' (value + 6 days, no year).
6367
- * - `weekly_date_range_with_year` — 'Jan 5 – 11, 2025'.
6368
- * - `lookup` — resolved per observation; see {@link LookupValueFormat}.
6369
- *
6370
- * The `isXValueFormat` guards (e.g. {@link isLookupValueFormat}, {@link isTemporalValueFormat}) narrow
6371
- * a held descriptor to a family without listing every member kind by hand.
6372
- */
6373
- export declare type ValueFormat = ExplicitValueFormat | LookupValueFormat;
5900
+ declare type ValueFormat = ExplicitValueFormat | LookupValueFormat;
6374
5901
 
6375
5902
  /**
6376
5903
  * Formats one value. When the underlying `ValueFormat` is a `lookup`, the second `observation` argument
@@ -6379,16 +5906,10 @@ export declare type ValueFormat = ExplicitValueFormat | LookupValueFormat;
6379
5906
  */
6380
5907
  export declare type ValueFormatter = (value: DataValue, observation?: Observation) => string;
6381
5908
 
6382
- /** Inputs to build a {@link ValueFormatter}: the format to apply, the locale, and number-formatting config. */
6383
5909
  export declare interface ValueFormatterFactoryParams<T = ValueFormat> {
6384
5910
  valueFormat: T;
6385
5911
  locale: Locale;
6386
5912
  numberFormat: NumberFormatConfig;
6387
- /**
6388
- * Advanced override merged onto the formatter's default Intl options, used by axis/tick label
6389
- * compaction (e.g. dropping the year on dense date axes). Standard value formatting omits it and
6390
- * lets each kind's defaults stand.
6391
- */
6392
5913
  intlOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions;
6393
5914
  }
6394
5915
 
@@ -6455,7 +5976,6 @@ export declare type VariablePredicate = {
6455
5976
  range: [DataValue, DataValue];
6456
5977
  };
6457
5978
 
6458
- /** Internal column names holding each observation's resolved visual channels (color, size, etc.). */
6459
5979
  export declare const VISUAL_VARIABLES: {
6460
5980
  readonly color: string;
6461
5981
  readonly size: string;
@@ -6469,6 +5989,8 @@ export declare const VISUAL_VARIABLES: {
6469
5989
  * `alpha`, `strokeWidth`) on each layer's dataset.
6470
5990
  */
6471
5991
  declare class VisualMapperCompiler extends PerLayerStage<VisualMapperCompilerInput> {
5992
+ private readonly geomCompiler;
5993
+ constructor(geomCompiler: GeomCompiler);
6472
5994
  protected dependencies(input: VisualMapperCompilerInput): readonly unknown[];
6473
5995
  protected compileLayer(layer: CompiledLayer, input: VisualMapperCompilerInput): CompiledLayer;
6474
5996
  }