@graphysdk/react-renderer 0.0.1-plugins.8 → 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,76 +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';
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';
34
55
  import { SVGProps } from 'react';
35
- import { SwatchShape } from '@graphysdk/viz-engine';
56
+ import { TextContent } from '@graphysdk/viz-engine';
36
57
  import { TextMeasurer } from '@graphysdk/viz-engine';
37
58
  import { TooltipContent } from '@graphysdk/viz-engine';
38
- import { TooltipRow } from '@graphysdk/viz-engine';
59
+ import { VizDiagnostic } from '@graphysdk/viz-engine';
39
60
 
40
61
  /**
41
- * The drawing surface a custom annotation paints into (ADR-035): a panel-positioned [0,1] frame.
42
- * `targets` are the resolved coordinates in **top-left [0,1]**; `area` carries the panel's pixel size
43
- * for the cases [0,1] cannot express (text extents, fixed-length leaders); `params` is the kind's typed
44
- * 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`.
45
65
  */
46
- export declare interface AnnotationDrawInput<TParams extends object = object> {
47
- targets: readonly ResolvedTarget[];
48
- area: {
49
- width: number;
50
- height: number;
51
- };
52
- params: TParams;
66
+ export declare interface AxisLabelSlotProps {
67
+ formattedAxes: FormattedAxis[];
68
+ labelRects: Partial<Record<LayoutEdge, Rect>>;
53
69
  }
54
70
 
55
71
  /**
56
- * A custom annotation's render half. `type` resolves it for a compiled annotation; `zOrder` is the
57
- * kind's default slot (background paints before geoms, foreground after) overridable per instance;
58
- * `draw` is a React render function that may reuse the render kit (theme tokens, `toPercent`, value
59
- * 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`.
60
75
  */
61
- export declare interface AnnotationPlugin {
62
- type: string;
63
- zOrder: AnnotationZOrder;
64
- draw: (input: AnnotationDrawInput) => ReactNode;
76
+ export declare interface AxisTicksSlotProps {
77
+ formattedAxes: FormattedAxis[];
78
+ tickRects: Partial<Record<LayoutEdge, Rect>>;
65
79
  }
66
80
 
67
- /** The render half an author supplies to {@link defineAnnotationRenderer} everything but `type`. */
68
- declare interface AnnotationRenderHalf<TParams extends object> {
69
- zOrder: AnnotationZOrder;
70
- 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';
71
87
  }
72
88
 
73
- 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';
74
96
 
75
97
  /**
76
98
  * Browser text measurer backed by OffscreenCanvas.
@@ -127,59 +149,79 @@ export declare class CanvasTextMeasurer implements TextMeasurer {
127
149
  }
128
150
 
129
151
  /**
130
- * Hover-overlay dot drawn at a non-primary hit's `(x, y)`: a single circle at reduced opacity
131
- * so it reads as secondary. Used for any companion hit same-layer sibling (e.g. another
132
- * 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.
133
155
  */
134
- export declare const CompanionDot: ({ hit }: CompanionDotProps) => JSX.Element;
135
-
136
- declare interface CompanionDotProps {
137
- hit: HoverHit;
138
- }
156
+ declare type CompiledLayerOf<G extends GeomName | string> = G extends GeomName ? CompiledLayerFor<G> : CompiledLayer;
139
157
 
140
- export declare type CoordKind = CoordSystem['type'];
158
+ declare type CoordKind = CoordSystem['type'];
141
159
 
142
160
  /** Maps a coord-kind discriminator to the corresponding `CoordSystem` member. */
143
161
  declare type CoordSystemFor<C extends CoordKind> = C extends 'cartesian' ? CartesianCoordSystem : C extends 'polar' ? PolarCoordSystem : never;
144
162
 
145
- export declare const darkTheme: string;
146
-
147
163
  /**
148
- * Binds a custom annotation's render half to its imported compile-half {@link AnnotationDef}: the kind
149
- * name (`def.type`) and the params type (`def.defaultParams`) are read from `def`, so the half never
150
- * re-declares them and cannot disagree with the definition the construction-level handshake mirroring
151
- * `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.
152
169
  */
153
- 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>;
154
171
 
155
172
  /**
156
- * Type-narrowing constructor for a plugin. Each plugin file declares its `(geom, coord)` pair and its
157
- * `highlightStrategy` inline; TypeScript infers `G`/`C`/`S` from the object literal — handlers then
158
- * receive `CompiledLayerFor<G>` / `CoordSystemFor<C>` with no `as` casts. Whether `getOverlayAnchor` is
159
- * required follows from the declared `highlightStrategy` via {@link GeomPluginForStrategy}. A custom geom
160
- * 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.
161
175
  *
162
- * Returns the broad `GeomPlugin` type (with the type parameters erased) so all plugins fit into one
163
- * registry. The cast inside this helper is safe because the registry routes every call to the plugin
164
- * 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.
165
179
  */
166
- export declare function defineGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams = PluginParams<G>>(plugin: GeomPluginForStrategy<G, C, S, TParams>): GeomPlugin;
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;
167
196
 
168
197
  /**
169
- * Binds a geom's render half to its imported compile-half {@link Geom} definition: the geom name
170
- * (`def.type`), the highlight strategy (`def.highlightStrategy`), and the params type
171
- * (`def.defaultParams`) are all read from `def`, so the half never re-declares them and cannot disagree
172
- * with the definition — the construction-level handshake. Whether `getOverlayAnchor` is required follows
173
- * from the def's declared `highlightStrategy`, and `render`/`renderHover` receive `layer.params` typed
174
- * from the def with no annotation. Built-in plugins keep {@link defineGeomPlugin}; reach for this only to
175
- * pair a custom geom's two halves.
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.
201
+ */
202
+ export declare const DefaultTooltip: ({ content }: TooltipSlotProps) => JSX.Element;
203
+
204
+ /**
205
+ * Dual-target renderer binding, keyed on whether the first argument is a compile definition or a built-in
206
+ * geom name:
176
207
  *
177
- * Returns the broad {@link GeomPlugin} (type parameters erased) so every plugin fits one registry; the
178
- * cast is the same variance bypass {@link defineGeomPlugin} performs, safe because the registry routes
179
- * 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.
180
217
  */
181
- 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
+ };
182
221
 
222
+ export declare function defineGeomRenderer<G extends GeomName>(geom: G, contract: GeomRenderContract): ResolvedGeomRenderer;
223
+
224
+ /** Dev-only compile-cache inspector; render inside a `<GraphProvider>`. See {@link DevToolsPanelProps}. */
183
225
  export declare const DevToolsPanel: ({ width, style, className }?: DevToolsPanelProps) => JSX.Element;
184
226
 
185
227
  /**
@@ -196,84 +238,320 @@ export declare interface DevToolsPanelProps {
196
238
  className?: string;
197
239
  }
198
240
 
199
- 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"];
200
250
 
201
251
  /**
202
- * Whether a region is large enough to carry a label: both dimensions must clear their minimum. Works in
203
- * any single consistent space — pass unit dimensions with unit thresholds, or pixels with pixel
204
- * thresholds. A space-filling geom (treemap) culls labels with it so a tile too small to fit text stays
205
- * 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.
206
254
  */
207
- 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
+ }
208
259
 
