insomni-plot 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE.md +674 -0
  2. package/README.md +81 -0
  3. package/dist/core.d.mts +340 -0
  4. package/dist/core.mjs +1047 -0
  5. package/dist/index.d.mts +3426 -0
  6. package/dist/index.mjs +12762 -0
  7. package/dist/interactions-DEFL_F4E.mjs +5395 -0
  8. package/dist/range-presets-CzECsu3V.d.mts +1523 -0
  9. package/package.json +34 -0
  10. package/src/annotations.d.ts +121 -0
  11. package/src/annotations.ts +438 -0
  12. package/src/axis.d.ts +184 -0
  13. package/src/axis.test.ts +131 -0
  14. package/src/axis.ts +765 -0
  15. package/src/colorbar.d.ts +69 -0
  16. package/src/colorbar.ts +294 -0
  17. package/src/colors.d.ts +57 -0
  18. package/src/colors.test.ts +28 -0
  19. package/src/colors.ts +486 -0
  20. package/src/core.ts +299 -0
  21. package/src/format.d.ts +54 -0
  22. package/src/format.ts +138 -0
  23. package/src/grammar/accessibility.d.ts +147 -0
  24. package/src/grammar/accessibility.test.ts +199 -0
  25. package/src/grammar/accessibility.ts +443 -0
  26. package/src/grammar/aes.d.ts +35 -0
  27. package/src/grammar/aes.test.ts +75 -0
  28. package/src/grammar/aes.ts +120 -0
  29. package/src/grammar/annotations.d.ts +86 -0
  30. package/src/grammar/annotations.test.ts +68 -0
  31. package/src/grammar/annotations.ts +336 -0
  32. package/src/grammar/attach-brush.d.ts +44 -0
  33. package/src/grammar/attach-brush.test.ts +214 -0
  34. package/src/grammar/attach-brush.ts +111 -0
  35. package/src/grammar/attach-presets.d.ts +33 -0
  36. package/src/grammar/attach-presets.test.ts +106 -0
  37. package/src/grammar/attach-presets.ts +215 -0
  38. package/src/grammar/chart.d.ts +952 -0
  39. package/src/grammar/chart.test.ts +118 -0
  40. package/src/grammar/chart.ts +1172 -0
  41. package/src/grammar/color-utils.d.ts +29 -0
  42. package/src/grammar/color-utils.test.ts +53 -0
  43. package/src/grammar/color-utils.ts +66 -0
  44. package/src/grammar/constants.d.ts +45 -0
  45. package/src/grammar/constants.ts +61 -0
  46. package/src/grammar/coord.d.ts +183 -0
  47. package/src/grammar/coord.test.ts +355 -0
  48. package/src/grammar/coord.ts +619 -0
  49. package/src/grammar/data/pivot.d.ts +57 -0
  50. package/src/grammar/data/pivot.ts +107 -0
  51. package/src/grammar/emphasis-driver.d.ts +69 -0
  52. package/src/grammar/emphasis-driver.test.ts +199 -0
  53. package/src/grammar/emphasis-driver.ts +205 -0
  54. package/src/grammar/equality.d.ts +3 -0
  55. package/src/grammar/equality.ts +40 -0
  56. package/src/grammar/facet.d.ts +63 -0
  57. package/src/grammar/facet.test.ts +60 -0
  58. package/src/grammar/facet.ts +175 -0
  59. package/src/grammar/geoms/_categorical.d.ts +94 -0
  60. package/src/grammar/geoms/_categorical.ts +0 -0
  61. package/src/grammar/geoms/_distribution.d.ts +52 -0
  62. package/src/grammar/geoms/_distribution.ts +125 -0
  63. package/src/grammar/geoms/_mark.d.ts +69 -0
  64. package/src/grammar/geoms/_mark.ts +136 -0
  65. package/src/grammar/geoms/_shape.d.ts +41 -0
  66. package/src/grammar/geoms/_shape.ts +74 -0
  67. package/src/grammar/geoms/aggregate.d.ts +95 -0
  68. package/src/grammar/geoms/aggregate.test.ts +554 -0
  69. package/src/grammar/geoms/aggregate.ts +840 -0
  70. package/src/grammar/geoms/area.d.ts +32 -0
  71. package/src/grammar/geoms/area.test.ts +165 -0
  72. package/src/grammar/geoms/area.ts +578 -0
  73. package/src/grammar/geoms/band.d.ts +27 -0
  74. package/src/grammar/geoms/band.test.ts +57 -0
  75. package/src/grammar/geoms/band.ts +126 -0
  76. package/src/grammar/geoms/bar.d.ts +56 -0
  77. package/src/grammar/geoms/bar.test.ts +367 -0
  78. package/src/grammar/geoms/bar.ts +1054 -0
  79. package/src/grammar/geoms/boxplot.d.ts +129 -0
  80. package/src/grammar/geoms/boxplot.test.ts +299 -0
  81. package/src/grammar/geoms/boxplot.ts +834 -0
  82. package/src/grammar/geoms/connected-scatter.d.ts +27 -0
  83. package/src/grammar/geoms/connected-scatter.test.ts +157 -0
  84. package/src/grammar/geoms/connected-scatter.ts +63 -0
  85. package/src/grammar/geoms/emphasis.d.ts +76 -0
  86. package/src/grammar/geoms/emphasis.test.ts +135 -0
  87. package/src/grammar/geoms/emphasis.ts +162 -0
  88. package/src/grammar/geoms/histogram.d.ts +75 -0
  89. package/src/grammar/geoms/histogram.test.ts +262 -0
  90. package/src/grammar/geoms/histogram.ts +740 -0
  91. package/src/grammar/geoms/index.d.ts +20 -0
  92. package/src/grammar/geoms/index.ts +77 -0
  93. package/src/grammar/geoms/interval.d.ts +31 -0
  94. package/src/grammar/geoms/interval.test.ts +154 -0
  95. package/src/grammar/geoms/interval.ts +342 -0
  96. package/src/grammar/geoms/line.d.ts +38 -0
  97. package/src/grammar/geoms/line.test.ts +247 -0
  98. package/src/grammar/geoms/line.ts +659 -0
  99. package/src/grammar/geoms/point.d.ts +57 -0
  100. package/src/grammar/geoms/point.test.ts +163 -0
  101. package/src/grammar/geoms/point.ts +545 -0
  102. package/src/grammar/geoms/polar.test.ts +216 -0
  103. package/src/grammar/geoms/ribbon.d.ts +21 -0
  104. package/src/grammar/geoms/ribbon.test.ts +170 -0
  105. package/src/grammar/geoms/ribbon.ts +87 -0
  106. package/src/grammar/geoms/ridgeline.d.ts +89 -0
  107. package/src/grammar/geoms/ridgeline.test.ts +247 -0
  108. package/src/grammar/geoms/ridgeline.ts +1164 -0
  109. package/src/grammar/geoms/rolling.d.ts +43 -0
  110. package/src/grammar/geoms/rolling.test.ts +217 -0
  111. package/src/grammar/geoms/rolling.ts +387 -0
  112. package/src/grammar/geoms/rug.d.ts +28 -0
  113. package/src/grammar/geoms/rug.test.ts +126 -0
  114. package/src/grammar/geoms/rug.ts +214 -0
  115. package/src/grammar/geoms/rule.d.ts +23 -0
  116. package/src/grammar/geoms/rule.test.ts +69 -0
  117. package/src/grammar/geoms/rule.ts +212 -0
  118. package/src/grammar/geoms/smooth.d.ts +54 -0
  119. package/src/grammar/geoms/smooth.test.ts +78 -0
  120. package/src/grammar/geoms/smooth.ts +337 -0
  121. package/src/grammar/geoms/text.d.ts +29 -0
  122. package/src/grammar/geoms/text.test.ts +64 -0
  123. package/src/grammar/geoms/text.ts +234 -0
  124. package/src/grammar/geoms/tile.d.ts +61 -0
  125. package/src/grammar/geoms/tile.test.ts +157 -0
  126. package/src/grammar/geoms/tile.ts +621 -0
  127. package/src/grammar/geoms/types.d.ts +319 -0
  128. package/src/grammar/geoms/types.ts +362 -0
  129. package/src/grammar/geoms/violin.d.ts +85 -0
  130. package/src/grammar/geoms/violin.test.ts +187 -0
  131. package/src/grammar/geoms/violin.ts +672 -0
  132. package/src/grammar/index.d.ts +22 -0
  133. package/src/grammar/index.ts +269 -0
  134. package/src/grammar/interactions/_disposable.d.ts +5 -0
  135. package/src/grammar/interactions/_disposable.ts +23 -0
  136. package/src/grammar/interactions/_z.d.ts +4 -0
  137. package/src/grammar/interactions/_z.ts +16 -0
  138. package/src/grammar/interactions/brush-selection.test.ts +262 -0
  139. package/src/grammar/interactions/brush.d.ts +63 -0
  140. package/src/grammar/interactions/brush.test.ts +483 -0
  141. package/src/grammar/interactions/brush.ts +452 -0
  142. package/src/grammar/interactions/crosshair.d.ts +19 -0
  143. package/src/grammar/interactions/crosshair.test.ts +127 -0
  144. package/src/grammar/interactions/crosshair.ts +76 -0
  145. package/src/grammar/interactions/hit-layer.d.ts +64 -0
  146. package/src/grammar/interactions/hit-layer.ts +246 -0
  147. package/src/grammar/interactions/legend.d.ts +19 -0
  148. package/src/grammar/interactions/legend.ts +101 -0
  149. package/src/grammar/interactions/menu.d.ts +93 -0
  150. package/src/grammar/interactions/menu.test.ts +373 -0
  151. package/src/grammar/interactions/menu.ts +342 -0
  152. package/src/grammar/interactions/selection.d.ts +25 -0
  153. package/src/grammar/interactions/selection.test.ts +289 -0
  154. package/src/grammar/interactions/selection.ts +142 -0
  155. package/src/grammar/interactions/series-readout.d.ts +91 -0
  156. package/src/grammar/interactions/series-readout.test.ts +668 -0
  157. package/src/grammar/interactions/series-readout.ts +422 -0
  158. package/src/grammar/interactions/series-snap.d.ts +70 -0
  159. package/src/grammar/interactions/series-snap.test.ts +214 -0
  160. package/src/grammar/interactions/series-snap.ts +218 -0
  161. package/src/grammar/interactions/tooltip-axis.test.ts +176 -0
  162. package/src/grammar/interactions/tooltip-touch.browser.test.ts +49 -0
  163. package/src/grammar/interactions/tooltip-touch.test.ts +161 -0
  164. package/src/grammar/interactions/tooltip.d.ts +140 -0
  165. package/src/grammar/interactions/tooltip.test.ts +406 -0
  166. package/src/grammar/interactions/tooltip.ts +622 -0
  167. package/src/grammar/interactions/transitions.d.ts +34 -0
  168. package/src/grammar/interactions/transitions.test.ts +172 -0
  169. package/src/grammar/interactions/transitions.ts +160 -0
  170. package/src/grammar/layout.d.ts +68 -0
  171. package/src/grammar/layout.ts +186 -0
  172. package/src/grammar/legend-merge.test.ts +332 -0
  173. package/src/grammar/mount.d.ts +78 -0
  174. package/src/grammar/mount.test.ts +479 -0
  175. package/src/grammar/mount.ts +2112 -0
  176. package/src/grammar/palettes.d.ts +54 -0
  177. package/src/grammar/palettes.test.ts +80 -0
  178. package/src/grammar/palettes.ts +167 -0
  179. package/src/grammar/pan-zoom.test.ts +398 -0
  180. package/src/grammar/phylo.d.ts +65 -0
  181. package/src/grammar/phylo.test.ts +59 -0
  182. package/src/grammar/phylo.ts +112 -0
  183. package/src/grammar/pipeline.auto-ticks.test.ts +40 -0
  184. package/src/grammar/pipeline.d.ts +158 -0
  185. package/src/grammar/pipeline.test.ts +463 -0
  186. package/src/grammar/pipeline.ts +1233 -0
  187. package/src/grammar/profiling.d.ts +8 -0
  188. package/src/grammar/profiling.ts +24 -0
  189. package/src/grammar/scales.d.ts +188 -0
  190. package/src/grammar/scales.test.ts +181 -0
  191. package/src/grammar/scales.ts +800 -0
  192. package/src/grammar/svg.d.ts +3 -0
  193. package/src/grammar/svg.ts +39 -0
  194. package/src/grammar/theme.d.ts +261 -0
  195. package/src/grammar/theme.test.ts +105 -0
  196. package/src/grammar/theme.ts +490 -0
  197. package/src/heatmap/cpu.ts +109 -0
  198. package/src/heatmap/gpu.ts +565 -0
  199. package/src/heatmap/types.ts +177 -0
  200. package/src/heatmap.browser.test.ts +308 -0
  201. package/src/heatmap.test.ts +320 -0
  202. package/src/heatmap.ts +123 -0
  203. package/src/index.d.ts +1 -0
  204. package/src/index.ts +8 -0
  205. package/src/interactions.d.ts +48 -0
  206. package/src/interactions.test.ts +226 -0
  207. package/src/interactions.ts +394 -0
  208. package/src/layout/box.d.ts +48 -0
  209. package/src/layout/box.test.ts +107 -0
  210. package/src/layout/box.ts +143 -0
  211. package/src/legend.d.ts +115 -0
  212. package/src/legend.ts +422 -0
  213. package/src/marks/curve.d.ts +43 -0
  214. package/src/marks/curve.ts +244 -0
  215. package/src/marks/stack.d.ts +53 -0
  216. package/src/marks/stack.ts +184 -0
  217. package/src/marks.d.ts +273 -0
  218. package/src/marks.test.ts +541 -0
  219. package/src/marks.ts +1292 -0
  220. package/src/navigator.test.ts +174 -0
  221. package/src/navigator.ts +393 -0
  222. package/src/range-presets.d.ts +113 -0
  223. package/src/range-presets.test.ts +345 -0
  224. package/src/range-presets.ts +349 -0
  225. package/src/scales.d.ts +98 -0
  226. package/src/scales.test.ts +103 -0
  227. package/src/scales.ts +695 -0
  228. package/src/stats/index.d.ts +200 -0
  229. package/src/stats/index.test.ts +349 -0
  230. package/src/stats/index.ts +740 -0
  231. package/src/stats/regression.d.ts +38 -0
  232. package/src/stats/regression.test.ts +56 -0
  233. package/src/stats/regression.ts +396 -0
  234. package/src/stats/rolling-window.d.ts +55 -0
  235. package/src/stats/rolling-window.test.ts +237 -0
  236. package/src/stats/rolling-window.ts +256 -0
  237. package/src/test-setup.ts +19 -0
  238. package/src/viewport/axis-state.d.ts +72 -0
  239. package/src/viewport/axis-state.ts +476 -0
  240. package/src/viewport.d.ts +170 -0
  241. package/src/viewport.test.ts +363 -0
  242. package/src/viewport.ts +510 -0
