@graphysdk/react-renderer 0.0.1-plugins.9 → 1.8.1-beta.1786024899180

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,78 +1,98 @@
1
- import { AnimationPlaybackOptions } from 'motion/react';
2
- import { AnnotationDef } from '@graphysdk/viz-engine';
3
- import { AnnotationZOrder } from '@graphysdk/viz-engine';
1
+ import { AnchorPlacement } from '@graphysdk/viz-engine';
4
2
  import { AppearanceConfig } from '@graphysdk/viz-engine';
3
+ import { BrandMarkVariant } from '@graphysdk/viz-engine';
5
4
  import { CartesianCoordSystem } from '@graphysdk/viz-engine';
5
+ import { ColorScheme } from '@graphysdk/viz-engine';
6
6
  import { Command } from '@graphysdk/viz-engine';
7
+ import { CommandStackSnapshot } from '@graphysdk/viz-engine';
8
+ import { CompiledLayer } from '@graphysdk/viz-engine';
7
9
  import { CompiledLayerFor } from '@graphysdk/viz-engine';
10
+ import { CompiledPanel } from '@graphysdk/viz-engine';
8
11
  import { CompiledSpec } from '@graphysdk/viz-engine';
9
- import { CompilerInput } from '@graphysdk/viz-engine';
12
+ import { Component } from 'react';
13
+ import { ComponentType } from 'react';
10
14
  import { CoordSystem } from '@graphysdk/viz-engine';
15
+ import { CreateGraphyBuilderOptions } from '@graphysdk/viz-engine';
11
16
  import { CSSProperties } from 'react';
12
17
  import { CustomPalettesInput } from '@graphysdk/viz-engine';
13
18
  import { Data } from '@graphysdk/viz-engine';
19
+ import { DispatchOptions } from '@graphysdk/viz-engine';
20
+ import { EditTarget } from '@graphysdk/viz-engine';
21
+ import { ErrorInfo } from 'react';
14
22
  import { FontSpec } from '@graphysdk/viz-engine';
23
+ import { FormattedAxis } from '@graphysdk/viz-engine';
24
+ import { FormattedHeadline } from '@graphysdk/viz-engine';
25
+ import { FormattedLegend } from '@graphysdk/viz-engine';
15
26
  import { Geom } from '@graphysdk/viz-engine';
16
- import { GeomIdentity } from '@graphysdk/viz-engine';
17
27
  import { GeomName } from '@graphysdk/viz-engine';
18
28
  import { GraphLayout } from '@graphysdk/viz-engine';
19
- import { GraphTheme } from '@graphysdk/viz-engine';
20
- import { HighlightStrategy } from '@graphysdk/viz-engine';
29
+ import { GraphyBuilder } from '@graphysdk/viz-engine';
30
+ import { HeadlineMeasurer } from '@graphysdk/viz-engine';
21
31
  import { HoverHit } from '@graphysdk/viz-engine';
22
32
  import { HoverState } from '@graphysdk/viz-engine';
33
+ import { IntroStaggerOrder } from '@graphysdk/viz-engine';
23
34
  import { JSX } from 'react/jsx-runtime';
35
+ import { LayerIntroPlan } from '@graphysdk/viz-engine';
36
+ import { LayoutEdge } from '@graphysdk/viz-engine';
24
37
  import { LineStyleType } from '@graphysdk/viz-engine';
25
38
  import { Locale } from '@graphysdk/viz-engine';
26
39
  import { MeasuredText } from '@graphysdk/viz-engine';
27
- import { MotionValue } from 'motion/react';
28
40
  import { Observation } from '@graphysdk/viz-engine';
29
- import { PanelHitTester } from '@graphysdk/viz-engine';
41
+ import { Plugin as Plugin_2 } from '@graphysdk/viz-engine';
42
+ import { PointerRegion } from '@graphysdk/viz-engine';
30
43
  import { PolarCoordSystem } from '@graphysdk/viz-engine';
44
+ import { ReactElement } from 'react';
31
45
  import { ReactNode } from 'react';
32
- import { RefCallback } from 'react';
33
- import { ResolvedTarget } from '@graphysdk/viz-engine';
34
- import { Stat } from '@graphysdk/viz-engine';
46
+ import { Rect } from '@graphysdk/viz-engine';
47
+ import { Ref } from 'react';
48
+ import { RefObject } from 'react';
49
+ import { RenderHitTester } from '@graphysdk/viz-engine';
50
+ import { ResolvedHeadlineSize } from '@graphysdk/viz-engine';
51
+ import { SourceContent } from '@graphysdk/viz-engine';
52
+ import { Spec } from '@graphysdk/viz-engine';
53
+ import { SpecInput } from '@graphysdk/viz-engine';
54
+ import { StoreApi } from 'zustand';
35
55
  import { SVGProps } from 'react';
36
- import { SwatchShape } from '@graphysdk/viz-engine';
56
+ import { TextContent } from '@graphysdk/viz-engine';
37
57
  import { TextMeasurer } from '@graphysdk/viz-engine';
38
58
  import { TooltipContent } from '@graphysdk/viz-engine';
39
- import { TooltipRow } from '@graphysdk/viz-engine';
40
- import { TransformStrategy } from '@graphysdk/viz-engine';
59
+ import { VizDiagnostic } from '@graphysdk/viz-engine';
41
60
 
42
61
  /**
43
- * The drawing surface a custom annotation paints into (ADR-035): a panel-positioned [0,1] frame.
44
- * `targets` are the resolved coordinates in **top-left [0,1]**; `area` carries the panel's pixel size
45
- * for the cases [0,1] cannot express (text extents, fixed-length leaders); `params` is the kind's typed
46
- * params. The draw function returns static nodes — annotations have no hover or tooltip.
62
+ * Props for the AxisLabel slot the axis title of every axis (e.g. "Revenue"), overridable via
63
+ * `slots.AxisLabel`. `labelRects` are SVG-local, keyed by edge. The tick band is a separate slot — see
64
+ * `AxisTicks`.
47
65
  */