209
- /** A geom's declared highlight composition strategy, or `null` when it opts out of highlighting. */
210
- 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];
211
262
 
212
263
  /**
213
- * Self-contained module owning everything a single `(geom, coord)` composition needs to paint
214
- * and respond to hover. Plugins are dispatched via the registry keyed on `(geom, coord)`
215
- * the registry guarantees the input shapes match the plugin's identity.
216
- *
217
- * Generic by default: a plugin parameterised as `GeomPlugin<'bar', 'cartesian'>` declares
218
- * narrow input types so its handlers receive `CompiledLayerFor<'bar'>` /
219
- * `CartesianCoordSystem` directly, no runtime checks. Use {@link defineGeomPlugin} to build
220
- * one — the helper centralizes the variance cast so individual plugin files stay free of
221
- * `as` assertions.
222
- *
223
- * The public shape is a flat `NoOverlayAnchor | OverlayAnchor` union so call sites can
224
- * invoke `plugin.render(...)` against `CompiledLayerFor<GeomIdentity>` without each call site
225
- * having to discriminate. Per-plugin enforcement of `getOverlayAnchor` happens at the
226
- * {@link defineGeomPlugin} boundary via {@link GeomPluginForStrategy}, keyed on the plugin's own
227
- * 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.
228
267
  */
229
- 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
+ }
230
276
 
231
- declare interface GeomPluginBase<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> {
232
- 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. */
233
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;
234
369
  /**
235
- * The geom's highlight composition strategy the same value its compile-half declares. Drives the
236
- * `getOverlayAnchor` requirement at the {@link defineGeomPlugin} boundary, so the render contract is
237
- * derived from a declared strategy rather than a closed geom-name lookup.
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`.
238
373
  */
239
- highlightStrategy: S;
240
- guideMode: HoverGuideMode | null;
241
- render: (input: GeomRenderInput<G, C, TParams>) => ReactNode;
242
- renderHover: (input: HoverRenderInput<G, C, TParams>) => ReactNode;
243
- renderHoverCompanions: (input: HoverCompanionsRenderInput<G, TParams>) => ReactNode;
374
+ render: GeomRender<G, C>;
244
375
  /**
245
- * Optional: paints the tooltip body when one of this geom's observations is the primary hit. The
246
- * renderer keeps the positioned, themed card and calls this for its contents only; returning `null`
247
- * falls through to the default body. The input carries the engine-built {@link TooltipContent} (feed
248
- * it to `TooltipBody` to reproduce the default) and the raw `primary.observation`.
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.
249
381
  */
250
- renderTooltip?: (input: TooltipRenderInput<G, C, TParams>) => ReactNode;
382
+ renderHighlight?: (input: HighlightRenderInput<G, C>) => ReactNode;
383
+ renderHover: (input: HoverRenderInput<G, C>) => ReactNode;
384
+ renderHoverCompanions: (input: HoverCompanionsRenderInput<G>) => ReactNode;
385
+ /**
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.
393
+ */
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;
251
397
  }