@@ -0,0 +1,1523 @@
1
+ import { BlendSpace, Color, Frame, FrameRect, GlyphAtlas, Group, Layer, LineCap, LineJoin, ResolvedCameraState, Vec2 } from "insomni";
2
+ import { CameraViewport, ViewportLike } from "insomni/viewport";
3
+
4
+ //#region src/scales.d.ts
5
+ type NumericRange = readonly [number, number];
6
+ type NumericDomain = readonly [number, number];
7
+ type DateDomain = readonly [Date, Date];
8
+ type TickFormatter<T> = (value: T) => string;
9
+ interface ContinuousScale {
10
+ (value: number): number;
11
+ readonly domain: NumericDomain;
12
+ readonly range: NumericRange;
13
+ invert(value: number): number;
14
+ ticks(count?: number): readonly number[];
15
+ tickFormat(count?: number): TickFormatter<number>;
16
+ }
17
+ interface TimeScale {
18
+ (value: Date): number;
19
+ readonly domain: DateDomain;
20
+ readonly range: NumericRange;
21
+ invert(value: number): Date;
22
+ ticks(count?: number): readonly Date[];
23
+ tickFormat(count?: number): TickFormatter<Date>;
24
+ }
25
+ interface BandScale<Domain> {
26
+ (value: Domain): number;
27
+ readonly domain: readonly Domain[];
28
+ readonly range: NumericRange;
29
+ bandwidth(): number;
30
+ step(): number;
31
+ ticks(): readonly Domain[];
32
+ tickFormat(): TickFormatter<Domain>;
33
+ }
34
+ type AxisScale<Value> = ContinuousScale | TimeScale | BandScale<Value>;
35
+ interface BandScaleOptions {
36
+ padding?: number;
37
+ paddingInner?: number;
38
+ paddingOuter?: number;
39
+ align?: number;
40
+ }
41
+ /**
42
+ * Time interval units accepted by {@link timeTicks} / {@link timeTickFormat}
43
+ * and by axis `ticks: { interval, step }` specs. `quarter` is sugar for
44
+ * `month` with step `3 × step`.
45
+ */
46
+ type TimeIntervalUnit = "millisecond" | "second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year";
47
+ declare function linearScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
48
+ declare function sqrtScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
49
+ declare function logScale(domain: NumericDomain, range: NumericRange): ContinuousScale;
50
+ declare function timeScale(domain: DateDomain, range: NumericRange): TimeScale;
51
+ /**
52
+ * Generate UTC tick dates at an explicit interval (e.g. every month, every
53
+ * 3 weeks, every quarter). Use this when the default `timeScale.ticks(count)`
54
+ * heuristic picks the wrong granularity for a known cadence. `step` defaults
55
+ * to `1`. `quarter` is sugar for `month, step: 3`.
56
+ */
57
+ declare function timeTicks(domain: DateDomain, unit: TimeIntervalUnit, step?: number): readonly Date[];
58
+ /**
59
+ * Tick formatter paired with {@link timeTicks}. The label shape is picked
60
+ * from the unit: years render `"2026"`, months `"Mar 2026"`, days `"Mar 14"`,
61
+ * etc. — matching the formatter used by {@link timeScale}.
62
+ */
63
+ declare function timeTickFormat(unit: TimeIntervalUnit, step?: number): TickFormatter<Date>;
64
+ declare function bandScale<Domain>(domain: readonly Domain[], range: NumericRange, options?: BandScaleOptions): BandScale<Domain>;
65
+ interface GroupedBandScale<Outer, Inner> {
66
+ /** Position (start) of the inner band within `outer`. */
67
+ (outer: Outer, inner: Inner): number;
68
+ /** Inner bandwidth — the rendered width of one inner bar. */
69
+ bandwidth(): number;
70
+ /** Inner step — distance between adjacent inner band starts. */
71
+ step(): number;
72
+ /** Outer band scale this is nested inside. */
73
+ readonly outer: BandScale<Outer>;
74
+ /** Ordered keys of the inner band scale. */
75
+ readonly innerKeys: readonly Inner[];
76
+ }
77
+ interface GroupedBandScaleOptions {
78
+ padding?: number;
79
+ paddingInner?: number;
80
+ paddingOuter?: number;
81
+ align?: number;
82
+ }
83
+ /**
84
+ * Build a nested band scale: each band of the `outer` scale is subdivided
85
+ * into one inner band per key. Returns a function `(outerKey, innerKey) →
86
+ * position` plus inner `bandwidth()` and `step()`.
87
+ *
88
+ * Use this for grouped bars / dot plots where each category has multiple
89
+ * series side by side. Outer scale handles category placement and
90
+ * spacing; inner band controls intra-group layout.
91
+ */
92
+ declare function groupedBandScale<Outer, Inner>(outer: BandScale<Outer>, innerKeys: readonly Inner[], options?: GroupedBandScaleOptions): GroupedBandScale<Outer, Inner>;
93
+ //#endregion
94
+ //#region src/axis.d.ts
95
+ /**
96
+ * Explicit time interval for axis ticks: render one tick per `step × unit`
97
+ * regardless of how many fit. `quarter` is sugar for `month, step: 3`. Pairs
98
+ * with {@link AxisOptions.ticks} on time scales — ignored on numeric / band
99
+ * scales (those fall back to a default tick count).
100
+ */
101
+ interface TickIntervalSpec {
102
+ interval: TimeIntervalUnit;
103
+ step?: number;
104
+ }
105
+ /**
106
+ * Tick generation strategy on continuous scales:
107
+ * - `number` — target count (current default behavior).
108
+ * - `"auto"` — derive count from the axis's pixel span (~80px/tick horizontal,
109
+ * ~50px/tick vertical). Cheap proxy for "as many ticks as comfortably fit."
110
+ * - `TickIntervalSpec` — explicit time interval (time scales only).
111
+ *
112
+ * `undefined` keeps the previous default of {@link DEFAULT_TICKS}.
113
+ */
114
+ type TickSpec = number | "auto" | TickIntervalSpec;
115
+ /**
116
+ * Loose tick formatter signature consumed by the grammar's `AxisSpec.format`.
117
+ * The runtime calls it with the active scale's tick value — `number` for
118
+ * linear/log/sqrt, `Date` for time, `string` for band scales. Typed loose so
119
+ * consumers can write `(v: number) => ...` or `(v: Date) => ...` directly
120
+ * without a wrapping cast; narrow inside the function to the type your scale
121
+ * produces.
122
+ */
123
+ type AxisTickFormatter = (value: any) => string;
124
+ interface AxisOrigin {
125
+ x?: number;
126
+ y?: number;
127
+ }
128
+ /**
129
+ * Configuration for axis tick labels. Currently only carries `maxWidth` —
130
+ * existing `labelFontSize` / `labelColor` / etc. live as flat keys on
131
+ * `AxisOptions` for back-compat. New per-label knobs go here.
132
+ */
133
+ interface AxisLabelConfig {
134
+ /**
135
+ * Maximum width in pixels for any tick label. Labels exceeding this width
136
+ * are truncated to the longest prefix that fits with a trailing ellipsis
137
+ * (`…`). Requires `atlas` to measure text. When unset, labels render at
138
+ * their natural width.
139
+ */
140
+ maxWidth?: number;
141
+ }
142
+ /**
143
+ * Axis title — accepts either a plain string or a config object. The object
144
+ * form lets callers cap the title's rendered width with an ellipsis.
145
+ */
146
+ type AxisTitle = string | {
147
+ text: string;
148
+ maxWidth?: number;
149
+ };
150
+ interface AxisOptions<Value> {
151
+ ticks?: TickSpec;
152
+ tickValues?: readonly Value[];
153
+ /**
154
+ * Render shorter, unlabeled minor tick marks between the major ticks.
155
+ * - Number `N`: subdivide each major interval into `N` parts (so `N=2`
156
+ * places one minor halfway between every adjacent major pair).
157
+ * - Array: explicit minor tick values, used as-is.
158
+ *
159
+ * Minor ticks never render labels and don't affect axis measurement.
160
+ */
161
+ minorTicks?: number | readonly Value[];
162
+ /** Length of minor tick marks. Default `tickSize / 2`. */
163
+ minorTickSize?: number;
164
+ /**
165
+ * Render a tick label only every `N`th major tick. Default `1` (every
166
+ * major). Use `2` to label every other tick, etc. The hidden ticks still
167
+ * draw their tick mark and grid line. The first tick is always labeled.
168
+ */
169
+ labelStep?: number;
170
+ /**
171
+ * Automatic label collision avoidance. `"auto"` measures the
172
+ * formatted tick labels and inflates `labelStep` until no two visible
173
+ * labels overlap (with a small padding). Off by default — opt in when
174
+ * the axis density is data-driven (dense band axes, time axes whose
175
+ * domain rescales on zoom). Falls back to `labelStep` when no atlas
176
+ * is available.
177
+ */
178
+ labelCollision?: "auto";
179
+ format?: TickFormatter<Value>;
180
+ atlas?: GlyphAtlas;
181
+ labels?: boolean;
182
+ tickSize?: number;
183
+ tickWidth?: number;
184
+ tickColor?: Color;
185
+ axisLine?: boolean;
186
+ axisLineWidth?: number;
187
+ axisLineColor?: Color;
188
+ /**
189
+ * Override the axis-line extent in pixels along the axis direction. Defaults
190
+ * to the scale's range. Pass `[0, plotFrame.width]` (or `.height`) to draw
191
+ * the spine across the full plot frame even when the data range is inset
192
+ * (framePadding / `prepareRange` reservations).
193
+ */
194
+ axisLineExtent?: readonly [number, number];
195
+ labelPadding?: number;
196
+ labelColor?: Color;
197
+ labelFontSize?: number;
198
+ labelFontFamily?: string;
199
+ labelFontWeight?: string;
200
+ labelFontStyle?: string;
201
+ /** Per-label configuration (currently `maxWidth` for ellipsis truncation). */
202
+ label?: AxisLabelConfig;
203
+ gridLines?: boolean;
204
+ gridLength?: number;
205
+ gridWidth?: number;
206
+ gridColor?: Color;
207
+ /**
208
+ * Axis title. Rendered upright alongside the axis — outside the tick
209
+ * labels for horizontal axes, above the axis line for vertical axes.
210
+ * Requires `atlas`. Skipped when undefined / empty. Pass an object form
211
+ * `{ text, maxWidth }` to cap the title width with ellipsis.
212
+ */
213
+ title?: AxisTitle;
214
+ titlePadding?: number;
215
+ titleFontSize?: number;
216
+ titleFontFamily?: string;
217
+ titleFontWeight?: string;
218
+ titleFontStyle?: string;
219
+ titleColor?: Color;
220
+ }
221
+ interface TruncateFontOptions {
222
+ fontSize: number;
223
+ fontFamily?: string;
224
+ fontWeight?: string;
225
+ fontStyle?: string;
226
+ }
227
+ /**
228
+ * Truncate `text` so its rendered width fits within `maxWidth`, appending an
229
+ * ellipsis when the original text is too wide. Returns the original text if
230
+ * it already fits or if measurement is impossible. Uses binary search over
231
+ * prefix lengths for O(log n) `measureText` calls.
232
+ */
233
+ declare function truncateToWidth(atlas: GlyphAtlas | undefined, text: string, maxWidth: number | undefined, font: TruncateFontOptions): string;
234
+ interface AxisMeasurement {
235
+ /**
236
+ * Total perpendicular space the axis needs. For horizontal axes this is
237
+ * tick size + label height + (title padding + title height when present),
238
+ * since the title sits below/above the labels. For vertical axes this is
239
+ * tick size + label width only — the title for vertical axes is rendered
240
+ * above the axis (parallel to the value range) and reported separately
241
+ * via `axialTitleOverhead` so callers can reserve vertical space for it.
242
+ */
243
+ readonly thickness: number;
244
+ /** Maximum measured width across all formatted tick labels. */
245
+ readonly maxLabelWidth: number;
246
+ /** Tick label height (font size). */
247
+ readonly labelHeight: number;
248
+ /** Whether a title is present and was measured. */
249
+ readonly hasTitle: boolean;
250
+ /** Measured title font size (0 if no title). */
251
+ readonly titleHeight: number;
252
+ /**
253
+ * Pixels the axis title needs *along* the value-range axis, on top of the
254
+ * axis itself. Non-zero only for vertical axes with a title (which is
255
+ * rendered above the axis line). Horizontal axes return 0 — their title's
256
+ * footprint is already included in `thickness`.
257
+ */
258
+ readonly axialTitleOverhead: number;
259
+ }
260
+ interface AxisBuilder<Value> {
261
+ addTo(layer: Layer, origin?: AxisOrigin): Layer;
262
+ /**
263
+ * Measure how much room the axis will occupy on its perpendicular axis.
264
+ * Use the result as `AXIS_MARGIN.bottom/left/...` so callers don't have to
265
+ * hand-tune padding for long tick labels or axis titles.
266
+ *
267
+ * Requires an atlas — pass either `options.atlas` at construction or one
268
+ * via the `atlas` argument here.
269
+ */
270
+ measure(atlas?: GlyphAtlas): AxisMeasurement;
271
+ readonly tickValues: readonly Value[];
272
+ }
273
+ declare function bottomAxis<Value>(scale: AxisScale<Value>, options?: AxisOptions<Value>): AxisBuilder<Value>;
274
+ declare function leftAxis<Value>(scale: AxisScale<Value>, options?: AxisOptions<Value>): AxisBuilder<Value>;
275
+ declare function topAxis<Value>(scale: AxisScale<Value>, options?: AxisOptions<Value>): AxisBuilder<Value>;
276
+ declare function rightAxis<Value>(scale: AxisScale<Value>, options?: AxisOptions<Value>): AxisBuilder<Value>;
277
+ //#endregion
278
+ //#region src/marks/curve.d.ts
279
+ /**
280
+ * Built-in interpolation modes between successive points:
281
+ * - `"linear"` — straight line (default).
282
+ * - `"step"` — horizontal then vertical at midpoint.
283
+ * - `"step-before"` — vertical then horizontal (value changes immediately).
284
+ * - `"step-after"` — horizontal then vertical (value held until next x).
285
+ * - `"monotone-x"` — Fritsch–Carlson monotone cubic. Smooth, no overshoot.
286
+ * Safe default for noisy series.
287
+ * - `"basis"` — uniform B-spline. Very smooth; clipped at endpoints.
288
+ * - `"catmull-rom"` — Catmull–Rom spline with tension 0.5 — passes through every point.
289
+ * - `"cardinal"` — Cardinal spline with tension 0.5 — looser, rounder curves.
290
+ *
291
+ * You can also pass a `CustomCurve` function for full control. See `defineCurve`.
292
+ */
293
+ type LineCurvePreset = "linear" | "step" | "step-before" | "step-after" | "monotone-x" | "basis" | "catmull-rom" | "cardinal";
294
+ /**
295
+ * Custom resampler. Receives the raw points and a sample-density hint
296
+ * (passed via `lineMark({ curveSamples })`), returns the polyline to draw.
297
+ * Endpoints should generally pass through `points[0]` and `points.at(-1)`.
298
+ */
299
+ type CustomCurve = (points: Vec2[], samples: number) => Vec2[];
300
+ type LineCurve = LineCurvePreset | CustomCurve;
301
+ /**
302
+ * Wrap a resampler function so it has a stable identity for use as a curve
303
+ * preset. The returned value is callable like a `CustomCurve` and may be
304
+ * passed wherever `LineCurve` is expected.
305
+ *
306
+ * ```ts
307
+ * const wobble = defineCurve((points, samples) => {
308
+ * // …emit your own polyline…
309
+ * });
310
+ * lineMark(data, { curve: wobble });
311
+ * ```
312
+ */
313
+ declare function defineCurve(resample: CustomCurve): CustomCurve;
314
+ /**
315
+ * Build a Catmull–Rom-family curve at a given tension.
316
+ * `tension = 0.5` is classic Catmull–Rom; `0` is a tighter spline; `1` is
317
+ * very loose. Useful for hand-drawn looking line charts.
318
+ */
319
+ declare function cardinalCurve(tension?: number): CustomCurve;
320
+ declare function resamplePoints(points: Vec2[], curve: LineCurve, samples: number): Vec2[];
321
+ //#endregion
322
+ //#region src/marks/stack.d.ts
323
+ interface StackSegment<T, K extends string = string> {
324
+ /** Original datum the segment came from. */
325
+ readonly datum: T;
326
+ /** Key of this segment within the datum (matches `keys[subIndex]`). */
327
+ readonly key: K;
328
+ /** Numeric value of this segment. */
329
+ readonly value: number;
330
+ /** Cumulative offset — where this segment starts (0 for the first key). */
331
+ readonly base: number;
332
+ /** `base + value` — where this segment ends. */
333
+ readonly top: number;
334
+ /** Index of this segment within its datum (0-based, same order as `keys`). */
335
+ readonly subIndex: number;
336
+ /** Index of the source datum in the input array. */
337
+ readonly datumIndex: number;
338
+ }
339
+ type StackOffset = "zero" | "expand" | "silhouette" | "wiggle";
340
+ type StackOrder = "input" | "ascending" | "descending" | "inside-out" | "appearance";
341
+ interface StackOptions<T, K extends string = string> {
342
+ /**
343
+ * Extract the numeric value for `key` from `datum`. Default: `datum[key]` —
344
+ * works when the data is a plain object keyed by string.
345
+ */
346
+ value?: (datum: T, key: K, datumIndex: number) => number;
347
+ /**
348
+ * Baseline offset:
349
+ * - `"zero"` (default) — bases at 0 going up.
350
+ * - `"expand"` — normalize each datum to `[0, 1]` (100% stacked).
351
+ * - `"silhouette"` — center the stack on 0 (`-rowSum/2 .. +rowSum/2`).
352
+ * - `"wiggle"` — minimize layer wiggle (Byron–Wattenberg streamgraph).
353
+ */
354
+ offset?: StackOffset;
355
+ /**
356
+ * Series ordering. Reorders the stack from inner-out:
357
+ * - `"input"` (default) — preserve `keys` order.
358
+ * - `"ascending"` — smallest total first.
359
+ * - `"descending"` — largest total first.
360
+ * - `"inside-out"` — largest in the middle (good for streamgraphs).
361
+ * - `"appearance"` — by index of each series' max value.
362
+ */
363
+ order?: StackOrder;
364
+ }
365
+ /**
366
+ * Flatten `data` into a list of `StackSegment`s — one per (datum, key) pair —
367
+ * with cumulative `base` / `top` offsets computed per datum. Supports
368
+ * `offset` (zero / expand / silhouette / wiggle) and `order` (input /
369
+ * ascending / descending / inside-out / appearance).
370
+ *
371
+ * Output order is `data[0].keys[0], data[0].keys[1], ..., data[1].keys[0],
372
+ * ...` — convenient for `barMark` / `areaMark`. `subIndex` reflects the
373
+ * post-`order` stacking position (0 = inner-most).
374
+ */
375
+ declare function stack<T, K extends string = string>(data: readonly T[], keys: readonly K[], options?: StackOptions<T, K>): StackSegment<T, K>[];
376
+ //#endregion
377
+ //#region src/marks.d.ts
378
+ interface MarkOrigin {
379
+ x?: number;
380
+ y?: number;
381
+ }
382
+ interface MarkBuilder {
383
+ addTo(layer: Layer, origin?: MarkOrigin): Layer;
384
+ readonly length: number;
385
+ }
386
+ type Accessor<T, V> = (datum: T, index: number) => V;
387
+ type ValueOrAccessor<T, V> = V | Accessor<T, V>;
388
+ /**
389
+ * Discrete marker glyphs for `pointMark`. The first four are filled solids;
390
+ * the `-open` variants render the same outline with no fill (caller must
391
+ * supply a stroke). `cross`, `plus`, and `star` are filled glyphs whose
392
+ * "arm" thickness scales with `radius`.
393
+ */
394
+ type PointShapeKind = "circle" | "square" | "triangle" | "diamond" | "circle-open" | "square-open" | "triangle-open" | "diamond-open" | "cross" | "plus" | "star";
395
+ /**
396
+ * Border treatment applied orthogonally to a point's shape kind.
397
+ *
398
+ * - `"solid"` (default): existing behavior — fill + optional solid stroke.
399
+ * - `"open"`: fill is suppressed; stroke uses `stroke ?? fill` as the color
400
+ * (equivalent to the `-open` shape variants, but composable with any kind).
401
+ * - `"dashed"` / `"dotted"`: fill is kept; the stroke is rendered as a polyline
402
+ * ring with a dash pattern. Required because SDF strokes don't support
403
+ * per-shape dashes; this routes the border through `tessellatePolyline`.
404
+ */
405
+ type PointBorderStyle = "solid" | "dashed" | "dotted" | "open";
406
+ /** Line-stroke dash treatment. Maps to polyline `dashPattern`. */
407
+ type LineDashStyle = "solid" | "dashed" | "dotted";
408
+ /** Default dash pattern emitted for `"dashed"` border/line styles. */
409
+ declare const DASHED_PATTERN: readonly number[];
410
+ /** Default dash pattern emitted for `"dotted"` border/line styles. */
411
+ declare const DOTTED_PATTERN: readonly number[];
412
+ interface PointMarkOptions<T> {
413
+ x: Accessor<T, number>;
414
+ y: Accessor<T, number>;
415
+ radius?: ValueOrAccessor<T, number>;
416
+ fill?: ValueOrAccessor<T, Color>;
417
+ stroke?: ValueOrAccessor<T, Color>;
418
+ strokeWidth?: ValueOrAccessor<T, number>;
419
+ shape?: ValueOrAccessor<T, PointShapeKind>;
420
+ /**
421
+ * Per-datum border treatment (`"solid"` | `"dashed"` | `"dotted"` | `"open"`).
422
+ * Composes with any shape kind; e.g. `shape: "circle", borderStyle: "open"`
423
+ * is equivalent to `shape: "circle-open"`. Dashed/dotted borders bypass the
424
+ * SDF shader (which has no dash support) and route through a polyline ring.
425
+ */
426
+ borderStyle?: ValueOrAccessor<T, PointBorderStyle>;
427
+ /**
428
+ * Optional secondary glyph drawn on top of the base shape at the same anchor.
429
+ * Pass `null` to skip overlay for a specific datum. The overlay's radius is
430
+ * `radius * overlayScale`; its color falls back to the base stroke (or fill
431
+ * if no stroke) so badges contrast against the base shape.
432
+ */
433
+ overlayGlyph?: ValueOrAccessor<T, PointShapeKind | null>;
434
+ /** Overlay radius as a fraction of the base radius. Default `0.6`. */
435
+ overlayScale?: ValueOrAccessor<T, number>;
436
+ /**
437
+ * Per-datum GPU emphasis key (P5-T3) applied to every primitive a point emits
438
+ * (base shape + overlay glyph). Used by boxplot/violin point overlays + mean
439
+ * markers to tag a whole entity's dots with the entity's key. `undefined` →
440
+ * untagged (no dim).
441
+ */
442
+ emphasisKey?: Accessor<T, number | undefined>;
443
+ group?: Group;
444
+ }
445
+ declare function pointMark<T>(data: readonly T[], options: PointMarkOptions<T>): MarkBuilder;
446
+ interface LineMarkOptions<T> {
447
+ x: Accessor<T, number>;
448
+ y: Accessor<T, number>;
449
+ stroke?: Color;
450
+ strokeWidth?: number;
451
+ curve?: LineCurve;
452
+ /** Resample density for smooth curves (`monotone-x`, `basis`). Default `16`. */
453
+ curveSamples?: number;
454
+ cap?: LineCap;
455
+ join?: LineJoin;
456
+ dashPattern?: readonly number[];
457
+ dashOffset?: number;
458
+ /**
459
+ * Categorical dash treatment. `"solid"` (default) leaves the stroke as a
460
+ * continuous line; `"dashed"` and `"dotted"` map to default dash patterns.
461
+ * `dashPattern` takes precedence when both are supplied.
462
+ */
463
+ dashStyle?: LineDashStyle;
464
+ defined?: Accessor<T, boolean>;
465
+ /**
466
+ * GPU emphasis key (P5-T3) applied to every polyline segment of this line.
467
+ * A whole line/series shares one key so it dims/un-dims together. `undefined`
468
+ * → untagged (no dim).
469
+ */
470
+ emphasisKey?: number;
471
+ group?: Group;
472
+ }
473
+ declare function lineMark<T>(data: readonly T[], options: LineMarkOptions<T>): MarkBuilder;
474
+ interface AreaMarkOptions<T> {
475
+ x: Accessor<T, number>;
476
+ y0: Accessor<T, number>;
477
+ y1: Accessor<T, number>;
478
+ fill?: Color;
479
+ stroke?: Color;
480
+ strokeWidth?: number;
481
+ defined?: Accessor<T, boolean>;
482
+ group?: Group;
483
+ }
484
+ declare function areaMark<T>(data: readonly T[], options: AreaMarkOptions<T>): MarkBuilder;
485
+ /**
486
+ * Bar border treatment — shared between `barMark`, `categoricalBarMark`,
487
+ * `stackedBarMark`, and `groupedBarMark`. Same routing as point borders:
488
+ * `"open"` suppresses fill; `"dashed"` / `"dotted"` emit a polyline ring
489
+ * around the rect because SDF rect strokes have no native dash support.
490
+ */
491
+ type BarBorderStyle = "solid" | "dashed" | "dotted" | "open";
492
+ interface BarMarkOptions<T> {
493
+ x: Accessor<T, number>;
494
+ y: Accessor<T, number>;
495
+ width: ValueOrAccessor<T, number>;
496
+ height: ValueOrAccessor<T, number>;
497
+ fill?: ValueOrAccessor<T, Color>;
498
+ stroke?: ValueOrAccessor<T, Color>;
499
+ strokeWidth?: ValueOrAccessor<T, number>;
500
+ cornerRadius?: ValueOrAccessor<T, number>;
501
+ rotation?: ValueOrAccessor<T, number>;
502
+ /** Per-datum border treatment. See {@link BarBorderStyle}. */
503
+ borderStyle?: ValueOrAccessor<T, BarBorderStyle>;
504
+ group?: Group;
505
+ }
506
+ declare function barMark<T>(data: readonly T[], options: BarMarkOptions<T>): MarkBuilder;
507
+ type BarOrientation = "y" | "x";
508
+ interface CategoricalBarMarkOptions<T, C> {
509
+ /**
510
+ * `"y"` (default) → vertical bars, value mapped to height.
511
+ * `"x"` → horizontal bars, value mapped to width.
512
+ */
513
+ orientation?: BarOrientation;
514
+ category: Accessor<T, C>;
515
+ value: Accessor<T, number>;
516
+ /** Band scale resolving categories to one of the layer's axes. */
517
+ categoryScale: BandScale<C>;
518
+ /** Continuous scale resolving values to the perpendicular axis. */
519
+ valueScale: ContinuousScale;
520
+ /** Baseline value (in domain units). Default `0`. */
521
+ baseline?: number;
522
+ fill?: ValueOrAccessor<T, Color>;
523
+ stroke?: ValueOrAccessor<T, Color>;
524
+ strokeWidth?: ValueOrAccessor<T, number>;
525
+ cornerRadius?: ValueOrAccessor<T, number>;
526
+ /** Per-datum border treatment. See {@link BarBorderStyle}. */
527
+ borderStyle?: ValueOrAccessor<T, BarBorderStyle>;
528
+ /** Per-datum GPU emphasis key (P5-T3). `undefined` → untagged (no dim). */
529
+ emphasisKey?: Accessor<T, number | undefined>;
530
+ group?: Group;
531
+ }
532
+ declare function categoricalBarMark<T, C>(data: readonly T[], options: CategoricalBarMarkOptions<T, C>): MarkBuilder;
533
+ interface StackedBarMarkOptions<T, C, K extends string> {
534
+ orientation?: BarOrientation;
535
+ category: Accessor<T, C>;
536
+ categoryScale: BandScale<C>;
537
+ valueScale: ContinuousScale;
538
+ /** Per-segment fill — typically `colorScale(palette, keys)` (a function of `K`). */
539
+ color: (key: K, segment: StackSegment<T, K>) => Color;
540
+ offset?: StackOffset;
541
+ order?: StackOrder;
542
+ value?: (datum: T, key: K, datumIndex: number) => number;
543
+ cornerRadius?: number;
544
+ stroke?: Color;
545
+ strokeWidth?: number;
546
+ /** Border treatment shared across every segment. See {@link BarBorderStyle}. */
547
+ borderStyle?: BarBorderStyle;
548
+ /** Per-segment GPU emphasis key (P5-T3). `undefined` → untagged (no dim). */
549
+ emphasisKey?: (key: K, segment: StackSegment<T, K>) => number | undefined;
550
+ group?: Group;
551
+ }
552
+ interface StackedMarkBuilder<T, K extends string> extends MarkBuilder {
553
+ /** Per-(datum, key) layout segments — useful for `valueLabelMark` over totals. */
554
+ readonly segments: readonly StackSegment<T, K>[];
555
+ }
556
+ declare function stackedBarMark<T, C, K extends string>(data: readonly T[], keys: readonly K[], options: StackedBarMarkOptions<T, C, K>): StackedMarkBuilder<T, K>;
557
+ interface GroupedBarMarkOptions<T, C, K extends string> {
558
+ orientation?: BarOrientation;
559
+ category: Accessor<T, C>;
560
+ groupedScale: GroupedBandScale<C, K>;
561
+ valueScale: ContinuousScale;
562
+ /** Per-key fill — typically `colorScale(palette, keys)`. */
563
+ color: (key: K) => Color;
564
+ /** Per-(datum, key) value resolver. Default `datum[key]`. */
565
+ value?: (datum: T, key: K, datumIndex: number) => number;
566
+ /** Domain-space baseline. Default `0`. */
567
+ baseline?: number;
568
+ cornerRadius?: number;
569
+ stroke?: Color;
570
+ strokeWidth?: number;
571
+ /** Border treatment shared across every bar in the grouped layout. */
572
+ borderStyle?: BarBorderStyle;
573
+ /** Per-(datum, key) GPU emphasis key (P5-T3). `undefined` → untagged. */
574
+ emphasisKey?: (datum: T, key: K, datumIndex: number) => number | undefined;
575
+ group?: Group;
576
+ }
577
+ declare function groupedBarMark<T, C, K extends string>(data: readonly T[], options: GroupedBarMarkOptions<T, C, K>): MarkBuilder;
578
+ interface StackedAreaMarkOptions<T, K extends string> {
579
+ /** Continuous x position resolver — already in layer-space pixels. */
580
+ x: Accessor<T, number>;
581
+ /** Continuous scale that maps stack base/top values to layer-space pixels. */
582
+ valueScale: ContinuousScale;
583
+ /** Per-series fill. */
584
+ color: (key: K) => Color;
585
+ offset?: StackOffset;
586
+ order?: StackOrder;
587
+ value?: (datum: T, key: K, datumIndex: number) => number;
588
+ defined?: Accessor<T, boolean>;
589
+ stroke?: Color;
590
+ strokeWidth?: number;
591
+ group?: Group;
592
+ }
593
+ declare function stackedAreaMark<T, K extends string>(data: readonly T[], keys: readonly K[], options: StackedAreaMarkOptions<T, K>): StackedMarkBuilder<T, K>;
594
+ //#endregion
595
+ //#region src/annotations.d.ts
596
+ type RuleAnchor = "start" | "middle" | "end";
597
+ interface RuleStyle {
598
+ stroke?: Color;
599
+ strokeWidth?: number;
600
+ dashPattern?: readonly number[];
601
+ dashOffset?: number;
602
+ }
603
+ interface RuleLabel {
604
+ label?: string;
605
+ /** Where the label sits along the rule. Default `"end"`. */
606
+ labelAnchor?: RuleAnchor;
607
+ /** Perpendicular offset from the rule (positive = away from the data). Default `6`. */
608
+ labelOffset?: number;
609
+ /** Tangential offset from the chosen anchor (positive = inwards). Default `6`. */
610
+ labelInset?: number;
611
+ labelColor?: Color;
612
+ fontSize?: number;
613
+ fontStyle?: string;
614
+ }
615
+ interface HorizontalRuleOptions extends RuleStyle, RuleLabel {
616
+ /** Value (in layer-space pixels, post-scale) of the horizontal rule. */
617
+ y: number;
618
+ x?: never;
619
+ /** Range of x covered. Required — typically `[0, plot.width]`. */
620
+ extent: readonly [number, number];
621
+ group?: Group;
622
+ }
623
+ interface VerticalRuleOptions extends RuleStyle, RuleLabel {
624
+ /** Value (in layer-space pixels, post-scale) of the vertical rule. */
625
+ x: number;
626
+ y?: never;
627
+ /** Range of y covered. Required — typically `[0, plot.height]`. */
628
+ extent: readonly [number, number];
629
+ group?: Group;
630
+ }
631
+ type RuleMarkOptions = HorizontalRuleOptions | VerticalRuleOptions;
632
+ declare function ruleMark(options: RuleMarkOptions): MarkBuilder;
633
+ interface BandStyle {
634
+ fill?: Color;
635
+ stroke?: Color;
636
+ strokeWidth?: number;
637
+ /** Draw the two perpendicular edge lines (uses `stroke`). Default `true` when `stroke` set. */
638
+ edges?: boolean;
639
+ }
640
+ interface BandLabel {
641
+ label?: string;
642
+ /** Where the label sits along the band. Default `"middle"`. */
643
+ labelAnchor?: RuleAnchor;
644
+ /** Perpendicular offset into the perpendicular extent (positive = inwards). Default `4`. */
645
+ labelOffset?: number;
646
+ labelColor?: Color;
647
+ fontSize?: number;
648
+ fontStyle?: string;
649
+ }
650
+ interface VerticalBandOptions extends BandStyle, BandLabel {
651
+ /** Range along the x axis (vertical band — fills between x0 and x1). */
652
+ x: readonly [number, number];
653
+ y?: never;
654
+ /** Required perpendicular extent (typically `[0, plot.height]`). */
655
+ extent: readonly [number, number];
656
+ group?: Group;
657
+ }
658
+ interface HorizontalBandOptions extends BandStyle, BandLabel {
659
+ /** Range along the y axis (horizontal band). */
660
+ y: readonly [number, number];
661
+ x?: never;
662
+ /** Required perpendicular extent (typically `[0, plot.width]`). */
663
+ extent: readonly [number, number];
664
+ group?: Group;
665
+ }
666
+ type BandMarkOptions = VerticalBandOptions | HorizontalBandOptions;
667
+ declare function bandMark(options: BandMarkOptions): MarkBuilder;
668
+ type ValueLabelAlign = "left" | "center" | "right";
669
+ /**
670
+ * Optional rounded-rectangle background drawn behind label text. When set
671
+ * (and an `atlas` is available for measurement), `valueLabelMark` measures
672
+ * each label and emits a `pushRect` underneath sized to the text bounds plus
673
+ * `padding`. The rect's anchor matches the label's `align` so the text stays
674
+ * visually centered on `(x, y) + offset`.
675
+ */
676
+ interface LabelBoxStyle {
677
+ fill?: Color;
678
+ stroke?: Color;
679
+ strokeWidth?: number;
680
+ /** Pixels added on each side. Number = uniform; object = per-edge. Default `4`. */
681
+ padding?: number | {
682
+ x?: number;
683
+ y?: number;
684
+ };
685
+ cornerRadius?: number;
686
+ }
687
+ interface ValueLabelMarkOptions<T> {
688
+ x: Accessor<T, number>;
689
+ y: Accessor<T, number>;
690
+ text: Accessor<T, string>;
691
+ /** Horizontal anchor of the text relative to (`x`,`y`). Default `"center"`. */
692
+ align?: ValueLabelAlign;
693
+ /** Pixel offset from (`x`,`y`). Default `{ x: 0, y: -4 }` (above). */
694
+ offset?: {
695
+ x?: number;
696
+ y?: number;
697
+ };
698
+ color?: ValueOrAccessor<T, Color>;
699
+ fontSize?: number;
700
+ fontStyle?: string;
701
+ /** Skip labels for which this returns false. */
702
+ filter?: Accessor<T, boolean>;
703
+ /** Optional rounded-rect background. */
704
+ box?: LabelBoxStyle;
705
+ /**
706
+ * Glyph atlas used to measure each label's true rendered width and height
707
+ * for `box` sizing. When omitted the box falls back to a coarse character-
708
+ * width estimate that visibly mismatches the glyphs at small font sizes.
709
+ */
710
+ atlas?: GlyphAtlas;
711
+ group?: Group;
712
+ }
713
+ declare function valueLabelMark<T>(data: readonly T[], options: ValueLabelMarkOptions<T>): MarkBuilder;
714
+ //#endregion
715
+ //#region src/layout/box.d.ts
716
+ interface MeasuredBox {
717
+ width: number;
718
+ height: number;
719
+ }
720
+ interface BoxOrigin {
721
+ x: number;
722
+ y: number;
723
+ }
724
+ /**
725
+ * A measure-then-place rectangle. Implementations must return a stable
726
+ * `measure()` (called by parents to size themselves) and an `addTo` that
727
+ * draws into a layer at the given top-left.
728
+ */
729
+ interface Placeable {
730
+ measure(): MeasuredBox;
731
+ addTo(layer: Layer, origin: BoxOrigin): void;
732
+ }
733
+ //#endregion
734
+ //#region src/legend.d.ts
735
+ interface PointSwatchSpec {
736
+ kind: "point";
737
+ fill?: Color;
738
+ stroke?: Color;
739
+ strokeWidth?: number;
740
+ shape?: PointShapeKind;
741
+ /** Default `5`. */
742
+ radius?: number;
743
+ /** Per-shape border treatment — matches the mark side. Default `"solid"`. */
744
+ borderStyle?: PointBorderStyle;
745
+ /** Optional secondary glyph drawn on the same anchor. `null`/undefined = none. */
746
+ overlayGlyph?: PointShapeKind | null;
747
+ /** Overlay radius as a fraction of `radius`. Default `0.6`. */
748
+ overlayScale?: number;
749
+ }
750
+ interface LineSwatchSpec {
751
+ kind: "line";
752
+ stroke: Color;
753
+ strokeWidth?: number;
754
+ dashPattern?: readonly number[];
755
+ /** Default `18`. */
756
+ width?: number;
757
+ }
758
+ interface BarSwatchSpec {
759
+ kind: "bar";
760
+ fill?: Color;
761
+ stroke?: Color;
762
+ strokeWidth?: number;
763
+ cornerRadius?: number;
764
+ /** Default `12`. */
765
+ size?: number;
766
+ }
767
+ interface AreaSwatchSpec {
768
+ kind: "area";
769
+ fill?: Color;
770
+ stroke?: Color;
771
+ strokeWidth?: number;
772
+ /** Default `14` (slight rectangle, not a square). */
773
+ width?: number;
774
+ /** Default `8`. */
775
+ height?: number;
776
+ }
777
+ type SwatchSpec = PointSwatchSpec | LineSwatchSpec | BarSwatchSpec | AreaSwatchSpec;
778
+ declare function pointSwatch(opts: Omit<PointSwatchSpec, "kind">): PointSwatchSpec;
779
+ declare function lineSwatch(opts: Omit<LineSwatchSpec, "kind">): LineSwatchSpec;
780
+ declare function barSwatch(opts: Omit<BarSwatchSpec, "kind">): BarSwatchSpec;
781
+ declare function areaSwatch(opts: Omit<AreaSwatchSpec, "kind">): AreaSwatchSpec;
782
+ interface LegendEntry {
783
+ label: string;
784
+ swatch: SwatchSpec;
785
+ /** Optional hint for interactivity layers to render the entry as "off". */
786
+ hidden?: boolean;
787
+ }
788
+ type LegendOrientation = "horizontal" | "vertical";
789
+ type LegendAlign = "start" | "end";
790
+ interface LegendOptions {
791
+ atlas: GlyphAtlas;
792
+ orientation?: LegendOrientation;
793
+ /** Anchor inside the layout box. Default `"start"` (left/top edge of box). */
794
+ align?: LegendAlign;
795
+ fontSize?: number;
796
+ fontStyle?: string;
797
+ labelColor?: Color;
798
+ /** Pixel gap between swatch and label. Default `6`. */
799
+ swatchGap?: number;
800
+ /** Pixel gap between entries. Default `14` (horizontal) or `4` (vertical). */
801
+ entryGap?: number;
802
+ /** Optional title rendered above the entries (left-aligned, bold). */
803
+ title?: string;
804
+ titleFontSize?: number;
805
+ titleColor?: Color;
806
+ /** Pixel gap between title and entries. Default `4`. */
807
+ titleGap?: number;
808
+ /** Alpha for hidden entries. Default `0.2`. */
809
+ dimAlpha?: number;
810
+ group?: Group;
811
+ }
812
+ interface LegendBuilder extends Placeable {
813
+ /** Number of entries (or, for color bars, palette samples). */
814
+ readonly length: number;
815
+ /** Total laid-out width and height in layer-space pixels. */
816
+ measure(): {
817
+ width: number;
818
+ height: number;
819
+ };
820
+ /** Bounding boxes of each entry, relative to the legend's top-left. */
821
+ getEntryBboxes(): (FrameRect & {
822
+ label: string;
823
+ })[];
824
+ }
825
+ /**
826
+ * Lay out a row (or column) of swatch + label pairs. Each mark contributes
827
+ * its own swatch shape via the `*Swatch(...)` helpers, so a dashed line shows
828
+ * a dashed swatch, a triangle point shows a triangle, etc.
829
+ *
830
+ * The legend is positioned by `origin` when added: `origin` is the *top-left*
831
+ * of the layout box. To right-align, measure first then offset:
832
+ *
833
+ * ```ts
834
+ * const lg = legend(entries, { atlas: a });
835
+ * const { width } = lg.measure();
836
+ * lg.addTo(layer, { x: outer.x + outer.width - width, y: outer.y });
837
+ * ```
838
+ */
839
+ declare function legend(entries: readonly LegendEntry[], options: LegendOptions): LegendBuilder;
840
+ //#endregion
841
+ //#region src/colors.d.ts
842
+ interface ContinuousPalette {
843
+ (t: number): Color;
844
+ readonly kind: "continuous";
845
+ readonly stops: readonly Color[];
846
+ /** Color space used to interpolate between adjacent stops. */
847
+ readonly blendSpace: BlendSpace;
848
+ /**
849
+ * Return a new palette with the same stops but interpolating in the given
850
+ * space. The original is unchanged. Cheap — palettes are stateless.
851
+ */
852
+ withBlendSpace(space: BlendSpace): ContinuousPalette;
853
+ }
854
+ interface CategoricalPalette {
855
+ (index: number): Color;
856
+ readonly kind: "categorical";
857
+ readonly colors: readonly Color[];
858
+ }
859
+ declare function colorScale(palette: ContinuousPalette, domain: readonly [number, number]): (value: number) => Color;
860
+ declare function colorScale<Domain>(palette: CategoricalPalette, domain: readonly Domain[]): (value: Domain) => Color;
861
+ declare const viridis: ContinuousPalette;
862
+ declare const plasma: ContinuousPalette;
863
+ declare const inferno: ContinuousPalette;
864
+ declare const magma: ContinuousPalette;
865
+ declare const cividis: ContinuousPalette;
866
+ declare const coolwarm: ContinuousPalette;
867
+ declare const rdbu: ContinuousPalette;
868
+ declare const spectral: ContinuousPalette;
869
+ declare const tableau10: CategoricalPalette;
870
+ declare const category10: CategoricalPalette;
871
+ declare const pastel: CategoricalPalette;
872
+ declare const set1: CategoricalPalette;
873
+ declare const set2: CategoricalPalette;
874
+ declare const set3: CategoricalPalette;
875
+ declare const dark2: CategoricalPalette;
876
+ declare const accent: CategoricalPalette;
877
+ declare const paired: CategoricalPalette;
878
+ declare const blues: ContinuousPalette;
879
+ declare const greens: ContinuousPalette;
880
+ declare const oranges: ContinuousPalette;
881
+ declare const reds: ContinuousPalette;
882
+ declare const purples: ContinuousPalette;
883
+ declare const greys: ContinuousPalette;
884
+ declare const brbg: ContinuousPalette;
885
+ declare const prgn: ContinuousPalette;
886
+ declare const piyg: ContinuousPalette;
887
+ declare const puor: ContinuousPalette;
888
+ /**
889
+ * Build a continuous palette from an arbitrary list of CSS hex stops.
890
+ * Stops are evenly spaced; sample with `palette(t)` where `t ∈ [0, 1]`.
891
+ */
892
+ declare function continuousPalette(hexStops: readonly string[]): ContinuousPalette;
893
+ /**
894
+ * Build a categorical palette from an arbitrary list of CSS hex colors.
895
+ * Indices wrap modulo length, so `palette(N)` always returns a valid color.
896
+ */
897
+ declare function categoricalPalette(hexColors: readonly string[]): CategoricalPalette;
898
+ //#endregion
899
+ //#region src/viewport/axis-state.d.ts
900
+ interface ContinuousAxisConfig {
901
+ type: "linear" | "log" | "sqrt";
902
+ domain: NumericDomain;
903
+ }
904
+ interface TimeAxisConfig {
905
+ type: "time";
906
+ domain: DateDomain;
907
+ }
908
+ interface BandAxisConfig<T = unknown> extends BandScaleOptions {
909
+ type: "band";
910
+ domain: readonly T[];
911
+ }
912
+ type ViewportAxisConfig<T = unknown> = ContinuousAxisConfig | TimeAxisConfig | BandAxisConfig<T>;
913
+ type VisibleDomainInput = readonly [number, number] | readonly [Date, Date];
914
+ interface Transform {
915
+ to: (v: number) => number;
916
+ from: (v: number) => number;
917
+ }
918
+ interface ContinuousAxisState {
919
+ kind: "continuous";
920
+ type: "linear" | "log" | "sqrt" | "time";
921
+ transform: Transform;
922
+ t0: number;
923
+ t1: number;
924
+ baseT0: number;
925
+ baseT1: number;
926
+ r0: number;
927
+ r1: number;
928
+ /**
929
+ * Memoized scale instance for the current (t0, t1, r0, r1, type, transform)
930
+ * tuple. Null after construction and after any mutation; populated lazily by
931
+ * `buildScale` / `applyAxis`. Pan/zoom/setVisibleDomain/clampAxisToBase/
932
+ * resetAxis/setAxisRange/copyContinuous all clear it via `invalidateScale`.
933
+ */
934
+ scaleCache: ContinuousScale | TimeScale | null;
935
+ }
936
+ interface BandAxisState<T> {
937
+ kind: "band";
938
+ domain: readonly T[];
939
+ options: BandScaleOptions;
940
+ r0: number;
941
+ r1: number;
942
+ /** See `ContinuousAxisState.scaleCache`. */
943
+ scaleCache: BandScale<T> | null;
944
+ }
945
+ type AxisState<T> = ContinuousAxisState | BandAxisState<T>;
946
+ //#endregion
947
+ //#region src/viewport.d.ts
948
+ /**
949
+ * Per-axis margin spec. A bare number applies to both axes. Values are
950
+ * fractions of the *visible* span and apply symmetrically to both edges.
951
+ */
952
+ type PanBoundsMargin = number | {
953
+ x?: number;
954
+ y?: number;
955
+ };
956
+ interface PanBoundsMargins {
957
+ /** Pan-past-content allowance when content is larger than the viewport. */
958
+ overshoot?: PanBoundsMargin;
959
+ /** Content drift allowance when content fits inside the viewport. */
960
+ drift?: PanBoundsMargin;
961
+ }
962
+ interface DataPanBoundsOptions extends PanBoundsMargins {}
963
+ interface DataViewportOptions<X = unknown, Y = unknown> {
964
+ /** Frame in pixel space (relative to parent if `parent` is supplied). */
965
+ frame: Frame;
966
+ /** Optional parent viewport — `frame` is interpreted in the parent's space. */
967
+ parent?: ViewportLike;
968
+ x: ViewportAxisConfig<X>;
969
+ y: ViewportAxisConfig<Y>;
970
+ /** Minimum domain-span multiplier (relative to base). Default: 0.001 */
971
+ minZoom?: number;
972
+ /** Maximum domain-span multiplier (relative to base). Default: 1000 */
973
+ maxZoom?: number;
974
+ /**
975
+ * Pan-bound the visible domain to the axis base domain (the initial domain
976
+ * passed in `x` / `y`). When zoomed in, panning stops at content edges
977
+ * (plus margin); when zoomed out far enough that content fits in view,
978
+ * the viewport cannot pan content off-screen. Zoom itself remains
979
+ * unrestricted past content — this only governs pan position.
980
+ */
981
+ panBounds?: DataPanBoundsOptions;
982
+ }
983
+ type ZoomFactor = number | {
984
+ x?: number;
985
+ y?: number;
986
+ };
987
+ interface LinearProjector {
988
+ /** sx = worldX * txSlope + txConst */
989
+ readonly txSlope: number;
990
+ readonly txConst: number;
991
+ /** sy = worldY * tySlope + tyConst */
992
+ readonly tySlope: number;
993
+ readonly tyConst: number;
994
+ }
995
+ interface DataViewport<X = unknown, Y = unknown> {
996
+ readonly mode: "data";
997
+ /** Frame relative to `parent` (or absolute if no parent). */
998
+ readonly frame: Frame;
999
+ /** Frame in root / canvas space — walks the parent chain on each read. */
1000
+ readonly absoluteFrame: Frame;
1001
+ /** Parent viewport, if this is a nested viewport. */
1002
+ readonly parent: ViewportLike | null;
1003
+ /** Mutable rect equal to `absoluteFrame`, refreshed in place on each access. */
1004
+ readonly clipRect: FrameRect;
1005
+ /** Identity camera (data-mode pan/zoom flows through axis scales). */
1006
+ readonly camera: ResolvedCameraState;
1007
+ readonly x: AxisScale<X>;
1008
+ readonly y: AxisScale<Y>;
1009
+ readonly visibleXDomain: NumericDomain | DateDomain | readonly X[];
1010
+ readonly visibleYDomain: NumericDomain | DateDomain | readonly Y[];
1011
+ /** Pan by pixel deltas in this viewport's frame space. */
1012
+ panBy(dxPx: number, dyPx: number): void;
1013
+ /** Zoom around a screen-pixel anchor in the viewport's absolute frame space. */
1014
+ zoomAt(anchorSx: number, anchorSy: number, factor: ZoomFactor): void;
1015
+ /** Reset to initial domain. */
1016
+ reset(): void;
1017
+ /**
1018
+ * Replace the frame (in the same space as the original frame).
1019
+ *
1020
+ * `reserve` (CSS-px insets) shrinks the axis pixel range without changing
1021
+ * the visible frame extent or `clipRect`. This is how the chart pipeline
1022
+ * tells the viewport about position-scale range reservations
1023
+ * (`framePadding` + per-geom `prepareRange`) so `dataToScreen` matches
1024
+ * where marks actually render. Defaults to zero insets — callers that
1025
+ * don't reserve any space (test viewports, manual mounts) can omit it.
1026
+ */
1027
+ setFrame(frame: Frame, reserve?: AxisRangeReserve): void;
1028
+ /** Convert a screen pixel to data coords. Returns null if outside the absolute frame. */
1029
+ screenToData(sx: number, sy: number): {
1030
+ x: X;
1031
+ y: Y;
1032
+ } | null;
1033
+ /** Convert data coords to screen pixels (absolute). */
1034
+ dataToScreen(dx: X, dy: Y): Vec2;
1035
+ /**
1036
+ * If both axes are linear continuous (not log, not time, not band), return a
1037
+ * pre-computed slope+intercept projector for use in hot loops. Returns null
1038
+ * for mixed or non-linear axis configurations. Recompute once per project
1039
+ * call; do not call per-entry.
1040
+ */
1041
+ getLinearProjector(): LinearProjector | null;
1042
+ /** Set the visible domain absolutely on one or both continuous axes. */
1043
+ setVisibleDomain(domains: {
1044
+ x?: VisibleDomainInput;
1045
+ y?: VisibleDomainInput;
1046
+ }): void;
1047
+ /** Replace the pan-bound config (or clear it with `null`). */
1048
+ setPanBounds(options: DataPanBoundsOptions | null): void;
1049
+ /** Subscribe to any view-state change. */
1050
+ onChange(cb: () => void): () => void;
1051
+ /** @internal */
1052
+ _state: DataInternal;
1053
+ }
1054
+ /** Anything that can be linked together — `linkViewports` ignores camera viewports. */
1055
+ type Viewport<X = unknown, Y = unknown> = DataViewport<X, Y> | CameraViewport;
1056
+ interface MutableFrameRect {
1057
+ x: number;
1058
+ y: number;
1059
+ width: number;
1060
+ height: number;
1061
+ }
1062
+ interface ResolvedAxisMargin {
1063
+ x: number;
1064
+ y: number;
1065
+ }
1066
+ interface ResolvedMargins {
1067
+ overshoot: ResolvedAxisMargin;
1068
+ drift: ResolvedAxisMargin;
1069
+ }
1070
+ interface DataInternal {
1071
+ mode: "data";
1072
+ frame: Frame;
1073
+ parent: ViewportLike | null;
1074
+ listeners: Set<() => void>;
1075
+ notifying: boolean;
1076
+ clipRect: MutableFrameRect;
1077
+ xAxis: AxisState<unknown>;
1078
+ yAxis: AxisState<unknown>;
1079
+ minZoom: number;
1080
+ maxZoom: number;
1081
+ panBoundsMargins: ResolvedMargins | null;
1082
+ rangeReserve: AxisRangeReserve;
1083
+ }
1084
+ /**
1085
+ * CSS-px insets that shrink the axis pixel range without changing the frame
1086
+ * extent. Mirrors the chart pipeline's `framePadding` + per-geom
1087
+ * `prepareRange` reservations so `dataToScreen` lines up with where marks
1088
+ * actually render.
1089
+ */
1090
+ interface AxisRangeReserve {
1091
+ readonly left: number;
1092
+ readonly right: number;
1093
+ readonly top: number;
1094
+ readonly bottom: number;
1095
+ }
1096
+ declare function createDataViewport<X = unknown, Y = unknown>(options: DataViewportOptions<X, Y>): DataViewport<X, Y>;
1097
+ interface LinkViewportsOptions {
1098
+ /** Sync X axis state. Default: true. */
1099
+ x?: boolean;
1100
+ /** Sync Y axis state. Default: false. */
1101
+ y?: boolean;
1102
+ }
1103
+ /**
1104
+ * Keep multiple data viewports in sync on selected axes. Camera viewports in
1105
+ * the array are skipped (link only makes sense between like-typed data axes).
1106
+ *
1107
+ * Any pan/zoom/reset on one data viewport propagates the current transformed-
1108
+ * domain endpoints to the others on the linked axes. Requires linked axes to
1109
+ * have matching `type` (e.g. both `linear`, or both `time`).
1110
+ */
1111
+ declare function linkViewports(viewports: readonly Viewport<any, any>[], options?: LinkViewportsOptions): () => void;
1112
+ //#endregion
1113
+ //#region src/stats/regression.d.ts
1114
+ /**
1115
+ * A fitted regression. `predict(x)` returns the mean-response estimate;
1116
+ * `seMean(x)` returns the standard error of that estimate (for confidence
1117
+ * bands). `summary` carries whatever fit-specific scalars are interesting
1118
+ * (slope, intercept, coefs, r²) — exposed for callers that want to render
1119
+ * an equation or stat label.
1120
+ */
1121
+ interface RegressionFit {
1122
+ readonly n: number;
1123
+ predict(x: number): number;
1124
+ /** Standard error of the mean response at `x`. Returns 0 when undefined. */
1125
+ seMean(x: number): number;
1126
+ readonly residualStdError: number;
1127
+ readonly summary: Readonly<Record<string, number | readonly number[]>>;
1128
+ }
1129
+ declare function linearFit(xs: readonly number[], ys: readonly number[]): RegressionFit | null;
1130
+ declare function polyFit(xs: readonly number[], ys: readonly number[], degree: number): RegressionFit | null;
1131
+ interface LoessOptions {
1132
+ /** Fraction of the sample used in each local fit. Default `0.5`. */
1133
+ span?: number;
1134
+ /** Degree of local polynomial: `1` (default) or `2`. */
1135
+ degree?: 1 | 2;
1136
+ }
1137
+ declare function loessFit(xs: readonly number[], ys: readonly number[], options?: LoessOptions): RegressionFit | null;
1138
+ interface ConfidenceBandPoint {
1139
+ x: number;
1140
+ yhat: number;
1141
+ lo: number;
1142
+ hi: number;
1143
+ }
1144
+ interface ConfidenceBandOptions {
1145
+ samples?: number;
1146
+ /** Confidence level. Default `0.95`. */
1147
+ level?: number;
1148
+ /** Override `[xmin, xmax]`. Defaults to data range. */
1149
+ domain?: readonly [number, number];
1150
+ }
1151
+ declare function confidenceBand(fit: RegressionFit, xs: readonly number[], options?: ConfidenceBandOptions): ConfidenceBandPoint[];
1152
+ //#endregion
1153
+ //#region src/stats/rolling-window.d.ts
1154
+ type RollingStatisticKind = "mean" | "median" | "min" | "max" | "sum" | "stddev";
1155
+ /**
1156
+ * Statistic to compute over each window. Strings cover the common cases;
1157
+ * `{ kind: 'quantile', p }` picks an arbitrary quantile (0..1); a function
1158
+ * receives the windowed `(values, items)` and returns any scalar — use it for
1159
+ * custom stats (mode, trimmed mean, sign of slope, …).
1160
+ */
1161
+ type RollingStatistic<T> = RollingStatisticKind | {
1162
+ kind: "quantile";
1163
+ p: number;
1164
+ } | ((values: readonly number[], items: readonly T[]) => number);
1165
+ /**
1166
+ * Window size.
1167
+ *
1168
+ * - A bare number is interpreted as `unit: 'count'` (k nearest items by axis
1169
+ * value, including self). Most predictable across axis types.
1170
+ * - `{ value, unit: 'domain' }` — sliding window of `value` data-axis units,
1171
+ * centered on the current item. For a time axis pass milliseconds (e.g.
1172
+ * `7 * 24 * 3_600_000` for a 7-day window). For a numeric axis the value is
1173
+ * in the axis's own units.
1174
+ * - `{ value, unit: 'count' }` — explicit count-window form.
1175
+ */
1176
+ type RollingWindow = number | {
1177
+ value: number;
1178
+ unit: "domain" | "count";
1179
+ };
1180
+ type RollingAxis = "x" | "y";
1181
+ interface RollingWindowOptions<T> {
1182
+ x: (datum: T, index: number) => number | Date;
1183
+ y: (datum: T, index: number) => number;
1184
+ window: RollingWindow;
1185
+ /** Default `"mean"`. */
1186
+ statistic?: RollingStatistic<T>;
1187
+ /**
1188
+ * Points failing the filter are excluded from every window *and* not
1189
+ * emitted on the output series — they vanish from both the aggregation
1190
+ * and the line that connects the results.
1191
+ */
1192
+ filter?: (datum: T, index: number) => boolean;
1193
+ /**
1194
+ * Axis the window slides along. Default `"x"` (slide on x, aggregate y);
1195
+ * `"y"` flips (slide on y, aggregate x). Output is sorted by the slide
1196
+ * axis so a `lineMark` connecting the points draws monotonically.
1197
+ */
1198
+ axis?: RollingAxis;
1199
+ }
1200
+ interface RollingPoint {
1201
+ x: number;
1202
+ y: number;
1203
+ /** Index into the original input `data` array. */
1204
+ sourceIndex: number;
1205
+ /** Number of points (after filter) that fell in this window. */
1206
+ count: number;
1207
+ }
1208
+ declare function rollingWindow<T>(data: readonly T[], options: RollingWindowOptions<T>): RollingPoint[];
1209
+ //#endregion
1210
+ //#region src/stats/index.d.ts
1211
+ type QuantileMethod = "type-7" | "type-1";
1212
+ /**
1213
+ * Quantile of an already-sorted ascending numeric array.
1214
+ *
1215
+ * - `"type-7"` (default): R's default — linear interpolation between order
1216
+ * stats with `h = (n - 1) * p`. The quantile method most users expect.
1217
+ * - `"type-1"`: inverse-of-empirical-CDF (step function). No interpolation.
1218
+ *
1219
+ * Returns `NaN` for empty input. Clamps `p` to `[0, 1]`.
1220
+ */
1221
+ declare function quantile(sorted: readonly number[], p: number, method?: QuantileMethod): number;
1222
+ type WhiskerRule = number | "minmax";
1223
+ interface BoxStats {
1224
+ /** Sample size (after filtering non-finite). */
1225
+ n: number;
1226
+ /** Smallest value in the sample. */
1227
+ min: number;
1228
+ /** Lower whisker endpoint (clamped to data range). */
1229
+ lowerWhisker: number;
1230
+ q1: number;
1231
+ median: number;
1232
+ q3: number;
1233
+ /** Upper whisker endpoint (clamped to data range). */
1234
+ upperWhisker: number;
1235
+ /** Largest value in the sample. */
1236
+ max: number;
1237
+ /** Q3 − Q1. */
1238
+ iqr: number;
1239
+ /** Values strictly outside the whiskers. Order preserves input order. */
1240
+ outliers: number[];
1241
+ /** Mean of the sample. Useful as an annotation; not used for box geometry. */
1242
+ mean: number;
1243
+ }
1244
+ interface BoxStatsOptions {
1245
+ /**
1246
+ * Whisker rule.
1247
+ *
1248
+ * - A number `k` (default `1.5`) — Tukey-style: whiskers extend to the most
1249
+ * extreme value within `k * IQR` of Q1/Q3. Anything beyond is an outlier.
1250
+ * - `"minmax"` — whiskers extend to the data min/max; no outliers.
1251
+ */
1252
+ whisker?: WhiskerRule;
1253
+ quantile?: QuantileMethod;
1254
+ }
1255
+ /**
1256
+ * Compute Tukey-style box statistics. Filters non-finite inputs.
1257
+ *
1258
+ * Returns `null` for empty input — caller decides how to render `n=0`.
1259
+ * For `n=1`, every quartile equals the single value and `outliers` is empty.
1260
+ */
1261
+ declare function boxStats(values: readonly number[], options?: BoxStatsOptions): BoxStats | null;
1262
+ type KdeKernel = "gaussian" | "epanechnikov";
1263
+ type KdeBandwidth = number | "silverman" | "scott";
1264
+ interface KdeOptions {
1265
+ /** Number of evaluation points. Default `64`, capped to `512`. */
1266
+ gridSize?: number;
1267
+ /**
1268
+ * Either a positive number (fixed bandwidth in data units) or a rule:
1269
+ * - `"silverman"` (default) — `1.06 · scale · n^(-1/5)` with the robust
1270
+ * scale `min(σ, IQR/1.34)`. Less sensitive to heavy tails / skew.
1271
+ * - `"scott"` — `1.06 · σ · n^(-1/5)` using the plain standard deviation
1272
+ * (no IQR robustification). Wider on skewed/heavy-tailed samples.
1273
+ */
1274
+ bandwidth?: KdeBandwidth;
1275
+ kernel?: KdeKernel;
1276
+ /**
1277
+ * Evaluate over `[min, max]` of the sample (`true`, default) or pad outward
1278
+ * by ~3 bandwidths so density tails reach near-zero (`false`).
1279
+ */
1280
+ trim?: boolean;
1281
+ /** Override the evaluation domain. Bypasses `trim`. */
1282
+ domain?: readonly [number, number];
1283
+ }
1284
+ interface KdeResult {
1285
+ /** Evaluation grid (length `gridSize`). */
1286
+ x: number[];
1287
+ /** Density at each grid point (length `gridSize`). */
1288
+ y: number[];
1289
+ /** Resolved bandwidth in data units. */
1290
+ bandwidth: number;
1291
+ }
1292
+ /**
1293
+ * Kernel density estimate.
1294
+ *
1295
+ * Returns `null` for empty input. For all-equal samples falls back to a tiny
1296
+ * bandwidth so callers don't divide by zero — the result is a narrow spike.
1297
+ */
1298
+ declare function kde(values: readonly number[], options?: KdeOptions): KdeResult | null;
1299
+ /**
1300
+ * Group an iterable into a `Map` keyed by `key(item)`. Insertion order
1301
+ * preserved per key; the map's key order is the order of first occurrence.
1302
+ */
1303
+ declare function groupBy<T, K>(items: Iterable<T>, key: (item: T, index: number) => K): Map<K, T[]>;
1304
+ type BinRule = "sturges" | "scott" | "fd" | "rice";
1305
+ type BinClosed = "left" | "right";
1306
+ interface BinBreaksOptions {
1307
+ /** Explicit bin count. Ignored if `binwidth` or `breaks` is set. */
1308
+ bins?: number;
1309
+ /** Explicit bin width in data units. Beats `bins` and `rule`. */
1310
+ binwidth?: number;
1311
+ /** Fully explicit edge array — bypasses rules entirely. */
1312
+ breaks?: readonly number[];
1313
+ /**
1314
+ * Rule used to pick a bin count when neither `bins`, `binwidth`, nor
1315
+ * `breaks` is provided.
1316
+ *
1317
+ * - `"sturges"` (default) — `⌈log₂ n⌉ + 1`. R / ggplot default.
1318
+ * - `"rice"` — `⌈2 · n^(1/3)⌉`. Slightly higher resolution.
1319
+ * - `"scott"` — `3.49 σ n^(-1/3)` width. Good for roughly normal data.
1320
+ * - `"fd"` — Freedman-Diaconis `2 · IQR · n^(-1/3)` width. Robust on skew.
1321
+ */
1322
+ rule?: BinRule;
1323
+ /** Clip edges to this. Default = `[min, max]` of data. */
1324
+ domain?: readonly [number, number];
1325
+ /** Round outer edges + step to nice numbers. Default `true`. */
1326
+ nice?: boolean;
1327
+ }
1328
+ interface BinResult {
1329
+ /** Inclusive lower edge for `closed: "left"` (the default). */
1330
+ x0: number;
1331
+ /** Exclusive upper edge for `closed: "left"`, except the final bin. */
1332
+ x1: number;
1333
+ /** Number of input values that fell in this bin. */
1334
+ count: number;
1335
+ /** `count / (n · width)`. Sums to 1 across all bins (∑ density · width). */
1336
+ density: number;
1337
+ }
1338
+ /**
1339
+ * How a histogram bin's `count` is converted into a y-axis value:
1340
+ * - `"count"` / `"frequency"`: raw `count`
1341
+ * - `"density"`: `count / (n · width)` — area sums to 1
1342
+ * - `"proportion"`: `count / n` — values sum to 1
1343
+ */
1344
+ type HistogramMeasure = "count" | "density" | "proportion" | "frequency";
1345
+ /**
1346
+ * Convert a `BinResult` to its measured value under `measure`. `n` is the
1347
+ * total sample count for the histogram (or per-group total for stacked /
1348
+ * faceted cases) — used to normalise `density` and `proportion`.
1349
+ */
1350
+ declare function histogramMeasureValue(bin: BinResult, n: number, measure: HistogramMeasure): number;
1351
+ interface BinOptions extends BinBreaksOptions {
1352
+ /**
1353
+ * Edge-closure convention. With `"left"` (default) bins are `[x0, x1)` and
1354
+ * the final bin includes the max as `[x_{k-1}, x_k]`. With `"right"` the
1355
+ * first bin is `[x_0, x_1]` and the rest are `(x_i, x_{i+1}]`.
1356
+ */
1357
+ closed?: BinClosed;
1358
+ }
1359
+ /**
1360
+ * Compute bin edges for the given data. Returns an array of length
1361
+ * `binCount + 1` describing the boundaries of each bin from left to right.
1362
+ *
1363
+ * Resolution order (highest precedence first): explicit `breaks` → `binwidth`
1364
+ * → `bins` → `rule` (default `"sturges"`).
1365
+ *
1366
+ * Returns `[]` for empty / all-non-finite input.
1367
+ */
1368
+ declare function binBreaks(values: readonly number[], options?: BinBreaksOptions): number[];
1369
+ /**
1370
+ * Bin a numeric sample into a sorted array of `BinResult`. Filters non-finite.
1371
+ *
1372
+ * Bins are half-open `[x0, x1)` by default (`closed: "left"`); the last bin
1373
+ * is closed on both ends so the maximum value is counted. With
1374
+ * `closed: "right"` the first bin is closed on both ends and the rest are
1375
+ * `(x0, x1]`.
1376
+ *
1377
+ * Values strictly outside the resolved domain are dropped.
1378
+ */
1379
+ declare function bin(values: readonly number[], options?: BinOptions): BinResult[];
1380
+ /**
1381
+ * Assign already-resolved values into known bin edges. Useful when the caller
1382
+ * has computed `breaks` once across multiple groups (so per-group histograms
1383
+ * align horizontally).
1384
+ */
1385
+ declare function binWithBreaks(values: readonly number[], breaks: readonly number[], closed?: BinClosed): BinResult[];
1386
+ interface BinByOptions extends BinOptions {
1387
+ /**
1388
+ * Use one shared edge array for every group (default `true`) so per-group
1389
+ * histograms align horizontally for stack/dodge/overlay. With `false`, each
1390
+ * group computes its own breaks against its own data.
1391
+ */
1392
+ sharedBreaks?: boolean;
1393
+ }
1394
+ interface BinByResult<K> {
1395
+ breaks: number[];
1396
+ groups: Map<K, BinResult[]>;
1397
+ }
1398
+ /**
1399
+ * Bin a flat numeric array partitioned by key. Group iteration order matches
1400
+ * first-occurrence order in the input.
1401
+ */
1402
+ declare function binBy<K>(values: readonly number[], keys: readonly K[], options?: BinByOptions): BinByResult<K>;
1403
+ /**
1404
+ * Deterministic uniform jitter offsets in `[-width/2, +width/2]`. Same
1405
+ * `(seed, count)` pair always produces the same sequence — important so that
1406
+ * jittered overlay points don't dance between renders.
1407
+ */
1408
+ declare function jitter(seed: number, count: number, width: number): number[];
1409
+ //#endregion
1410
+ //#region src/range-presets.d.ts
1411
+ /** Axis the controller drives. */
1412
+ type RangePresetAxis = "x" | "y";
1413
+ /**
1414
+ * Resolved domain a preset applies to the viewport. Numeric pair for linear /
1415
+ * log / score axes; Date pair for time axes.
1416
+ */
1417
+ type RangePresetDomain = NumericDomain | DateDomain;
1418
+ /** Optional getter form for the data-extent fallback. */
1419
+ type RangePresetDataDomain = RangePresetDomain | (() => RangePresetDomain);
1420
+ /**
1421
+ * Context passed to a preset's `resolve` function. `dataDomain` is the full
1422
+ * extent of the underlying data (either provided by the consumer at
1423
+ * construction time or, if omitted, the viewport's current visible domain
1424
+ * sampled at construction). `now` is the current time (epoch ms) — relevant
1425
+ * for time presets; helpers like `linearPreset` ignore it.
1426
+ */
1427
+ interface RangePresetContext {
1428
+ dataDomain: RangePresetDomain;
1429
+ now: number;
1430
+ }
1431
+ /**
1432
+ * A single preset entry. `resolve` returns the domain the viewport should be
1433
+ * set to when the user activates this key. Return `null` to express "this
1434
+ * preset has no valid domain given the current data" — the controller will
1435
+ * leave the viewport untouched and not mark itself active.
1436
+ */
1437
+ interface RangePreset {
1438
+ key: string;
1439
+ label: string;
1440
+ resolve: (ctx: RangePresetContext) => RangePresetDomain | null;
1441
+ }
1442
+ interface RangePresetsOptions {
1443
+ /**
1444
+ * Any `DataViewport` regardless of axis-value type — accepts the
1445
+ * `DataViewport<number, number>` returned by `MountedPlot.viewport` as
1446
+ * well as the unparameterized form. The controller only reads visible
1447
+ * domains and applies new ones through the viewport's continuous-axis
1448
+ * API, so X/Y value types are not constrained at this layer.
1449
+ */
1450
+ viewport: DataViewport<any, any>;
1451
+ axis: RangePresetAxis;
1452
+ presets: readonly RangePreset[];
1453
+ /**
1454
+ * Full data extent. Either a fixed `[min, max]` (numbers or Dates) or a
1455
+ * getter that re-reads on each preset evaluation. If omitted, the
1456
+ * controller snapshots the viewport's current visible domain at
1457
+ * construction time and reuses it as the data domain.
1458
+ */
1459
+ dataDomain?: RangePresetDataDomain;
1460
+ /** Defaults to `Date.now`. Re-evaluated on every `setActive`. */
1461
+ now?: () => number;
1462
+ /**
1463
+ * Relative tolerance for the diff-against-snapshot custom check. A change
1464
+ * smaller than `epsilon * max(|a|, |b|, 1)` on either endpoint is ignored.
1465
+ * Defaults to `1e-6` — tight enough to detect a one-pixel pan, loose
1466
+ * enough to absorb float round-trips through the axis scale.
1467
+ */
1468
+ epsilon?: number;
1469
+ }
1470
+ /** Subscriber receives the new active key, or `null` for "custom". */
1471
+ type RangePresetsSubscriber = (active: string | null) => void;
1472
+ interface RangePresetsController {
1473
+ readonly presets: readonly RangePreset[];
1474
+ /**
1475
+ * Activate a preset by key. Re-resolves the preset, applies the resulting
1476
+ * domain to the viewport, and remembers the resolved snapshot so future
1477
+ * `onChange` events can detect manual changes. Passing `null` clears the
1478
+ * active state without touching the viewport.
1479
+ */
1480
+ setActive(key: string | null): void;
1481
+ /** Currently active preset key, or `null` if user has panned/zoomed manually. */
1482
+ getActive(): string | null;
1483
+ /** Subscribe to active-state changes. Returns an unsubscribe fn. */
1484
+ subscribe(fn: RangePresetsSubscriber): () => void;
1485
+ dispose(): void;
1486
+ }
1487
+ declare function createRangePresets(opts: RangePresetsOptions): RangePresetsController;
1488
+ /**
1489
+ * Recognized time-preset keys. Each maps to a window relative to `now`
1490
+ * (except `MAX`, which uses the full data extent, and `YTD` which goes
1491
+ * from January 1 of the current year to `now`).
1492
+ */
1493
+ type TimePresetKey = "24H" | "7D" | "1M" | "3M" | "6M" | "1Y" | "YTD" | "MAX";
1494
+ /**
1495
+ * Time-axis preset. Resolves to a `DateDomain` ending at `now` (or the data
1496
+ * domain's end, for `MAX`). Built-in months use calendar-approximate
1497
+ * constants (30/90/182/365 days) — close enough for chart UI.
1498
+ */
1499
+ declare function timePreset(key: TimePresetKey, label?: string): RangePreset;
1500
+ /**
1501
+ * Linear / numeric preset showing the last `span` units of `dataDomain` —
1502
+ * i.e. `[max - span, max]`. Useful for "last 100m", "last 50 errors", etc.
1503
+ * `MAX`-style behavior comes from passing `Infinity` (will clamp to the
1504
+ * full data domain).
1505
+ */
1506
+ declare function linearPreset(opts: {
1507
+ key: string;
1508
+ label: string;
1509
+ span: number;
1510
+ }): RangePreset;
1511
+ /**
1512
+ * Log-axis preset showing the last `decades` decades of the data — i.e.
1513
+ * `[max / 10^decades, max]`, clamped to the data domain's lower bound.
1514
+ * The viewport itself stays linear-or-log depending on its scale config;
1515
+ * this helper just picks the domain endpoints.
1516
+ */
1517
+ declare function logPreset(opts: {
1518
+ key: string;
1519
+ label: string;
1520
+ decades: number;
1521
+ }): RangePreset;
1522
+ //#endregion
1523
+ export { polyFit as $, LineCurvePreset as $n, SwatchSpec as $t, binBreaks as A, MarkOrigin as An, groupedBandScale as Ar, pastel as At, RollingPoint as B, categoricalBarMark as Bn, set3 as Bt, KdeBandwidth as C, CategoricalBarMarkOptions as Cn, GroupedBandScaleOptions as Cr, dark2 as Ct, QuantileMethod as D, LineDashStyle as Dn, TimeIntervalUnit as Dr, magma as Dt, KdeResult as E, GroupedBarMarkOptions as En, TickFormatter as Er, inferno as Et, histogramMeasureValue as F, StackedBarMarkOptions as Fn, timeTickFormat as Fr, purples as Ft, rollingWindow as G, stackedBarMark as Gn, BarSwatchSpec as Gt, RollingStatisticKind as H, lineMark as Hn, tableau10 as Ht, jitter as I, StackedMarkBuilder as In, timeTicks as Ir, rdbu as It, LoessOptions as J, StackOrder as Jn, LegendEntry as Jt, ConfidenceBandOptions as K, StackOffset as Kn, LegendAlign as Kt, kde as L, ValueOrAccessor as Ln, reds as Lt, binWithBreaks as M, PointMarkOptions as Mn, logScale as Mr, plasma as Mt, boxStats as N, PointShapeKind as Nn, sqrtScale as Nr, prgn as Nt, WhiskerRule as O, LineMarkOptions as On, TimeScale as Or, oranges as Ot, groupBy as P, StackedAreaMarkOptions as Pn, timeScale as Pr, puor as Pt, loessFit as Q, LineCurve as Qn, PointSwatchSpec as Qt, quantile as R, areaMark as Rn, set1 as Rt, HistogramMeasure as S, BarOrientation as Sn, GroupedBandScale as Sr, coolwarm as St, KdeOptions as T, DOTTED_PATTERN as Tn, NumericRange as Tr, greys as Tt, RollingWindow as U, pointMark as Un, viridis as Ut, RollingStatistic as V, groupedBarMark as Vn, spectral as Vt, RollingWindowOptions as W, stackedAreaMark as Wn, AreaSwatchSpec as Wt, confidenceBand as X, stack as Xn, LegendOrientation as Xt, RegressionFit as Y, StackSegment as Yn, LegendOptions as Yt, linearFit as Z, CustomCurve as Zn, LineSwatchSpec as Zt, BinOptions as _, valueLabelMark as _n, AxisScale as _r, categoricalPalette as _t, RangePresetDomain as a, BandMarkOptions as an, AxisMeasurement as ar, createDataViewport as at, BoxStats as b, BarBorderStyle as bn, ContinuousScale as br, colorScale as bt, RangePresetsSubscriber as c, LabelBoxStyle as cn, AxisTickFormatter as cr, ContinuousAxisConfig as ct, linearPreset as d, ValueLabelAlign as dn, TickSpec as dr, VisibleDomainInput as dt, areaSwatch as en, cardinalCurve as er, DataPanBoundsOptions as et, logPreset as f, ValueLabelMarkOptions as fn, bottomAxis as fr, CategoricalPalette as ft, BinClosed as g, ruleMark as gn, truncateToWidth as gr, brbg as gt, BinByResult as h, bandMark as hn, topAxis as hr, blues as ht, RangePresetDataDomain as i, pointSwatch as in, AxisLabelConfig as ir, Viewport as it, binBy as j, PointBorderStyle as jn, linearScale as jr, piyg as jt, bin as k, MarkBuilder as kn, bandScale as kr, paired as kt, TimePresetKey as l, RuleAnchor as ln, AxisTitle as lr, TimeAxisConfig as lt, BinByOptions as m, VerticalRuleOptions as mn, rightAxis as mr, accent as mt, RangePresetAxis as n, legend as nn, resamplePoints as nr, DataViewportOptions as nt, RangePresetsController as o, HorizontalBandOptions as on, AxisOptions as or, linkViewports as ot, timePreset as p, VerticalBandOptions as pn, leftAxis as pr, ContinuousPalette as pt, ConfidenceBandPoint as q, StackOptions as qn, LegendBuilder as qt, RangePresetContext as r, lineSwatch as rn, AxisBuilder as rr, LinkViewportsOptions as rt, RangePresetsOptions as s, HorizontalRuleOptions as sn, AxisOrigin as sr, BandAxisConfig as st, RangePreset as t, barSwatch as tn, defineCurve as tr, DataViewport as tt, createRangePresets as u, RuleMarkOptions as un, TickIntervalSpec as ur, ViewportAxisConfig as ut, BinResult as v, Accessor as vn, BandScale as vr, category10 as vt, KdeKernel as w, DASHED_PATTERN as wn, NumericDomain as wr, greens as wt, BoxStatsOptions as x, BarMarkOptions as xn, DateDomain as xr, continuousPalette as xt, BinRule as y, AreaMarkOptions as yn, BandScaleOptions as yr, cividis as yt, RollingAxis as z, barMark as zn, set2 as zt };