@wick-charts/react 0.1.2 → 0.2.2

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
@@ -5,6 +5,70 @@ import { ReactNode } from 'react';
5
5
 
6
6
  export declare const andromeda: ThemePreset;
7
7
 
8
+ /**
9
+ * Chart-level animation configuration. Split into two independent domains so
10
+ * per-series defaults can't bleed into viewport-interaction timings:
11
+ *
12
+ * - `points` — animations applied to data: per-series entrance tween, live-
13
+ * tracking smoothing of the last candle/bar/line value, pulse cadence.
14
+ * These values act as defaults for each series. Per-series options always
15
+ * win unless the chart-level category is explicitly `false`, in which case
16
+ * the whole category is forced off.
17
+ *
18
+ * - `viewport` — animations applied to viewport interactions: post-gesture
19
+ * rebound duration and Y-axis range smoothing.
20
+ *
21
+ * User-initiated pan/zoom (wheel, drag, touch) always applies instantly. The
22
+ * latency of an exponential chase reads as laggy in practice. API-driven
23
+ * transitions (`fitToData`, `scrollToEnd`) and post-gesture rebound still
24
+ * tween via their own fixed-duration ease-out.
25
+ */
26
+ declare interface AnimationsConfig {
27
+ /**
28
+ * Data-series animations. `false` disables every point animation (entrance,
29
+ * live-smoothing, pulse) across every series. An object overrides individual
30
+ * categories; omitted fields fall back to the built-in defaults.
31
+ */
32
+ points?: false | {
33
+ /** Per-point entrance duration (ms). `false`/`0` disables. Default: 400. */
34
+ enterMs?: AnimationTime;
35
+ /**
36
+ * Exponential-smoothing time constant (ms) for live last-value chase.
37
+ * `false`/`0` disables smoothing — the last point snaps to the target.
38
+ * Default: 70 ms (equivalent to the legacy `liveSmoothRate = 14`).
39
+ */
40
+ smoothMs?: AnimationTime;
41
+ /** Pulse cycle period (ms) for the line last-point halo. Default: 600. */
42
+ pulseMs?: AnimationTime;
43
+ };
44
+ /**
45
+ * Viewport interaction animations. `false` disables both rebound and Y-axis
46
+ * smoothing — viewport changes snap instantly.
47
+ */
48
+ viewport?: false | {
49
+ /** Rebound (snap-back) duration after pan/zoom overshoot (ms). Default: 350. */
50
+ reboundMs?: AnimationTime;
51
+ /**
52
+ * Y-axis range transition scale. **Frame-count-based, calibrated at
53
+ * 60 Hz — not wall-clock ms.** Each render frame closes
54
+ * `min(1, 16 / yAxisMs)` of the remaining gap. Default 80 ≈ the
55
+ * legacy 0.2-per-frame closure. `false` / `0` snaps the Y range
56
+ * instantly. Unlike `smoothMs` / `entryMs` / `reboundMs`, the
57
+ * perceptual duration scales with frame time on refresh rates far
58
+ * from 60 Hz.
59
+ */
60
+ yAxisMs?: AnimationTime;
61
+ };
62
+ }
63
+
64
+ /** Options passed when creating a new {@link ChartInstance}. */
65
+ /**
66
+ * Time-value or boolean used throughout the animation API. `false` disables
67
+ * the category; a number configures its duration/time-constant in milliseconds
68
+ * (`0` also disables, useful when the caller wants a number shape).
69
+ */
70
+ declare type AnimationTime = number | false;
71
+
8
72
  /**
9
73
  * Defines a bound for the Y axis.
10
74
  *
@@ -23,7 +87,17 @@ export declare interface AxisConfig {
23
87
 
24
88
  export declare const ayuMirage: ThemePreset;
25
89
 
26
- export declare function BarSeries({ data, options, label, id: idProp, onSeriesId }: BarSeriesProps): null;
90
+ /**
91
+ * Entrance animation styles for bars.
92
+ * - `'none'` — no animation.
93
+ * - `'fade'` — opacity 0→1.
94
+ * - `'grow'` — height grows from baseline.
95
+ * - `'slide'` — translates in from the right with a fade.
96
+ * - `'fade-grow'` *(default)* — fade + grow combined.
97
+ */
98
+ declare type BarEntryAnimation = 'none' | 'fade' | 'grow' | 'slide' | 'fade-grow';
99
+
100
+ export declare function BarSeries({ data, options, id: idProp }: BarSeriesProps): null;
27
101
 
28
102
  /** Visual options for a bar series. */