252
398
 
253
399
  /**
254
- * Picks the plugin shape required for a plugin's own declared `highlightStrategy`. Only the
255
- * `'overlay-anchor'` strategy demands the anchor reader; `'observation-rerender'` and `null` (no
256
- * highlight) both resolve to {@link NoOverlayAnchorGeomPlugin}.
257
- *
258
- * Used by {@link defineGeomPlugin} to enforce the strategy→shape coupling at plugin-definition
259
- * sites — `observation-rerender` plugins cannot declare `getOverlayAnchor`, `overlay-anchor` ones must
260
- * — 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.
261
404
  */
262
- 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
+ }
263
409
 
264
- /**
265
- * The render half an author supplies to {@link defineGeomRenderer}: every field of a plugin except the
266
- * two its imported compile-half definition already declares — `geom` and `highlightStrategy`.
267
- * `getOverlayAnchor` is gated on the def's declared strategy via {@link GeomPluginForStrategy}, so the
268
- * render contract is bound to the definition rather than re-declared.
269
- */
270
- 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;
271
412
 
272
- export declare interface GeomRenderInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
273
- 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>;
274
418
  coordSystem: CoordSystemFor<C>;
275
419
  isAnimated: boolean;
276
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;
277
555
  }
278
556
 
279
557
  /**
@@ -284,27 +562,38 @@ export declare interface GeomRenderInput<G extends string = GeomName, C extends
284
562
  export declare type GraphMode = 'readonly' | 'editable';
285
563
 
286
564
  /**
287
- * Owns the compiled spec for a graph and exposes it via {@link useCompiledSpec}, plus a
288
- * `dispatch` for applying {@link Command}s.
565
+ * Owns the compiled spec for a graph and exposes it via {@link useCompiledSelector}, plus a
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.
289
569
  */
290
- export declare const GraphProvider: ({ data, input, formattingLocale, onChange, customPalettes, theme, themeOverrides: themeOverridesInput, geoms, plugins, annotationPlugins, id, children, }: GraphProviderProps) => JSX.Element;
570
+ export declare const GraphProvider: ({ data, input, plugins, formattingLocale, handleRef, onChange, onError, onWarnings, customPalettes, colorScheme, themeOverrides, children, }: GraphProviderProps) => JSX.Element;
291
571
 
292
- 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 {
293
574
  data: Data;
294
- 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[];
295
583
  formattingLocale?: Locale;
296
- onChange?: (next: CompilerInput) => void;
297
- 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;
298
595
  themeOverrides?: ThemeOverrides;
299
596
  customPalettes?: CustomPalettesInput;
300
- /** Custom geom definitions, injected per-instance into the compiler (override built-ins by name). */
301
- geoms?: readonly Geom[];
302
- /** Custom geom render plugins, injected per-instance (override built-ins by `(geom, coord)`). */
303
- plugins?: readonly GeomPlugin[];
304
- /** Custom annotation render plugins, injected per-instance and resolved by annotation `type` (ADR-035). */
305
- annotationPlugins?: readonly AnnotationPlugin[];
306
- /** Stable chart id. Stamped onto the rendered root as `data-graphy-id`; enables the editor bridge. */
307
- id?: string;
308
597
  children: ReactNode;
309
598
  }
310
599
 