48
- export declare interface AnnotationDrawInput<TParams extends object = object> {
49
- targets: readonly ResolvedTarget[];
50
- area: {
51
- width: number;
52
- height: number;
53
- };
54
- params: TParams;
66
+ export declare interface AxisLabelSlotProps {
67
+ formattedAxes: FormattedAxis[];
68
+ labelRects: Partial<Record<LayoutEdge, Rect>>;
55
69
  }
56
70
 
57
71
  /**
58
- * A custom annotation's render half. `type` resolves it for a compiled annotation; `zOrder` is the
59
- * kind's default slot (background paints before geoms, foreground after) overridable per instance;
60
- * `draw` is a React render function that may reuse the render kit (theme tokens, `toPercent`, value
61
- * readers) but never participates in hover.
72
+ * Props for the AxisTicks slot the tick lines and tick labels of every axis, overridable via
73
+ * `slots.AxisTicks`. `tickRects` are SVG-local, keyed by edge. The axis title is a separate slot — see
74
+ * `AxisLabel`.
62
75
  */
63
- export declare interface AnnotationPlugin {
64
- type: string;
65
- zOrder: AnnotationZOrder;
66
- draw: (input: AnnotationDrawInput) => ReactNode;
76
+ export declare interface AxisTicksSlotProps {
77
+ formattedAxes: FormattedAxis[];
78
+ tickRects: Partial<Record<LayoutEdge, Rect>>;
67
79
  }
68
80
 
69
- /** The render half an author supplies to {@link defineAnnotationRenderer} everything but `type`. */
70
- declare interface AnnotationRenderHalf<TParams extends object> {
71
- zOrder: AnnotationZOrder;
72
- draw: (input: AnnotationDrawInput<TParams>) => ReactNode;
81
+ /** Made with Graphy provenance badge capsule + glyph + system-face copy. */
82
+ export declare const BrandMark: ({ visual, placement }: BrandMarkProps) => JSX.Element;
83
+
84
+ declare interface BrandMarkProps {
85
+ visual: Exclude<BrandMarkVisual, 'hidden'>;
86
+ placement: 'footer' | 'header';
73
87
  }
74
88
 
75
- export { AnnotationZOrder }
89
+ /**
90
+ * How the badge paints at the current frame size:
91
+ * - `full` — glyph + "Made with Graphy" pill
92
+ * - `mini` — circular glyph capsule (also the under-200 px ladder step)
93
+ * - `hidden` — flag off, or frame below the minimum footprint
94
+ */
95
+ export declare type BrandMarkVisual = 'full' | 'mini' | 'hidden';
76
96
 
77
97
  /**
78
98
  * Browser text measurer backed by OffscreenCanvas.
@@ -129,59 +149,79 @@ export declare class CanvasTextMeasurer implements TextMeasurer {
129
149
  }
130
150
 
131
151
  /**
132
- * Hover-overlay dot drawn at a non-primary hit's `(x, y)`: a single circle at reduced opacity
133
- * so it reads as secondary. Used for any companion hit same-layer sibling (e.g. another
134
- * series at the primary X) or cross-layer related.
152
+ * The compiled layer a renderer's handlers receive. A built-in renderer keyed to a `GeomName` gets
153
+ * its param-narrowed `CompiledLayerFor<G>`; a custom renderer (`G = string`) is downstream of
154
+ * serialisation, never sees the plugins array, and reads the base {@link CompiledLayer} dynamically.
135
155
  */
136
- export declare const CompanionDot: ({ hit }: CompanionDotProps) => JSX.Element;
137
-
138
- declare interface CompanionDotProps {
139
- hit: HoverHit;
140
- }
156
+ declare type CompiledLayerOf<G extends GeomName | string> = G extends GeomName ? CompiledLayerFor<G> : CompiledLayer;
141
157
 
142
- export declare type CoordKind = CoordSystem['type'];
158
+ declare type CoordKind = CoordSystem['type'];
143
159
 
144
160
  /** Maps a coord-kind discriminator to the corresponding `CoordSystem` member. */
145
161
  declare type CoordSystemFor<C extends CoordKind> = C extends 'cartesian' ? CartesianCoordSystem : C extends 'polar' ? PolarCoordSystem : never;
146
162
 
147
- export declare const darkTheme: string;
148
-
149
163
  /**
150
- * Binds a custom annotation's render half to its imported compile-half {@link AnnotationDef}: the kind
151
- * name (`def.type`) and the params type (`def.defaultParams`) are read from `def`, so the half never
152
- * re-declares them and cannot disagree with the definition the construction-level handshake mirroring
153
- * `defineGeomRenderer`. `draw` receives the resolved targets with `params` typed from the def.
164
+ * Ergonomic entry point for a React app: pass `plugins` once and get back a {@link GraphyKit} the
165
+ * typed builder plus a `GraphProvider` that already carries them. Pure sugar over the primitives
166
+ * (`createGraphyBuilder`, `<GraphProvider plugins>`); use those directly for headless or advanced
167
+ * wiring. The `const` type parameter captures the `plugins` tuple literally, so `kit.geom.<customName>`
168
+ * is typed.
154
169
  */
155
- export declare function defineAnnotationRenderer<TParams extends object>(def: AnnotationDef<TParams>, half: AnnotationRenderHalf<TParams>): AnnotationPlugin;
170
+ export declare function createGraphyKit<const P extends readonly Plugin_2[] = []>(options?: CreateGraphyBuilderOptions<P>): GraphyKit<P>;
156
171
 
157
172
  /**
158
- * Type-narrowing constructor for a plugin. Each plugin file declares its `(geom, coord)` pair and its
159
- * `highlightStrategy` inline; TypeScript infers `G`/`C`/`S` from the object literal — handlers then
160
- * receive `CompiledLayerFor<G>` / `CoordSystemFor<C>` with no `as` casts. Whether `getOverlayAnchor` is
161
- * required follows from the declared `highlightStrategy` via {@link GeomPluginForStrategy}. A custom geom
162
- * supplies its params type `TParams` (e.g. by annotating `render`'s input) to type `layer.params`.
173
+ * Per-chart Zustand store for what is selected. Nothing in the geom paint path reads it, so a
174
+ * selection change repaints no geoms and re-runs no layout.
163
175
  *
164
- * Returns the broad `GeomPlugin` type (with the type parameters erased) so all plugins fit into one
165
- * registry. The cast inside this helper is safe because the registry routes every call to the plugin
166
- * keyed by `(geom, coord)`.
176
+ * `setSelection` holds the current state when the incoming selection is structurally equal to it: a
177
+ * target is rebuilt from whatever was clicked rather than handed back, so only a structural check
178
+ * keeps a repeated click on the same thing from notifying every subscriber.
179
+ */
180
+ declare const createSelectionStore: () => StoreApi<SelectionStoreState>;
181
+
182
+ /** vanilla-extract class that binds the dark token values; set it on any ancestor to flip to dark. */
183
+ export declare const darkTheme: string;
184
+
185
+ /** Default paint for the footer region — caption, source line, and optional footer-anchored badge. */
186
+ export declare const DefaultFooter: ({ ref, footerRect, mode, caption, isCaptionVisible, source, isSourceVisible, brandMark, }: FooterSlotProps) => JSX.Element | null;
187
+
188
+ /** Default paint for the grid region — per-tick gridlines, drawn inside the panel frame rect. */
189
+ export declare const DefaultGrid: ({ axes, panel, panelFrameRect, panelRect }: GridSlotProps) => JSX.Element;
190
+
191
+ /** Default paint for the header region — title, subtitle, and optional header-anchored badge. */
192
+ export declare const DefaultHeader: ({ ref, headerRect, mode, title, isTitleVisible, subtitle, isSubtitleVisible, brandMark, }: HeaderSlotProps) => JSX.Element | null;
193
+
194
+ /** The built-in glyph for each {@link SwatchShape}. */
195
+ export declare const DefaultSwatch: ({ shape, color, lineType, width, height, }: Omit<SwatchSlotProps, "surface" | "label">) => JSX.Element;
196
+
197
+ /**
198
+ * Default tooltip body. Paints the box, header, and rows from the formatted {@link content}, and
199
+ * resolves each row's swatch shape off its geom's render contract for the active coord system.
200
+ * Positioning lives in the {@link Tooltip} wrapper, never here.
167
201
  */
168
- export declare function defineGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams = PluginParams<G>>(plugin: GeomPluginForStrategy<G, C, S, TParams>): GeomPlugin;
202
+ export declare const DefaultTooltip: ({ content }: TooltipSlotProps) => JSX.Element;
169
203
 
170
204
  /**
171
- * Binds a geom's render half to its imported compile-half {@link Geom} definition: the geom name
172
- * (`def.type`), the highlight strategy (`def.highlightStrategy`), and the params type
173
- * (`def.defaultParams`) are all read from `def`, so the half never re-declares them and cannot disagree
174
- * with the definition — the construction-level handshake. Whether `getOverlayAnchor` is required follows
175
- * from the def's declared `highlightStrategy`, and `render`/`renderHover` receive `layer.params` typed
176
- * from the def with no annotation. Built-in plugins keep {@link defineGeomPlugin}; reach for this only to
177
- * pair a custom geom's two halves.
205
+ * Dual-target renderer binding, keyed on whether the first argument is a compile definition or a built-in
206
+ * geom name:
178
207
  *
179
- * Returns the broad {@link GeomPlugin} (type parameters erased) so every plugin fits one registry; the
180
- * cast is the same variance bypass {@link defineGeomPlugin} performs, safe because the registry routes
181
- * each call by `(geom, coord)`.
208
+ * - **Whole new geom** `defineGeomRenderer(definition, contract)` pairs the render contract with its
209
+ * compile definition, producing a {@link GeomRendererDefinition}. Registering the result registers both
210
+ * sides: the compile definition is reachable at `.definition` and the geom name is read from it, so the
211
+ * two halves cannot drift.
212
+ * - **Render-only override** — `defineGeomRenderer('bar', contract)` rebinds only the paint half of an
213
+ * existing built-in, producing a {@link ResolvedGeomRenderer} that carries no `.definition`. The built-in
214
+ * compile half keeps running (nothing re-seeds the compile registry); only the render registry changes.
215
+ * The name is constrained to {@link GeomName}, so a by-name override of an unknown built-in is a
216
+ * compile-time error. To restyle a *custom* geom, rebind its definition (which you hold) via the first form.
182
217
  */
183
- export declare function defineGeomRenderer<Def extends Geom, C extends CoordKind>(def: Def, half: GeomRenderHalf<Def, C>): GeomPlugin;
218
+ export declare function defineGeomRenderer<Definition extends Geom<unknown>>(definition: Definition, contract: GeomRenderContract): GeomRendererDefinition & {
219
+ readonly definition: Definition;
220
+ };
221
+
222
+ export declare function defineGeomRenderer<G extends GeomName>(geom: G, contract: GeomRenderContract): ResolvedGeomRenderer;
184
223
 
224
+ /** Dev-only compile-cache inspector; render inside a `<GraphProvider>`. See {@link DevToolsPanelProps}. */
185
225
  export declare const DevToolsPanel: ({ width, style, className }?: DevToolsPanelProps) => JSX.Element;
186
226
 
187
227
  /**
@@ -198,84 +238,320 @@ export declare interface DevToolsPanelProps {
198
238
  className?: string;
199
239
  }
200
240
 
201
- export declare const emToPx: (em: number, textScale: number) => number;
241
+ /** Props the renderer passes to the editor layer filling the `EditorSurface` slot. */
242
+ export declare interface EditorSurfaceSlotProps {
243
+ /** The frame's content box — the element the layer measures, listens on and aligns its chrome to. */
244
+ frameElement: HTMLElement;
245
+ /** The panel's rect within that box, so chrome positioned from `layout` shares its origin. */
246
+ panelRect: Rect;
247
+ }
248
+
249
+ declare const FONT_STYLES: readonly ["normal", "italic", "oblique"];
202
250
 
203
251
  /**
204
- * Whether a region is large enough to carry a label: both dimensions must clear their minimum. Works in
205
- * any single consistent space — pass unit dimensions with unit thresholds, or pixels with pixel
206
- * thresholds. A space-filling geom (treemap) culls labels with it so a tile too small to fit text stays
207
- * unlabelled rather than overflowing.
252
+ * A CSS length keeping its unit so measurement can treat it like CSS paint does: em scales with
253
+ * `textScale` (see `emToPx`), px stays absolute.
208
254
  */
209
- export declare function fitsLabel(width: number, height: number, minWidth: number, minHeight: number): boolean;
255
+ export declare interface FontLength {
256
+ value: number;
257
+ unit: 'px' | 'em';
258
+ }
210
259
 
211
- /** A geom's declared highlight composition strategy, or `null` when it opts out of highlighting. */
212
- declare type GeomHighlightStrategy = HighlightStrategy | null;
260
+ /** A CSS `font-style` keyword accepted by a measured font token override. */
261
+ export declare type FontStyle = (typeof FONT_STYLES)[number];
213
262
 
214
263
  /**
215
- * Self-contained module owning everything a single `(geom, coord)` composition needs to paint
216
- * and respond to hover. Plugins are dispatched via the registry keyed on `(geom, coord)`
217
- * the registry guarantees the input shapes match the plugin's identity.
218
- *
219
- * Generic by default: a plugin parameterised as `GeomPlugin<'bar', 'cartesian'>` declares
220
- * narrow input types so its handlers receive `CompiledLayerFor<'bar'>` /
221
- * `CartesianCoordSystem` directly, no runtime checks. Use {@link defineGeomPlugin} to build
222
- * one — the helper centralizes the variance cast so individual plugin files stay free of
223
- * `as` assertions.
224
- *
225
- * The public shape is a flat `NoOverlayAnchor | OverlayAnchor` union so call sites can
226
- * invoke `plugin.render(...)` against `CompiledLayerFor<GeomIdentity>` without each call site
227
- * having to discriminate. Per-plugin enforcement of `getOverlayAnchor` happens at the
228
- * {@link defineGeomPlugin} boundary via {@link GeomPluginForStrategy}, keyed on the plugin's own
229
- * declared `highlightStrategy` — so a custom geom is gated by what it declares, not a name lookup.
264
+ * A structured override of a measured font token. Omitted fields keep the token's default (which
265
+ * still cascades from the leaf tokens, e.g. `fontSizeXs`), so `{ weight: 600 }` changes only the
266
+ * weight.
230
267
  */
231
- export declare type GeomPlugin<G extends string = GeomIdentity, C extends CoordKind = CoordKind> = NoOverlayAnchorGeomPlugin<G, C, GeomHighlightStrategy, PluginParams<G>> | OverlayAnchorGeomPlugin<G, C, GeomHighlightStrategy, PluginParams<G>>;
268
+ export declare interface FontTokenOverride {
269
+ family?: string;
270
+ weight?: number;
271
+ style?: FontStyle;
272
+ size?: FontLength;
273
+ /** Unitless multiplier; sizes HTML line boxes (legend band). Canvas measurement ignores it. */
274
+ lineHeight?: number;
275
+ }
232
276
 
233
- declare interface GeomPluginBase<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> {
234
- geom: G;
277
+ /**
278
+ * Props for the Footer slot, overridable via `slots.Footer` on `GraphRenderer`. Caption editing in
279
+ * `editable` mode is internal to this default; an override replacing the region opts out of it.
280
+ */
281
+ export declare interface FooterSlotProps {
282
+ /** Forward this to the region's outer element — the layout measures the rendered DOM to reserve its space. */
283
+ ref?: React.Ref<HTMLDivElement>;
284
+ footerRect: Rect;
285
+ mode?: GraphMode;
286
+ caption: TextContent | null;
287
+ isCaptionVisible: boolean;
288
+ source: SourceContent | null;
289
+ isSourceVisible: boolean;
290
+ /**
291
+ * Resolved badge visual for footer placement. `hidden` when the mark is off, below the min
292
+ * footprint, or configured for header placement.
293
+ */
294
+ brandMark: BrandMarkVisual;
295
+ }
296
+
297
+ /** A custom geom `renderHoverCompanions` handler's input: the layer plus the primary and related hover hits. */
298
+ export declare type GeomHoverCompanionsRendererInput = HoverCompanionsRenderInput;
299
+
300
+ /** Page-relative cursor coordinates the push-path tooltip anchors to. */
301
+ export declare interface GeomHoverCursor {
302
+ clientX: number;
303
+ clientY: number;
304
+ }
305
+
306
+ /**
307
+ * Pushes a hovered observation key into the central hover, or clears this layer's hover with `null`.
308
+ * A non-null key requires a `cursor` — the overlay intercepts the pointer events the cursor-follow
309
+ * tooltip would otherwise read, so the anchor can only come from the geom's own handler. Consumed by
310
+ * {@link InteractiveOverlayApi.pushHover} and returned by `useGeomHover`.
311
+ */
312
+ export declare interface GeomHoverPush {
313
+ (key: string, cursor: GeomHoverCursor): void;
314
+ (key: null): void;
315
+ }
316
+
317
+ /** A custom geom `renderHover` handler's input: the hovered layer plus the primary/group/related hover hits. */
318
+ export declare type GeomHoverRendererInput = HoverRenderInput;
319
+
320
+ /** A custom geom `getOverlayAnchor` handler's input: the layer, coord system, and the matched observation. */
321
+ export declare type GeomOverlayAnchorRendererInput = OverlayAnchorInput;
322
+
323
+ /** An overlay-hosted geom's paint function — receives the guaranteed overlay wiring on `input.overlay`. */
324
+ export declare type GeomOverlayRenderFn<G extends GeomName | string = string, C extends CoordKind = CoordKind> = (input: GeomOverlayRenderInput<G, C>) => ReactNode;
325
+
326
+ /**
327
+ * The input an overlay-hosted render (`{ fn, options: { overlay: true } }`) receives: the standard render
328
+ * input plus a guaranteed {@link InteractiveOverlayApi}. The renderer always supplies it, so the geom's
329
+ * render uses `overlay` unconditionally — it never decides where it is mounted, only what it paints.
330
+ */
331
+ export declare interface GeomOverlayRenderInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> extends GeomRenderInput<G, C> {
332
+ overlay: InteractiveOverlayApi;
333
+ }
334
+
335
+ /**
336
+ * A geom's `render`: either a plain panel-SVG paint function, or an overlay-hosted one paired with
337
+ * `options` — so a geom that must paint into the interactive overlay declares
338
+ * `{ fn, options: { overlay: true } }` without a second render entry point. The
339
+ * renderer decides where each is mounted; the geom only decides what it paints. `render` is the single
340
+ * paint declaration either way.
341
+ */
342
+ export declare type GeomRender<G extends GeomName | string = string, C extends CoordKind = CoordKind> = GeomRenderFn<G, C> | {
343
+ fn: GeomOverlayRenderFn<G, C>;
344
+ options: GeomRenderOptions;
345
+ };
346
+
347
+ /**
348
+ * The render side of a geom: everything a single `(geom, coord)` composition needs to render and
349
+ * respond to hover. Generic over the geom name and coord kind, so a built-in renderer parameterised
350
+ * as `GeomRenderContract<'bar', 'cartesian'>` receives param-narrowed inputs (`CompiledLayerFor<'bar'>`,
351
+ * `CartesianCoordSystem`), while the default `<string, CoordKind>` instantiation is the base/custom
352
+ * contract a plugin author writes against. The geom's name, highlight strategy, and `params` type are
353
+ * NOT restated here — they are read off the compile definition this contract is bound to (see
354
+ * {@link defineGeomRenderer}). This is the single declaration of a geom's render contract; the built-in
355
+ * narrow form below and the base/custom form (`geom-renderer.ts`) are both instantiations of it.
356
+ */
357
+ export declare interface GeomRenderContract<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
358
+ /** The coord system this contract paints under. A geom may bind one contract per coord. */
235
359
  coord: C;
360
+ swatchShape?: SwatchShape;
361
+ /**
362
+ * The hover-guide mode this `(geom, coord)` draws when it is the hovered layer (a
363
+ * {@link HoverGuideMode}). Omit it — or contribute `null` — to draw no guide (the geom's mark is its
364
+ * own highlight). A declared mode the composition can't realise draws nothing: a polar bar's `'band'`
365
+ * fills a wedge, but a pie/donut (no category band) resolves to an empty one. `resolveHoverGuideMode`
366
+ * reads the hovered layer's mode to pick the one guide the chart draws.
367
+ */
368
+ guideMode?: HoverGuideMode | null;
369
+ /**
370
+ * The geom's paint. A plain function paints into the panel SVG; the `{ fn, options: { overlay: true } }`
371
+ * form paints into a screen-aligned portal above the central capture layer for a live/drag-driven geom
372
+ * that owns its pointer events (force-directed), with the wiring on `input.overlay`.
373
+ */
374
+ render: GeomRender<G, C>;
236
375
  /**
237
- * The geom's highlight composition strategy the same value its compile-half declares. Drives the
238
- * `getOverlayAnchor` requirement at the {@link defineGeomPlugin} boundary, so the render contract is
239
- * derived from a declared strategy rather than a closed geom-name lookup.
376
+ * Optional repaint of the matched subset for the highlight overlay. Omit it and the highlight layer
377
+ * falls back to {@link render}. A geom overrides it when the plain column-grouped render would
378
+ * misrepresent a matched subset — a bar repaints each matched observation as an isolated stack segment
379
+ * (from its compiled stack role), so a lone mid-stack match keeps its square-edged silhouette and
380
+ * single-width border instead of regrouping into a standalone rounded bar.
240
381
  */
241
- highlightStrategy: S;
242
- guideMode: HoverGuideMode | null;
243
- render: (input: GeomRenderInput<G, C, TParams>) => ReactNode;
244
- renderHover: (input: HoverRenderInput<G, C, TParams>) => ReactNode;
245
- renderHoverCompanions: (input: HoverCompanionsRenderInput<G, TParams>) => ReactNode;
382
+ renderHighlight?: (input: HighlightRenderInput<G, C>) => ReactNode;
383
+ renderHover: (input: HoverRenderInput<G, C>) => ReactNode;
384
+ renderHoverCompanions: (input: HoverCompanionsRenderInput<G>) => ReactNode;
246
385
  /**
247
- * Optional: paints the tooltip body when one of this geom's observations is the primary hit. The
248
- * renderer keeps the positioned, themed card and calls this for its contents only; returning `null`
249
- * falls through to the default body. The input carries the engine-built {@link TooltipContent} (feed
250
- * it to `TooltipBody` to reproduce the default) and the raw `primary.observation`.
386
+ * Render-side spatial query for a `'render-hit-test'` layer whose geometry is precomputed into the
387
+ * compiled spec (sankey ribbons, treemap tiles, voronoi cells). A
388
+ * **factory**: given the render input it returns the per-cursor {@link RenderHitTester};
389
+ * the renderer memoizes the factory on `layer.data`, so the read runs once per data change and the
390
+ * per-move query allocates nothing. The author writes no hook; the renderer registers the tester on its
391
+ * behalf. The cursor arrives in panel-local `[0,1]` with a top-left origin — the frame the geom paints
392
+ * in. Returns the declared identity key of the observation under the cursor, or `null` for a miss.
251
393
  */
252
- renderTooltip?: (input: TooltipRenderInput<G, C, TParams>) => ReactNode;
394
+ hitTest?: (input: GeomRenderInput<G, C>) => RenderHitTester;
395
+ /** Panel-space anchor for a matched observation; required when the def highlights via overlay-anchor. */
396
+ getOverlayAnchor?: (input: OverlayAnchorInput<G, C>) => OverlayAnchor | null;
253
397
  }
254
398
 
255
399
  /**
256
- * Picks the plugin shape required for a plugin's own declared `highlightStrategy`. Only the
257
- * `'overlay-anchor'` strategy demands the anchor reader; `'observation-rerender'` and `null` (no
258
- * highlight) both resolve to {@link NoOverlayAnchorGeomPlugin}.
259
- *
260
- * Used by {@link defineGeomPlugin} to enforce the strategy→shape coupling at plugin-definition
261
- * sites — `observation-rerender` plugins cannot declare `getOverlayAnchor`, `overlay-anchor` ones must
262
- * — for any geom name, built-in or custom, with no closed geom-name lookup.
400
+ * A render contract paired with the compile definition it paints for. The engine recovers the definition
401
+ * structurally from `.definition` (React-free), and the renderer reads the geom name from the same
402
+ * definition so the compile and render sides are one declaration consumed twice, never two matched by
403
+ * a string.
263
404
  */
264
- declare type GeomPluginForStrategy<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> = S extends 'overlay-anchor' ? OverlayAnchorGeomPlugin<G, C, S, TParams> : NoOverlayAnchorGeomPlugin<G, C, S, TParams>;
405
+ export declare interface GeomRendererDefinition extends ResolvedGeomRenderer {
406
+ /** The compile definition this renderer paints for. Held by reference — the single source of identity. */
407
+ readonly definition: Geom<unknown>;
408
+ }
265
409
 
266
- /**
267
- * The render half an author supplies to {@link defineGeomRenderer}: every field of a plugin except the
268
- * two its imported compile-half definition already declares — `geom` and `highlightStrategy`.
269
- * `getOverlayAnchor` is gated on the def's declared strategy via {@link GeomPluginForStrategy}, so the
270
- * render contract is bound to the definition rather than re-declared.
271
- */
272
- declare type GeomRenderHalf<Def extends Geom, C extends CoordKind> = Omit<GeomPluginForStrategy<Def['type'] & string, C, Def['highlightStrategy'], Def['defaultParams']>, 'geom' | 'highlightStrategy'>;
410
+ /** A custom geom render handler's input — the base instantiation of the typed built-in inputs. */
411
+ export declare type GeomRendererInput = GeomRenderInput;
273
412
 
274
- export declare interface GeomRenderInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
275
- layer: CompiledLayerFor<G, TParams>;
413
+ /** A geom's panel-SVG paint function the plain `render` form. */
414
+ export declare type GeomRenderFn<G extends GeomName | string = string, C extends CoordKind = CoordKind> = (input: GeomRenderInput<G, C>) => ReactNode;
415
+
416
+ declare interface GeomRenderInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
417
+ layer: CompiledLayerOf<G>;
276
418
  coordSystem: CoordSystemFor<C>;
277
419
  isAnimated: boolean;
278
420
  formattingLocale: Locale;
421
+ intro?: LayerIntroPlan | null;
422
+ }
423
+
424
+ /**
425
+ * Hosting options for an overlay render. The presence of the object form already declares overlay hosting;
426
+ * `overlay: true` makes the call site read explicitly (and leaves room for further hosting options later).
427
+ */
428
+ export declare interface GeomRenderOptions {
429
+ /**
430
+ * Mount this render's output in a screen-aligned portal above the central capture layer, rather than in
431
+ * the panel SVG — for a live or drag-driven geom that must own its pointer events (force-directed). The
432
+ * renderer then supplies {@link GeomOverlayRenderInput.overlay}.
433
+ */
434
+ overlay: true;
435
+ }
436
+
437
+ export declare interface GraphAnimationProps {
438
+ /**
439
+ * Settings for the intro animation played on first mount. `false` disables it, `true` forces it on,
440
+ * an object overrides individual intro settings. Defaults on.
441
+ */
442
+ intro?: boolean | Partial<IntroAnimationOptions>;
443
+ }
444
+
445
+ /** Write access to the graph's spec: applying {@link Command}s and closing the runs they form. */
446
+ export declare interface GraphCommands {
447
+ /** Applies a command to the provider's live spec. */
448
+ dispatch: (command: Command, options?: DispatchOptions) => void;
449
+ /**
450
+ * Closes a run of `{ transient: true }` dispatches and fires `onChange` once for it. Call it when
451
+ * the gesture ends — pointer up, blur. Forgetting only delays the notification rather than
452
+ * corrupting undo: the run covers one {@link EditTarget}, and the next committed dispatch, undo,
453
+ * redo or external change closes it.
454
+ */
455
+ seal: () => void;
456
+ }
457
+
458
+ /**
459
+ * The single place a chart surfaces an error in place — instead of unwinding the page to a blank
460
+ * screen. Two failure modes converge here: a non-throwing **compile failure** the host passes via
461
+ * {@link GraphErrorBoundaryProps.forcedErrors}, and a **render-throw** from a renderer component that
462
+ * this boundary catches. Both render the same {@link GraphErrorPanel}, so there is exactly one panel
463
+ * call site rather than one per failure mode.
464
+ */
465
+ export declare class GraphErrorBoundary extends Component<GraphErrorBoundaryProps, GraphErrorBoundaryState> {
466
+ state: GraphErrorBoundaryState;
467
+ static getDerivedStateFromError(error: Error): GraphErrorBoundaryState;
468
+ componentDidCatch(error: Error, info: ErrorInfo): void;
469
+ componentDidUpdate(prevProps: GraphErrorBoundaryProps): void;
470
+ render(): ReactNode;
471
+ }
472
+
473
+ declare interface GraphErrorBoundaryProps {
474
+ children: ReactNode;
475
+ /**
476
+ * Compile failures to surface in place, rendered through the same panel as a caught render-throw
477
+ * so a chart has one predictable place for its errors.
478
+ */
479
+ forcedErrors?: VizDiagnostic[] | null;
480
+ /**
481
+ * When any key changes after a caught render-throw, the boundary clears it and
482
+ * retries — so a fixed spec recovers. */
483
+ resetKeys?: readonly unknown[];
484
+ /**
485
+ * Called with a single-element list when a child throws during render
486
+ * (not for `forcedErrors`, which the host already reported).
487
+ */
488
+ onError?: (errors: VizDiagnostic[]) => void;
489
+ }
490
+
491
+ declare interface GraphErrorBoundaryState {
492
+ caughtDiagnostic: VizDiagnostic | null;
493
+ }
494
+
495
+ /**
496
+ * Shared in-place fallback for a chart that failed to compile or render. Shows each diagnostic's
497
+ * `code`, `message`, and `suggestion` — richer than a bare error message — so an end user can file
498
+ * a useful report and a developer can see what to fix. Theme-independent: it can render before the
499
+ * theme provider mounts.
500
+ */
501
+ export declare const GraphErrorPanel: ({ errors }: GraphErrorPanelProps) => JSX.Element;
502
+
503
+ declare interface GraphErrorPanelProps {
504
+ /** The compile failure(s). The first headlines; any remaining are listed beneath it. */
505
+ errors: VizDiagnostic[];
506
+ }
507
+
508
+ /**
509
+ * Imperative handle on a graph, for an app's key handling, toolbar or menu bar mounted above the
510
+ * tree the hooks can reach. Obtained through {@link GraphProviderProps.handleRef}.
511
+ */
512
+ export declare interface GraphHandle {
513
+ /** Write access to the graph's spec, the same surface {@link useGraphCommands} serves inside the tree. */
514
+ commands: GraphCommands;
515
+ /**
516
+ * Registers a listener fired on every change to the graph; returns the unsubscribe. With
517
+ * {@link GraphHandle.getCompiled} it is what `useSyncExternalStore` needs, so a surface outside the
518
+ * graph's tree stays in step with it. Subscribing before the graph compiles is valid.
519
+ */
520
+ subscribe: (onGraphChange: () => void) => () => void;
521
+ /** The graph's compiled spec as of now, or `null` before its first successful compile. */
522
+ getCompiled: () => CompiledSpec | null;
523
+ /**
524
+ * Reverse the most recent command. Returns whether the chart took the step, so a caller driving
525
+ * this from a keystroke can leave the key to the app when the chart has nothing to undo or the
526
+ * older spec no longer compiles.
527
+ */
528
+ undo: () => boolean;
529
+ /** Re-apply the most recently undone command. Returns whether the chart took the step. */
530
+ redo: () => boolean;
531
+ /** What the chart holds selected as of now; empty when nothing is. */
532
+ getSelection: () => readonly EditTarget[];
533
+ /** Replace what the chart holds selected. */
534
+ setSelection: (next: readonly EditTarget[]) => void;
535
+ /** Registers a listener fired on every change to the selection; returns the unsubscribe. */
536
+ subscribeSelection: (onSelectionChange: () => void) => () => void;
537
+ }
538
+
539
+ /** Undo/redo controls plus the command stack's own snapshot, which a history UI reads. */
540
+ export declare type GraphHistory = CommandStackSnapshot & {
541
+ /** Reverses the most recent command; returns whether the chart took the step, as {@link GraphHandle.undo}. */
542
+ undo: () => boolean;
543
+ /** Re-applies the most recently undone command; returns whether the chart took the step, as {@link GraphHandle.undo}. */
544
+ redo: () => boolean;
545
+ };
546
+
547
+ export declare interface GraphHistoryShortcutsOptions {
548
+ /**
549
+ * What to listen on. Defaults to `window`; an element — or a ref holding one — scopes the chords
550
+ * to a subtree, and `null` binds nothing.
551
+ */
552
+ target?: EventTarget | RefObject<EventTarget | null> | null;
553
+ /** Set to `false` to unbind without moving the call out of the component. Defaults to `true`. */
554
+ enabled?: boolean;
279
555
  }
280
556
 
281
557
  /**
@@ -287,32 +563,37 @@ export declare type GraphMode = 'readonly' | 'editable';
287
563
 
288
564
  /**
289
565
  * Owns the compiled spec for a graph and exposes it via {@link useCompiledSelector}, plus a
290
- * `dispatch` for applying {@link Command}s. Wraps the chart in {@link GraphErrorBoundary} so an
291
- * invalid spec surfaces its error in place instead of unwinding the surrounding app — every chart
292
- * gets this isolation for free, with no per-chart boundary to author.
566
+ * `dispatch` for applying {@link Command}s. Both failure modes a non-throwing compile failure and a
567
+ * render-throw from a renderer component converge on a single {@link GraphErrorBoundary} that shows
568
+ * the error in place, so a broken chart never blanks the page.
293
569
  */
294
- export declare const GraphProvider: (props: GraphProviderProps) => JSX.Element;
570
+ export declare const GraphProvider: ({ data, input, plugins, formattingLocale, handleRef, onChange, onError, onWarnings, customPalettes, colorScheme, themeOverrides, children, }: GraphProviderProps) => JSX.Element;
295
571
 
296
- declare interface GraphProviderProps {
572
+ /** Props for {@link GraphProvider}: the data and spec input to compile, plus color scheme, locale and plugin wiring. */
573
+ export declare interface GraphProviderProps {
297
574
  data: Data;
298
- input: CompilerInput;
575
+ input: SpecInput;
576
+ /**
577
+ * Custom geoms, stats, and transforms (and their render halves) registered for this graph. Seeds
578
+ * the compiler and builds the per-provider render resolver from one array. Construction-time config,
579
+ * frozen at mount — change the registered set by remounting (React `key`); `data`/`input`/`colorScheme`
580
+ * stay reactive.
581
+ */
582
+ plugins?: readonly Plugin_2[];
299
583
  formattingLocale?: Locale;
300
- onChange?: (next: CompilerInput) => void;
301
- theme?: GraphTheme;
584
+ /**
585
+ * Filled with this graph's {@link GraphHandle}, for callers mounted outside the provider where the
586
+ * hooks can't reach. `useGraphHistoryShortcuts` binds the undo/redo chords to one.
587
+ */
588
+ handleRef?: Ref<GraphHandle>;
589
+ onChange?: (next: SpecInput) => void;
590
+ /** Fires with the compile failure(s) whenever a compile/recompile/dispatch produces errors. */
591
+ onError?: (errors: VizDiagnostic[]) => void;
592
+ /** Fires with any warnings a successful compile produced. */
593
+ onWarnings?: (warnings: VizDiagnostic[]) => void;
594
+ colorScheme?: ColorScheme;
302
595
  themeOverrides?: ThemeOverrides;
303
596
  customPalettes?: CustomPalettesInput;
304
- /** Custom geom definitions, injected per-instance into the compiler (override built-ins by name). */
305
- geoms?: readonly Geom[];
306
- /** Custom stat definitions (`defineStat`), injected per-instance into the compiler (override built-ins by `type`). */
307
- stats?: readonly Stat[];
308
- /** Custom transform strategies (`defineTransform`), injected per-instance (override built-ins by `transformType`). */
309
- transforms?: readonly TransformStrategy[];
310
- /** Custom geom render plugins, injected per-instance (override built-ins by `(geom, coord)`). */
311
- plugins?: readonly GeomPlugin[];
312
- /** Custom annotation render plugins, injected per-instance and resolved by annotation `type` (ADR-035). */
313
- annotationPlugins?: readonly AnnotationPlugin[];
314
- /** Stable chart id. Stamped onto the rendered root as `data-graphy-id`; enables the editor bridge. */
315
- id?: string;
316
597
  children: ReactNode;
317
598
  }
318
599
 
@@ -322,17 +603,20 @@ declare interface GraphProviderProps {
322
603
  * no `config` prop: every consumer must wrap with `<GraphProvider>` so commands can operate
323
604
  * on the live spec.
324
605
  */
325
- export declare const GraphRenderer: ({ sizing, onResize, ...props }: GraphRendererProps) => JSX.Element;
606
+ export declare const GraphRenderer: ({ slots, sizing, onResize, ...rest }: GraphRendererProps) => JSX.Element;
326
607
 
327
- /** Props for {@link GraphRenderer}: container sizing, interaction toggles, and display mode. */
608
+ /** Props for {@link GraphRenderer}: container sizing, interaction toggles and per-region slot overrides. */
328
609
  export declare interface GraphRendererProps {
329
610
  /** Controls how the graph responds to its container size. Defaults to filling the parent container. */
330
611
  sizing?: GraphSizing;
331
612
  /** Callback invoked when the graph's container is resized. Fires in every sizing mode. */
332
613
  onResize?: ResizeObserverOnResize;
333
614
  isAnimated?: boolean;
615
+ animation?: GraphAnimationProps;
334
616
  showTooltips?: boolean;
335
617
  mode?: GraphMode;
618
+ /** Per-region component overrides. Unspecified regions render their default. */
619
+ slots?: GraphSlots;
336
620
  }
337
621
 
338
622
  /** Controls how the graph claims space in its container. */
@@ -356,13 +640,118 @@ export declare type GraphSizing = {
356
640
  aspectRatio: number;
357
641
  };
358
642
 
359
- export declare interface HoverCompanionsRenderInput<G extends string = GeomName, TParams = PluginParams<G>> {
360
- layer: CompiledLayerFor<G, TParams>;
643
+ /**
644
+ * Region overrides for `GraphRenderer`. A slot replaces how one region paints; the viz-engine `Spec`
645
+ * still owns whether a region exists and what data it receives, and an override gets the same
646
+ * render-ready props as its default.
647
+ *
648
+ * Layout-safe regions (`Header`, `Footer`, `Tooltip`, `Grid`, `Swatch`, `EditorSurface`) are bare
649
+ * components — DOM-measured, reserving no edge space or painting inside a box the layout already sized.
650
+ * Layout-coupled regions (`AxisTicks`, `AxisLabel`, `Legend`, `Headline`) are
651
+ * {@link SlotOverride}s that also declare their reserved size via `measure`, else paint and the
652
+ * reserved band desync. The tick and title bands are separate slots so overriding one leaves the other
653
+ * on its default.
654
+ */
655
+ export declare interface GraphSlots {
656
+ Header?: ComponentType<HeaderSlotProps>;
657
+ Footer?: ComponentType<FooterSlotProps>;
658
+ Tooltip?: ComponentType<TooltipSlotProps>;
659
+ Grid?: ComponentType<GridSlotProps>;
660
+ Swatch?: ComponentType<SwatchSlotProps>;
661
+ /**
662
+ * The chart's editor layer, mounted over the frame in `mode="editable"`. Only
663
+ * `@graphysdk/react-renderer/editable` exports something that fills it, so a read-only embed bundles
664
+ * no editing code.
665
+ */
666
+ EditorSurface?: ComponentType<EditorSurfaceSlotProps>;
667
+ Legend?: SlotOverride<LegendSlotProps, (legend: FormattedLegend, ctx: SlotMeasureContext) => number>;
668
+ Headline?: SlotOverride<HeadlineSlotProps, HeadlineMeasurer>;
669
+ AxisTicks?: SlotOverride<AxisTicksSlotProps, (axis: FormattedAxis, ctx: SlotMeasureContext) => number>;
670
+ AxisLabel?: SlotOverride<AxisLabelSlotProps, (axis: FormattedAxis, ctx: SlotMeasureContext) => number>;
671
+ }
672
+
673
+ /**
674
+ * A plugin-bound authoring kit: the typed `geom`/`stat`/`transform`/`scale`/`coord` factories plus
675
+ * `createSpec`/`pipe`, and a `GraphProvider` pre-bound to the same `plugins` — so what can be written
676
+ * and what can render derive from one array and cannot diverge. Generic over the `plugins` tuple so
677
+ * the typed per-plugin builder methods (`geom.<name>`, …) flow through to the React entry point.
678
+ */
679
+ export declare interface GraphyKit<P extends readonly Plugin_2[] = readonly Plugin_2[]> extends GraphyBuilder<P> {
680
+ GraphProvider: (props: Omit<GraphProviderProps, 'plugins'>) => ReactElement;
681
+ }
682
+
683
+ /**
684
+ * Props for the Grid slot, overridable via `slots.Grid` on `GraphRenderer`. `panelRect` is in
685
+ * SVG-local coordinates.
686
+ */
687
+ export declare interface GridSlotProps {
688
+ axes: FormattedAxis[];
689
+ panel: CompiledPanel;
690
+ panelFrameRect: GraphLayout['panelFrame'];
691
+ panelRect: GraphLayout['panel'];
692
+ }
693
+
694
+ /**
695
+ * Props for the Header slot, overridable via `slots.Header` on `GraphRenderer`. Title editing in
696
+ * `editable` mode is internal to this default; an override replacing the region opts out of it.
697
+ */
698
+ export declare interface HeaderSlotProps {
699
+ /** Forward this to the region's outer element — the layout measures the rendered DOM to reserve its space. */
700
+ ref?: React.Ref<HTMLDivElement>;
701
+ headerRect: Rect;
702
+ mode?: GraphMode;
703
+ title: TextContent | null;
704
+ isTitleVisible: boolean;
705
+ subtitle: TextContent | null;
706
+ isSubtitleVisible: boolean;
707
+ /**
708
+ * Resolved badge visual for header placement. `hidden` when the mark is off, below the min
709
+ * footprint, or configured for footer placement.
710
+ */
711
+ brandMark: BrandMarkVisual;
712
+ }
713
+
714
+ /** Props for the Headline slot, overridable via `slots.Headline` on `GraphRenderer`. */
715
+ export declare interface HeadlineSlotProps {
716
+ headline: FormattedHeadline;
717
+ rect: Rect;
718
+ resolvedSize: ResolvedHeadlineSize;
719
+ /** Leading strip items to paint; the rest are hidden because they would overflow the band. */
720
+ visibleItemCount: number;
721
+ }
722
+
723
+ /**
724
+ * The input a geom's {@link GeomRenderContract.renderHighlight} receives: the matched-subset layer plus
725
+ * the panel rect, so a geom that repaints matched observations in absolute panel pixels (bars drawing
726
+ * isolated stack segments) has the frame it needs. A superset of {@link GeomRenderInput}, so a geom that
727
+ * doesn't override `renderHighlight` still paints through the plain `render`.
728
+ */
729
+ declare interface HighlightRenderInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> extends GeomRenderInput<G, C> {
730
+ panelRect: GraphLayout['panel'];
731
+ /** The full layer `layer` was filtered from — for context the subset can't see, like a stack's silhouette. */
732
+ sourceLayer: CompiledLayerOf<G>;
733
+ }
734
+
735
+ declare interface HoverCompanionsRenderInput<G extends GeomName | string = string> {
736
+ layer: CompiledLayerOf<G>;
361
737
  primary: HoverHit;
362
738
  related: HoverHit[];
363
739
  }
364
740
 
365
- export declare type HoverGuideMode = 'continuous' | 'bar';
741
+ /**
742
+ * The shape of positional guide the `HoverGuide` draws for a hovered observation, contributed per
743
+ * `(geom, coord)` renderer via `guideMode`:
744
+ *
745
+ * - `'band'` — a rectangle over the hovered category's band on the main axis (bars).
746
+ * - `'crosshair'` — a rule at the hovered value: a straight line under cartesian, a centre-to-rim spoke
747
+ * under polar (line and area).
748
+ *
749
+ * A renderer that omits `guideMode` draws no guide (scatter points). A declared `'band'` still draws
750
+ * nothing where the composition has no category band — a pie/donut resolves to an empty wedge. The chart
751
+ * draws the guide of whichever layer the cursor resolves to, so a combo shows a band over a hovered bar
752
+ * and a crosshair over a hovered line; see `resolveHoverGuideMode`.
753
+ */
754
+ declare type HoverGuideMode = 'crosshair' | 'band';
366
755
 
367
756
  /**
368
757
  * Hosts the hover store + engine for the surrounding `<GraphProvider>`. Subscribes only to the
@@ -375,8 +764,8 @@ declare interface HoverProviderProps {
375
764
  children: ReactNode;
376
765
  }
377
766
 
378
- export declare interface HoverRenderInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
379
- layer: CompiledLayerFor<G, TParams>;
767
+ declare interface HoverRenderInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
768
+ layer: CompiledLayerOf<G>;
380
769
  coordSystem: CoordSystemFor<C>;
381
770
  primary: HoverHit;
382
771
  group: HoverHit[];
@@ -387,41 +776,77 @@ export declare interface HoverRenderInput<G extends string = GeomName, C extends
387
776
  declare interface HoverSlice {
388
777
  hover: HoverState;
389
778
  /**
390
- * Whether the pointer is over the chart's interactive surface. Drives the tooltip's open/close at
391
- * the enter/leave boundary. Held as state (not a ref) because that transition must re-render the
392
- * tooltip; the cursor *position* stays a ref (see `HoverContextValue.cursorRef`) so pointer-rate
393
- * moves reposition through floating-ui without re-rendering.
779
+ * Box the tooltip anchors against while the pointer is over a pinned anchor, in client
780
+ * coordinates. `null` during normal cursor hover, when the tooltip follows the cursor.
781
+ */
782
+ tooltipAnchor: TooltipAnchor | null;
783
+ /**
784
+ * Chrome hit regions the tracker tests before the geom hit-test, highest priority first. Whoever
785
+ * paints over the plot publishes its area here so hover detection stays in one pointer pipeline.
394
786
  */
395
- isPointerInside: boolean;
787
+ pointerRegions: readonly PointerRegion[];
788
+ /** A gesture owns the pointer: the tracker runs no query, so nothing recomputes behind it. */
789
+ isPointerSuspended: boolean;
396
790
  }
397
791
 
398
792
  declare interface HoverStoreActions {
399
793
  setHoverState: (next: HoverState) => void;
400
- setPointerInside: (value: boolean) => void;
794
+ setTooltipAnchor: (next: TooltipAnchor | null) => void;
795
+ /**
796
+ * Replaces one publisher's regions. Keying by publisher lets several features hold regions at once
797
+ * without clobbering each other; an empty list retracts the publisher entirely.
798
+ */
799
+ publishPointerRegions: (publisher: string, regions: readonly PointerRegion[]) => void;
800
+ setPointerSuspended: (next: boolean) => void;
401
801
  }
402
802
 
803
+ /** State of the hover store: the current {@link HoverState} plus its setter. */
403
804
  declare type HoverStoreState = HoverSlice & HoverStoreActions;
404
805
 
405
- /** Lightens a hex colour by mixing it toward white by `amount` (`0` keeps the colour, `1` is white). */
406
- export declare function lighten(hex: string, amount: number): string;
407
-
408
- export declare const lightTheme: string;
409
-
410
806
  /**
411
- * Mixes a hex colour toward another by `amount` in `[0, 1]` (`0` keeps `hex`, `1` returns `towardHex`),
412
- * channel-wise in sRGB. Returns an `rgb(...)` string. Used for within-group shading where leaves share a
413
- * group hue and vary only in lightness.
807
+ * The overlay wiring handed to an overlay-hosted render via {@link GeomOverlayRenderInput.overlay}. The
808
+ * geom writes only its simulation, marks, and drag handlers; the renderer owns the on-screen rect
809
+ * measurement, the portal alignment, and the push wiring.
414
810
  */
415
- export declare function mixColor(hex: string, towardHex: string, amount: number): string;
811
+ export declare interface InteractiveOverlayApi {
812
+ /** Feeds the hovered observation's identity key into the unified hover store — the push path. */
813
+ pushHover: GeomHoverPush;
814
+ /** The panel's on-screen rect in client pixels, so the overlay can place its marks. */
815
+ panelRect: ScreenRect;
816
+ }
817
+
818
+ declare interface IntroAnimationOptions {
819
+ /** Whether the entrance plays at all */
820
+ enabled: boolean;
821
+ /** Multiplier applied to every entrance duration and stagger delay. */
822
+ durationScale: number;
823
+ /** Whether geoms that support staggered entrance (bars) enter staggered rather than all at once. */
824
+ stagger: boolean;
825
+ /** The order staggered point geoms enter in. Bars and slices always enter in visual order. */
826
+ staggerOrder: IntroStaggerOrder;
827
+ /** Total geom count across all layers above which the entrance is skipped entirely. */
828
+ maxAnimatedGeoms: number;
829
+ }
830
+
831
+ /** Props for the Legend slot, overridable via `slots.Legend` on `GraphRenderer`. */
832
+ export declare interface LegendSlotProps {
833
+ formattedLegends: FormattedLegend[];
834
+ rects: Partial<Record<LayoutEdge, Rect>>;
835
+ textScale: number;
836
+ }
837
+
838
+ /** Scales the color's HSL lightness up by `amount` (0–1). */
839
+ export declare const lightenCss: (colorString: string, amount: number) => string;
840
+
841
+ /** vanilla-extract class that binds the light token values; the default theme. */
842
+ export declare const lightTheme: string;
416
843
 
417
844
  /**
418
- * Plugin shape for geoms that don't compute an overlay anchor — either `'observation-rerender'`
419
- * geoms (matched rows re-render in place) or geoms that opt out of highlighting entirely
420
- * (`null` strategy). The renderer never asks these plugins for an anchor, so the property is forbidden.
845
+ * The element font tokens JS measurement reproduces. Overridden structurally
846
+ * ({@link FontTokenOverride}); the CSS variable's shorthand is serialized from the same object
847
+ * measurement reads, so paint and layout move together.
421
848
  */
422
- declare interface NoOverlayAnchorGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> extends GeomPluginBase<G, C, S, TParams> {
423
- getOverlayAnchor?: never;
424
- }
849
+ export declare type MeasuredFontTokenKey = 'fontDataLabel' | 'fontStackTotal' | 'fontCategoryLabel' | 'fontTickLabel' | 'fontAxisLabel' | 'fontLegendLabel' | 'fontGoalLineLabel';
425
850
 
426
851
  /**
427
852
  * Anchor point in normalized [0,1] coord-space where a highlight overlay marker
@@ -432,88 +857,140 @@ export declare interface OverlayAnchor {
432
857
  y: number;
433
858
  }
434
859
 
435
- declare interface OverlayAnchorGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> extends GeomPluginBase<G, C, S, TParams> {
436
- /** Returns the panel-space anchor for a matched observation. */
437
- getOverlayAnchor: (input: OverlayAnchorInput<G, C, TParams>) => OverlayAnchor | null;
438
- }
439
-
440
- export declare interface OverlayAnchorInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
441
- layer: CompiledLayerFor<G, TParams>;
860
+ declare interface OverlayAnchorInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
861
+ layer: CompiledLayerOf<G>;
442
862
  coordSystem: CoordSystemFor<C>;
443
863
  observation: Observation;
444
864
  }
445
865
 
446
- export { PanelHitTester }
447
-
448
866
  /**
449
- * Portals an interactive overlay to `document.body`, fixed over the given panel rect at the documented
450
- * top `z-index`. Owns only the placement and chrome; the simulation canvas it wraps paints inside. Sized
451
- * to the rect with `pointerEvents: 'none'` so gaps fall through to the chart — the overlay's own marks
452
- * re-enable pointer events to capture drags.
867
+ * Drops the selected targets `spec` no longer holds, and hands back the same list when it holds all
868
+ * of them a selection that survived a command must not churn its subscribers.
869
+ *
870
+ * Validated against the spec rather than compiled output: the compiler silently drops annotations it
871
+ * cannot place, and a target whose annotation failed to resolve has to stay selected for the user to
872
+ * be able to repair it.
453
873
  */
454
- export declare function PanelOverlayPortal({ rect, children }: {
455
- rect: PanelScreenRect;
456
- children: ReactNode;
457
- }): ReactNode;
874
+ export declare const pruneSelection: (selection: readonly EditTarget[], spec: Spec) => readonly EditTarget[];
458
875
 
459
- /** The panel's on-screen rectangle in page-relative pixels — the frame a portalled overlay aligns to. */
460
- export declare interface PanelScreenRect {
461
- left: number;
462
- top: number;
876
+ export { RenderHitTester }
877
+
878
+ /** Fires on each deduped size change — the shape of `GraphRenderer`'s `onResize` callback. */
879
+ export declare type ResizeObserverOnResize = (state: ResizeObserverState) => void;
880
+
881
+ /** The observed element's content-box size in CSS pixels, rounded to integers. */
882
+ export declare interface ResizeObserverState {
463
883
  width: number;
464
884
  height: number;
885
+ /** True until the first ResizeObserver measurement lands. */
886
+ isDefault: boolean;
465
887
  }
466
888
 
889
+ /** Frame-size ladder: hidden below 120×80, mini below 200 wide (or when variant is mini). */
890
+ export declare const resolveBrandMarkVisual: (enabled: boolean, frameSize: {
891
+ width: number;
892
+ height: number;
893
+ }, variant?: BrandMarkVariant) => BrandMarkVisual;
894
+
467
895
  /**
468
- * Walks a kind-tagged geom-layout dataset (the output of `createMarkTable`), yielding each observation paired
469
- * with its discriminant `kind`. The render half switches on `kind` to build its per-kind marks, instead
470
- * of re-reading the kind column inline in every plugin. `Kind` narrows the discriminant to the geom's own
471
- * union (e.g. `'node' | 'link'`) for an exhaustive switch.
896
+ * A render contract with its resolved geom name attached the shape the per-provider resolver returns
897
+ * and every render site consumes. Both a built-in `GeomRenderer` and a custom {@link GeomRendererDefinition}
898
+ * conform to it, so the consumers paint built-ins and customs through one type.
472
899
  */
473
- export declare function partitionByKind<Kind extends string = string>(data: Iterable<Observation>, kindColumn: string): Generator<{
474
- kind: Kind;
475
- observation: Observation;
476
- }>;
900
+ export declare interface ResolvedGeomRenderer extends GeomRenderContract {
901
+ /** The geom name this renderer paints — the resolver dispatches on it. */
902
+ geom: string;
903
+ }
477
904
 
478
- /** The compiled-layer params a plugin's handlers see built-ins default from the geom name; a custom geom supplies its own. */
479
- declare type PluginParams<G extends string> = CompiledLayerFor<G>['params'];
905
+ /** An element's on-screen rect in client coordinates what a fixed-position overlay aligns to. */
906
+ export declare interface ScreenRect {
907
+ left: number;
908
+ top: number;
909
+ width: number;
910
+ height: number;
911
+ }
480
912
 
481
- /**
482
- * Hover-overlay dot drawn at the primary hit's `(x, y)`: colored disc with a thicker white
483
- * border and an outer white ring offset by a 1px gap. Radius is data-driven so bubble charts
484
- * whose size channel carries data don't distort under hover.
485
- */
486
- export declare const PrimaryDot: ({ hit, ...rest }: PrimaryDotProps) => JSX.Element;
913
+ declare interface SelectionSlice {
914
+ /**
915
+ * The {@link EditTarget}s the graph holds selected. A list even though nothing selects more than
916
+ * one target: widening it later would break the handle, the panel contract and every consumer at
917
+ * once.
918
+ */
919
+ selection: readonly EditTarget[];
920
+ }
487
921
 
488
- declare interface PrimaryDotProps {
489
- hit: HoverHit;
490
- 'data-testid'?: string;
922
+ /** The vanilla zustand store `GraphProvider` holds; consumers read it via `useGraphSelection`. */
923
+ export declare type SelectionStore = ReturnType<typeof createSelectionStore>;
924
+
925
+ declare interface SelectionStoreActions {
926
+ setSelection: (next: readonly EditTarget[]) => void;
927
+ clearSelection: () => void;
491
928
  }
492
929
 
493
- export declare type ResizeObserverOnResize = (state: ResizeObserverState) => void;
930
+ /** State of the selection store: what the graph holds selected plus the actions replacing it. */
931
+ export declare type SelectionStoreState = SelectionSlice & SelectionStoreActions;
494
932
 
495
- export declare interface ResizeObserverState {
496
- width: number;
497
- height: number;
498
- /** True until the first ResizeObserver measurement lands. */
499
- isDefault: boolean;
933
+ /**
934
+ * Passed as the second argument to a layout-coupled slot's `measure`, so it can size its band from
935
+ * real text metrics — the same Canvas-backed measurer the built-in measurers use — rather than
936
+ * constructing its own. A `measure` whose size is unrelated to text can ignore it.
937
+ */
938
+ export declare interface SlotMeasureContext {
939
+ /** Measures a string at a given font; returns `{ width, height, ascent, descent }` in CSS pixels. */
940
+ measureText: TextMeasurer['measureText'];
941
+ /** Active text-scale multiplier; multiply an em size by this to get the pixel size to measure at. */
942
+ textScale: number;
500
943
  }
501
944
 
502
- export { ResolvedTarget }
945
+ /**
946
+ * A layout-coupled slot: the region's `render` paired with the `measure` the layout uses to reserve
947
+ * its space. `measure` mirrors the matching `LayoutMeasurer` method, so paint and reserved space can't
948
+ * disagree. Give it a stable reference — a `measure` whose identity changes each render takes effect on
949
+ * the next paint but doesn't retrigger layout.
950
+ */
951
+ export declare interface SlotOverride<Props, Measure> {
952
+ /** The component that paints the region. */
953
+ render: ComponentType<Props>;
954
+ /**
955
+ * Returns the region's reserved size. Receives the region's formatted data plus a
956
+ * {@link SlotMeasureContext} (`measureText`, `textScale`) for sizing from real text metrics.
957
+ */
958
+ measure: Measure;
959
+ }
503
960
 
504
961
  /**
505
- * Shared shape glyph used by both the tooltip and the legend.
962
+ * The vocabulary of legend/tooltip/headline marks the {@link Swatch} can paint. A geom picks the one
963
+ * that best evokes its on-canvas mark via `swatchShape` on its render contract (see
964
+ * {@link GeomRenderContract}); it is a render concern, so the engine never resolves it.
965
+ *
966
+ * - `square` — a filled rect (bars)
967
+ * - `line` — a horizontal stroke (lines)
968
+ * - `area` — a filled region with a stroke accent (areas)
969
+ * - `circle` — a filled dot (points)
970
+ * - `slice` — a pie / donut wedge (polar bars)
506
971
  */
507
- export declare const Swatch: ({ shape, color, lineType, width, height, }: SwatchProps) => JSX.Element;
972
+ export declare type SwatchShape = 'square' | 'line' | 'circle' | 'area' | 'slice';
508
973
 
509
- declare interface SwatchProps {
974
+ /**
975
+ * Props for the Swatch slot (`slots.Swatch` on `GraphRenderer`). An override must paint inside the
976
+ * `width` × `height` box it receives.
977
+ *
978
+ * Switch on `shape`, `surface` or `label` and delegate the rest to
979
+ * {@link DefaultSwatch}.
980
+ */
981
+ export declare interface SwatchSlotProps {
510
982
  shape: SwatchShape;
511
983
  color: string;
984
+ surface: SwatchSurface;
985
+ label?: string;
512
986
  lineType?: LineStyleType;
513
987
  width?: number;
514
988
  height?: number;
515
989
  }
516
990
 
991
+ /** The UI surface a swatch is painted on. Lets a Swatch slot restyle one surface and delegate the rest. */
992
+ export declare type SwatchSurface = 'legend' | 'tooltip' | 'headline' | 'callout' | 'rule-label';
993
+
517
994
  /**
518
995
  * Creates a font-ready, cached text measurer and shares it with descendants via context.
519
996
  * Renders nothing until the measurer resolves, so consumers reading the context always
@@ -538,171 +1015,215 @@ declare interface TextMeasurerProviderProps {
538
1015
  measurer?: TextMeasurer;
539
1016
  }
540
1017
 
1018
+ /** The name of a single theme token. */
541
1019
  export declare type ThemeKey = keyof ThemeValues;
542
1020
 
543
- export declare type ThemeOverrides = Partial<ThemeValues>;
1021
+ /**
1022
+ * A partial set of token values layered over a base theme — the shape of the `themeOverrides`
1023
+ * prop. Measured font tokens take a {@link FontTokenOverride}; every other token takes its CSS
1024
+ * string value.
1025
+ */
1026
+ export declare type ThemeOverrides = Partial<Omit<ThemeValues, MeasuredFontTokenKey>> & {
1027
+ [Key in MeasuredFontTokenKey]?: FontTokenOverride;
1028
+ };
544
1029
 
545
- export declare const ThemeProvider: ({ theme, themeOverrides, appearance, children }: ThemeProviderProps) => JSX.Element;
1030
+ /** Resolves the base theme plus per-graph `themeOverrides` into token values and publishes them to the subtree. */
1031
+ export declare const ThemeProvider: ({ colorScheme, themeOverrides, appearance, children }: ThemeProviderProps) => JSX.Element;
546
1032
 
547
1033
  declare interface ThemeProviderProps {
548
1034
  appearance: AppearanceConfig;
549
- theme: GraphTheme;
1035
+ colorScheme: ColorScheme;
550
1036
  themeOverrides?: ThemeOverrides;
551
1037
  children: ReactNode;
552
1038
  }
553
1039
 
1040
+ /** Every theme token mapped to its resolved CSS string value. */
554
1041
  export declare type ThemeValues = Record<keyof typeof vars, string>;
555
1042
 
556
1043
  /**
557
- * The default tooltip body the optional header and one row per hovered observation, with the
558
- * in-place primary-row highlight. Presentational and engine-decoupled: it renders a
559
- * {@link TooltipContent} and nothing else, so the built-in `Tooltip` and a custom geom's
560
- * `renderTooltip` paint the same rows. The positioned, themed card around it stays the renderer's.
1044
+ * Where a pinned callout asks the tooltip to anchor: the marker point in client coordinates, plus
1045
+ * the direction the mini sits, so the tooltip expands over the mini the way it was placed.
561
1046
  */
562
- export declare const TooltipBody: ({ content }: TooltipBodyProps) => JSX.Element;
563
-
564
- declare interface TooltipBodyProps {
565
- content: TooltipContent;
566
- }
567
-
568
- /** Page-relative cursor position the tooltip anchors to. `null` when the pointer is outside the chart. */
569
- export declare interface TooltipCursor {
570
- clientX: number;
571
- clientY: number;
1047
+ declare interface TooltipAnchor {
1048
+ x: number;
1049
+ y: number;
1050
+ placement: AnchorPlacement;
1051
+ /**
1052
+ * Annotation the anchor belongs to, so the tooltip can resolve a comment's text by id. Absent when
1053
+ * a geom overlay anchors to the cursor instead.
1054
+ */
1055
+ annotationId?: string;
572
1056
  }
573
1057
 
574
- /**
575
- * Input a plugin's {@link GeomPluginBase.renderTooltip} receives when one of its observations is the
576
- * primary hit. `content` is the body the built-in tooltip would render — feed it to `TooltipBody` to
577
- * reproduce the default — while `primary.observation` carries the geom's own columns for a bespoke
578
- * layout. The card around the body (positioning, theme) stays the renderer's.
579
- */
580
- export declare interface TooltipRenderInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
1058
+ /** Props for the Tooltip slot, overridable via `slots.Tooltip` on `GraphRenderer`. Positioning stays built in. */
1059
+ export declare interface TooltipSlotProps {
1060
+ /** Render-ready tooltip body, already formatted by the viz-engine runtime. */
581
1061
  content: TooltipContent;
582
- layer: CompiledLayerFor<G, TParams>;
583
- coordSystem: CoordSystemFor<C>;
584
- primary: HoverHit;
585
- group: HoverHit[];
586
- related: ReadonlyMap<string, HoverHit[]>;
587
1062
  }
588
1063
 
589
1064
  /**
590
- * One tooltip row swatch, label, value, with the optional in-place highlight. Exposed so a custom
591
- * geom's `renderTooltip` can map `content.rows` itself and interleave its own content between rows,
592
- * rather than rendering the whole {@link TooltipBody} as an opaque block.
1065
+ * A nested SVG that establishes a `[0, 1]` unit coordinate space (top-left origin) stretched to fill
1066
+ * the geom panel. A layout geom paints its geometry in raw unit coords so paint and hit-test never re-project against each
1067
+ * other. `preserveAspectRatio="none"` maps the unit square onto the (usually non-square) panel exactly.
593
1068
  */
594
- export declare const TooltipRowView: ({ row, shouldHighlight }: TooltipRowViewProps) => JSX.Element;
1069
+ export declare const UnitSpaceSvg: ({ children, ...rest }: UnitSpaceSvgProps) => ReactNode;
595
1070
 
596
- declare interface TooltipRowViewProps {
597
- row: TooltipRow;
598
- shouldHighlight: boolean;
1071
+ declare interface UnitSpaceSvgProps extends Omit<SVGProps<SVGSVGElement>, 'viewBox' | 'preserveAspectRatio'> {
1072
+ children: ReactNode;
599
1073
  }
600
1074
 
601
- export declare const toPercent: (value: number) => string;
602
-
603
1075
  /**
604
- * Converts a normalized [0,1] x-coordinate to viewBox coordinate (identity transform).
605
- * Used inside nested SVGs with viewBox="0 0 1 1".
1076
+ * Subscribes to a derived slice of the compiled spec. The subscription only fires when the
1077
+ * selector's result changes by reference, so combined with the compiler's per-stage memoization
1078
+ * this skips re-renders whenever the selected slice is unchanged.
1079
+ *
1080
+ * @example
1081
+ * ```ts
1082
+ * const layers = useCompiledSelector((compiled) => compiled.layers);
1083
+ * const xAxis = useCompiledSelector((compiled) => compiled.guides.axes[0]);
1084
+ * ```
606
1085
  */
607
- export declare function toViewBoxX(normalized: number): number;
1086
+ export declare const useCompiledSelector: <Selected>(selector: (compiled: CompiledSpec) => Selected) => Selected;
608
1087
 
609
1088
  /**
610
- * Converts a normalized [0,1] y-coordinate to viewBox coordinate (Y-inverted).
611
- * SVG y=0 is at the top, but data y=0 is at the bottom, so we invert.
612
- * Used inside nested SVGs with viewBox="0 0 1 1".
1089
+ * Tracks an element's on-screen rect, so that a fixed-position overlay can sit exactly over it in pixel
1090
+ * space. `screenRect` is `null` until the first measurement lands, and drops back to `null` whenever the
1091
+ * element collapses to nothing.
1092
+ *
1093
+ * The element's own size changes go through the shared {@link useResizeObserver} lifecycle; this hook adds
1094
+ * the screen-position concerns the observer can't see — viewport scroll/resize move the element's screen
1095
+ * position without resizing it. Every trigger is coalesced into at most one `getBoundingClientRect` per
1096
+ * animation frame, so a burst of scroll events forces a single reflow rather than one per event. Only a
1097
+ * changed rect updates state, so a settled overlay does not re-render every frame.
613
1098
  */
614
- export declare function toViewBoxY(normalized: number): number;
1099
+ export declare function useElementScreenRect<T extends Element = SVGRectElement>(): {
1100
+ measureRef: (node: T | null) => void;
1101
+ screenRect: ScreenRect | null;
1102
+ };
615
1103
 
616
1104
  /**
617
- * An `<svg>` preset for painting geom-layout geometry in panel `[0,1]` unit space: `viewBox="0 0 1 1"`,
618
- * `preserveAspectRatio="none"` (so unit coordinates stretch to fill the panel exactly, the way a bar
619
- * chart's bars do), and `overflow: visible`.
1105
+ * Registers a layout geom's render-side hit tester so a `'render-hit-test'` layer inherits central hover
1106
+ * and the built-in tooltip — the path `bar` takes with no pointer overlay of its own. The pull path.
620
1107
  *
621
- * Chrome gotcha (documented once here, so geoms don't rediscover it): with a sub-unit `viewBox`, native
622
- * SVG fill hit-testing is unreliable at this scale — which is why geom-layout geoms answer a numeric
623
- * `useGeomHitTest` query instead of relying on pointer events landing on these shapes. Paint here; hit-test
624
- * through the engine.
1108
+ * The cursor passed to the tester is panel-local `[0, 1]`, top-left origin the frame the geom paints in.
625
1109
  */
626
- export declare const UnitSpaceSvg: ({ children, style, ...rest }: SVGProps<SVGSVGElement>) => ReactNode;
1110
+ export declare function useGeomHitTest(layerId: string, tester: RenderHitTester): void;
627
1111
 
628
1112
  /**
629
- * Returns an `index colour` resolver over the chart's default categorical palette the same
630
- * `DEFAULT_COLOR_PALETTE` the compiler bakes into a colour-encoded layer so a custom geom that
631
- * assigns its own colour indices draws from the chart's palette instead of inventing a private one.
632
- * Colours therefore match what a sibling layer's colour scale would produce, and follow the engine's
633
- * palette rather than a hardcoded array.
1113
+ * The push half of the render-hit-test keystone, for a geom whose geometry keeps changing after it is
1114
+ * drawn (a live simulation) and so owns its own pointer surface above the central capture layer. The
1115
+ * geom's pointer handlers call the returned setter with the hovered observation's `identityKey`; the
1116
+ * engine resolves it through the same `byKey` lookup the pull path uses, so the geom inherits the tooltip
1117
+ * and `renderHover`. Since the overlay intercepts the pointer events, the tooltip is anchored at the
1118
+ * supplied cursor. Pass `null` to clear. Geoms with geometry fixed once drawn use `useGeomHitTest`.
634
1119
  *
635
- * Out of scope (ADR-034 open question #4): declaring a colour *aesthetic* with its own scale + legend,
636
- * or honouring a per-spec custom palette. This primitive only ensures the colours match the default.
1120
+ * Escape hatch: an interactive geom should instead declare an overlay-hosted `render`
1121
+ * (`{ fn, options: { overlay: true } }`), which the renderer portals into a screen-aligned overlay and
1122
+ * hands a ready `pushHover` via `input.overlay`. Reach for this hook only when that form is not enough.
637
1123
  */
638
- export declare function useCategoricalColor(): (index: number) => string;
1124
+ export declare function useGeomHover(layerId: string): GeomHoverPush;
639
1125
 
640
1126
  /**
641
- * Subscribes to a derived slice of the compiled spec. The subscription only fires when the
642
- * selector's result changes by reference, so combined with the compiler's per-stage memoization
643
- * this skips re-renders whenever the selected slice is unchanged.
1127
+ * Returns the graph's command controls.
644
1128
  *
645
1129
  * @example
646
- * ```ts
647
- * const layers = useCompiledSelector((compiled) => compiled.layers);
648
- * const xAxis = useCompiledSelector((compiled) => compiled.guides.axes[0]);
1130
+ * ```tsx
1131
+ * const { dispatch, seal } = useGraphCommands();
1132
+ *
1133
+ * const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
1134
+ * const command = new SetAppearanceCornerRadiusCommand({ cornerRadius: event.target.valueAsNumber });
1135
+ * dispatch(command, { transient: true });
1136
+ * };
1137
+ *
1138
+ * const handlePointerUp = () => seal();
1139
+ *
1140
+ * <input type="range" onChange={handleChange} onPointerUp={handlePointerUp}
1141
+ * />
649
1142
  * ```
650
1143
  */
651
- export declare const useCompiledSelector: <Selected>(selector: (compiled: CompiledSpec) => Selected) => Selected;
1144
+ export declare const useGraphCommands: () => GraphCommands;
652
1145
 
653
1146
  /**
654
- * Registers a geom-layout geom's render-side hit-test for its layer, so the central hover engine routes
655
- * the cursor to it the geom inherits the built-in hover, tooltip, and (in time) accessibility
656
- * instead of hand-rolling a pointer overlay. The geom declares `spatialKind: 'render-hit-test'`; this
657
- * hook supplies the matching closure.
1147
+ * The graph a surface edits: the handle it was given, or one synthesized from the `<GraphProvider>`
1148
+ * it sits inside. An explicit handle wins, so a surface nested inside one chart can still edit
1149
+ * another. Returns `null` for a surface that is neither.
658
1150
  *
659
- * The `tester` receives the cursor in panel `[0, 1]` with a top-left origin (the frame the geom
660
- * paints in) and returns the declared identity key of the observation under it, or `null`. It may
661
- * close over freshly computed geometry each render — a stable wrapper reads the latest through a ref,
662
- * so the registration effect runs once per layer rather than every render.
1151
+ * A host writing its own editing UI needs this and nothing else from us, which is why it sits here
1152
+ * rather than beside the panel: reaching a chart is not itself an editing concern.
663
1153
  */
664
- export declare function useGeomHitTest(layerId: string, tester: PanelHitTester): void;
1154
+ export declare const useGraphHandle: (handle?: GraphHandle) => GraphHandle | null;
665
1155
 
666
1156
  /**
667
- * The push counterpart to `useGeomHitTest`, for a geom-layout geom that owns its own pointer surface a
668
- * live simulation whose interactive overlay intercepts pointer events before the central capture
669
- * layer sees them, so the engine cannot pull a hit. The geom already knows which observation is under
670
- * the cursor and pushes its declared identity key; the engine resolves it to the same unified
671
- * `HoverState` the pull path produces, and the cursor is forwarded so the central tooltip tracks.
1157
+ * Subscribes to the graph's undo history. Every command that reaches the chart a renderer's own
1158
+ * inline edit, a host panel's dispatch, an agent's streamed command is undoable through it.
672
1159
  *
673
- * Returns a stable `setHovered(key, cursor?)`: a key with the page-relative cursor opens the tooltip
674
- * on that observation; `null` clears it.
1160
+ * @example
1161
+ * ```tsx
1162
+ * const { undo, canUndo, undoDescription } = useGraphHistory();
1163
+ * <button disabled={!canUndo} title={undoDescription ?? undefined} onClick={undo}>Undo</button>
1164
+ * ```
675
1165
  */
676
- export declare function useGeomHover(layerId: string): (key: string | null, cursor?: TooltipCursor) => void;
677
-
678
- export declare const useGraphCommandDispatcher: () => ((command: Command) => void);
1166
+ export declare const useGraphHistory: () => GraphHistory;
679
1167
 
680
1168
  /**
681
- * Subscribe to a slice of the hover state from inside a `HoverProvider`.
1169
+ * Binds ⌘/Ctrl+Z, ⌘/Ctrl+Shift+Z and Ctrl+Y to a graph's undo history for as long as the calling
1170
+ * component is mounted, driving the {@link GraphHandle} a `<GraphProvider handleRef>` fills.
1171
+ *
1172
+ * Going through the ref rather than the context means it can be called from wherever the app's key
1173
+ * handling lives — typically above the provider, out of reach of the hooks.
1174
+ *
1175
+ * @example
1176
+ * ```tsx
1177
+ * const EditableChart = () => {
1178
+ * const handleRef = useRef<GraphHandle>(null);
1179
+ * useGraphHistoryShortcuts(handleRef);
1180
+ *
1181
+ * return (
1182
+ * <GraphProvider input={input} data={data} handleRef={handleRef}>
1183
+ * <GraphRenderer mode="editable" />
1184
+ * </GraphProvider>
1185
+ * );
1186
+ * };
1187
+ * ```
1188
+ *
1189
+ * A chord the app or an inline editor already handled is left alone, as is one typed into a text
1190
+ * control and one the chart declines — nothing to step, or an older spec the loaded data can no
1191
+ * longer render. An app-level undo sharing the page keeps those.
682
1192
  */
683
- export declare function useHoverState<T>(selector: (state: HoverStoreState) => T): T;
1193
+ export declare const useGraphHistoryShortcuts: (handleRef: RefObject<GraphHandle | null>, options?: GraphHistoryShortcutsOptions) => void;
684
1194
 
685
1195
  /**
686
- * Tracks the on-screen rect of the element the returned `ref` is attached to, across container resize and
687
- * page scroll. Only a live-simulation geom needs this (ADR-034 decision 4): it paints and hit-tests
688
- * render-side every frame, so its interactive overlay must sit above the chart in pixel space. Every other
689
- * geom-layout geom routes hover through the engine (decision 2) and never tracks a screen rect.
1196
+ * Subscribes to what the graph holds selected, from inside a `<GraphProvider>`. The store is
1197
+ * per-chart and shared with {@link GraphHandle}, so a surface outside the tree and one inside it
1198
+ * always agree on what is selected. A surface outside the tree writes through the handle's
1199
+ * `setSelection`; the canvas overlay writes to the same store off the context.
1200
+ *
1201
+ * @example
1202
+ * ```tsx
1203
+ * const selection = useGraphSelection();
1204
+ * const selected = selection.length === 1 ? selection[0] : null;
1205
+ * ```
690
1206
  */
691
- export declare function usePanelScreenRect(): {
692
- ref: RefCallback<Element>;
693
- rect: PanelScreenRect | null;
694
- };
1207
+ export declare const useGraphSelection: () => readonly EditTarget[];
695
1208
 
696
1209
  /**
697
- * Animates SVG path transitions using `d3-interpolate-path` for smooth morphing
698
- * when data points are added or removed (Framer Motion doesn't support this natively).
1210
+ * The graph's compiled spec, re-read whenever the graph changes, or `null` before its first
1211
+ * successful compile.
699
1212
  *
700
- * Based on: https://github.com/framer/motion/issues/451#issuecomment-761302824
1213
+ * Returns the whole spec: `useSyncExternalStore` compares snapshots by reference, so a selector
1214
+ * building a fresh object per call would re-render forever. Derive slices at the call site.
1215
+ */
1216
+ export declare const useHandleCompiled: (handle: GraphHandle) => CompiledSpec | null;
1217
+
1218
+ /**
1219
+ * Subscribe to a slice of the hover state from inside a `HoverProvider`.
701
1220
  */
702
- export declare const useSvgPathMorph: (pathDefinition: string, config: AnimationPlaybackOptions) => MotionValue<string>;
1221
+ export declare function useHoverState<T>(selector: (state: HoverStoreState) => T): T;
703
1222
 
1223
+ /** Returns the {@link TextMeasurer} from context; throws outside a `<TextMeasurerProvider>`. */
704
1224
  export declare const useTextMeasurer: () => TextMeasurer;
705
1225
 
1226
+ /** CSS custom-property references for every theme token; read at paint sites as `vars.tooltipBackground`, etc. */
706
1227
  export declare const vars: {
707
1228
  white: `var(--${string})`;
708
1229
  black: `var(--${string})`;
@@ -751,9 +1272,6 @@ export declare const vars: {
751
1272
  graphBackground: `var(--${string})`;
752
1273
  gridLineColor: `var(--${string})`;
753
1274
  originLineColor: `var(--${string})`;
754
- targetLineColor: `var(--${string})`;
755
- targetLineMarkerColor: `var(--${string})`;
756
- targetLineLabelTextColor: `var(--${string})`;
757
1275
  legendBackground: `var(--${string})`;
758
1276
  legendBorderColor: `var(--${string})`;
759
1277
  legendFocusOutlineColor: `var(--${string})`;
@@ -769,9 +1287,34 @@ export declare const vars: {
769
1287
  arrowAnnotationStickerOutlineColor: `var(--${string})`;
770
1288
  arrowAnnotationStickerOutlineColorInverse: `var(--${string})`;
771
1289
  annotationFrameBorderColor: `var(--${string})`;
772
- annotationMenuTriggerIconColor: `var(--${string})`;
1290
+ editMenuTriggerIconColor: `var(--${string})`;
773
1291
  heatmapEmptyTileBackground: `var(--${string})`;
774
1292
  dataLabelOutsideBackground: `var(--${string})`;
1293
+ dataLabelTextColor: `var(--${string})`;
1294
+ dataLabelInsideTextColor: `var(--${string})`;
1295
+ stackTotalTextColor: `var(--${string})`;
1296
+ stackTotalBackground: `var(--${string})`;
1297
+ stackTotalStroke: `var(--${string})`;
1298
+ gridLineWidth: `var(--${string})`;
1299
+ gridLineDash: `var(--${string})`;
1300
+ gridLineDotted: `var(--${string})`;
1301
+ tooltipBorderRadius: `var(--${string})`;
1302
+ tooltipBorderWidth: `var(--${string})`;
1303
+ tooltipPaddingBlock: `var(--${string})`;
1304
+ tooltipPaddingInline: `var(--${string})`;
1305
+ tooltipRowGap: `var(--${string})`;
1306
+ tooltipShadow: `var(--${string})`;
1307
+ editorControlHeight: `var(--${string})`;
1308
+ tickLabelOffset: `var(--${string})`;
1309
+ legendItemGap: `var(--${string})`;
1310
+ legendSwatchGap: `var(--${string})`;
1311
+ legendSwatchWidth: `var(--${string})`;
1312
+ legendSwatchHeight: `var(--${string})`;
1313
+ legendPillPaddingInline: `var(--${string})`;
1314
+ legendPillPaddingBlock: `var(--${string})`;
1315
+ legendPillBorderWidth: `var(--${string})`;
1316
+ headlineRowGap: `var(--${string})`;
1317
+ headlineItemGap: `var(--${string})`;
775
1318
  canvasDefault: `var(--${string})`;
776
1319
  canvasBlue: `var(--${string})`;
777
1320
  canvasCyan: `var(--${string})`;
@@ -800,6 +1343,7 @@ export declare const vars: {
800
1343
  zIndexToolbar: `var(--${string})`;
801
1344
  zIndexToolbarTooltip: `var(--${string})`;
802
1345
  zIndexToolbarPopover: `var(--${string})`;
1346
+ zIndexEditorPopover: `var(--${string})`;
803
1347
  toolbarBackgroundColor: `var(--${string})`;
804
1348
  toolbarButtonBackgroundColor: `var(--${string})`;
805
1349
  toolbarButtonBackgroundColorHovered: `var(--${string})`;
@@ -813,10 +1357,6 @@ export declare const vars: {
813
1357
  tooltipPrimaryRowColor: `var(--${string})`;
814
1358
  hoverGuideLineColor: `var(--${string})`;
815
1359
  hoverGuideFillColor: `var(--${string})`;
816
- hoveredBarBorderColor: `var(--${string})`;
817
- hoveredPointRingColor: `var(--${string})`;
818
- pointStrokeColor: `var(--${string})`;
819
- stackedBarHoverBorderColor: `var(--${string})`;
820
1360
  fontFamilyDefault: `var(--${string})`;
821
1361
  fontFamilyHeading: `var(--${string})`;
822
1362
  fontWeightRegular: `var(--${string})`;
@@ -850,6 +1390,7 @@ export declare const vars: {
850
1390
  fontAxisLabel: `var(--${string})`;
851
1391
  fontDataLabel: `var(--${string})`;
852
1392
  fontStackTotal: `var(--${string})`;
1393
+ fontCategoryLabel: `var(--${string})`;
853
1394
  fontLegendLabel: `var(--${string})`;
854
1395
  fontSeriesLabel: `var(--${string})`;
855
1396
  fontTooltipLabel: `var(--${string})`;
@@ -895,6 +1436,12 @@ export declare const vars: {
895
1436
  fontTextEditorLink: `var(--${string})`;
896
1437
  fontHighlightModeTitle: `var(--${string})`;
897
1438
  fontHighlightModeSubtitle: `var(--${string})`;
1439
+ fontEditorControlLabel: `var(--${string})`;
1440
+ fontEditorControlValue: `var(--${string})`;
1441
+ fontEditorSectionTitle: `var(--${string})`;
1442
+ fontEditorCaption: `var(--${string})`;
898
1443
  };
899
1444
 
1445
+ export { VizDiagnostic }
1446
+
900
1447
  export { }