29
103
  export declare interface BarSeriesOptions {
@@ -34,27 +108,108 @@ export declare interface BarSeriesOptions {
34
108
  barWidthRatio: number;
35
109
  /** Stacking mode. Default: 'off'. */
36
110
  stacking: StackingMode;
111
+ /**
112
+ * Entrance animation style for newly appended bars. Style is specific to
113
+ * the bar series; there is no chart-level override for style — only for
114
+ * duration. Default: `'fade-grow'`.
115
+ *
116
+ * @see entryMs — cross-linked duration for this animation.
117
+ */
118
+ entryAnimation?: BarEntryAnimation;
119
+ /** @deprecated Use {@link entryAnimation} instead. */
120
+ enterAnimation?: BarEntryAnimation;
121
+ /**
122
+ * Entrance animation duration in milliseconds. `false` or `0` disables the
123
+ * per-bar entrance (equivalent to `entryAnimation: 'none'`). Omit to inherit
124
+ * from {@link AnimationsConfig.points}.`entryMs` (default 400).
125
+ *
126
+ * When `animations.points.entryMs` is `false` at the chart level, the
127
+ * entrance is forced off regardless of this field.
128
+ *
129
+ * @see BarSeriesOptions.entryAnimation
130
+ */
131
+ entryMs?: number | false;
132
+ /** @deprecated Use {@link entryMs} instead. */
133
+ enterMs?: number | false;
134
+ /**
135
+ * Exponential-smoothing time constant (ms) for live-tracking the last
136
+ * bar's value under `updateLastPoint`. `0` or `false` snaps directly to the
137
+ * target (no smoothing). Omit to inherit from
138
+ * {@link AnimationsConfig.points}.`smoothMs` (default 70 ms).
139
+ *
140
+ * When `animations.points.smoothMs` is `false` at the chart level, smoothing
141
+ * is forced off regardless of this field.
142
+ */
143
+ smoothMs?: number | false;
37
144
  }
38
145
 
39
146
  declare interface BarSeriesProps {
40
147
  /** Array of datasets — one per layer. A single-layer bar chart uses `[data]`. */
41
148
  data: TimePoint[][];
42
149
  options?: Partial<BarSeriesOptions>;
43
- /** Display label shown in the tooltip. */
44
- label?: string;
45
- /** Stable series ID. Prefer this over `onSeriesId` — same value across remounts. */
150
+ /** Stable series ID same value across remounts. */
46
151
  id?: string;
47
- /** @deprecated Use the `id` prop instead. */
48
- onSeriesId?: (id: string) => void;
49
152
  }
50
153
 
51
154
  /** @deprecated Use {@link StackingMode} instead. */
52
155
  export declare type BarStacking = StackingMode;
53
156
 
54
- /** Lookup a theme by name (defaults to "One Dark Pro") */
55
- export declare function buildTheme(themeName: string): ChartTheme;
157
+ /**
158
+ * Snapshot every visible series / layer at `time`. Used by hover overlays
159
+ * (`InfoBar` in hover mode, `Tooltip`). Hidden series and hidden layers are
160
+ * skipped automatically — the returned array reflects what the chart would
161
+ * actually be drawing.
162
+ *
163
+ * The result is cached per `(chart, cacheKey)` slot; as long as
164
+ * `chart.getOverlayVersion()` hasn't changed and `time`/`sort` match the
165
+ * cached call, the **same reference** is returned. This makes
166
+ * `React.memo((a, b) => a.snapshots === b.snapshots)` skip renders on
167
+ * crosshair moves that stay within one data point.
168
+ */
169
+ export declare function buildHoverSnapshots(chart: ChartInstance, args: BuildHoverSnapshotsArgs): readonly SeriesSnapshot[];
56
170
 
57
- export declare function CandlestickSeries({ data, options, id: idProp, onSeriesId }: CandlestickSeriesProps): null;
171
+ export declare interface BuildHoverSnapshotsArgs {
172
+ /** Timestamp to resolve each series at. Typically `crosshair.time`. */
173
+ time: number;
174
+ /** Sort the output by numeric value. Default: `'none'`. */
175
+ sort?: SnapshotSort;
176
+ /**
177
+ * Cache bucket key. Distinct buckets (e.g. `'tooltip'`, `'infobar-hover'`)
178
+ * let separate overlays cache independently so they don't invalidate each
179
+ * other on every call.
180
+ */
181
+ cacheKey: string;
182
+ }
183
+
184
+ /**
185
+ * Snapshot every visible series / layer at its own last point. For
186
+ * multi-layer renderers each layer contributes its own `time`, so ragged
187
+ * streams (layer 1 newer than layer 0) show correctly instead of stalling
188
+ * at the primary layer's timestamp.
189
+ *
190
+ * Cached by `(chart, cacheKey)` + `sort`, invalidated on
191
+ * `chart.getOverlayVersion()` bump. `time` is not an input, so the slot is
192
+ * stored with `time: null`.
193
+ */
194
+ export declare function buildLastSnapshots(chart: ChartInstance, args: BuildLastSnapshotsArgs): readonly SeriesSnapshot[];
195
+
196
+ export declare interface BuildLastSnapshotsArgs {
197
+ sort?: SnapshotSort;
198
+ cacheKey: string;
199
+ }
200
+
201
+ /**
202
+ * Entrance animation styles for candlesticks (shown when a new candle appears via `appendData`).
203
+ * - `'none'` — no animation.
204
+ * - `'fade'` — opacity 0→1.
205
+ * - `'unfold'` — body scales from the open line outward.
206
+ * - `'slide'` — translates in from the right with a fade.
207
+ * - `'fade-unfold'` *(default)* — fade + unfold combined; the default because a lone fade is
208
+ * hard to notice in streaming demos, while the body-unfold gives a clear visual anchor.
209
+ */
210
+ declare type CandlestickEntryAnimation = 'none' | 'fade' | 'unfold' | 'slide' | 'fade-unfold';
211
+
212
+ export declare function CandlestickSeries({ data, options, id: idProp }: CandlestickSeriesProps): null;
58
213
 
59
214
  /** Visual options for a candlestick series. */
60
215
  export declare interface CandlestickSeriesOptions {
@@ -67,16 +222,49 @@ export declare interface CandlestickSeriesOptions {
67
222
  /** Width of candle body as a fraction of the available bar slot (0-1). */
68
223
  bodyWidthRatio: number;
69
224
  /** Apply a subtle vertical gradient to candle bodies. Default: true. */
225
+ bodyGradient?: boolean;
226
+ /** @deprecated Use {@link bodyGradient} instead. */
70
227
  candleGradient?: boolean;
228
+ /**
229
+ * Entrance animation style for newly appended candles. Style is specific to
230
+ * the candlestick; there is no chart-level override for style — only for
231
+ * duration. Default: `'unfold'`.
232
+ *
233
+ * @see entryMs — cross-linked duration for this animation.
234
+ */
235
+ entryAnimation?: CandlestickEntryAnimation;
236
+ /** @deprecated Use {@link entryAnimation} instead. */
237
+ enterAnimation?: CandlestickEntryAnimation;
238
+ /**
239
+ * Entrance animation duration in milliseconds. `false` or `0` disables the
240
+ * per-candle entrance (equivalent to `entryAnimation: 'none'`). Omit to
241
+ * inherit from {@link AnimationsConfig.points}.`entryMs` (default 400).
242
+ *
243
+ * When `animations.points.entryMs` is `false` at the chart level, the
244
+ * entrance is forced off regardless of this field.
245
+ *
246
+ * @see CandlestickSeriesOptions.entryAnimation
247
+ */
248
+ entryMs?: number | false;
249
+ /** @deprecated Use {@link entryMs} instead. */
250
+ enterMs?: number | false;
251
+ /**
252
+ * Exponential-smoothing time constant (ms) for live-tracking the last
253
+ * candle's O/H/L/C under `updateLastPoint`. `0` or `false` snaps directly
254
+ * to the target (no smoothing). Omit to inherit from
255
+ * {@link AnimationsConfig.points}.`smoothMs` (default 70 ms).
256
+ *
257
+ * When `animations.points.smoothMs` is `false` at the chart level, smoothing
258
+ * is forced off regardless of this field.
259
+ */
260
+ smoothMs?: number | false;
71
261
  }
72
262
 
73
263
  declare interface CandlestickSeriesProps {
74
264
  data: OHLCInput[];
75
265
  options?: Partial<CandlestickSeriesOptions>;
76
- /** Stable series ID. Prefer this over `onSeriesId` — same value across remounts. */
266
+ /** Stable series ID — same value across remounts. */
77
267
  id?: string;
78
- /** @deprecated Use the `id` prop instead. */
79
- onSeriesId?: (id: string) => void;
80
268
  }
81
269
 
82
270
  export declare const catppuccin: ThemePreset;
@@ -84,9 +272,16 @@ export declare const catppuccin: ThemePreset;
84
272
  /**
85
273
  * Top-level React wrapper that creates a {@link ChartInstance} and provides it to children via context.
86
274
  * Owns the DOM container and canvas lifecycle; renders children as an overlay layer.
87
- * Detects `<Legend>` children and renders them outside the chart area.
275
+ *
276
+ * Detects `<Title>`, `<InfoBar>`, and `<Legend>` children and positions them as:
277
+ * - Title + InfoBar — absolutely-positioned *overlays* stacked at the top of the canvas
278
+ * block, so the canvas (and therefore the grid) fills the full container height. The stacked
279
+ * height is measured and fed back into `chart.setPadding({ top })` so series data stays below
280
+ * them.
281
+ * - Legend — flex sibling at the bottom (or right, when `position="right"`), so its height is
282
+ * reserved by browser layout.
88
283
  */
89
- export declare function ChartContainer({ children, theme, axis, padding, gradient, interactive, grid, style, className }: ChartContainerProps): JSX_2.Element;
284
+ export declare function ChartContainer({ children, theme, axis, padding, gradient, interactive, grid, headerLayout, perf, style, className, }: ChartContainerProps): JSX_2.Element;
90
285
 
91
286
  /** Props for the {@link ChartContainer} component. */
92
287
  declare interface ChartContainerProps {
@@ -115,8 +310,29 @@ declare interface ChartContainerProps {
115
310
  gradient?: boolean;
116
311
  /** Enable zoom, pan, and crosshair interactions. Defaults to true. */
117
312
  interactive?: boolean;
118
- /** Show the background grid. Defaults to true. */
119
- grid?: boolean;
313
+ /** Background grid configuration. Default: `{ visible: true }`. */
314
+ grid?: {
315
+ visible: boolean;
316
+ };
317
+ /**
318
+ * How `<Title>` and `<InfoBar>` are positioned relative to the canvas.
319
+ * - `'overlay'` (default): absolute overlays on top of the canvas — the grid
320
+ * and Y-axis labels render full-height behind the header strip.
321
+ * - `'inline'`: flex siblings above the canvas — the canvas (and grid) are
322
+ * shifted down by the measured header height, so nothing renders behind
323
+ * the title. The chart background still spans the full container.
324
+ */
325
+ headerLayout?: 'overlay' | 'inline';
326
+ /**
327
+ * Enable runtime performance instrumentation. Off by default.
328
+ *
329
+ * - `true` — attach a {@link PerfMonitor} and render a visible HUD overlay on this chart.
330
+ * - `{ hud: true, windowMs, maxSamples, ... }` — same, with monitor options.
331
+ * - `{ hud: false, monitor }` — attach to an existing monitor without rendering the HUD.
332
+ *
333
+ * Only read at mount; changing this prop after the chart is created is ignored.
334
+ */
335
+ perf?: PerfOption;
120
336
  style?: CSSProperties;
121
337
  className?: string;
122
338
  }
@@ -127,6 +343,14 @@ declare interface ChartEvents {
127
343
  viewportChange: () => void;
128
344
  dataUpdate: () => void;
129
345
  seriesChange: () => void;
346
+ /**
347
+ * Fired whenever any state that affects **overlay components** (InfoBar,
348
+ * Tooltip, Legend, YLabel, PieLegend, PieTooltip) changes. Superset of
349
+ * `dataUpdate` and `seriesChange` — also fires on visibility toggles,
350
+ * series option changes, and theme swaps. Overlay components should
351
+ * subscribe to this instead of stacking multiple listeners.
352
+ */
353
+ overlayChange: () => void;
130
354
  }
131
355
 
132
356
  /**
@@ -151,44 +375,38 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
151
375
  layers?: number;
152
376
  id?: string;
153
377
  }>): string;
154
- /** @deprecated Pass options as first argument: `addLineSeries({ layers: N, ...options })` */
155
- addLineSeries(layerCount: number, options?: Partial<LineSeriesOptions>): string;
156
- /** Set data for a specific layer within a line series. */
157
- setLineLayerData(id: string, layerIndex: number, data: TimePoint[]): void;
158
378
  /** Add a bar series and return its unique ID. */
159
379
  addBarSeries(options?: Partial<BarSeriesOptions & {
160
380
  layers?: number;
161
381
  id?: string;
162
382
  }>): string;
163
- /** @deprecated Pass options as first argument: `addBarSeries({ layers: N, ...options })` */
164
- addBarSeries(layerCount: number, options?: Partial<BarSeriesOptions>): string;
165
- /** Set data for a specific layer within a bar series. */
166
- setBarLayerData(id: string, layerIndex: number, data: TimePoint[]): void;
167
383
  /** Add a pie/donut series. Set `innerRadiusRatio > 0` for donut. */
168
384
  addPieSeries(options?: Partial<PieSeriesOptions & {
169
385
  id?: string;
170
386
  }>): string;
171
- /** Set data for a pie/donut series. */
172
- setPieData(id: string, data: PieSliceData[]): void;
173
387
  /** Remove a series by ID and clean up its resources. */
174
388
  removeSeries(id: string): void;
175
- /** Replace all data for a series (batch load). Accepts `Date` objects for time fields. */
176
- setSeriesData(id: string, data: OHLCInput[] | TimePointInput[]): void;
389
+ /**
390
+ * Replace all data for a series.
391
+ *
392
+ * - Single-layer series (candlestick, single-layer line/bar, pie): pass `data` directly.
393
+ * - Multi-layer series (line/bar with multiple layers): pass `layerIndex` to target a specific layer.
394
+ *
395
+ * For line/bar accepts `TimePointInput[]` (time may be `Date`); for candlestick accepts `OHLCInput[]`;
396
+ * for pie accepts `PieSliceData[]`. Time fields are normalized internally.
397
+ */
398
+ setSeriesData(id: string, data: unknown, layerIndex?: number): void;
177
399
  /** Append a new data point to the end of a series (real-time tick). */
178
- appendData(id: string, point: OHLCInput | TimePointInput): void;
400
+ appendData(id: string, point: OHLCInput | TimePointInput, layerIndex?: number): void;
179
401
  /** Update the last data point of a series in place (e.g. live candle update). */
180
- updateData(id: string, point: OHLCInput | TimePointInput): void;
402
+ updateData(id: string, point: OHLCInput | TimePointInput, layerIndex?: number): void;
181
403
  /** Update visual options (color, width, etc.) for an existing series. */
182
404
  updateSeriesOptions(id: string, options: Partial<CandlestickSeriesOptions> | Partial<LineSeriesOptions> | Partial<BarSeriesOptions> | Partial<PieSeriesOptions>): void;
183
405
  /**
184
- * Batch multiple updates: suppress recomputes until `fn` returns.
185
- * Equivalent to `beginUpdate()` / `endUpdate()` but safer (always calls endUpdate even on throw).
406
+ * Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
407
+ * inside `fn` still flush the batch so counters don't leak across calls.
186
408
  */
187
409
  batch(fn: () => void): void;
188
- /** @deprecated Use {@link batch} instead. */
189
- beginUpdate(): void;
190
- /** @deprecated Use {@link batch} instead. */
191
- endUpdate(): void;
192
410
  /** Show or hide a series. Hidden series are not rendered and excluded from Y-range. */
193
411
  setSeriesVisible(seriesId: string, visible: boolean): void;
194
412
  isSeriesVisible(seriesId: string): boolean;
@@ -198,6 +416,22 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
198
416
  /** Auto-fit the viewport to show all data across every series. */
199
417
  fitContent(): void;
200
418
  getVisibleRange(): VisibleRange;
419
+ /**
420
+ * Imperatively set the visible time range.
421
+ *
422
+ * Two forms:
423
+ * - `{ from, to }` — explicit millisecond range. Cancels any in-flight
424
+ * animation and applies immediately. Auto-scroll stays on only if the
425
+ * tail is inside the new range (mirrors pan semantics).
426
+ * - `number N` — shorthand for "show the last N bars from the tail".
427
+ * Resolved against the current data bounds and data interval; keeps
428
+ * auto-scroll on so streaming ticks continue to track the tail.
429
+ * No-op if data hasn't loaded yet.
430
+ *
431
+ * Typical use: on mount, zoom to the last N bars while keeping the full
432
+ * buffer available for pan-back history inspection.
433
+ */
434
+ setVisibleRange(range: VisibleRange | number): void;
201
435
  getYRange(): YRange;
202
436
  getCrosshairPosition(): CrosshairPosition | null;
203
437
  /** Get the last visible value and whether the absolute last point is on screen. */
@@ -210,57 +444,129 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
210
444
  getLastData(seriesId: string): OHLCData | TimePoint | null;
211
445
  /** Find the data point closest to the given timestamp within one data interval. */
212
446
  getDataAtTime(seriesId: string, time: number): OHLCData | TimePoint | null;
213
- /** Get all layers' data at a given time for multi-layer series (Bar/Line with stacking). */
447
+ /**
448
+ * Get all layers' data at a given time for multi-layer series (Bar/Line
449
+ * with stacking). Each entry carries the owning `layerIndex` and the
450
+ * snapped sample time — callers must not derive layer identity from the
451
+ * array index, because hidden layers are filtered out.
452
+ */
214
453
  getLayerSnapshots(seriesId: string, time: number): {
454
+ layerIndex: number;
455
+ time: number;
215
456
  value: number;
216
457
  color: string;
217
458
  }[] | null;
218
459
  getSeriesIds(): string[];
460
+ /**
461
+ * Type of a registered series, or `null` for unknown ids. `'pie'` for
462
+ * `PieRenderer`; everything else is a time-series (`'time'`).
463
+ */
464
+ getSeriesType(seriesId: string): 'pie' | 'time' | null;
465
+ /**
466
+ * Filter `getSeriesIds()` by renderer type. `'pie'` returns pie series,
467
+ * `'time'` returns line/bar/candlestick.
468
+ *
469
+ * - `opts.visibleOnly` — exclude series with `isSeriesVisible=false`; for
470
+ * multi-layer series also exclude when every layer is hidden.
471
+ * - `opts.singleLayerOnly` — exclude series with more than one layer.
472
+ * Useful for YLabel fallback priority (stick to a single line first).
473
+ */
474
+ getSeriesIdsByType(type: 'pie' | 'time', opts?: {
475
+ visibleOnly?: boolean;
476
+ singleLayerOnly?: boolean;
477
+ }): string[];
478
+ /**
479
+ * Cumulative top last-value for stacked series — the point a YLabel badge
480
+ * anchors to on the rendered stack head. Falls back to `getLastValue` for
481
+ * series without stacked concepts (Candlestick, single-layer Line/Bar).
482
+ * Returns `null` for unknown ids or empty series.
483
+ */
484
+ getStackedLastValue(seriesId: string): {
485
+ value: number;
486
+ isLive: boolean;
487
+ } | null;
488
+ /**
489
+ * Per-layer last snapshots with each layer's own `time`. Returns `null`
490
+ * for single-layer renderers or when no visible layer has data. Used by
491
+ * overlay components that must display every layer in last-mode even
492
+ * when layers advance at different rates (ragged streams).
493
+ */
494
+ getLayerLastSnapshots(seriesId: string): {
495
+ layerIndex: number;
496
+ time: number;
497
+ value: number;
498
+ color: string;
499
+ }[] | null;
219
500
  /** Get the primary display color for a series. */
220
501
  getSeriesColor(seriesId: string): string | null;
221
502
  getSeriesLabel(seriesId: string): string | undefined;
222
- /** Get per-layer colors for a series. Returns null for non-bar/line series (e.g. candlestick, pie). */
503
+ /** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
223
504
  getSeriesLayers(seriesId: string): {
224
505
  color: string;
225
506
  }[] | null;
226
- /** Get all pie slices with computed colors and percentages. Returns null for non-pie series. */
227
- getPieSlices(seriesId: string): {
228
- label: string;
229
- value: number;
230
- percent: number;
231
- color: string;
232
- }[] | null;
233
- /** Get the hovered pie slice info (label, value, percentage, color). Returns null if no hover. */
234
- getPieHoverInfo(seriesId: string): {
235
- label: string;
236
- value: number;
237
- percent: number;
238
- color: string;
239
- } | null;
240
- /** Apply a new theme and update candlestick series colors. Line series keep their individual colors. */
507
+ /** Get all slices with computed colors and percentages. Returns null for series without slice data (e.g. candlestick, line, bar). */
508
+ getSliceInfo(seriesId: string): SliceInfo[] | null;
509
+ /** Get hover info (label/value/percent/color) for the currently hovered element, or null. */
510
+ getHoverInfo(seriesId: string): HoverInfo | null;
511
+ /** Apply a new theme and update series colors where appropriate. */
241
512
  setTheme(theme: ChartTheme): void;
242
513
  getTheme(): ChartTheme;
243
514
  /** Update axis configuration and re-render. */
244
515
  setAxis(config: AxisConfig): void;
516
+ /**
517
+ * Apply label-density knobs driven by `<YAxis labelCount=… minLabelSpacing=…>`
518
+ * — kept separate from {@link setAxis} so component props don't force a
519
+ * Y-animation reset. Triggers a viewportChange + repaint so static charts
520
+ * react without waiting for pan/zoom.
521
+ */
522
+ setYAxisLabelDensity(params: {
523
+ labelCount?: number | null;
524
+ minLabelSpacing?: number | null;
525
+ }): void;
526
+ /** Label-density knobs for the time axis — mirror of {@link setYAxisLabelDensity}. */
527
+ setTimeAxisLabelDensity(params: {
528
+ labelCount?: number | null;
529
+ minLabelSpacing?: number | null;
530
+ }): void;
245
531
  getMediaSize(): Size;
246
532
  /** Returns layout metrics for the chart area, Y axis, and time axis. */
247
533
  getLayout(): ChartLayout;
248
534
  getDataInterval(): number;
249
- /** Update viewport padding at runtime. Refits the visible range to current data bounds. */
535
+ /**
536
+ * Update viewport padding at runtime. Refits the visible time range to
537
+ * current data bounds **only when horizontal padding (left/right) changes**
538
+ * — vertical padding only affects the Y-range computation, so touching it
539
+ * shouldn't reset the user's zoom / auto-scroll state. This matters when
540
+ * a wrapper re-applies padding reactively (e.g. in response to a Title /
541
+ * InfoBar ResizeObserver).
542
+ */
250
543
  setPadding(padding: ChartOptions['padding']): void;
251
544
  /** Show or hide the background grid. Takes effect on the next render frame. */
252
- setGrid(grid: boolean): void;
545
+ setGrid(grid: {
546
+ visible: boolean;
547
+ }): void;
548
+ /**
549
+ * Set the visual state for one side of the chart. Typically called in
550
+ * response to the `onEdgeReached` callback:
551
+ * - `loading` while a history fetch is in flight,
552
+ * - `no-data` once the fetch confirmed there's nothing more,
553
+ * - `idle` when the host no longer wants any edge affordance.
554
+ * The state persists until replaced. `has-more` is accepted for API
555
+ * symmetry and currently renders identically to `idle`.
556
+ */
557
+ setEdgeState(side: EdgeSide, state: EdgeState): void;
558
+ /** Read the current host-declared state for a given edge. */
559
+ getEdgeState(side: EdgeSide): EdgeState;
253
560
  /** Notify chart that a YLabel is present (affects right padding). */
254
561
  setYLabel(has: boolean): void;
255
- private updatePieHover;
256
562
  private updateViewportPadding;
257
563
  /** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
258
564
  destroy(): void;
565
+ /** The attached performance monitor, or `null` when instrumentation is disabled. */
566
+ getPerfMonitor(): PerfMonitor | null;
259
567
  /** Compute the earliest and latest timestamps across all series. */
260
568
  private getDataBounds;
261
569
  private onDataChanged;
262
- /** Check whether the last data point of any series falls within the visible time range. */
263
- private isLastPointVisible;
264
570
  private updateDataInterval;
265
571
  private updateYRange;
266
572
  /** Resolve an {@link AxisBound} to a concrete numeric value. */
@@ -274,8 +580,17 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
274
580
  private updateScales;
275
581
  /** Expensive: background, grid, all series. Only on data/viewport/resize change. */
276
582
  private renderMain;
277
- /** Cheap overlay: crosshair, nearest-point dots, pulse animation. */
583
+ /** Cheap overlay: crosshair, nearest-point dots, pulse animation, edge indicator. */
278
584
  private renderOverlay;
585
+ private drawEdgeIndicators;
586
+ /**
587
+ * Pick the boundary time to anchor the edge indicator at. Prefer the
588
+ * cached value emitted by the most recent `edgeReached` — that's the
589
+ * *exact* point the user overshot. Fall back to the current data edge
590
+ * when no gesture has fired yet (host might invoke `setEdgeState`
591
+ * directly on mount to show a "no-data" marker from the start).
592
+ */
593
+ private resolveEdgeBoundary;
279
594
  }
280
595
 
281
596
  /** Layout metrics describing the chart area, Y axis, and time axis sizes. */
@@ -285,7 +600,6 @@ export declare interface ChartLayout {
285
600
  xAxisHeight: number;
286
601
  }
287
602
 
288
- /** Options passed when creating a new {@link ChartInstance}. */
289
603
  export declare interface ChartOptions {
290
604
  theme?: ChartTheme;
291
605
  axis?: AxisConfig;
@@ -305,8 +619,50 @@ export declare interface ChartOptions {
305
619
  };
306
620
  /** Enable zoom, pan, and crosshair interactions. Defaults to true. */
307
621
  interactive?: boolean;
308
- /** Show the background grid. Defaults to true. */
309
- grid?: boolean;
622
+ /** Background grid configuration. Default: `{ visible: true }`. */
623
+ grid?: {
624
+ visible: boolean;
625
+ };
626
+ /**
627
+ * Animation control. Split into `points` (data-series animations) and
628
+ * `viewport` (pan/zoom rebound + Y-axis smoothing). See
629
+ * {@link AnimationsConfig} for the full shape and defaults.
630
+ *
631
+ * Shorthands:
632
+ * - `animations: true` (or omitted) uses built-in defaults.
633
+ * - `animations: false` disables every animation category.
634
+ * - `animations: { points: false }` disables all data-series animations.
635
+ * - `animations: { viewport: false }` disables rebound + Y-axis smoothing.
636
+ *
637
+ * Per-series options (`enterMs`, `smoothMs`, etc.) override chart-level
638
+ * defaults unless the category is explicitly `false` — then the chart-
639
+ * level gate wins.
640
+ */
641
+ animations?: boolean | AnimationsConfig;
642
+ /**
643
+ * Invoked after the user releases a pan/zoom gesture that pulled the
644
+ * viewport past a data edge by more than 10% of the visible range. Hosts
645
+ * typically respond by prefetching more history and calling
646
+ * {@link ChartInstance.setEdgeState} to show a spinner or "no more data"
647
+ * indicator at the corresponding edge.
648
+ */
649
+ onEdgeReached?: (info: EdgeReachedInfo) => void;
650
+ /**
651
+ * Runtime performance instrumentation. Opt-in — absent by default so the
652
+ * hot render path stays free of timing/counting overhead.
653
+ *
654
+ * - `false` / omitted — no instrumentation, no HUD, byte-identical to a perf-free build.
655
+ * - `true` — create an internal {@link PerfMonitor} and mount a visible HUD overlay.
656
+ * - `{ hud: true, ...options }` — same, with monitor options forwarded.
657
+ * - `{ hud: false, ...options }` — instrument but do not render a HUD (useful when
658
+ * the host app consumes stats via `monitor.onFrame` and renders its own UI).
659
+ * - `PerfMonitor` instance — attach to a pre-constructed monitor. Useful when several
660
+ * charts share one telemetry sink. HUD defaults to off in this mode.
661
+ */
662
+ perf?: boolean | PerfMonitor | (PerfMonitorOptions & {
663
+ hud?: boolean;
664
+ monitor?: PerfMonitor;
665
+ });
310
666
  }
311
667
 
312
668
  /**
@@ -370,6 +726,8 @@ export declare interface ChartTheme {
370
726
  };
371
727
  }
372
728
 
729
+ export declare function computeTooltipPosition(args: TooltipPositionArgs): TooltipPosition;
730
+
373
731
  /**
374
732
  * Build a complete {@link ThemePreset} from a partial config.
375
733
  * Only `background` is required — everything else is derived from it.
@@ -396,6 +754,27 @@ export declare function detectInterval(times: number[]): number;
396
754
 
397
755
  export declare const dracula: ThemePreset;
398
756
 
757
+ /** Payload for {@link ChartOptions.onEdgeReached}. */
758
+ declare interface EdgeReachedInfo {
759
+ side: EdgeSide;
760
+ /** Time units the user pulled past the soft bound. */
761
+ overshoot: number;
762
+ /** Soft-bound timestamp that was crossed (dataStart - leftPad or dataEnd + rightPad). */
763
+ boundaryTime: number;
764
+ }
765
+
766
+ /** Which data side the user pulled past during a gesture. */
767
+ declare type EdgeSide = 'left' | 'right';
768
+
769
+ /**
770
+ * Host-controlled visual state for a chart edge:
771
+ * - `idle`: nothing rendered (default).
772
+ * - `loading`: a subtle spinner appears in the overshoot area.
773
+ * - `no-data`: a dashed boundary line + "No more data" label appears at the data edge.
774
+ * - `has-more`: reserved — currently behaves like `idle`. Use when more data exists but is not being fetched.
775
+ */
776
+ declare type EdgeState = 'idle' | 'loading' | 'no-data' | 'has-more';
777
+
399
778
  declare class EventEmitter<Events = Record<string, Listener>> {
400
779
  private listeners;
401
780
  on<K extends string & keyof Events>(event: K, listener: Events[K] & Listener): void;
@@ -404,10 +783,37 @@ declare class EventEmitter<Events = Record<string, Listener>> {
404
783
  removeAllListeners(): void;
405
784
  }
406
785
 
786
+ /**
787
+ * Compact notation for large magnitudes (K / M / B / T) with adaptive
788
+ * precision for values below 1. Default decimal count for the compact
789
+ * suffixes can be overridden via `opts.decimals`.
790
+ */
791
+ export declare function formatCompact(v: number, opts?: {
792
+ decimals?: number;
793
+ }): string;
794
+
407
795
  export declare function formatDate(timestamp: number): string;
408
796
 
797
+ /**
798
+ * Full-precision display that adapts decimal count to the value's magnitude.
799
+ * No K/M/B compression — callers wanting a short label should use
800
+ * `formatCompact` instead.
801
+ */
802
+ export declare function formatPriceAdaptive(v: number): string;
803
+
409
804
  export declare function formatTime(timestamp: number, interval: number): string;
410
805
 
806
+ declare type FrameKind = 'main' | 'overlay';
807
+
808
+ declare interface FrameTimingSample {
809
+ /** Wall-clock ms for the most recent frame. */
810
+ last: number;
811
+ /** Median frame time over the rolling window. */
812
+ p50: number;
813
+ /** Tail frame time (worst 5%) — useful for spotting stutter. */
814
+ p95: number;
815
+ }
816
+
411
817
  export declare const githubLight: ThemePreset;
412
818
 
413
819
  export declare const gruvbox: ThemePreset;
@@ -416,26 +822,129 @@ export declare const handwritten: ThemePreset;
416
822
 
417
823
  export declare const highContrast: ThemePreset;
418
824
 
825
+ /** Shape returned by {@link SeriesRenderer.getHoverInfo} / per-slice entries of {@link SeriesRenderer.getSliceInfo}. */
826
+ export declare interface HoverInfo {
827
+ label: string;
828
+ value: number;
829
+ percent: number;
830
+ color: string;
831
+ }
832
+
833
+ /**
834
+ * Compact OHLC/series info bar rendered as a flex row above the chart canvas.
835
+ * Pairs with {@link Tooltip} (which then only renders its floating near-cursor part).
836
+ *
837
+ * Pass a render-prop child for a custom layout — the built-in UI is used when
838
+ * {@link children} is omitted.
839
+ */
840
+ export declare function InfoBar({ sort, format, children }: InfoBarProps): JSX_2.Element | null;
841
+
842
+ /** Props for the {@link InfoBar} component. */
843
+ export declare interface InfoBarProps {
844
+ /** Sort order for line values (default: 'none'). */
845
+ sort?: TooltipSort;
846
+ /**
847
+ * Custom formatter for every displayed number in the default UI. Called per
848
+ * cell with the field hint (`'open' | 'high' | 'low' | 'close' | 'volume' | 'value'`).
849
+ * Defaults: adaptive precision for ohlc/value, compact (K/M/B/T) for volume.
850
+ * Ignored when {@link children} is a render-prop.
851
+ */
852
+ format?: TooltipFormatter;
853
+ /**
854
+ * Render-prop escape hatch. Receives the computed snapshots and replaces the
855
+ * entire built-in layout. Filter, reorder, or re-style rows here without
856
+ * re-implementing any data wiring.
857
+ */
858
+ children?: (ctx: InfoBarRenderContext) => ReactNode;
859
+ }
860
+
861
+ /** Context passed to the {@link InfoBar} render-prop. */
862
+ export declare interface InfoBarRenderContext {
863
+ readonly snapshots: readonly SeriesSnapshot[];
864
+ /** Timestamp displayed. In hover mode it's the crosshair time; in last-mode it's the newest point. */
865
+ readonly time: number;
866
+ /** `true` while the user's pointer is over the chart (hover mode). */
867
+ readonly isHover: boolean;
868
+ }
869
+
419
870
  export declare const lavenderMist: ThemePreset;
420
871
 
421
- export declare function Legend({ items, position, mode }: LegendProps): JSX_2.Element | null;
872
+ export declare function Legend({ items, position, mode, children }: LegendProps): JSX_2.Element | null;
422
873
 
874
+ /**
875
+ * Canonical legend item — the unit every framework Legend passes to its
876
+ * scoped slot (and the shape the built-in default UI consumes).
877
+ *
878
+ * Each item is **self-contained**: it carries its own identity and
879
+ * pre-bound `toggle` / `isolate` closures, so slot code can freely sort,
880
+ * filter, or remap items without risking a click on one row mutating the
881
+ * wrong series.
882
+ *
883
+ * - `id` — row key for list rendering (React `key`, Vue `:key`,
884
+ * Svelte `each … (key)`). Guaranteed unique within the array.
885
+ * - `seriesId` — owning series id, **never includes a layer suffix**.
886
+ * Use this for filtering/grouping; not unique across multi-layer rows.
887
+ * - `layerIndex` — layer within a multi-layer series; `undefined` for
888
+ * single-layer rows.
889
+ * - `isDisabled` — current toggled-off state, derived from
890
+ * `isSeriesVisible` / `isLayerVisible` plus local isolate-state in the
891
+ * Legend component.
892
+ */
423
893
  export declare interface LegendItem {
894
+ readonly id: string;
895
+ readonly seriesId: string;
896
+ readonly layerIndex?: number;
897
+ readonly label: string;
898
+ readonly color: string;
899
+ readonly isDisabled: boolean;
900
+ /** Toggle just this item's visibility. */
901
+ readonly toggle: () => void;
902
+ /** Show only this item; calling again reveals everything (isolate/unisolate). */
903
+ readonly isolate: () => void;
904
+ }
905
+
906
+ /**
907
+ * Minimal visual shape the {@link LegendProps.items} override accepts — just
908
+ * the pieces the built-in swatch/label UI needs. The canonical
909
+ * {@link LegendItem} (re-exported from `@wick-charts/core`) carries full
910
+ * identity plus `toggle`/`isolate` closures; those aren't meaningful when a
911
+ * consumer hands in a pre-baked, non-interactive legend.
912
+ */
913
+ export declare interface LegendItemOverride {
424
914
  label: string;
425
915
  color: string;
426
916
  }
427
917
 
918
+ /**
919
+ * Legend interaction mode.
920
+ * - `'toggle'` — click toggles the clicked item on/off (default).
921
+ * - `'isolate'` — click shows only that item; click again shows all.
922
+ * - `'solo'` — **@deprecated** alias for `'isolate'`, kept for back-compat.
923
+ */
924
+ declare type LegendMode = 'toggle' | 'isolate' | 'solo';
925
+
428
926
  export declare interface LegendProps {
429
- /** Override auto-detected items. When omitted, derived from series layers. */
430
- items?: LegendItem[];
927
+ /**
928
+ * Static override for auto-detected items. Renders a non-interactive legend
929
+ * with just swatch + label. Ignored when {@link children} is a render-prop.
930
+ */
931
+ items?: LegendItemOverride[];
431
932
  /** Layout position. Default: 'bottom'. */
432
933
  position?: 'bottom' | 'right';
934
+ /** Click behavior for the built-in UI. Default: `'toggle'`. Ignored when {@link children} is provided. */
935
+ mode?: LegendMode;
433
936
  /**
434
- * Click behavior:
435
- * - `'toggle'` click toggles individual items on/off (default)
436
- * - `'solo'` click shows only that item; click again shows all
937
+ * Render-prop escape hatch. Receives the computed `items` (each carrying
938
+ * its own `toggle()` / `isolate()` closures) and fully replaces the
939
+ * built-in flex row / column. Callers can filter, reorder, and re-style
940
+ * without reimplementing visibility wiring.
437
941
  */
438
- mode?: 'toggle' | 'solo';
942
+ children?: (ctx: LegendRenderContext) => ReactNode;
943
+ }
944
+
945
+ /** Context passed to the {@link Legend} render-prop. */
946
+ declare interface LegendRenderContext {
947
+ readonly items: readonly LegendItem[];
439
948
  }
440
949
 
441
950
  export declare const lightPink: ThemePreset;
@@ -445,7 +954,15 @@ export declare const lightTheme: ChartTheme;
445
954
  /** @deprecated Use {@link TimePoint} instead. */
446
955
  export declare type LineData = TimePoint;
447
956
 
448
- export declare function LineSeries({ data, options, label, id: idProp, onSeriesId }: LineSeriesProps): null;
957
+ /**
958
+ * Entrance animation styles for new line points.
959
+ * - `'none'` — new segments appear instantly.
960
+ * - `'grow'` *(default)* — trailing segment reveals left-to-right.
961
+ * - `'fade'` — geometry fixed; trailing segment strokes in with alpha 0→1.
962
+ */
963
+ declare type LineEntryAnimation = 'none' | 'grow' | 'fade';
964
+
965
+ export declare function LineSeries({ data, options, id: idProp }: LineSeriesProps): null;
449
966
 
450
967
  /** Visual options for a line series. */
451
968
  export declare interface LineSeriesOptions {
@@ -453,24 +970,67 @@ export declare interface LineSeriesOptions {
453
970
  label?: string;
454
971
  /** One color per layer. */
455
972
  colors: string[];
456
- lineWidth: number;
457
- /** Whether to render a gradient area fill below the line (or between stacked layers). */
458
- areaFill: boolean;
459
- /** Whether to show an animated pulsing dot at the last data point. */
973
+ /** Stroke width in CSS pixels. Default: 1. `0` hides the line stroke. */
974
+ strokeWidth: number;
975
+ /** Area-fill configuration. Default: `{ visible: true }`. */
976
+ area: {
977
+ visible: boolean;
978
+ };
979
+ /**
980
+ * Whether to show an animated pulsing dot at the last data point.
981
+ */
460
982
  pulse: boolean;
983
+ /**
984
+ * Pulse cycle period in milliseconds. `false`/`0` disables the halo (both
985
+ * the drawing and the associated animation loop). Omit to inherit from
986
+ * {@link AnimationsConfig.points}.`pulseMs` (default 600 ms). When
987
+ * `animations.points.pulseMs` is `false` at the chart level the pulse is
988
+ * forced off regardless of this field.
989
+ */
990
+ pulseMs?: number | false;
461
991
  /** Stacking mode. Default: 'off'. */
462
992
  stacking: StackingMode;
993
+ /**
994
+ * Entrance animation style for new points. Style is specific to the line
995
+ * series; there is no chart-level override for style — only for duration.
996
+ * Default: `'grow'`.
997
+ *
998
+ * @see entryMs — cross-linked duration for this animation.
999
+ */
1000
+ entryAnimation?: LineEntryAnimation;
1001
+ /** @deprecated Use {@link entryAnimation} instead. */
1002
+ enterAnimation?: LineEntryAnimation;
1003
+ /**
1004
+ * Entrance animation duration in milliseconds. `false` or `0` disables the
1005
+ * per-point entrance (equivalent to `entryAnimation: 'none'`). Omit to
1006
+ * inherit from {@link AnimationsConfig.points}.`entryMs` (default 400).
1007
+ *
1008
+ * When `animations.points.entryMs` is `false` at the chart level, the
1009
+ * entrance is forced off regardless of this field.
1010
+ *
1011
+ * @see LineSeriesOptions.entryAnimation
1012
+ */
1013
+ entryMs?: number | false;
1014
+ /** @deprecated Use {@link entryMs} instead. */
1015
+ enterMs?: number | false;
1016
+ /**
1017
+ * Exponential-smoothing time constant (ms) for live-tracking the last
1018
+ * point's value under `updateLastPoint`. `0` or `false` snaps directly to
1019
+ * the target (no smoothing). Omit to inherit from
1020
+ * {@link AnimationsConfig.points}.`smoothMs` (default 70 ms).
1021
+ *
1022
+ * When `animations.points.smoothMs` is `false` at the chart level, smoothing
1023
+ * is forced off regardless of this field.
1024
+ */
1025
+ smoothMs?: number | false;
463
1026
  }
464
1027
 
465
1028
  declare interface LineSeriesProps {
466
1029
  /** Array of datasets — one per layer. A single line uses `[data]`. */
467
1030
  data: TimePoint[][];
468
1031
  options?: Partial<LineSeriesOptions>;
469
- label?: string;
470
- /** Stable series ID. Prefer this over `onSeriesId` — same value across remounts. */
1032
+ /** Stable series ID — same value across remounts. */
471
1033
  id?: string;
472
- /** @deprecated Use the `id` prop instead. */
473
- onSeriesId?: (id: string) => void;
474
1034
  }
475
1035
 
476
1036
  declare type Listener = (...args: any[]) => void;
@@ -492,7 +1052,16 @@ export declare function NumberFlow({ value, format, locale, spinDuration, classN
492
1052
 
493
1053
  declare interface NumberFlowProps {
494
1054
  value: number;
495
- format?: Intl.NumberFormatOptions;
1055
+ /**
1056
+ * Value-to-string formatter. Defaults to the current locale's
1057
+ * `Intl.NumberFormat` when omitted. Pass the shared `formatCompact` /
1058
+ * `formatPriceAdaptive` helpers or your own function to customize.
1059
+ *
1060
+ * `Intl.NumberFormatOptions` is also accepted (legacy) — it's routed
1061
+ * through the built-in `Intl.NumberFormat` for back-compat with callers
1062
+ * from before this prop was a function.
1063
+ */
1064
+ format?: ((value: number) => string) | Intl.NumberFormatOptions;
496
1065
  locale?: string;
497
1066
  spinDuration?: number;
498
1067
  className?: string;
@@ -520,42 +1089,267 @@ export declare const panda: ThemePreset;
520
1089
 
521
1090
  export declare const peachCream: ThemePreset;
522
1091
 
523
- export declare function PieLegend({ seriesId, format }: PieLegendProps): JSX_2.Element | null;
1092
+ /**
1093
+ * Collects per-frame timing, draw-call counts, per-series render ms, and heap
1094
+ * usage. All collection sites short-circuit to no-ops when no monitor is
1095
+ * attached to the chart, so the zero-perf path remains byte-identical to a
1096
+ * monitorless build.
1097
+ *
1098
+ * Not thread-safe (there are no threads) — a single chart writes to a single
1099
+ * monitor. Multiple charts may share a monitor, but `perSeries` keys must be
1100
+ * unique across charts or samples will collide.
1101
+ */
1102
+ declare class PerfMonitor {
1103
+ /** Draw-call tally map for the main layer. Mutated in place by the counting context; cleared at the start of each frame. */
1104
+ readonly drawCallsMain: Map<string, number>;
1105
+ /** Draw-call tally map for the overlay layer. Same contract as `drawCallsMain`. */
1106
+ readonly drawCallsOverlay: Map<string, number>;
1107
+ private readonly mainMs;
1108
+ private readonly overlayMs;
1109
+ private readonly mainStamps;
1110
+ private readonly overlayStamps;
1111
+ private readonly perSeriesMs;
1112
+ private readonly perSeriesStamps;
1113
+ private readonly listeners;
1114
+ private readonly windowMs;
1115
+ private readonly maxSamples;
1116
+ private readonly heapInterval;
1117
+ private heapCounter;
1118
+ private heapMb;
1119
+ private mainFrameCount;
1120
+ private overlayFrameCount;
1121
+ /** Most recent frame timestamp across either layer — anchor for age-based trimming. */
1122
+ private lastStamp;
1123
+ constructor(options?: PerfMonitorOptions);
1124
+ /** Clear the draw-call tally for the given layer. Call at the start of each frame. */
1125
+ resetDrawCalls(kind: FrameKind): void;
1126
+ /** Record a completed frame's wall-clock duration and emit stats to subscribers. */
1127
+ recordFrame(kind: FrameKind, ms: number, timestamp: number): void;
1128
+ /** Record one series renderer's time slice inside the main pass. Called from inside `renderMain`, so the enclosing `recordFrame('main', …)` trim takes care of age. */
1129
+ recordSeries(id: string, ms: number, timestamp?: number): void;
1130
+ /** Drop samples older than `windowMs` behind the newest recorded stamp, and enforce the hard per-layer cap. */
1131
+ private trimAll;
1132
+ /** Subscribe to per-frame stat snapshots. Returns an unsubscribe function. */
1133
+ onFrame(cb: (stats: PerfStats) => void): () => void;
1134
+ /** Snapshot current stats. Safe to call outside frames. */
1135
+ getStats(): PerfStats;
1136
+ destroy(): void;
1137
+ private sampleHeap;
1138
+ }
1139
+
1140
+ declare interface PerfMonitorOptions {
1141
+ /**
1142
+ * Age of the rolling window in milliseconds. Samples older than this are
1143
+ * dropped from FPS / percentile calculations. Defaults to 5000 (5 seconds).
1144
+ *
1145
+ * Tradeoff: smaller windows react faster to load changes but are noisier;
1146
+ * larger windows smooth spikes but lag. 5 s is a balance — it's long
1147
+ * enough that p95 is meaningful and short enough that numbers visibly
1148
+ * settle when you stop interacting.
1149
+ */
1150
+ windowMs?: number;
1151
+ /**
1152
+ * Hard cap on retained samples per layer. Belt-and-suspenders against a
1153
+ * runaway high-FPS layer growing the buffer unbounded. Defaults to 2000.
1154
+ */
1155
+ maxSamples?: number;
1156
+ /** Sample `performance.memory` every Nth main frame. Defaults to 30 (~0.5s at 60fps). */
1157
+ heapSampleEveryNFrames?: number;
1158
+ }
1159
+
1160
+ declare type PerfOption = NonNullable<ChartOptions['perf']>;
1161
+
1162
+ declare interface PerfStats {
1163
+ /**
1164
+ * Main-layer renders per second, derived from the interval between recent main
1165
+ * frames. Charts render on demand (data change, pan, zoom, resize, streaming
1166
+ * tick), so `0` during an idle chart is normal — not a stutter. `0` until two
1167
+ * frames have been recorded.
1168
+ */
1169
+ mainRendersPerSec: number;
1170
+ /** Same as {@link mainRendersPerSec} but for the overlay (crosshair + pulse) layer. */
1171
+ overlayRendersPerSec: number;
1172
+ /** @deprecated Alias for {@link mainRendersPerSec}. Kept for older consumers. */
1173
+ fps: number;
1174
+ mainFrameMs: FrameTimingSample;
1175
+ overlayFrameMs: FrameTimingSample;
1176
+ /** Total frames recorded per layer. Lets consumers distinguish "no data yet" from "drew once and ms is 0". */
1177
+ frameCount: {
1178
+ main: number;
1179
+ overlay: number;
1180
+ };
1181
+ drawCalls: {
1182
+ main: Record<string, number>;
1183
+ overlay: Record<string, number>;
1184
+ };
1185
+ perSeries: Record<string, FrameTimingSample>;
1186
+ /** `null` on browsers without `performance.memory` (Firefox, Safari). */
1187
+ heapMb: number | null;
1188
+ }
1189
+
1190
+ /**
1191
+ * How per-slice labels are drawn on a pie/donut.
1192
+ *
1193
+ * - `'outside'` (default) — a short leader line runs from the slice edge to a
1194
+ * text block outside the pie. Reserves horizontal space so the pie shrinks
1195
+ * to fit. Matches the canonical infographic look.
1196
+ * - `'inside'` — text sits on the slice. Auto-skipped when the measured text
1197
+ * won't fit the slice chord at the label radius.
1198
+ * - `'none'` — the renderer draws no labels (PieLegend / PieTooltip still work).
1199
+ */
1200
+ declare interface PieLabelsOptions {
1201
+ /** Default: `'outside'`. */
1202
+ mode?: 'inside' | 'outside' | 'none';
1203
+ /** What to display per label. Default: `'both'` (e.g. `"BTC 42%"`). */
1204
+ content?: 'percent' | 'label' | 'both';
1205
+ /** Font size in CSS pixels. Default: 11. */
1206
+ fontSize?: number;
1207
+ /**
1208
+ * Slices narrower than this (in degrees) get no on-pie label. Default: 2.5°
1209
+ * (≈ 0.7% of the pie). Raise when many tiny slices would crowd the
1210
+ * de-cluster pass into unreadable overlaps.
1211
+ */
1212
+ minSliceAngle?: number;
1213
+ /** Leader-line radial segment length in CSS pixels. Default: 12. */
1214
+ elbowLen?: number;
1215
+ /** Gap between leader line and text in CSS pixels. Default: 6. */
1216
+ legPad?: number;
1217
+ /**
1218
+ * Radial distance in CSS pixels from the pie's outer edge to the natural
1219
+ * label anchor point. The label then extends horizontally outward by
1220
+ * {@link railWidth} before the text starts. Clamped internally against
1221
+ * {@link elbowLen}. Default: 14.
1222
+ */
1223
+ distance?: number;
1224
+ /**
1225
+ * Horizontal length of the final leader-line segment in CSS pixels — the
1226
+ * straight tail that runs from the radial elbow out to the text anchor.
1227
+ * Higher values give a longer visible "rail" before the label. Default: 16.
1228
+ */
1229
+ railWidth?: number;
1230
+ /**
1231
+ * Minimum vertical gap between adjacent outside labels on the same side,
1232
+ * expressed as a multiplier of {@link fontSize}. Default: 1.8.
1233
+ */
1234
+ labelGap?: number;
1235
+ /**
1236
+ * @deprecated No effect in the radial per-slice layout. A label's X is
1237
+ * pinned to its slice midangle, so forcing the "other" side would flip
1238
+ * text direction without moving the label and push text across the pie.
1239
+ * Kept in the option surface for backwards compatibility.
1240
+ */
1241
+ balanceSides?: boolean;
1242
+ }
1243
+
1244
+ export declare function PieLegend({ seriesId, mode: modeProp, format, position, children }: PieLegendProps): JSX_2.Element | null;
524
1245
 
525
- export declare type PieLegendFormat = 'value' | 'percent';
1246
+ /**
1247
+ * Legend row content.
1248
+ *
1249
+ * - `'value'` — only the absolute value (e.g. `25`).
1250
+ * - `'percent'` — only the percentage (e.g. `25.0%`).
1251
+ * - `'both'` (default) — value + percent side-by-side; value is bold, percent dimmed.
1252
+ */
1253
+ export declare type PieLegendMode = 'value' | 'percent' | 'both';
1254
+
1255
+ /**
1256
+ * Where the legend sits relative to the pie canvas.
1257
+ *
1258
+ * - `'bottom'` (default) — flex sibling below the canvas. Matches the time-series `<Legend>` layout.
1259
+ * - `'right'` — flex sibling on the right of the canvas.
1260
+ * - `'overlay'` — absolute overlay on top of the canvas. Back-compat escape hatch for callers
1261
+ * that were relying on the old positioning; stacks with any `<Title>` at the top-left and can
1262
+ * collide with it, so use sparingly.
1263
+ */
1264
+ export declare type PieLegendPosition = 'bottom' | 'right' | 'overlay';
526
1265
 
527
1266
  export declare interface PieLegendProps {
528
- seriesId: string;
529
- /** Display format: 'value' shows absolute + percent, 'percent' shows only percent. Default: 'value'. */
530
- format?: PieLegendFormat;
1267
+ /**
1268
+ * Owning series id. **Optional** when omitted, the first visible pie
1269
+ * series is picked.
1270
+ */
1271
+ seriesId?: string;
1272
+ /** Default: `'both'`. See {@link PieLegendMode}. */
1273
+ mode?: PieLegendMode;
1274
+ /** Custom formatter for the absolute slice value. Default: shared `formatCompact`. */
1275
+ format?: ValueFormatter;
1276
+ /** Layout placement. Default: `'bottom'`. See {@link PieLegendPosition}. */
1277
+ position?: PieLegendPosition;
1278
+ /** Render-prop escape hatch. Receives slices + mode + format, replaces default UI. */
1279
+ children?: (ctx: PieLegendRenderContext) => ReactNode;
1280
+ }
1281
+
1282
+ /** Context passed to the {@link PieLegend} render-prop. */
1283
+ export declare interface PieLegendRenderContext {
1284
+ readonly slices: readonly SliceInfo[];
1285
+ readonly mode: PieLegendMode;
1286
+ readonly format: ValueFormatter;
531
1287
  }
532
1288
 
533
1289
  /** Pie chart series. Set `options.innerRadiusRatio` > 0 for donut. */
534
- export declare function PieSeries({ data, options, id: idProp, onSeriesId }: PieSeriesProps): null;
1290
+ export declare function PieSeries({ data, options, id: idProp }: PieSeriesProps): null;
535
1291
 
536
1292
  /** Visual options for a pie/donut series. `innerRadiusRatio > 0` makes it a donut. */
537
1293
  export declare interface PieSeriesOptions {
538
1294
  /** Palette fallback (defaults to theme.seriesColors). */
539
1295
  colors?: string[];
540
- /** 0 = pie, 0.6 = donut. Fraction of outer radius used as inner hole. */
1296
+ /** 0 = pie, 0.6 = donut. Fraction of the outer radius used as an inner hole. */
541
1297
  innerRadiusRatio: number;
542
- /** Gap between slices in radians (default 0.02). */
1298
+ /** Gap between slices in degrees. Default: 1.15° ( 0.02 rad). */
543
1299
  padAngle: number;
544
- /** Slice border color (default transparent). */
545
- strokeColor: string;
546
- /** Slice border width (default 0). */
547
- strokeWidth: number;
548
- /** Display label shown in tooltip. */
1300
+ /** Display the label shown in the tooltip. */
549
1301
  label?: string;
1302
+ /** Per-slice label rendering on the pie itself. See {@link PieLabelsOptions}. */
1303
+ sliceLabels?: PieLabelsOptions;
1304
+ /**
1305
+ * When `true`, enables pie motion effects: the outside-label entrance
1306
+ * draw-in on mount / data swap, and the hover-explode slice offset.
1307
+ * Default: `false` — labels paint fully on the first frame and hovered
1308
+ * slices stay in place (hover feedback is left to the tooltip / legend /
1309
+ * cursor). Enable for presentations where the motion adds narrative
1310
+ * value.
1311
+ */
1312
+ animate?: boolean;
1313
+ /**
1314
+ * Ambient *outer* drop-shadow behind each slice. Adds lift for flat
1315
+ * dashboards. Omit / `false` to disable (default). `true` uses soft
1316
+ * defaults; pass an object to tune individually.
1317
+ */
1318
+ shadow?: boolean | {
1319
+ /** Shadow color. Default: `rgba(0, 0, 0, 0.22)`. */
1320
+ color?: string;
1321
+ /** Blur radius in CSS pixels. Default: 24. */
1322
+ blur?: number;
1323
+ /** Horizontal offset in CSS pixels. Default: 0. */
1324
+ offsetX?: number;
1325
+ /** Vertical offset in CSS pixels. Default: 10. */
1326
+ offsetY?: number;
1327
+ };
1328
+ /**
1329
+ * Inner rim darkening — a dark band near each slice's outer edge that
1330
+ * gives the pie an "inset" / pressed look. Implemented by extending the
1331
+ * per-slice radial gradient with a dark edge stop, so it pairs naturally
1332
+ * with the existing depth gradient. Omit / `false` to disable (default).
1333
+ */
1334
+ innerShadow?: boolean | {
1335
+ /**
1336
+ * Rim color. Default: `rgba(0, 0, 0, 0.1)` (blended over the slice
1337
+ * color). Accepts any CSS color.
1338
+ */
1339
+ color?: string;
1340
+ /**
1341
+ * Fraction of the slice's radial span covered by the darkened band
1342
+ * (0..1). Default: 0.3 — darkening starts at 70% of the radius.
1343
+ */
1344
+ depth?: number;
1345
+ };
550
1346
  }
551
1347
 
552
1348
  declare interface PieSeriesProps {
553
1349
  data: PieSliceData[];
554
1350
  options?: Partial<PieSeriesOptions>;
555
- /** Stable series ID. Prefer this over `onSeriesId` — same value across remounts. */
1351
+ /** Stable series ID — same value across remounts. */
556
1352
  id?: string;
557
- /** @deprecated Use the `id` prop instead. */
558
- onSeriesId?: (id: string) => void;
559
1353
  }
560
1354
 
561
1355
  /** Optional min/max constraints for the Y axis. Omit a side for automatic scaling. */
@@ -568,10 +1362,24 @@ export declare interface PieSliceData {
568
1362
  }
569
1363
 
570
1364
  /** Tooltip for pie/donut charts. Shows hovered slice label, value, and percentage. */
571
- export declare function PieTooltip({ seriesId }: PieTooltipProps): JSX_2.Element | null;
1365
+ export declare function PieTooltip({ seriesId, format, children }: PieTooltipProps): JSX_2.Element | null;
572
1366
 
573
1367
  declare interface PieTooltipProps {
574
- seriesId: string;
1368
+ /**
1369
+ * Owning series id. **Optional** — when omitted, the first visible pie
1370
+ * series is picked.
1371
+ */
1372
+ seriesId?: string;
1373
+ /** Custom formatter for the slice value. Default: shared `formatCompact`. */
1374
+ format?: ValueFormatter;
1375
+ /** Render-prop escape hatch — receives hover info + format, replaces default UI. */
1376
+ children?: (ctx: PieTooltipRenderContext) => ReactNode;
1377
+ }
1378
+
1379
+ /** Context passed to the {@link PieTooltip} render-prop. */
1380
+ declare interface PieTooltipRenderContext {
1381
+ readonly info: HoverInfo;
1382
+ readonly format: ValueFormatter;
575
1383
  }
576
1384
 
577
1385
  export declare const quietLight: ThemePreset;
@@ -588,6 +1396,32 @@ export declare const rosePineDawn: ThemePreset;
588
1396
 
589
1397
  export declare const sandDune: ThemePreset;
590
1398
 
1399
+ /**
1400
+ * Frozen, per-row snapshot of a series at a single point in time. The shape
1401
+ * `buildHoverSnapshots` / `buildLastSnapshots` return to overlay slot
1402
+ * consumers.
1403
+ *
1404
+ * **Two identities, one snapshot:**
1405
+ * - `id` is the **row key** — guaranteed unique within the returned array,
1406
+ * safe for React `key`, Vue `:key`, Svelte `each … (key)`. For single-layer
1407
+ * series this equals the `seriesId`; for multi-layer series it's
1408
+ * `${seriesId}_layer${layerIndex}`.
1409
+ * - `seriesId` is the **owning series identity** — never carries a layer
1410
+ * suffix, not unique across multi-layer rows. Use it for filtering or
1411
+ * grouping (e.g. `snapshots.filter(s => s.seriesId === 'btc')`).
1412
+ *
1413
+ * `data` is a shallow clone, frozen along with the snapshot itself — a slot
1414
+ * consumer cannot accidentally mutate the chart's internal store.
1415
+ */
1416
+ export declare interface SeriesSnapshot {
1417
+ readonly id: string;
1418
+ readonly seriesId: string;
1419
+ readonly layerIndex?: number;
1420
+ readonly label?: string;
1421
+ readonly data: Readonly<OHLCData> | Readonly<TimePoint>;
1422
+ readonly color: string;
1423
+ }
1424
+
591
1425
  /** Supported primary series types. */
592
1426
  export declare type SeriesType = 'candlestick' | 'line' | 'bar' | 'pie';
593
1427
 
@@ -597,9 +1431,14 @@ declare interface Size {
597
1431
  height: number;
598
1432
  }
599
1433
 
1434
+ export declare type SliceInfo = HoverInfo;
1435
+
1436
+ /** Sort order applied to a snapshot list by the helper. */
1437
+ export declare type SnapshotSort = 'none' | 'asc' | 'desc';
1438
+
600
1439
  export declare const solarizedLight: ThemePreset;
601
1440
 
602
- export declare function Sparkline({ data, theme, variant, valuePosition, formatValue, label, sublabel, color, negativeColor, areaFill, width, height, lineWidth, gradient, style, }: SparklineProps): JSX_2.Element;
1441
+ export declare function Sparkline({ data, theme, variant, valuePosition, formatValue, label, sublabel, color, negativeColor, area, areaFill, width, height, strokeWidth, gradient, style, }: SparklineProps): JSX_2.Element;
603
1442
 
604
1443
  export declare interface SparklineProps {
605
1444
  data: TimePoint[];
@@ -619,13 +1458,17 @@ export declare interface SparklineProps {
619
1458
  /** Secondary color for negative bars */
620
1459
  negativeColor?: string;
621
1460
  /** Show area fill under line */
1461
+ area?: {
1462
+ visible: boolean;
1463
+ };
1464
+ /** @deprecated Use {@link area} instead. */
622
1465
  areaFill?: boolean;
623
1466
  /** Chart width (default: 140) */
624
1467
  width?: number;
625
1468
  /** Overall height (default: 48) */
626
1469
  height?: number;
627
- /** Line width (default: 1.5) */
628
- lineWidth?: number;
1470
+ /** Stroke width in CSS pixels (default: 1.5) */
1471
+ strokeWidth?: number;
629
1472
  /** Show chart background gradient (default: true) */
630
1473
  gradient?: boolean;
631
1474
  /** Container style override */
@@ -675,13 +1518,17 @@ export declare interface ThemePreset {
675
1518
 
676
1519
  export declare const ThemeProvider: Provider<ChartTheme | null>;
677
1520
 
678
- /** All built-in themes keyed by display name */
679
- export declare const themes: Record<string, ThemePreset>;
680
-
681
- declare function TimeAxis(): JSX_2.Element;
1521
+ declare function TimeAxis({ labelCount, minLabelSpacing }?: TimeAxisProps): JSX_2.Element;
682
1522
  export { TimeAxis }
683
1523
  export { TimeAxis as XAxis }
684
1524
 
1525
+ declare interface TimeAxisProps {
1526
+ /** Desired number of labels (≥ 2). Overrides chart-level `axis.x.labelCount`. */
1527
+ labelCount?: number;
1528
+ /** Minimum pixel gap between adjacent labels (hard floor). Overrides chart-level. */
1529
+ minLabelSpacing?: number;
1530
+ }
1531
+
685
1532
  /** A single time-value data point for line and bar series. Time is a timestamp in milliseconds. */
686
1533
  export declare interface TimePoint {
687
1534
  time: number;
@@ -698,19 +1545,50 @@ declare class TimeScale {
698
1545
  private to;
699
1546
  private width;
700
1547
  private pixelRatio;
701
- update(range: VisibleRange, mediaWidth: number, pixelRatio: number): void;
1548
+ private dataInterval;
1549
+ private labelCountHintValue;
1550
+ private minSpacingValue;
1551
+ private resolvedInterval;
1552
+ private lastInterval;
1553
+ /**
1554
+ * "want" (desired interval post-floor) at the time lastInterval was snapped.
1555
+ * Hysteresis band anchors to this so micro range drift back across a tier
1556
+ * boundary doesn't flip the label density — mirrors YScale, see there for
1557
+ * the full reasoning.
1558
+ */
1559
+ private lastWant;
1560
+ /** Identifies the dataInterval bucket whose tier list drove lastInterval. */
1561
+ private lastBucketKey;
1562
+ private get labelCountHint();
1563
+ private get minLabelSpacing();
1564
+ update(range: VisibleRange, mediaWidth: number, pixelRatio: number, dataInterval?: number): void;
1565
+ setLabelCount(n: number | null | undefined): void;
1566
+ setMinSpacing(px: number | null | undefined): void;
1567
+ private resetHysteresis;
702
1568
  timeToX(time: number): number;
703
1569
  timeToBitmapX(time: number): number;
704
1570
  xToTime(x: number): number;
705
1571
  pixelDeltaToTimeDelta(pixelDelta: number): number;
706
1572
  barWidthMedia(dataInterval: number): number;
707
1573
  barWidthBitmap(dataInterval: number): number;
1574
+ /**
1575
+ * Evenly spaced "nice" tick times. Resolution uses the cached
1576
+ * `dataInterval` set by `update()`; callers that bypass `update()` can
1577
+ * still pass it here for back-compat.
1578
+ */
708
1579
  niceTickValues(dataInterval: number): {
709
1580
  ticks: number[];
710
1581
  tickInterval: number;
711
1582
  };
712
1583
  getRange(): VisibleRange;
713
1584
  getMediaWidth(): number;
1585
+ /**
1586
+ * Resolve the next tick interval. Walks `niceTimeIntervals(dataInterval)`
1587
+ * looking for the smallest tier ≥ desired spacing; retains the previous
1588
+ * tier inside a ratio band so micro pan/zoom doesn't flip label density.
1589
+ */
1590
+ private resolveInterval;
1591
+ private countTicks;
714
1592
  }
715
1593
 
716
1594
  /**
@@ -720,25 +1598,116 @@ declare class TimeScale {
720
1598
  export declare type TimeValue = number | Date;
721
1599
 
722
1600
  /**
723
- * Two-part tooltip:
724
- * 1. Compact legend always visible in top-left
725
- * 2. Floating glass tooltip near cursor on hover
1601
+ * Chart title / subtitle bar rendered as a flex row above the chart canvas
1602
+ * (above {@link InfoBar} when both are present). Hoisted out of the
1603
+ * overlay layer by {@link ChartContainer}, so browser flex layout reserves
1604
+ * its height and ResizeObserver drives a Y-range recompute.
1605
+ *
1606
+ * Place it at the top of a chart's children — its slot in the DOM is
1607
+ * determined by `ChartContainer`, not by source order:
1608
+ * ```tsx
1609
+ * <ChartContainer>
1610
+ * <Title sub="Live Candlestick">BTC/USD</Title>
1611
+ * <InfoBar />
1612
+ * <CandlestickSeries data={data} />
1613
+ * ...
1614
+ * </ChartContainer>
1615
+ * ```
1616
+ */
1617
+ export declare function Title({ children, sub, style }: TitleProps): JSX_2.Element;
1618
+
1619
+ /** Props for the {@link Title} component. */
1620
+ export declare interface TitleProps {
1621
+ /** Primary label (e.g. "BTC/USD"). */
1622
+ children?: ReactNode;
1623
+ /**
1624
+ * Secondary label rendered in a muted colour next to the primary one (e.g.
1625
+ * "Live Candlestick", "1m", series count).
1626
+ */
1627
+ sub?: ReactNode;
1628
+ /** Extra styles merged onto the flex row. */
1629
+ style?: CSSProperties;
1630
+ }
1631
+
1632
+ /**
1633
+ * Floating near-cursor glass tooltip that appears while hovering the chart.
1634
+ *
1635
+ * Hover-only: without a crosshair position, the component renders `null`.
1636
+ * The companion {@link InfoBar} shows last-known values when no hover is active.
726
1637
  */
727
- export declare function Tooltip({ seriesId, sort, showLegend, legend }: TooltipProps): JSX_2.Element | null;
1638
+ export declare function Tooltip({ sort, format, children }: TooltipProps): JSX_2.Element | null;
1639
+
1640
+ /** Fields surfaced to a `Tooltip` / `InfoBar` formatter. */
1641
+ export declare type TooltipField = 'open' | 'high' | 'low' | 'close' | 'volume' | 'value';
1642
+
1643
+ /** Signature for the tooltip formatter — single prop, field hint. */
1644
+ export declare type TooltipFormatter = (value: number, field: TooltipField) => string;
1645
+
1646
+ export declare interface TooltipPosition {
1647
+ left: number;
1648
+ top: number;
1649
+ }
1650
+
1651
+ /**
1652
+ * Pure positioning for a floating tooltip. Flip the tooltip to the other
1653
+ * side of the cursor when the preferred side would overflow, then clamp
1654
+ * into `[0, chart - size]` so the flipped side can't spill past the
1655
+ * opposite edge either.
1656
+ *
1657
+ * Works for time-series tooltips anchoring to the crosshair and pie
1658
+ * tooltips anchoring to a slice centroid alike — pass the chart bounds
1659
+ * that match the overlay container (media size minus axis gutters for
1660
+ * time-series, full media size for pie).
1661
+ */
1662
+ export declare interface TooltipPositionArgs {
1663
+ /** Cursor or anchor x in CSS pixels, relative to the chart area origin. */
1664
+ x: number;
1665
+ /** Cursor or anchor y in CSS pixels, relative to the chart area origin. */
1666
+ y: number;
1667
+ /** Plot-area width in CSS pixels. */
1668
+ chartWidth: number;
1669
+ /** Plot-area height in CSS pixels. */
1670
+ chartHeight: number;
1671
+ /** Tooltip box width in CSS pixels. */
1672
+ tooltipWidth: number;
1673
+ /** Tooltip box height in CSS pixels. */
1674
+ tooltipHeight: number;
1675
+ /** Gap between anchor and tooltip. Defaults to 16. */
1676
+ offsetX?: number;
1677
+ offsetY?: number;
1678
+ }
728
1679
 
729
1680
  /**
730
1681
  * Props for the {@link Tooltip} component.
731
- * By default shows all series. Use `seriesId` to show one, or `sort` to order values.
1682
+ * Renders the built-in floating glass panel by default. Pass a render-prop
1683
+ * child to replace its *contents* — the positioned container (with flip/clamp)
1684
+ * stays.
732
1685
  */
733
- declare interface TooltipProps {
734
- /** Show only this series. When omitted, show all series. */
735
- seriesId?: string;
736
- /** Sort order for line values when showing all series (default: 'none'). */
1686
+ export declare interface TooltipProps {
1687
+ /** Sort order for line values (default: 'none'). */
737
1688
  sort?: TooltipSort;
738
- /** Show the compact legend strip in the top-left corner (default: true). */
739
- showLegend?: boolean;
740
- /** @deprecated Use `showLegend` instead. */
741
- legend?: boolean;
1689
+ /**
1690
+ * Custom formatter for every displayed number in the default UI. Called per
1691
+ * row with the field hint (`'open' | 'high' | 'low' | 'close' | 'volume' | 'value'`).
1692
+ * Defaults: adaptive precision for ohlc/value, compact (K/M/B/T) for volume.
1693
+ * Ignored when {@link children} is a render-prop.
1694
+ */
1695
+ format?: TooltipFormatter;
1696
+ /**
1697
+ * Render-prop escape hatch. Receives the hover snapshots and replaces the
1698
+ * built-in panel contents. The floating container (positioning, blur glass,
1699
+ * clamping) is preserved — use
1700
+ * [`computeTooltipPosition`](../../core/src/tooltip-position.ts) directly if
1701
+ * you need your own container.
1702
+ */
1703
+ children?: (ctx: TooltipRenderContext) => ReactNode;
1704
+ }
1705
+
1706
+ /** Context passed to the {@link Tooltip} render-prop. */
1707
+ export declare interface TooltipRenderContext {
1708
+ readonly snapshots: readonly SeriesSnapshot[];
1709
+ /** Crosshair timestamp — the tooltip is hover-only, so this is always a real hover time. */
1710
+ readonly time: number;
742
1711
  }
743
1712
 
744
1713
  /** Sort order for multi-series tooltip values. */
@@ -771,6 +1740,28 @@ export declare function useVisibleRange(chart: ChartInstance): VisibleRange;
771
1740
 
772
1741
  export declare function useYRange(chart: ChartInstance): YRange;
773
1742
 
1743
+ /**
1744
+ * Default value formatters used across the chart library.
1745
+ *
1746
+ * Two shapes are exposed:
1747
+ * - `formatCompact` — compact K/M/B/T suffixes for large magnitudes, adaptive
1748
+ * precision for values < 1. Intended for axis labels, legends, pie slices.
1749
+ * - `formatPriceAdaptive` — full-precision display that scales decimal count
1750
+ * to the magnitude. Intended for tooltip rows where users want to read the
1751
+ * exact number.
1752
+ *
1753
+ * Both handle the full range of JS numbers the library is likely to see:
1754
+ * - 0, ±Infinity, NaN
1755
+ * - BTC-style sub-cent prices (0.00001234 → "0.00001234", not "1.234e-5")
1756
+ * - trillion-scale values (5.4e12 → "5.40T")
1757
+ * - Number.MAX_SAFE_INTEGER falls back to scientific notation (>= 1e15)
1758
+ *
1759
+ * Consumers can import these directly, or swap them in per-component via the
1760
+ * `format` prop on Tooltip/InfoBar/YAxis/etc. (see framework wrappers).
1761
+ */
1762
+ /** Signature for a value-to-string formatter (YAxis, YLabel, Pie, Sparkline, NumberFlow). */
1763
+ export declare type ValueFormatter = (value: number) => string;
1764
+
774
1765
  /** Time range (timestamps in milliseconds) of the currently visible portion of the chart. */
775
1766
  export declare interface VisibleRange {
776
1767
  from: number;
@@ -779,17 +1770,17 @@ export declare interface VisibleRange {
779
1770
 
780
1771
  /** Configuration for the X (time) axis. */
781
1772
  export declare interface XAxisConfig {
782
- /** Height in pixels. Default: 30. */
1773
+ /** Height in CSS pixels. Default: 30. */
783
1774
  height?: number;
784
1775
  /** Whether the axis is visible. Default: true. When false, height is treated as 0. */
785
1776
  visible?: boolean;
786
1777
  }
787
1778
 
788
- export declare function YAxis(): JSX_2.Element;
1779
+ export declare function YAxis({ format, labelCount, minLabelSpacing }?: YAxisProps): JSX_2.Element;
789
1780
 
790
1781
  /** Configuration for the Y axis. */
791
1782
  export declare interface YAxisConfig {
792
- /** Width in pixels. Default: 55. */
1783
+ /** Width in CSS pixels. Default: 55. */
793
1784
  width?: number;
794
1785
  /** Minimum bound. Default: 'auto'. */
795
1786
  min?: AxisBound;
@@ -799,12 +1790,59 @@ export declare interface YAxisConfig {
799
1790
  visible?: boolean;
800
1791
  }
801
1792
 
802
- export declare function YLabel({ seriesId, color }: YLabelProps): JSX_2.Element | null;
1793
+ export declare interface YAxisProps {
1794
+ /**
1795
+ * Custom tick-label formatter. When supplied, overrides the built-in
1796
+ * range-adaptive formatter for this axis.
1797
+ */
1798
+ format?: ValueFormatter;
1799
+ /**
1800
+ * Desired number of labels (≥ 2). Overrides any chart-level `axis.y.labelCount`.
1801
+ * Realized count may differ ±1 after the 1-2-5 snap.
1802
+ */
1803
+ labelCount?: number;
1804
+ /** Minimum pixel gap between adjacent labels (hard floor). Overrides chart-level. */
1805
+ minLabelSpacing?: number;
1806
+ }
1807
+
1808
+ export declare function YLabel({ seriesId, color, format, children }: YLabelProps): JSX_2.Element | null;
1809
+
1810
+ /** Direction of the current value vs. previous close. Drives the badge color in the default UI. */
1811
+ declare type YLabelDirection = 'up' | 'down' | 'neutral';
803
1812
 
804
1813
  declare interface YLabelProps {
805
- seriesId: string;
1814
+ /**
1815
+ * Owning series id. **Optional** — when omitted, the first visible
1816
+ * single-layer time series is picked, falling back to the first visible
1817
+ * multi-layer time series. `null` (no compatible series) renders nothing.
1818
+ */
1819
+ seriesId?: string;
806
1820
  /** Override badge color (e.g. line color). If not set, uses up/down/neutral from theme. */
807
1821
  color?: string;
1822
+ /**
1823
+ * Custom formatter. Routed through NumberFlow as its `format` prop so the
1824
+ * digit-by-digit animation still plays on the output string — NumberFlow
1825
+ * animates whichever characters the formatter returns.
1826
+ */
1827
+ format?: ValueFormatter;
1828
+ /**
1829
+ * Render-prop escape hatch. Receives the resolved value, pixel position, and
1830
+ * direction metadata. Replaces the built-in badge + dashed line entirely.
1831
+ */
1832
+ children?: (ctx: YLabelRenderContext) => ReactNode;
1833
+ }
1834
+
1835
+ /** Context passed to the {@link YLabel} render-prop. */
1836
+ declare interface YLabelRenderContext {
1837
+ readonly value: number;
1838
+ /** Pixel Y of the badge anchor (already account for current viewport). */
1839
+ readonly y: number;
1840
+ /** Final background color chosen by the built-in UI — handy if you want to match the dashed line accent. */
1841
+ readonly bgColor: string;
1842
+ /** `true` while the chart is tracking a live last point (still mutating). */
1843
+ readonly isLive: boolean;
1844
+ readonly direction: YLabelDirection;
1845
+ readonly format: ValueFormatter;
808
1846
  }
809
1847
 
810
1848
  /** Min/max value range for the Y axis. */
@@ -822,20 +1860,69 @@ declare class YScale {
822
1860
  private max;
823
1861
  private height;
824
1862
  private pixelRatio;
1863
+ private labelCountHintValue;
1864
+ private minSpacingValue;
1865
+ /** Cached {1,2,5}×10^k interval resolved at update-time; null when degenerate. */
1866
+ private resolvedInterval;
1867
+ /** Previous resolved interval — drives the ratio-band hysteresis. */
1868
+ private lastInterval;
1869
+ /**
1870
+ * rawInterval at the moment `lastInterval` was snapped. Anchoring the
1871
+ * hysteresis band to *raw-at-snap* (not `lastInterval`) is what creates
1872
+ * naturally asymmetric tier hysteresis: after an escalation the band
1873
+ * starts just above the tier boundary, so live range drift back across
1874
+ * the boundary doesn't instantly flip back.
1875
+ */
1876
+ private lastRawInterval;
1877
+ /** Custom formatter (driven by `<YAxis format=…>`). */
1878
+ private customFormat;
1879
+ private get labelCountHint();
1880
+ private get minLabelSpacing();
825
1881
  /** Recalculate the scale with a new Y range, chart height, and device pixel ratio. */
826
1882
  update(range: YRange, mediaHeight: number, pixelRatio: number): void;
1883
+ /** Desired label count. Invalid values (NaN, <2, Infinity) clear the hint. */
1884
+ setLabelCount(n: number | null | undefined): void;
1885
+ /** Minimum pixel gap between adjacent labels. */
1886
+ setMinSpacing(px: number | null | undefined): void;
1887
+ private resetHysteresis;
1888
+ /** Install (or clear) a custom formatter. */
1889
+ setFormat(fn: ValueFormatter | null): void;
1890
+ /** Read back the currently installed formatter — null when the built-in is active. */
1891
+ getFormat(): ValueFormatter | null;
827
1892
  /** Convert a value to a Y position in CSS (media) pixels. */
828
1893
  valueToY(value: number): number;
829
1894
  /** Convert a value to a Y position in physical (bitmap) pixels. */
830
1895
  valueToBitmapY(value: number): number;
831
1896
  /** Convert a Y position in CSS pixels back to a value. */
832
1897
  yToValue(y: number): number;
833
- /** Generate evenly spaced "nice" tick values that fit the current range and chart height. */
1898
+ /**
1899
+ * Generate evenly spaced "nice" tick values for the current range and
1900
+ * chart height. Pure read — resolution happens in `update()`.
1901
+ */
834
1902
  niceTickValues(): number[];
835
1903
  getRange(): YRange;
836
1904
  getMediaHeight(): number;
837
- /** Format a value for display, adapting decimal places to the visible range magnitude. */
1905
+ /**
1906
+ * Format a tick value for display. When a custom formatter is installed
1907
+ * (via {@link setFormat} or the `format` prop on `<YAxis>`), it wins;
1908
+ * otherwise decimals are driven by the resolved tick interval — so
1909
+ * ticks at step 100 never render as "42000.0" just because the visible
1910
+ * range happens to be < 1000.
1911
+ */
838
1912
  formatY(value: number): string;
1913
+ /**
1914
+ * Resolve the interval to a {1,2,5}×10^k tick size that satisfies the pixel
1915
+ * floor and (optionally) targets `labelCountHint` labels.
1916
+ *
1917
+ * Hysteresis: the band [0.8×, 1.25×] is anchored to **rawInterval at last
1918
+ * snap**, not to `lastInterval`. After an escalation (e.g. 100 → 200),
1919
+ * `lastRawInterval` sits just past the tier boundary, so typical live
1920
+ * range drift back across the boundary stays inside the band and does
1921
+ * *not* de-escalate. This prevents the 2-tier flicker users see when new
1922
+ * candles nudge the Y range across a raw-interval threshold.
1923
+ */
1924
+ private resolveInterval;
1925
+ private countTicks;
839
1926
  }
840
1927
 
841
1928
  export { }