@@ -314,23 +603,155 @@ declare interface GraphProviderProps {
314
603
  * no `config` prop: every consumer must wrap with `<GraphProvider>` so commands can operate
315
604
  * on the live spec.
316
605
  */
317
- export declare const GraphRenderer: (props: GraphRendererProps) => JSX.Element;
606
+ export declare const GraphRenderer: ({ slots, sizing, onResize, ...rest }: GraphRendererProps) => JSX.Element;
318
607
 
608
+ /** Props for {@link GraphRenderer}: container sizing, interaction toggles and per-region slot overrides. */
319
609
  export declare interface GraphRendererProps {
320
- width: number;
321
- height: number;
610
+ /** Controls how the graph responds to its container size. Defaults to filling the parent container. */
611
+ sizing?: GraphSizing;
612
+ /** Callback invoked when the graph's container is resized. Fires in every sizing mode. */
613
+ onResize?: ResizeObserverOnResize;
322
614
  isAnimated?: boolean;
615
+ animation?: GraphAnimationProps;
323
616
  showTooltips?: boolean;
324
617
  mode?: GraphMode;
618
+ /** Per-region component overrides. Unspecified regions render their default. */
619
+ slots?: GraphSlots;
620
+ }
621
+
622
+ /** Controls how the graph claims space in its container. */
623
+ export declare type GraphSizing = {
624
+ mode: 'responsive';
625
+ } | {
626
+ mode: 'fixed';
627
+ width: number;
628
+ height: number;
629
+ } | {
630
+ mode: 'keepAspectRatio';
631
+ intrinsicWidth: number;
632
+ intrinsicHeight: number;
633
+ } | {
634
+ mode: 'keepAspectRatio';
635
+ intrinsicWidth: number;
636
+ aspectRatio: number;
637
+ } | {
638
+ mode: 'keepAspectRatio';
639
+ intrinsicHeight: number;
640
+ aspectRatio: number;
641
+ };
642
+
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;
325
712
  }
326
713
 
327
- export declare interface HoverCompanionsRenderInput<G extends string = GeomName, TParams = PluginParams<G>> {
328
- layer: CompiledLayerFor<G, TParams>;
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>;
329
737
  primary: HoverHit;
330
738
  related: HoverHit[];
331
739
  }
332
740
 
333
- 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';
334
755
 
335
756
  /**
336
757
  * Hosts the hover store + engine for the surrounding `<GraphProvider>`. Subscribes only to the
@@ -343,8 +764,8 @@ declare interface HoverProviderProps {
343
764
  children: ReactNode;
344
765
  }
345
766
 
346
- export declare interface HoverRenderInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
347
- layer: CompiledLayerFor<G, TParams>;
767
+ declare interface HoverRenderInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
768
+ layer: CompiledLayerOf<G>;
348
769
  coordSystem: CoordSystemFor<C>;
349
770
  primary: HoverHit;
350
771
  group: HoverHit[];
@@ -355,41 +776,77 @@ export declare interface HoverRenderInput<G extends string = GeomName, C extends
355
776
  declare interface HoverSlice {
356
777
  hover: HoverState;
357
778
  /**
358
- * Whether the pointer is over the chart's interactive surface. Drives the tooltip's open/close at
359
- * the enter/leave boundary. Held as state (not a ref) because that transition must re-render the
360
- * tooltip; the cursor *position* stays a ref (see `HoverContextValue.cursorRef`) so pointer-rate
361
- * 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.
362
781
  */
363
- isPointerInside: boolean;
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.
786
+ */
787
+ pointerRegions: readonly PointerRegion[];
788
+ /** A gesture owns the pointer: the tracker runs no query, so nothing recomputes behind it. */
789
+ isPointerSuspended: boolean;
364
790
  }
365
791
 
366
792
  declare interface HoverStoreActions {
367
793
  setHoverState: (next: HoverState) => void;
368
- 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;
369
801
  }
370
802
 
803
+ /** State of the hover store: the current {@link HoverState} plus its setter. */
371
804
  declare type HoverStoreState = HoverSlice & HoverStoreActions;
372
805
 
373
- /** Lightens a hex colour by mixing it toward white by `amount` (`0` keeps the colour, `1` is white). */
374
- export declare function lighten(hex: string, amount: number): string;
806
+ /**
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.
810
+ */
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
+ }
375
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. */
376
842
  export declare const lightTheme: string;
377
843
 
378
844
  /**
379
- * Mixes a hex colour toward another by `amount` in `[0, 1]` (`0` keeps `hex`, `1` returns `towardHex`),
380
- * channel-wise in sRGB. Returns an `rgb(...)` string. Used for within-group shading where leaves share a
381
- * group hue and vary only in lightness.
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.
382
848
  */
383
- export declare function mixColor(hex: string, towardHex: string, amount: number): string;
384
-
385
- /**
386
- * Plugin shape for geoms that don't compute an overlay anchor — either `'observation-rerender'`
387
- * geoms (matched rows re-render in place) or geoms that opt out of highlighting entirely
388
- * (`null` strategy). The renderer never asks these plugins for an anchor, so the property is forbidden.
389
- */
390
- declare interface NoOverlayAnchorGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> extends GeomPluginBase<G, C, S, TParams> {
391
- getOverlayAnchor?: never;
392
- }
849
+ export declare type MeasuredFontTokenKey = 'fontDataLabel' | 'fontStackTotal' | 'fontCategoryLabel' | 'fontTickLabel' | 'fontAxisLabel' | 'fontLegendLabel' | 'fontGoalLineLabel';
393
850
 
394
851
  /**
395
852
  * Anchor point in normalized [0,1] coord-space where a highlight overlay marker
@@ -400,79 +857,140 @@ export declare interface OverlayAnchor {
400
857
  y: number;
401
858
  }
402
859
 
403
- declare interface OverlayAnchorGeomPlugin<G extends string, C extends CoordKind, S extends GeomHighlightStrategy, TParams> extends GeomPluginBase<G, C, S, TParams> {
404
- /** Returns the panel-space anchor for a matched observation. */
405
- getOverlayAnchor: (input: OverlayAnchorInput<G, C, TParams>) => OverlayAnchor | null;
406
- }
407
-
408
- export declare interface OverlayAnchorInput<G extends string = GeomName, C extends CoordKind = CoordKind, TParams = PluginParams<G>> {
409
- layer: CompiledLayerFor<G, TParams>;
860
+ declare interface OverlayAnchorInput<G extends GeomName | string = string, C extends CoordKind = CoordKind> {
861
+ layer: CompiledLayerOf<G>;
410
862
  coordSystem: CoordSystemFor<C>;
411
863
  observation: Observation;
412
864
  }
413
865
 
414
- export { PanelHitTester }
866
+ /**
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.
873
+ */
874
+ export declare const pruneSelection: (selection: readonly EditTarget[], spec: Spec) => readonly EditTarget[];
875
+
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 {
883
+ width: number;
884
+ height: number;
885
+ /** True until the first ResizeObserver measurement lands. */
886
+ isDefault: boolean;
887
+ }
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;
415
894
 
416
895
  /**
417
- * Portals an interactive overlay to `document.body`, fixed over the given panel rect at the documented
418
- * top `z-index`. Owns only the placement and chrome; the simulation canvas it wraps paints inside. Sized
419
- * to the rect with `pointerEvents: 'none'` so gaps fall through to the chart — the overlay's own marks
420
- * re-enable pointer events to capture drags.
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.
421
899
  */
422
- export declare function PanelOverlayPortal({ rect, children }: {
423
- rect: PanelScreenRect;
424
- children: ReactNode;
425
- }): ReactNode;
900
+ export declare interface ResolvedGeomRenderer extends GeomRenderContract {
901
+ /** The geom name this renderer paints — the resolver dispatches on it. */
902
+ geom: string;
903
+ }
426
904
 
427
- /** The panel's on-screen rectangle in page-relative pixelsthe frame a portalled overlay aligns to. */
428
- export declare interface PanelScreenRect {
905
+ /** An element's on-screen rect in client coordinateswhat a fixed-position overlay aligns to. */
906
+ export declare interface ScreenRect {
429
907
  left: number;
430
908
  top: number;
431
909
  width: number;
432
910
  height: number;
433
911
  }
434
912
 
435
- /**
436
- * Walks a kind-tagged geom-layout dataset (the output of `createMarkTable`), yielding each observation paired
437
- * with its discriminant `kind`. The render half switches on `kind` to build its per-kind marks, instead
438
- * of re-reading the kind column inline in every plugin. `Kind` narrows the discriminant to the geom's own
439
- * union (e.g. `'node' | 'link'`) for an exhaustive switch.
440
- */
441
- export declare function partitionByKind<Kind extends string = string>(data: Iterable<Observation>, kindColumn: string): Generator<{
442
- kind: Kind;
443
- observation: Observation;
444
- }>;
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
+ }
921
+
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;
928
+ }
445
929
 
446
- /** The compiled-layer params a plugin's handlers see built-ins default from the geom name; a custom geom supplies its own. */
447
- declare type PluginParams<G extends string> = CompiledLayerFor<G>['params'];
930
+ /** State of the selection store: what the graph holds selected plus the actions replacing it. */
931
+ export declare type SelectionStoreState = SelectionSlice & SelectionStoreActions;
448
932
 
449
933
  /**
450
- * Hover-overlay dot drawn at the primary hit's `(x, y)`: colored disc with a thicker white
451
- * border and an outer white ring offset by a 1px gap. Radius is data-driven so bubble charts
452
- * whose size channel carries data don't distort under hover.
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.
453
937
  */
454
- export declare const PrimaryDot: ({ hit, ...rest }: PrimaryDotProps) => JSX.Element;
455
-
456
- declare interface PrimaryDotProps {
457
- hit: HoverHit;
458
- 'data-testid'?: string;
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;
459
943
  }
460
944
 
461
- 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
+ }
462
960
 
463
961
  /**
464
- * 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)
465
971
  */
466
- export declare const Swatch: ({ shape, color, lineType, width, height, }: SwatchProps) => JSX.Element;
972
+ export declare type SwatchShape = 'square' | 'line' | 'circle' | 'area' | 'slice';
467
973
 
468
- 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 {
469
982
  shape: SwatchShape;
470
983
  color: string;
984
+ surface: SwatchSurface;
985
+ label?: string;
471
986
  lineType?: LineStyleType;
472
987
  width?: number;
473
988
  height?: number;
474
989
  }
475
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
+
476
994
  /**
477
995
  * Creates a font-ready, cached text measurer and shares it with descendants via context.
478
996
  * Renders nothing until the measurer resolves, so consumers reading the context always
@@ -497,171 +1015,215 @@ declare interface TextMeasurerProviderProps {
497
1015
  measurer?: TextMeasurer;
498
1016
  }
499
1017
 
1018
+ /** The name of a single theme token. */
500
1019
  export declare type ThemeKey = keyof ThemeValues;
501
1020
 
502
- 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
+ };
503
1029
 
504
- 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;
505
1032
 
506
1033
  declare interface ThemeProviderProps {
507
1034
  appearance: AppearanceConfig;
508
- theme: GraphTheme;
1035
+ colorScheme: ColorScheme;
509
1036
  themeOverrides?: ThemeOverrides;
510
1037
  children: ReactNode;
511
1038
  }
512
1039
 
1040
+ /** Every theme token mapped to its resolved CSS string value. */
513
1041
  export declare type ThemeValues = Record<keyof typeof vars, string>;
514
1042
 
515
1043
  /**
516
- * The default tooltip body the optional header and one row per hovered observation, with the
517
- * in-place primary-row highlight. Presentational and engine-decoupled: it renders a
518
- * {@link TooltipContent} and nothing else, so the built-in `Tooltip` and a custom geom's
519
- * `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.
520
1046
  */
521
- export declare const TooltipBody: ({ content }: TooltipBodyProps) => JSX.Element;
522
-
523
- declare interface TooltipBodyProps {
524
- content: TooltipContent;
525
- }
526
-
527
- /** Page-relative cursor position the tooltip anchors to. `null` when the pointer is outside the chart. */
528
- export declare interface TooltipCursor {
529
- clientX: number;
530
- 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;
531
1056
  }
532
1057
 
533
- /**
534
- * Input a plugin's {@link GeomPluginBase.renderTooltip} receives when one of its observations is the
535
- * primary hit. `content` is the body the built-in tooltip would render — feed it to `TooltipBody` to
536
- * reproduce the default — while `primary.observation` carries the geom's own columns for a bespoke
537
- * layout. The card around the body (positioning, theme) stays the renderer's.
538
- */
539
- 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. */
540
1061
  content: TooltipContent;
541
- layer: CompiledLayerFor<G, TParams>;
542
- coordSystem: CoordSystemFor<C>;
543
- primary: HoverHit;
544
- group: HoverHit[];
545
- related: ReadonlyMap<string, HoverHit[]>;
546
1062
  }
547
1063
 
548
1064
  /**
549
- * One tooltip row swatch, label, value, with the optional in-place highlight. Exposed so a custom
550
- * geom's `renderTooltip` can map `content.rows` itself and interleave its own content between rows,
551
- * 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.
552
1068
  */
553
- export declare const TooltipRowView: ({ row, shouldHighlight }: TooltipRowViewProps) => JSX.Element;
1069
+ export declare const UnitSpaceSvg: ({ children, ...rest }: UnitSpaceSvgProps) => ReactNode;
554
1070
 
555
- declare interface TooltipRowViewProps {
556
- row: TooltipRow;
557
- shouldHighlight: boolean;
1071
+ declare interface UnitSpaceSvgProps extends Omit<SVGProps<SVGSVGElement>, 'viewBox' | 'preserveAspectRatio'> {
1072
+ children: ReactNode;
558
1073
  }
559
1074
 
560
- export declare const toPercent: (value: number) => string;
561
-
562
1075
  /**
563
- * Converts a normalized [0,1] x-coordinate to viewBox coordinate (identity transform).
564
- * 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
+ * ```
565
1085
  */
566
- export declare function toViewBoxX(normalized: number): number;
1086
+ export declare const useCompiledSelector: <Selected>(selector: (compiled: CompiledSpec) => Selected) => Selected;
567
1087
 
568
1088
  /**
569
- * Converts a normalized [0,1] y-coordinate to viewBox coordinate (Y-inverted).
570
- * SVG y=0 is at the top, but data y=0 is at the bottom, so we invert.
571
- * 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.
572
1098
  */
573
- 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
+ };
574
1103
 
575
1104
  /**
576
- * An `<svg>` preset for painting geom-layout geometry in panel `[0,1]` unit space: `viewBox="0 0 1 1"`,
577
- * `preserveAspectRatio="none"` (so unit coordinates stretch to fill the panel exactly, the way a bar
578
- * 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.
579
1107
  *
580
- * Chrome gotcha (documented once here, so geoms don't rediscover it): with a sub-unit `viewBox`, native
581
- * SVG fill hit-testing is unreliable at this scale — which is why geom-layout geoms answer a numeric
582
- * `useGeomHitTest` query instead of relying on pointer events landing on these shapes. Paint here; hit-test
583
- * through the engine.
1108
+ * The cursor passed to the tester is panel-local `[0, 1]`, top-left origin the frame the geom paints in.
584
1109
  */
585
- export declare const UnitSpaceSvg: ({ children, style, ...rest }: SVGProps<SVGSVGElement>) => ReactNode;
1110
+ export declare function useGeomHitTest(layerId: string, tester: RenderHitTester): void;
586
1111
 
587
1112
  /**
588
- * Returns an `index colour` resolver over the chart's default categorical palette the same
589
- * `DEFAULT_COLOR_PALETTE` the compiler bakes into a colour-encoded layer so a custom geom that
590
- * assigns its own colour indices draws from the chart's palette instead of inventing a private one.
591
- * Colours therefore match what a sibling layer's colour scale would produce, and follow the engine's
592
- * 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`.
593
1119
  *
594
- * Out of scope (ADR-034 open question #4): declaring a colour *aesthetic* with its own scale + legend,
595
- * 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.
596
1123
  */
597
- export declare function useCategoricalColor(): (index: number) => string;
1124
+ export declare function useGeomHover(layerId: string): GeomHoverPush;
598
1125
 
599
1126
  /**
600
- * Subscribes to a derived slice of the compiled spec. The subscription only fires when the
601
- * selector's result changes by reference, so combined with the compiler's per-stage memoization
602
- * this skips re-renders whenever the selected slice is unchanged.
1127
+ * Returns the graph's command controls.
603
1128
  *
604
1129
  * @example
605
- * ```ts
606
- * const layers = useCompiledSelector((compiled) => compiled.layers);
607
- * 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
+ * />
608
1142
  * ```
609
1143
  */
610
- export declare const useCompiledSelector: <Selected>(selector: (compiled: CompiledSpec) => Selected) => Selected;
1144
+ export declare const useGraphCommands: () => GraphCommands;
611
1145
 
612
1146
  /**
613
- * Registers a geom-layout geom's render-side hit-test for its layer, so the central hover engine routes
614
- * the cursor to it the geom inherits the built-in hover, tooltip, and (in time) accessibility
615
- * instead of hand-rolling a pointer overlay. The geom declares `spatialKind: 'render-hit-test'`; this
616
- * 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.
617
1150
  *
618
- * The `tester` receives the cursor in panel `[0, 1]` with a top-left origin (the frame the geom
619
- * paints in) and returns the declared identity key of the observation under it, or `null`. It may
620
- * close over freshly computed geometry each render — a stable wrapper reads the latest through a ref,
621
- * 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.
622
1153
  */
623
- export declare function useGeomHitTest(layerId: string, tester: PanelHitTester): void;
1154
+ export declare const useGraphHandle: (handle?: GraphHandle) => GraphHandle | null;
624
1155
 
625
1156
  /**
626
- * The push counterpart to `useGeomHitTest`, for a geom-layout geom that owns its own pointer surface a
627
- * live simulation whose interactive overlay intercepts pointer events before the central capture
628
- * layer sees them, so the engine cannot pull a hit. The geom already knows which observation is under
629
- * the cursor and pushes its declared identity key; the engine resolves it to the same unified
630
- * `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.
631
1159
  *
632
- * Returns a stable `setHovered(key, cursor?)`: a key with the page-relative cursor opens the tooltip
633
- * 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
+ * ```
634
1165
  */
635
- export declare function useGeomHover(layerId: string): (key: string | null, cursor?: TooltipCursor) => void;
636
-
637
- export declare const useGraphCommandDispatcher: () => ((command: Command) => void);
1166
+ export declare const useGraphHistory: () => GraphHistory;
638
1167
 
639
1168
  /**
640
- * 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.
641
1192
  */
642
- export declare function useHoverState<T>(selector: (state: HoverStoreState) => T): T;
1193
+ export declare const useGraphHistoryShortcuts: (handleRef: RefObject<GraphHandle | null>, options?: GraphHistoryShortcutsOptions) => void;
643
1194
 
644
1195
  /**
645
- * Tracks the on-screen rect of the element the returned `ref` is attached to, across container resize and
646
- * page scroll. Only a live-simulation geom needs this (ADR-034 decision 4): it paints and hit-tests
647
- * render-side every frame, so its interactive overlay must sit above the chart in pixel space. Every other
648
- * 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
+ * ```
649
1206
  */
650
- export declare function usePanelScreenRect(): {
651
- ref: RefCallback<Element>;
652
- rect: PanelScreenRect | null;
653
- };
1207
+ export declare const useGraphSelection: () => readonly EditTarget[];
654
1208
 
655
1209
  /**
656
- * Animates SVG path transitions using `d3-interpolate-path` for smooth morphing
657
- * 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.
658
1212
  *
659
- * 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`.
660
1220
  */
661
- export declare const useSvgPathMorph: (pathDefinition: string, config: AnimationPlaybackOptions) => MotionValue<string>;
1221
+ export declare function useHoverState<T>(selector: (state: HoverStoreState) => T): T;
662
1222
 
1223
+ /** Returns the {@link TextMeasurer} from context; throws outside a `<TextMeasurerProvider>`. */
663
1224
  export declare const useTextMeasurer: () => TextMeasurer;
664
1225
 
1226
+ /** CSS custom-property references for every theme token; read at paint sites as `vars.tooltipBackground`, etc. */
665
1227
  export declare const vars: {
666
1228
  white: `var(--${string})`;
667
1229
  black: `var(--${string})`;
@@ -710,9 +1272,6 @@ export declare const vars: {
710
1272
  graphBackground: `var(--${string})`;
711
1273
  gridLineColor: `var(--${string})`;
712
1274
  originLineColor: `var(--${string})`;
713
- targetLineColor: `var(--${string})`;
714
- targetLineMarkerColor: `var(--${string})`;
715
- targetLineLabelTextColor: `var(--${string})`;
716
1275
  legendBackground: `var(--${string})`;
717
1276
  legendBorderColor: `var(--${string})`;
718
1277
  legendFocusOutlineColor: `var(--${string})`;
@@ -728,9 +1287,34 @@ export declare const vars: {
728
1287
  arrowAnnotationStickerOutlineColor: `var(--${string})`;
729
1288
  arrowAnnotationStickerOutlineColorInverse: `var(--${string})`;
730
1289
  annotationFrameBorderColor: `var(--${string})`;
731
- annotationMenuTriggerIconColor: `var(--${string})`;
1290
+ editMenuTriggerIconColor: `var(--${string})`;
732
1291
  heatmapEmptyTileBackground: `var(--${string})`;
733
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})`;
734
1318
  canvasDefault: `var(--${string})`;
735
1319
  canvasBlue: `var(--${string})`;
736
1320
  canvasCyan: `var(--${string})`;
@@ -759,6 +1343,7 @@ export declare const vars: {
759
1343
  zIndexToolbar: `var(--${string})`;
760
1344
  zIndexToolbarTooltip: `var(--${string})`;
761
1345
  zIndexToolbarPopover: `var(--${string})`;
1346
+ zIndexEditorPopover: `var(--${string})`;
762
1347
  toolbarBackgroundColor: `var(--${string})`;
763
1348
  toolbarButtonBackgroundColor: `var(--${string})`;
764
1349
  toolbarButtonBackgroundColorHovered: `var(--${string})`;
@@ -772,10 +1357,6 @@ export declare const vars: {
772
1357
  tooltipPrimaryRowColor: `var(--${string})`;
773
1358
  hoverGuideLineColor: `var(--${string})`;
774
1359
  hoverGuideFillColor: `var(--${string})`;
775
- hoveredBarBorderColor: `var(--${string})`;
776
- hoveredPointRingColor: `var(--${string})`;
777
- pointStrokeColor: `var(--${string})`;
778
- stackedBarHoverBorderColor: `var(--${string})`;
779
1360
  fontFamilyDefault: `var(--${string})`;
780
1361
  fontFamilyHeading: `var(--${string})`;
781
1362
  fontWeightRegular: `var(--${string})`;
@@ -809,6 +1390,7 @@ export declare const vars: {
809
1390
  fontAxisLabel: `var(--${string})`;
810
1391
  fontDataLabel: `var(--${string})`;
811
1392
  fontStackTotal: `var(--${string})`;
1393
+ fontCategoryLabel: `var(--${string})`;
812
1394
  fontLegendLabel: `var(--${string})`;
813
1395
  fontSeriesLabel: `var(--${string})`;
814
1396
  fontTooltipLabel: `var(--${string})`;
@@ -854,6 +1436,12 @@ export declare const vars: {
854
1436
  fontTextEditorLink: `var(--${string})`;
855
1437
  fontHighlightModeTitle: `var(--${string})`;
856
1438
  fontHighlightModeSubtitle: `var(--${string})`;
1439
+ fontEditorControlLabel: `var(--${string})`;
1440
+ fontEditorControlValue: `var(--${string})`;
1441
+ fontEditorSectionTitle: `var(--${string})`;
1442
+ fontEditorCaption: `var(--${string})`;
857
1443
  };
858
1444
 
1445
+ export { VizDiagnostic }
1446
+
859
1447
  export { }