@wick-charts/vue 0.5.1 → 0.6.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 (80) hide show
  1. package/README.md +18 -3
  2. package/dist/BarSeries.vue.js +26 -20
  3. package/dist/CandlestickSeries.vue.js +30 -24
  4. package/dist/ChartContainer.vue.js +88 -65
  5. package/dist/HeatmapSeries.vue.js +40 -0
  6. package/dist/HeatmapSeries.vue2.js +4 -0
  7. package/dist/LineSeries.vue.js +26 -20
  8. package/dist/PieSeries.vue.js +24 -18
  9. package/dist/core/dist/animation/animator.js +21 -21
  10. package/dist/core/dist/animation/config.js +114 -87
  11. package/dist/core/dist/animation/easing.js +3 -1
  12. package/dist/core/dist/animation/intro-wave.js +68 -0
  13. package/dist/core/dist/axis/dom-labels.js +13 -14
  14. package/dist/core/dist/chart/badge-animator.js +56 -0
  15. package/dist/core/dist/chart/edge-indicators.js +28 -13
  16. package/dist/core/dist/chart/last-value.js +18 -17
  17. package/dist/core/dist/chart/y-target.js +13 -12
  18. package/dist/core/dist/chart.js +679 -452
  19. package/dist/core/dist/components/annotation-chip.js +28 -0
  20. package/dist/core/dist/components/edge-indicator.js +58 -26
  21. package/dist/core/dist/components/loading-indicator.js +46 -0
  22. package/dist/core/dist/components/marker.js +49 -31
  23. package/dist/core/dist/components/reference-line.js +27 -36
  24. package/dist/core/dist/components/time-region.js +30 -28
  25. package/dist/core/dist/data/store.js +38 -19
  26. package/dist/core/dist/data/sync.js +60 -38
  27. package/dist/core/dist/heatmap-tooltip.js +16 -0
  28. package/dist/core/dist/interactions/handler.js +67 -43
  29. package/dist/core/dist/perf/perf-hud.js +5 -5
  30. package/dist/core/dist/scales/x-scale.js +85 -37
  31. package/dist/core/dist/scales/y-scale.js +38 -27
  32. package/dist/core/dist/series/bar-intro.js +18 -0
  33. package/dist/core/dist/series/bar.js +337 -205
  34. package/dist/core/dist/series/base-multi-layer.js +224 -96
  35. package/dist/core/dist/series/candlestick-intro.js +57 -0
  36. package/dist/core/dist/series/candlestick.js +454 -276
  37. package/dist/core/dist/series/heatmap.js +387 -0
  38. package/dist/core/dist/series/line-intro.js +85 -0
  39. package/dist/core/dist/series/line.js +590 -282
  40. package/dist/core/dist/series/painters/builtins.js +2 -2
  41. package/dist/core/dist/series/pie.js +396 -259
  42. package/dist/core/dist/series/register-builtins.js +8 -7
  43. package/dist/core/dist/series/types.js +6 -0
  44. package/dist/core/dist/series/wave-intro.js +26 -0
  45. package/dist/core/dist/theme/create.js +69 -63
  46. package/dist/core/dist/theme/themes/mint-breeze.js +2 -2
  47. package/dist/core/dist/theme/themes/sand-dune.js +2 -2
  48. package/dist/core/dist/utils/color.js +109 -34
  49. package/dist/core/dist/utils/deep-equal.js +18 -0
  50. package/dist/core/dist/utils/time.js +25 -19
  51. package/dist/index.d.ts +1405 -123
  52. package/dist/index.js +177 -130
  53. package/dist/ui/Crosshair.vue.js +20 -21
  54. package/dist/ui/HeatmapTooltip.vue.js +96 -0
  55. package/dist/ui/HeatmapTooltip.vue2.js +4 -0
  56. package/dist/ui/InfoBar.vue.js +46 -47
  57. package/dist/ui/Legend.vue.js +80 -79
  58. package/dist/ui/PieLegend.vue.js +5 -5
  59. package/dist/ui/PieTooltip.vue.js +77 -67
  60. package/dist/ui/Tooltip.vue.js +60 -60
  61. package/dist/ui/YAxis.vue.js +2 -2
  62. package/dist/ui/YLabel.vue.js +94 -61
  63. package/package.json +2 -2
  64. package/src/BarSeries.vue +23 -7
  65. package/src/CandlestickSeries.vue +23 -7
  66. package/src/ChartContainer.vue +134 -12
  67. package/src/HeatmapSeries.vue +69 -0
  68. package/src/LineSeries.vue +23 -7
  69. package/src/PieSeries.vue +22 -6
  70. package/src/index.ts +84 -3
  71. package/src/ui/Crosshair.vue +1 -2
  72. package/src/ui/HeatmapTooltip.vue +144 -0
  73. package/src/ui/InfoBar.vue +1 -2
  74. package/src/ui/Legend.vue +13 -0
  75. package/src/ui/Marker.vue +1 -1
  76. package/src/ui/PieTooltip.vue +10 -0
  77. package/src/ui/ReferenceLine.vue +2 -2
  78. package/src/ui/TimeRegion.vue +2 -2
  79. package/src/ui/Tooltip.vue +2 -2
  80. package/src/ui/YLabel.vue +123 -8
@@ -1,62 +1,76 @@
1
- import { AnimationConfig as z, DEFAULT_LINE_PULSE as N } from "./animation/config.js";
2
- import { createViewportEngine as H } from "./animation/viewport-engine.js";
1
+ import { AnimationConfig as F, DEFAULT_LINE_PULSE as E } from "./animation/config.js";
2
+ import { easeOutCubic as L } from "./animation/easing.js";
3
+ import { createViewportEngine as N } from "./animation/viewport-engine.js";
3
4
  import { CanvasManager as W } from "./canvas-manager.js";
4
- import { drawEdgeIndicators as E, resolveEdgeBoundary as F } from "./chart/edge-indicators.js";
5
- import { computeFitToData as O } from "./chart/fit-to-data.js";
6
- import { getLastValue as Y, getPreviousClose as B, getStackedLastValue as X } from "./chart/last-value.js";
7
- import { resolveMaxVisibleBars as _, resolvePadding as D, resolvePerfOptions as $, isSameHorizontalPadding as T } from "./chart/options.js";
8
- import { computePan as U, computeZoom as Z } from "./chart/pan-zoom-math.js";
9
- import { StreamingCadence as G } from "./chart/streaming-cadence.js";
10
- import { computeStreamingTarget as j } from "./chart/streaming-target.js";
11
- import { resolvePaddingTime as C } from "./chart/viewport-padding.js";
12
- import { computeTargetYRange as q, resolveBound as M } from "./chart/y-target.js";
13
- import { renderCrosshair as J } from "./components/crosshair.js";
14
- import { renderGrid as K } from "./components/grid.js";
15
- import { renderMarker as Q } from "./components/marker.js";
16
- import { renderReferenceLine as tt } from "./components/reference-line.js";
17
- import { resolveTimeRegionColors as it, renderTimeRegion as et } from "./components/time-region.js";
18
- import { EventEmitter as st } from "./events.js";
19
- import { InteractionHandler as rt } from "./interactions/handler.js";
20
- import { RenderScheduler as R } from "./render-scheduler.js";
21
- import { XScale as nt } from "./scales/x-scale.js";
22
- import { YScale as at } from "./scales/y-scale.js";
23
- import { resolveSeriesDefinition as ht } from "./series/definition.js";
24
- import { catppuccin as ot } from "./theme/themes/catppuccin.js";
25
- import { normalizeTime as g, detectInterval as lt } from "./utils/time.js";
26
- let ct = 0, dt = 0, mt = 0, ut = 0;
27
- function I(m, t) {
28
- return m === t ? !0 : !m || !t || m.length !== t.length ? !1 : m.every((i, e) => i === t[e]);
5
+ import { resolveEdgeAnchorValue as O, drawEdgeIndicators as Y, resolveEdgeBoundary as B } from "./chart/edge-indicators.js";
6
+ import { computeFitToData as X } from "./chart/fit-to-data.js";
7
+ import { getLastValue as Z, getPreviousClose as _, getStackedLastValue as U } from "./chart/last-value.js";
8
+ import { resolveMaxVisibleBars as $, resolvePadding as C, resolvePerfOptions as G, isSameHorizontalPadding as R } from "./chart/options.js";
9
+ import { computePan as q, computeZoom as K } from "./chart/pan-zoom-math.js";
10
+ import { StreamingCadence as j } from "./chart/streaming-cadence.js";
11
+ import { computeStreamingTarget as J } from "./chart/streaming-target.js";
12
+ import { resolvePaddingTime as M } from "./chart/viewport-padding.js";
13
+ import { computeTargetYRange as Q, resolveBound as I } from "./chart/y-target.js";
14
+ import { renderCrosshair as tt } from "./components/crosshair.js";
15
+ import { renderGrid as it } from "./components/grid.js";
16
+ import { renderMarker as et } from "./components/marker.js";
17
+ import { renderReferenceLine as st } from "./components/reference-line.js";
18
+ import { resolveTimeRegionColors as rt, renderTimeRegion as nt } from "./components/time-region.js";
19
+ import { EventEmitter as at } from "./events.js";
20
+ import { InteractionHandler as ht } from "./interactions/handler.js";
21
+ import { RenderScheduler as P } from "./render-scheduler.js";
22
+ import { XScale as ot } from "./scales/x-scale.js";
23
+ import { YScale as lt } from "./scales/y-scale.js";
24
+ import { resolveSeriesDefinition as ct } from "./series/definition.js";
25
+ import { isTimeSeriesRenderer as S } from "./series/types.js";
26
+ import { catppuccin as dt } from "./theme/themes/catppuccin.js";
27
+ import { clamp as mt } from "./utils/math.js";
28
+ import { normalizeTime as y, detectInterval as ut } from "./utils/time.js";
29
+ const ft = 200, gt = 1500;
30
+ let pt = 0, yt = 0, vt = 0, bt = 0;
31
+ function H(f, t) {
32
+ return f === t ? !0 : !f || !t || f.length !== t.length ? !1 : f.every((i, e) => i === t[e]);
29
33
  }
30
- function ft(m, t) {
31
- return m === t ? !0 : !m || !t || m.length !== t.length ? !1 : m.every((i, e) => i === t[e]);
34
+ function St(f, t) {
35
+ return f === t ? !0 : !f || !t || f.length !== t.length ? !1 : f.every((i, e) => i === t[e]);
32
36
  }
33
- class Ot extends st {
37
+ class qt extends at {
34
38
  /** Canvas lifecycle and DPR-aware sizing. */
35
- #n;
39
+ #a;
36
40
  /** Schedules main-layer redraws (background, grid, series). */
37
- #i;
41
+ #e;
38
42
  /** Schedules overlay redraws (crosshair). */
39
- #l;
43
+ #h;
40
44
  /** Maps time values to horizontal pixel coordinates. */
41
45
  timeScale;
42
46
  /** Maps price/value to vertical pixel coordinates. */
43
47
  yScale;
44
48
  /** Zoom, pan, crosshair — null when interactive=false. */
45
- #W;
49
+ #C;
46
50
  /** All registered series (candlestick, line, bar, pie). */
47
51
  #t = [];
48
52
  /** Point annotations (event markers) drawn on the overlay layer, kept out of the series model. */
49
53
  #m = [];
50
54
  /** Time-region bands drawn on the main layer behind the series, kept out of the series model. */
51
- #v = [];
55
+ #b = [];
52
56
  /** Reference lines (horizontal threshold / vertical event) drawn on the overlay layer, kept out of the series model. */
53
- #g = [];
57
+ #y = [];
58
+ /**
59
+ * Overlay-intro latch. Annotations ride the *initial* reveal, then stay put.
60
+ * `#overlayIntroSeen` flips once we've actually watched an intro in progress
61
+ * (guards against latching on an empty chart before any data arrives);
62
+ * `#overlayIntroSettled` freezes the front at `1` afterward so a series added
63
+ * later — which re-arms `getIntroFront()`'s global min — can't blink settled
64
+ * markers / reference lines back out.
65
+ */
66
+ #tt = !1;
67
+ #it = !1;
54
68
  /** Active visual theme (colors, fonts, grid style). */
55
- #r;
69
+ #n;
56
70
  /** Whether to render the background grid. */
57
- #E;
71
+ #O;
58
72
  /** Detected time interval between data points (milliseconds). */
59
- #a = 6e4;
73
+ #r = 6e4;
60
74
  /** Current crosshair position, null when cursor is outside the chart. */
61
75
  #d = null;
62
76
  /**
@@ -66,30 +80,45 @@ class Ot extends st {
66
80
  * `time`/`y`; `'time'` (programmatic {@link setCrosshair}) keeps
67
81
  * `time`/`y` and re-projects the pixel position.
68
82
  */
69
- #F = "pointer";
83
+ #Y = "pointer";
70
84
  /** Whether the last overlay pass painted anything — gates the full-bitmap
71
85
  * clear so an empty overlay isn't cleared again every main frame. */
72
- #q = !1;
86
+ #et = !1;
73
87
  /** User-specified Y-axis bounds (auto, fixed, percentage). */
74
- #h = {};
88
+ #l = {};
75
89
  /** Cached series ID list — invalidated on add/remove. */
76
- #A = null;
90
+ #T = null;
77
91
  /** Axis visibility and sizing configuration. */
78
- #I = {};
92
+ #H = {};
79
93
  /** Host-declared state per edge — drives the edge-indicator overlay. */
80
- #x = { left: "idle", right: "idle" };
94
+ #S = { left: "idle", right: "idle" };
95
+ /** Wall-clock start of a `loading` exit fade per side — set when the host
96
+ * drops `loading` back to `idle`/`has-more`, so the indicator fades out
97
+ * over {@link EDGE_EXIT_FADE_MS} instead of vanishing the same frame the
98
+ * fetched data lands. `null` = no fade in flight. */
99
+ #V = { left: null, right: null };
81
100
  /** Cached boundary timestamps from the last `edgeReached` emission, by side. */
82
- #J = { left: null, right: null };
101
+ #st = { left: null, right: null };
102
+ /** Latest placeholder shapes a `loading` indicator reported per side —
103
+ * the morph hand-off candidate consumed by {@link prependData}. */
104
+ #B = { left: null, right: null };
105
+ /** True after a prepend consumed a side's placeholder snapshot: the series'
106
+ * reveal owns those pixels now, so the still-`loading` indicator is hidden
107
+ * and the upcoming `loading → idle` transition skips its exit fade. Reset
108
+ * by the next {@link setEdgeState} call. */
109
+ #R = { left: !1, right: !1 };
83
110
  /** Host-supplied callback fired when the user releases a pan/zoom past a data edge. */
84
- #K;
111
+ #rt;
112
+ /** Per-side custom painter for the edge `loading` spinner. `null` falls back to the built-in dots. */
113
+ #nt = { left: null, right: null };
85
114
  /** Nesting depth for batch updates. Suppresses recomputes while > 0. */
86
- #w = 0;
115
+ #A = 0;
87
116
  /** True when batched operations include data changes (triggers full onDataChanged on end). */
88
- #O = !1;
117
+ #X = !1;
89
118
  /** True when batched operations include visibility changes (triggers Y-range + redraw on end). */
90
- #C = !1;
119
+ #M = !1;
91
120
  /** True when batched operations bumped overlay version (emits a single overlayChange on flush). */
92
- #Y = !1;
121
+ #Z = !1;
93
122
  /**
94
123
  * Monotonic counter bumped on any mutation that affects overlay output —
95
124
  * data, visibility, series options, theme. Used by snapshot helpers in
@@ -99,17 +128,17 @@ class Ot extends st {
99
128
  *
100
129
  * @internal
101
130
  */
102
- #Q = 0;
131
+ #at = 0;
103
132
  get yAxisWidth() {
104
- const t = this.#I.y;
133
+ const t = this.#H.y;
105
134
  return t?.visible === !1 ? 0 : t?.width ?? 55;
106
135
  }
107
136
  get xAxisHeight() {
108
- const t = this.#I.x;
137
+ const t = this.#H.x;
109
138
  return t?.visible === !1 ? 0 : t?.height ?? 30;
110
139
  }
111
140
  /** Resolved animation config derived from `options.animations` at construction. */
112
- #f;
141
+ #g;
113
142
  /**
114
143
  * Single source of truth for the chart's X / Y viewport animation.
115
144
  * Push-model API: chart emits intent signals (`onPointAppended`,
@@ -118,7 +147,7 @@ class Ot extends st {
118
147
  * matching targets back through `computeXTarget` / `computeYTarget`
119
148
  * closures supplied at construction.
120
149
  */
121
- #e;
150
+ #s;
122
151
  /** EMA-smoothed real-wall-clock cadence between streaming appends. The
123
152
  * X spring's `setSettleMs` is fed from this so the settle time stays just
124
153
  * longer than the producer's tick interval — the spring never quite
@@ -134,31 +163,31 @@ class Ot extends st {
134
163
  * field carries the decision across that gap. The closure also reads
135
164
  * `#dataStart` / `#dataEnd` directly for the timestamps.
136
165
  */
137
- #tt = !1;
166
+ #ht = !1;
138
167
  /** Earliest data timestamp registered across all series, `null` before any data has arrived. */
139
168
  #k = null;
140
169
  /** Latest data timestamp registered across all series, `null` before any data has arrived. */
141
- #s = null;
170
+ #i = null;
142
171
  /**
143
172
  * Previous {@link #dataEnd} value. Streaming-target math reads it to
144
173
  * preserve any pan offset across ticks: a user who panned a few bars
145
174
  * left of the tail keeps that offset as new bars arrive, instead of
146
175
  * snapping back to the natural-pin position every frame.
147
176
  */
148
- #P = null;
177
+ #z = null;
149
178
  /**
150
179
  * Warm-up window flag — `setVisibleRange({ from, bars })` arms it and
151
180
  * the streaming-target math suppresses pan ticks while the data hasn't
152
181
  * reached the right edge yet. Cleared by user interaction or once the
153
182
  * data fills the window.
154
183
  */
155
- #L = !1;
184
+ #w = !1;
156
185
  /**
157
186
  * Cap for fit-to-data. When the data span exceeds this, the fit anchors
158
187
  * the right edge and trims the left. Resolved once at construction from
159
188
  * `options.viewport.maxVisibleBars`.
160
189
  */
161
- #it;
190
+ #ot;
162
191
  /**
163
192
  * Committed logical X range — the "where X should be" target. Engine
164
193
  * eases its visual toward this. Pan / zoom / setRange / fitContent /
@@ -167,29 +196,29 @@ class Ot extends st {
167
196
  */
168
197
  #o = { from: 0, to: 0 };
169
198
  /** Padded Y range — engine's raw Y target plus pixel padding from `#padding`. */
170
- #V = { min: 0, max: 0 };
199
+ #F = { min: 0, max: 0 };
171
200
  /**
172
201
  * Auto-scroll flag — when `true`, streaming ticks slide X to track the
173
202
  * data tail. Pan flips this off when the gesture pushes the tail off
174
203
  * screen; chart's per-frame autoscroll check re-engages it when a
175
204
  * follow-up pan brings the tail back into view.
176
205
  */
177
- #y = !0;
206
+ #u = !0;
178
207
  /** Resolved viewport padding (top/bottom px + left/right pixels-or-intervals). */
179
208
  #c;
180
209
  /** Cached chart width — passed by interactions on every call; kept for the rare
181
210
  * call that omits it (e.g. programmatic `chart.pan(...)` without explicit width). */
182
- #z = 0;
211
+ #E = 0;
183
212
  /** Active performance monitor, or `null` when instrumentation is disabled (the default). */
184
- #S;
213
+ #x;
185
214
  /** When true, `destroy()` tears down the monitor; false for caller-supplied monitors we must not destroy. */
186
- #B = !1;
215
+ #_ = !1;
187
216
  /** Visible HUD overlay (mounted by the perf config); non-null only when the caller requested one. */
188
- #X = null;
217
+ #U = null;
189
218
  constructor(t, i) {
190
- super(), i?.axis && (this.#I = i.axis, this.#h = { min: i.axis.y?.min, max: i.axis.y?.max }), this.#r = i?.theme ?? ot.theme, this.#E = i?.grid?.visible !== !1, this.#f = z.resolve(i?.animations), this.#it = _(i?.viewport?.maxVisibleBars), this.#c = D(i?.padding), this.#D = new G();
191
- const e = this.#f.axis.y, s = this.#f.axis.x;
192
- this.#e = H({
219
+ super(), i?.axis && (this.#H = i.axis, this.#l = { min: i.axis.y?.min, max: i.axis.y?.max }), this.#n = i?.theme ?? dt.theme, this.#O = i?.grid?.visible !== !1, this.#g = F.resolve(i?.animations), this.#ot = $(i?.viewport?.maxVisibleBars), this.#c = C(i?.padding), this.#D = new j();
220
+ const e = this.#g.axis.y, s = this.#g.axis.x;
221
+ this.#s = N({
193
222
  initial: { yRange: { min: 0, max: 0 }, xRange: { from: 0, to: 0 } },
194
223
  y: {
195
224
  curve: e.curve,
@@ -197,28 +226,32 @@ class Ot extends st {
197
226
  stickyMs: e.stickyMs,
198
227
  stickyFloorMs: e.stickyFloorMs,
199
228
  gestureMs: e.gestureMs,
200
- toggleMs: this.#f.toggleMs
229
+ toggleMs: this.#g.toggleMs
201
230
  },
202
231
  x: {
203
232
  curve: s.curve,
204
233
  settleMs: s.settleMs,
205
234
  gestureMs: s.gestureMs
206
235
  },
207
- computeXTarget: () => this.#pt(),
208
- computeYTarget: ({ xTarget: h }) => this.#nt(h),
209
- onWake: () => this.#i?.markDirty()
210
- }), this.#K = i?.onEdgeReached, this.#N = i?.viewport?.initialRange;
211
- const r = $(i?.perf);
212
- this.#S = r.monitor, this.#B = r.ownsMonitor, this.#n = new W(t, this.#S ?? void 0), this.timeScale = new nt(), this.yScale = new at();
213
- const n = this.#f.axis.ticksMs;
214
- this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#i = new R((h) => this.#G(h)), this.#l = new R(
215
- (h) => this.#j("overlay", h, () => this.renderOverlay(h))
216
- ), this.#S && r.hud && (this.#X = r.hud(t, this.#S));
217
- const a = i?.interactive !== !1;
218
- this.#W = a ? new rt(this.#n.canvas, this, this.timeScale, this.yScale) : null, this.#n.on("resize", () => {
219
- this.syncScales(), this.#G(), this.emit("viewportChange");
220
- }), this.syncScales(), this.#W?.on("crosshairMove", (h) => {
221
- this.#F = "pointer", this.#d = h, this.#l.markDirty(), this.emit("crosshairMove", h), this.#_(h);
236
+ computeXTarget: () => this.#Rt(),
237
+ computeYTarget: ({ xTarget: a }) => this.#gt(a),
238
+ onWake: () => this.#e?.markDirty()
239
+ }), this.#rt = i?.onEdgeReached, this.#N = i?.viewport?.initialRange;
240
+ const r = G(i?.perf);
241
+ this.#x = r.monitor, this.#_ = r.ownsMonitor, this.#a = new W(t, this.#x ?? void 0), this.timeScale = new ot(), this.yScale = new lt(), this.timeScale.setLocale(i?.locale), this.timeScale.setTimeZone(i?.timeZone);
242
+ const n = this.#g.axis.ticksMs;
243
+ this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#e = new P((a) => this.#J(a)), this.#h = new P(
244
+ (a) => this.#Q("overlay", a, () => this.renderOverlay(a))
245
+ ), this.#x && r.hud && (this.#U = r.hud(t, this.#x));
246
+ const h = i?.interactive !== !1;
247
+ this.#C = h ? new ht(this.#a.canvas, this, this.timeScale, this.yScale) : null, this.#a.on("resize", () => {
248
+ this.syncScales(), this.#J(), this.emit("viewportChange");
249
+ }), this.syncScales(), this.#C?.on("crosshairMove", (a) => {
250
+ this.#Y = "pointer", this.#d = a, this.#h.markDirty(), this.emit("crosshairMove", a), this.#G(a);
251
+ }), this.#C?.on("click", (a) => {
252
+ this.emit("pointClick", { ...a, spatialHit: this.#ft(a) });
253
+ }), this.#C?.on("dblclick", (a) => {
254
+ this.fitContent(), this.emit("pointDoubleClick", { ...a, spatialHit: this.#ft(a) });
222
255
  });
223
256
  }
224
257
  /**
@@ -233,9 +266,9 @@ class Ot extends st {
233
266
  * `setSeriesVisible` / `setLayerVisible` calls — produce one overlay
234
267
  * render instead of N.
235
268
  */
236
- #b() {
237
- if (this.#Q++, this.#w > 0) {
238
- this.#Y = !0;
269
+ #p() {
270
+ if (this.#at++, this.#A > 0) {
271
+ this.#Z = !0;
239
272
  return;
240
273
  }
241
274
  this.emit("overlayChange");
@@ -249,39 +282,39 @@ class Ot extends st {
249
282
  * @internal
250
283
  */
251
284
  getOverlayVersion() {
252
- return this.#Q;
285
+ return this.#at;
253
286
  }
254
287
  /**
255
288
  * Return a series ID: use the provided hint if it's non-empty and not already taken,
256
289
  * otherwise generate a new auto ID. Auto-generated IDs never collide with each other
257
290
  * or with user-provided IDs because they use a monotonically increasing counter.
258
291
  */
259
- #lt(t) {
292
+ #bt(t) {
260
293
  if (t && !this.#t.some((e) => e.id === t))
261
294
  return t;
262
295
  let i;
263
296
  do
264
- i = `series_${++ct}`;
297
+ i = `series_${++pt}`;
265
298
  while (this.#t.some((e) => e.id === i));
266
299
  return i;
267
300
  }
268
301
  addSeries(t, i = {}) {
269
- const e = typeof t == "string" ? ht(t) : t;
302
+ const e = typeof t == "string" ? ct(t) : t;
270
303
  if (!e)
271
304
  throw new Error(
272
305
  `[wick-charts] Unknown series type '${String(t)}'. The string form of addSeries resolves through the series registry — call registerBuiltinSeries() once at startup, or pass the definition directly: chart.addSeries(LineSeriesDef, options).`
273
306
  );
274
- const { layers: s, id: r, label: n, labels: a, ...h } = i, l = s ?? 1, o = {
275
- ...this.#f.defaults(e.type),
276
- ...h,
277
- ...this.#f.overrides(e.type)
278
- }, u = e.create({ theme: this.#r, layerCount: l }, o), d = a ?? (typeof n == "string" ? [n] : void 0), c = this.#lt(r);
279
- return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id: c, labels: d, renderer: u, visible: !0 }), this.#A = null, this.emit("seriesChange"), this.#b(), c;
307
+ const { layers: s, id: r, label: n, labels: h, ...a } = i, l = s ?? 1, o = {
308
+ ...this.#g.defaults(e.type),
309
+ ...a,
310
+ ...this.#g.overrides(e.type)
311
+ }, u = e.create({ theme: this.#n, layerCount: l }, o), d = h ?? (typeof n == "string" ? [n] : void 0), c = this.#bt(r);
312
+ return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id: c, labels: d, renderer: u, visible: !0 }), this.#T = null, this.emit("seriesChange"), this.#p(), c;
280
313
  }
281
314
  /** Remove a series by ID and clean up its resources. */
282
315
  removeSeries(t) {
283
316
  const i = this.#t.findIndex((e) => e.id === t);
284
- i >= 0 && (this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#A = null, this.#i.markDirty(), this.emit("seriesChange"), this.#b());
317
+ i >= 0 && (this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#T = null, this.#e.markDirty(), this.emit("seriesChange"), this.#p());
285
318
  }
286
319
  // --- Markers (point annotations) -----------------------------------------
287
320
  /**
@@ -294,27 +327,27 @@ class Ot extends st {
294
327
  * appear in series queries.
295
328
  */
296
329
  addMarker(t) {
297
- const i = `marker_${++dt}`;
298
- return this.#m.push(this.#et(i, t)), this.#l.markDirty(), i;
330
+ const i = `marker_${++yt}`;
331
+ return this.#m.push(this.#lt(i, t)), this.#h.markDirty(), i;
299
332
  }
300
333
  /** Replace a marker's config in place. No-op when the id is unknown. */
301
334
  updateMarker(t, i) {
302
335
  const e = this.#m.findIndex((s) => s.id === t);
303
- e !== -1 && (this.#m[e] = this.#et(t, i), this.#l.markDirty());
336
+ e !== -1 && (this.#m[e] = this.#lt(t, i), this.#h.markDirty());
304
337
  }
305
338
  /** Remove a marker by id. No-op when the id is unknown. */
306
339
  removeMarker(t) {
307
340
  const i = this.#m.findIndex((e) => e.id === t);
308
- i !== -1 && (this.#m.splice(i, 1), this.#l.markDirty());
341
+ i !== -1 && (this.#m.splice(i, 1), this.#h.markDirty());
309
342
  }
310
343
  /** Read-only snapshot of the registered markers (normalized). Mutate via add / update / removeMarker. */
311
344
  getMarkers() {
312
345
  return this.#m;
313
346
  }
314
- #et(t, i) {
347
+ #lt(t, i) {
315
348
  return {
316
349
  id: t,
317
- time: g(i.time),
350
+ time: y(i.time),
318
351
  value: i.value,
319
352
  seriesId: i.seriesId,
320
353
  shape: i.shape ?? "dot",
@@ -324,41 +357,47 @@ class Ot extends st {
324
357
  };
325
358
  }
326
359
  /** Resolve a marker's Y value: explicit `value`, else snapped to its series at `time`. Null when neither resolves. */
327
- #ct(t) {
360
+ #St(t) {
328
361
  if (t.value !== void 0 && Number.isFinite(t.value)) return t.value;
329
362
  if (t.seriesId === void 0) return null;
330
363
  const i = this.getDataAtTime(t.seriesId, t.time);
331
364
  return i ? "close" in i ? i.close : i.value : null;
332
365
  }
333
366
  /** Resolve a marker's color: explicit `color`, else the series color, else the theme line color. */
334
- #dt(t) {
367
+ #xt(t) {
335
368
  if (t.color !== void 0) return t.color;
336
369
  if (t.seriesId !== void 0) {
337
370
  const i = this.getSeriesColor(t.seriesId);
338
371
  if (i !== null) return i;
339
372
  }
340
- return this.#r.line.color;
373
+ return this.#n.line.color;
341
374
  }
342
- #mt(t) {
375
+ #kt(t, i, e) {
343
376
  if (this.#m.length === 0) return;
344
- const i = performance.now() / (N * 2 * Math.PI) % 1;
345
- for (const e of this.#m) {
346
- const s = this.#ct(e);
347
- s !== null && Q({
377
+ const s = performance.now() / (E * 2 * Math.PI) % 1, r = this.#$();
378
+ for (const n of this.#m) {
379
+ const h = this.#St(n);
380
+ if (h === null) continue;
381
+ const a = this.#mt(r, n.time);
382
+ if (a <= 0) continue;
383
+ const l = t.context.globalAlpha;
384
+ a < 1 && (t.context.globalAlpha = l * a), et({
348
385
  scope: t,
349
386
  timeScale: this.timeScale,
350
387
  yScale: this.yScale,
351
- theme: this.#r,
388
+ theme: this.#n,
352
389
  marker: {
353
- time: e.time,
354
- value: s,
355
- color: this.#dt(e),
356
- shape: e.shape,
357
- pulse: e.pulse,
358
- label: e.label
390
+ time: n.time,
391
+ value: h,
392
+ color: this.#xt(n),
393
+ shape: n.shape,
394
+ pulse: n.pulse,
395
+ label: n.label
359
396
  },
360
- phase: i
361
- });
397
+ phase: s,
398
+ plotWidth: i,
399
+ plotHeight: e
400
+ }), a < 1 && (t.context.globalAlpha = l);
362
401
  }
363
402
  }
364
403
  // --- Time regions (shaded bands) -----------------------------------------
@@ -374,42 +413,42 @@ class Ot extends st {
374
413
  * legend.
375
414
  */
376
415
  addRegion(t) {
377
- const i = `region_${++mt}`;
378
- return this.#v.push(this.#st(i, t)), this.#i.markDirty(), i;
416
+ const i = `region_${++vt}`;
417
+ return this.#b.push(this.#ct(i, t)), this.#e.markDirty(), i;
379
418
  }
380
419
  /** Replace a region's config in place. No-op when the id is unknown. */
381
420
  updateRegion(t, i) {
382
- const e = this.#v.findIndex((s) => s.id === t);
383
- e !== -1 && (this.#v[e] = this.#st(t, i), this.#i.markDirty());
421
+ const e = this.#b.findIndex((s) => s.id === t);
422
+ e !== -1 && (this.#b[e] = this.#ct(t, i), this.#e.markDirty());
384
423
  }
385
424
  /** Remove a region by id. No-op when the id is unknown. */
386
425
  removeRegion(t) {
387
- const i = this.#v.findIndex((e) => e.id === t);
388
- i !== -1 && (this.#v.splice(i, 1), this.#i.markDirty());
426
+ const i = this.#b.findIndex((e) => e.id === t);
427
+ i !== -1 && (this.#b.splice(i, 1), this.#e.markDirty());
389
428
  }
390
429
  /** Read-only snapshot of the registered regions (normalized). Mutate via add / update / removeRegion. */
391
430
  getRegions() {
392
- return this.#v;
431
+ return this.#b;
393
432
  }
394
- #st(t, i) {
395
- const { to: e } = i, s = e === void 0 || e === "now" ? null : g(e);
433
+ #ct(t, i) {
434
+ const { to: e } = i, s = e === void 0 || e === "now" ? null : y(e);
396
435
  return {
397
436
  id: t,
398
- from: g(i.from),
437
+ from: y(i.from),
399
438
  to: s,
400
439
  fill: i.fill,
401
440
  color: i.color,
402
441
  label: i.label
403
442
  };
404
443
  }
405
- #ut(t, i, e) {
406
- if (this.#v.length !== 0)
407
- for (const s of this.#v) {
408
- const { color: r, fill: n } = it(s, this.#r);
409
- et({
444
+ #At(t, i, e) {
445
+ if (this.#b.length !== 0)
446
+ for (const s of this.#b) {
447
+ const { color: r, fill: n } = rt(s, this.#n);
448
+ nt({
410
449
  scope: t,
411
450
  timeScale: this.timeScale,
412
- theme: this.#r,
451
+ theme: this.#n,
413
452
  region: { from: s.from, to: s.to, fill: n, color: r, label: s.label },
414
453
  plotWidth: i,
415
454
  plotHeight: e
@@ -428,24 +467,24 @@ class Ot extends st {
428
467
  * legend.
429
468
  */
430
469
  addLine(t) {
431
- const i = `line_${++ut}`;
432
- return this.#g.push(this.#rt(i, t)), this.#l.markDirty(), i;
470
+ const i = `line_${++bt}`;
471
+ return this.#y.push(this.#dt(i, t)), this.#h.markDirty(), i;
433
472
  }
434
473
  /** Replace a reference line's config in place. No-op when the id is unknown. */
435
474
  updateLine(t, i) {
436
- const e = this.#g.findIndex((s) => s.id === t);
437
- e !== -1 && (this.#g[e] = this.#rt(t, i), this.#l.markDirty());
475
+ const e = this.#y.findIndex((s) => s.id === t);
476
+ e !== -1 && (this.#y[e] = this.#dt(t, i), this.#h.markDirty());
438
477
  }
439
478
  /** Remove a reference line by id. No-op when the id is unknown. */
440
479
  removeLine(t) {
441
- const i = this.#g.findIndex((e) => e.id === t);
442
- i !== -1 && (this.#g.splice(i, 1), this.#l.markDirty());
480
+ const i = this.#y.findIndex((e) => e.id === t);
481
+ i !== -1 && (this.#y.splice(i, 1), this.#h.markDirty());
443
482
  }
444
483
  /** Read-only snapshot of the registered reference lines (normalized). Mutate via add / update / removeLine. */
445
484
  getLines() {
446
- return this.#g;
485
+ return this.#y;
447
486
  }
448
- #rt(t, i) {
487
+ #dt(t, i) {
449
488
  const { time: e, value: s } = i, r = {
450
489
  id: t,
451
490
  color: i.color,
@@ -453,27 +492,32 @@ class Ot extends st {
453
492
  style: i.style ?? "dashed",
454
493
  width: i.width ?? 1
455
494
  };
456
- return e !== void 0 ? { ...r, orientation: "vertical", coord: g(e) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
457
- }
458
- #ft(t, i, e) {
459
- if (this.#g.length !== 0)
460
- for (const s of this.#g)
461
- tt({
462
- scope: t,
463
- timeScale: this.timeScale,
464
- yScale: this.yScale,
465
- theme: this.#r,
466
- line: {
467
- orientation: s.orientation,
468
- coord: s.coord,
469
- color: s.color ?? this.#r.line.color,
470
- style: s.style,
471
- width: s.width,
472
- label: s.label
473
- },
474
- plotWidth: i,
475
- plotHeight: e
476
- });
495
+ return e !== void 0 ? { ...r, orientation: "vertical", coord: y(e) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
496
+ }
497
+ #wt(t, i, e) {
498
+ if (this.#y.length === 0) return;
499
+ const s = this.#$();
500
+ for (const r of this.#y) {
501
+ const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#mt(s, r.coord) : L(s);
502
+ if (n <= 0) continue;
503
+ const h = t.context.globalAlpha;
504
+ n < 1 && (t.context.globalAlpha = h * n), st({
505
+ scope: t,
506
+ timeScale: this.timeScale,
507
+ yScale: this.yScale,
508
+ theme: this.#n,
509
+ line: {
510
+ orientation: r.orientation,
511
+ coord: r.coord,
512
+ color: r.color ?? this.#n.line.color,
513
+ style: r.style,
514
+ width: r.width,
515
+ label: r.label
516
+ },
517
+ plotWidth: i,
518
+ plotHeight: e
519
+ }), n < 1 && (t.context.globalAlpha = h);
520
+ }
477
521
  }
478
522
  /**
479
523
  * Read-only snapshot of the chart's X / Y viewport animation. The same
@@ -482,7 +526,49 @@ class Ot extends st {
482
526
  * underlying animator mutates between frames.
483
527
  */
484
528
  getAnimationState() {
485
- return this.#e.getAnimationState();
529
+ return this.#s.getAnimationState();
530
+ }
531
+ /**
532
+ * Reveal-front position in `[0, 1]` of any in-flight initial-load intro.
533
+ * With a `seriesId`, that series' own front; without, the minimum across
534
+ * every visible time series — overlays wait for the slowest reveal. `1`
535
+ * whenever nothing is introing. Overlay components (the Y-label badge,
536
+ * annotations) read this to reveal their entrance in step with the draw.
537
+ */
538
+ getIntroFront(t) {
539
+ if (t !== void 0)
540
+ return this.#t.find((e) => e.id === t)?.renderer.getIntroFront?.() ?? 1;
541
+ let i = 1;
542
+ for (const e of this.#t) {
543
+ if (!e.visible) continue;
544
+ const s = e.renderer.getIntroFront?.();
545
+ s !== void 0 && s < i && (i = s);
546
+ }
547
+ return i;
548
+ }
549
+ /**
550
+ * Reveal front for annotations (markers / reference lines). The same as
551
+ * {@link getIntroFront}() during the initial reveal, but latched to `1` once
552
+ * that first reveal has settled — so introducing another series afterward
553
+ * doesn't re-hide overlays that already surfaced. Latches only after an intro
554
+ * has actually been observed in progress, so an annotation drawn on a chart
555
+ * that has no data yet still rides the reveal once data arrives.
556
+ */
557
+ #$() {
558
+ if (this.#it) return 1;
559
+ const t = this.getIntroFront();
560
+ return t < 1 ? (this.#tt = !0, t) : (this.#tt && (this.#it = !0), 1);
561
+ }
562
+ /**
563
+ * Entrance alpha for an overlay pinned at `time`, given the intro reveal
564
+ * `front`. Transparent until the sweep reaches the element's normalized X,
565
+ * then fades in over a short band — so annotations surface in step with the
566
+ * series drawing past them. `1` once the intro has settled.
567
+ */
568
+ #mt(t, i) {
569
+ if (t >= 1) return 1;
570
+ const { from: e, to: s } = this.timeScale.getRange(), r = s - e, n = r > 0 ? (i - e) / r : 0;
571
+ return mt((t - n) / 0.12, 0, 1);
486
572
  }
487
573
  /**
488
574
  * Replace all data for a series.
@@ -495,13 +581,32 @@ class Ot extends st {
495
581
  */
496
582
  setSeriesData(t, i, e) {
497
583
  const s = this.#t.find((r) => r.id === t);
498
- s && (this.#$ = !0, s.renderer.setData(i, e));
584
+ s && (this.#q = !0, s.renderer.setData(i, e));
499
585
  }
500
586
  /** Append a new data point to the end of a series (real-time tick). */
501
587
  appendData(t, i, e) {
502
588
  const s = this.#t.find((r) => r.id === t);
503
589
  s === void 0 || s.renderer.appendPoint === void 0 || s.renderer.appendPoint(i, e);
504
590
  }
591
+ /**
592
+ * Prepend older points to the front of a series — the "load more history on
593
+ * scroll" path. Unlike {@link setSeriesData}, this deliberately does NOT set
594
+ * `#dataReplaceSnapPending`: the visible window and the data inside it don't
595
+ * change, so the viewport must stay put (X) and the Y axis eases if newly
596
+ * revealed history shifts the range — instead of the full-span re-fit and
597
+ * instant dual-axis snap a bulk `setSeriesData` replace would force. Routes
598
+ * through the streaming `onPointAppended` engine signal via `onDataChanged`.
599
+ *
600
+ * `points` must be sorted ascending and sit strictly before the current
601
+ * first point; the sync reconciler guarantees this before calling here.
602
+ * No-op if the renderer doesn't support prepend.
603
+ */
604
+ prependData(t, i, e) {
605
+ const s = this.#t.find((n) => n.id === t);
606
+ if (s === void 0 || s.renderer.prependPoints === void 0) return;
607
+ const r = this.#B.left;
608
+ r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at < gt && (s.renderer.setHistoryHandoff(r.bars), this.#R.left = !0, this.#V.left = null, this.#B.left = null, this.#h.markDirty()), s.renderer.prependPoints(i, e);
609
+ }
505
610
  /** Update the last data point of a series in place (e.g. live candle update). */
506
611
  updateData(t, i, e) {
507
612
  this.#t.find((s) => s.id === t)?.renderer.updateLastPoint?.(i, e);
@@ -526,29 +631,29 @@ class Ot extends st {
526
631
  updateSeriesOptions(t, i) {
527
632
  const e = this.#t.find((o) => o.id === t);
528
633
  if (!e) return;
529
- const s = e.labels, r = e.renderer.getLayerColors().slice(), n = this.#f.overrides(e.renderer.kind);
530
- e.renderer.updateOptions({ ...i, ...n }), "label" in i && typeof i.label == "string" && (e.labels = [i.label]), this.#i.markDirty();
531
- const a = e.renderer.getLayerColors(), h = r.length !== a.length || r.some((o, u) => o !== a[u]), l = !I(s, e.labels);
532
- (h || l) && this.#b();
634
+ const s = e.labels, r = e.renderer.getLayerColors().slice(), n = this.#g.overrides(e.renderer.kind);
635
+ e.renderer.updateOptions({ ...i, ...n }), "label" in i && typeof i.label == "string" && (e.labels = [i.label]), this.#e.markDirty();
636
+ const h = e.renderer.getLayerColors(), a = r.length !== h.length || r.some((o, u) => o !== h[u]), l = !H(s, e.labels);
637
+ (a || l) && this.#p();
533
638
  }
534
639
  /**
535
640
  * Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
536
641
  * inside `fn` still flush the batch so counters don't leak across calls.
537
642
  */
538
643
  batch(t) {
539
- this.#w++;
644
+ this.#A++;
540
645
  try {
541
646
  t();
542
647
  } finally {
543
- if (--this.#w <= 0) {
544
- if (this.#w = 0, this.#O)
545
- this.#O = !1, this.#C = !1, this.onDataChanged();
546
- else if (this.#C) {
547
- this.#C = !1, this.#u = !0;
648
+ if (--this.#A <= 0) {
649
+ if (this.#A = 0, this.#X)
650
+ this.#X = !1, this.#M = !1, this.onDataChanged();
651
+ else if (this.#M) {
652
+ this.#M = !1, this.#f = !0;
548
653
  const i = performance.now();
549
- this.#e.onAxisReconfig(i), this.#p(i), this.#i.markDirty();
654
+ this.#s.onAxisReconfig(i), this.#v(i), this.#e.markDirty();
550
655
  }
551
- this.#Y && (this.#Y = !1, this.emit("overlayChange"));
656
+ this.#Z && (this.#Z = !1, this.emit("overlayChange"));
552
657
  }
553
658
  }
554
659
  }
@@ -562,15 +667,15 @@ class Ot extends st {
562
667
  setSeriesVisible(t, i) {
563
668
  const e = this.#t.find((n) => n.id === t);
564
669
  if (!e || e.visible === i) return;
565
- e.visible = i, this.#b();
566
- const s = this.#f.toggleMs;
567
- if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#i.markDirty(), this.#w > 0) {
568
- this.#C = !0;
670
+ e.visible = i, this.#p();
671
+ const s = this.#g.toggleMs;
672
+ if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#e.markDirty(), this.#A > 0) {
673
+ this.#M = !0;
569
674
  return;
570
675
  }
571
- this.#u = !0;
676
+ this.#f = !0;
572
677
  const r = performance.now();
573
- this.#e.onSeriesVisibilityChanged(r), this.#p(r);
678
+ this.#s.onSeriesVisibilityChanged(r), this.#v(r);
574
679
  }
575
680
  /**
576
681
  * Whether streaming ticks slide the visible range to track the data tail.
@@ -580,7 +685,7 @@ class Ot extends st {
580
685
  * containing the tail.
581
686
  */
582
687
  getAutoScroll() {
583
- return this.#y;
688
+ return this.#u;
584
689
  }
585
690
  isSeriesVisible(t) {
586
691
  return this.#t.find((i) => i.id === t)?.visible ?? !0;
@@ -596,17 +701,17 @@ class Ot extends st {
596
701
  * layer's geometry on screen until the engine's Y ease settles.
597
702
  */
598
703
  setLayerVisible(t, i, e) {
599
- const s = this.#t.find((a) => a.id === t);
704
+ const s = this.#t.find((h) => h.id === t);
600
705
  if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(i) === e) return;
601
- s.renderer.setLayerVisible(i, e), this.#b();
602
- const r = this.#f.toggleMs;
603
- if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#i.markDirty(), this.#w > 0) {
604
- this.#C = !0;
706
+ s.renderer.setLayerVisible(i, e), this.#p();
707
+ const r = this.#g.toggleMs;
708
+ if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#e.markDirty(), this.#A > 0) {
709
+ this.#M = !0;
605
710
  return;
606
711
  }
607
- this.#u = !0;
712
+ this.#f = !0;
608
713
  const n = performance.now();
609
- this.#e.onSeriesVisibilityChanged(n), this.#p(n);
714
+ this.#s.onSeriesVisibilityChanged(n), this.#v(n);
610
715
  }
611
716
  isLayerVisible(t, i) {
612
717
  return this.#t.find((e) => e.id === t)?.renderer.isLayerVisible(i) ?? !0;
@@ -615,11 +720,11 @@ class Ot extends st {
615
720
  fitContent() {
616
721
  const { first: t, last: i } = this.getDataBounds();
617
722
  if (t === void 0 || i === void 0) return;
618
- const e = this.#n.size.media.width - this.yAxisWidth;
619
- this.#R(t, i, e), this.#H({ xEase: !0 });
723
+ const e = this.#a.size.media.width - this.yAxisWidth;
724
+ this.#P(t, i, e), this.#W({ xEase: !0 });
620
725
  }
621
726
  getVisibleRange() {
622
- return this.#o.from === 0 && this.#o.to === 0 ? this.#e.getAnimationState().xRange : this.#o;
727
+ return this.#o.from === 0 && this.#o.to === 0 ? this.#s.getAnimationState().xRange : this.#o;
623
728
  }
624
729
  /**
625
730
  * Return the full span of registered data, or `null` before any data has
@@ -627,7 +732,7 @@ class Ot extends st {
627
732
  * tracking and is cheaper than recomputing from series stores.
628
733
  */
629
734
  getDataRange() {
630
- return this.#k === null || this.#s === null ? null : { from: this.#k, to: this.#s };
735
+ return this.#k === null || this.#i === null ? null : { from: this.#k, to: this.#i };
631
736
  }
632
737
  /**
633
738
  * Imperatively set the visible time range. Three forms:
@@ -656,29 +761,29 @@ class Ot extends st {
656
761
  setVisibleRange(t, i) {
657
762
  if (typeof t == "number") {
658
763
  if (!Number.isInteger(t) || t < 2) return;
659
- const { first: n, last: a } = this.getDataBounds();
660
- if (n === void 0 || a === void 0) return;
661
- const h = Math.max(n, a - (t - 1) * this.#a), l = this.#n.size.media.width - this.yAxisWidth;
662
- this.#R(h, a, l), this.#H();
764
+ const { first: n, last: h } = this.getDataBounds();
765
+ if (n === void 0 || h === void 0) return;
766
+ const a = Math.max(n, h - (t - 1) * this.#r), l = this.#a.size.media.width - this.yAxisWidth;
767
+ this.#P(a, h, l), this.#W();
663
768
  return;
664
769
  }
665
770
  if ("bars" in t) {
666
771
  if (!Number.isInteger(t.bars) || t.bars < 2) return;
667
- const n = g(t.from), a = n + t.bars * this.#a;
668
- this.#y = this.#s !== null && this.#s >= n && this.#s <= a, this.#T({ from: n, to: a }, { emitChange: !0 }), this.#L = !0, this.#H();
772
+ const n = y(t.from), h = n + t.bars * this.#r;
773
+ this.#u = this.#i !== null && this.#i >= n && this.#i <= h, this.#L({ from: n, to: h }, { emitChange: !0 }), this.#w = !0, this.#W();
669
774
  return;
670
775
  }
671
- const e = g(t.from), s = g(t.to);
672
- this.#y = this.#s !== null && this.#s >= e && this.#s <= s, this.#L = !1;
673
- const r = this.#T({ from: e, to: s }, { emitChange: !0 });
776
+ const e = y(t.from), s = y(t.to);
777
+ this.#u = this.#i !== null && this.#i >= e && this.#i <= s, this.#w = !1;
778
+ const r = this.#L({ from: e, to: s }, { emitChange: !0 });
674
779
  if (i?.gesture) {
675
- r && this.#vt();
780
+ r && this.#Mt();
676
781
  return;
677
782
  }
678
- this.#H();
783
+ this.#W();
679
784
  }
680
785
  getYRange() {
681
- return this.#V;
786
+ return this.#F;
682
787
  }
683
788
  getCrosshairPosition() {
684
789
  return this.#d;
@@ -719,7 +824,7 @@ class Ot extends st {
719
824
  setCrosshair(t) {
720
825
  if (t === null) {
721
826
  if (this.#d === null) return;
722
- this.#d = null, this.#l.markDirty(), this.emit("crosshairMove", null);
827
+ this.#d = null, this.#h.markDirty(), this.emit("crosshairMove", null);
723
828
  return;
724
829
  }
725
830
  const { time: i } = t;
@@ -731,29 +836,29 @@ class Ot extends st {
731
836
  else if (e !== null)
732
837
  s = e.y;
733
838
  else {
734
- const a = this.#V;
735
- s = (a.min + a.max) / 2;
839
+ const h = this.#F;
840
+ s = (h.min + h.max) / 2;
736
841
  }
737
842
  if (!Number.isFinite(s) || e !== null && e.time === i && e.y === s) return;
738
843
  const r = this.timeScale.timeToX(i), n = this.yScale.valueToY(s);
739
- this.#F = "time", this.#d = { mediaX: r, mediaY: n, time: i, y: s }, this.#l.markDirty(), this.emit("crosshairMove", this.#d);
844
+ this.#Y = "time", this.#d = { mediaX: r, mediaY: n, time: i, y: s }, this.#h.markDirty(), this.emit("crosshairMove", this.#d);
740
845
  }
741
846
  /** Get the last visible value and whether the absolute last point is on screen. */
742
847
  getLastValue(t) {
743
- return Y(t, this.#t, this.#e.getAnimationState().xRange);
848
+ return Z(t, this.#t, this.#s.getAnimationState().xRange);
744
849
  }
745
850
  /** Get the second-to-last value, useful for computing change. */
746
851
  getPreviousClose(t) {
747
- return B(t, this.#t);
852
+ return _(t, this.#t);
748
853
  }
749
854
  getLastData(t) {
750
855
  const i = this.#t.find((e) => e.id === t);
751
- return !i || i.renderer.kind === "pie" ? null : i.renderer.getLastDataPoint();
856
+ return !i || !S(i.renderer) ? null : i.renderer.getLastDataPoint();
752
857
  }
753
858
  /** Find the data point closest to the given timestamp within one data interval. */
754
859
  getDataAtTime(t, i) {
755
860
  const e = this.#t.find((s) => s.id === t);
756
- return e ? e.renderer.getDataAtTime?.(i, this.#a) ?? null : null;
861
+ return e ? e.renderer.getDataAtTime?.(i, this.#r) ?? null : null;
757
862
  }
758
863
  /**
759
864
  * Get all layers' data at a given time for multi-layer series (Bar/Line
@@ -763,25 +868,28 @@ class Ot extends st {
763
868
  */
764
869
  getLayerSnapshots(t, i) {
765
870
  const e = this.#t.find((s) => s.id === t);
766
- return e?.visible ? e.renderer.getLayerSnapshots?.(i, this.#a) ?? null : null;
871
+ return e?.visible ? e.renderer.getLayerSnapshots?.(i, this.#r) ?? null : null;
767
872
  }
768
873
  getSeriesIds() {
769
- return this.#A || (this.#A = this.#t.map((t) => t.id)), this.#A.slice();
874
+ return this.#T || (this.#T = this.#t.map((t) => t.id)), this.#T.slice();
770
875
  }
771
876
  /**
772
- * True if any visible series is non-pie (line / bar / candlestick). Used
773
- * to gate crosshair rendering — a pie-only chart has no meaningful x/y
774
- * coordinate system, so the dashed hairlines would just be visual noise.
877
+ * True if any visible series has a time axis (line / bar / candlestick).
878
+ * Used to gate crosshair rendering — a spatial-only chart (pie, heatmap)
879
+ * has no meaningful x/y coordinate system, so the dashed hairlines would
880
+ * just be visual noise.
775
881
  */
776
- #gt() {
882
+ #Tt() {
777
883
  for (const t of this.#t)
778
- if (t.visible && t.renderer.kind !== "pie")
884
+ if (t.visible && S(t.renderer))
779
885
  return !0;
780
886
  return !1;
781
887
  }
782
888
  /**
783
- * Filter `getSeriesIds()` by renderer type. `'pie'` returns pie series,
784
- * `'time'` returns line/bar/candlestick.
889
+ * Filter `getSeriesIds()` by renderer type. A concrete kind (`'pie'`,
890
+ * `'heatmap'`, a custom kind, …) returns series of that exact kind;
891
+ * `'time'` returns every time-axis series (line/bar/candlestick and any
892
+ * custom time-series kind).
785
893
  *
786
894
  * - `opts.visibleOnly` — exclude series with `isSeriesVisible=false`; for
787
895
  * multi-layer series also exclude when every layer is hidden.
@@ -791,20 +899,19 @@ class Ot extends st {
791
899
  getSeriesIdsByType(t, i) {
792
900
  const e = i?.visibleOnly === !0, s = i?.singleLayerOnly === !0, r = [];
793
901
  for (const n of this.#t) {
794
- const a = n.renderer.kind === "pie";
795
- if (t === "pie" && !a || t === "time" && a) continue;
902
+ if (!(t === "time" ? S(n.renderer) : n.renderer.kind === t)) continue;
796
903
  const h = n.renderer.getLayerCount();
797
904
  if (!(s && h > 1)) {
798
905
  if (e) {
799
906
  if (!n.visible) continue;
800
907
  if (h > 1) {
801
- let l = !1;
802
- for (let o = 0; o < h; o++)
803
- if (n.renderer.isLayerVisible(o)) {
804
- l = !0;
908
+ let a = !1;
909
+ for (let l = 0; l < h; l++)
910
+ if (n.renderer.isLayerVisible(l)) {
911
+ a = !0;
805
912
  break;
806
913
  }
807
- if (!l) continue;
914
+ if (!a) continue;
808
915
  }
809
916
  }
810
917
  r.push(n.id);
@@ -819,7 +926,22 @@ class Ot extends st {
819
926
  * Returns `null` for unknown ids or empty series.
820
927
  */
821
928
  getStackedLastValue(t) {
822
- return X(t, this.#t, this.#e.getAnimationState().xRange);
929
+ return U(t, this.#t, this.#s.getAnimationState().xRange);
930
+ }
931
+ /**
932
+ * Stacked value at an arbitrary `time` — the cumulative stack top at that
933
+ * sample, sharing a basis with {@link getStackedLastValue}. Falls back to the
934
+ * nearest raw sample (close / value) for renderers without a stacked concept
935
+ * (Candlestick, single-layer Line/Bar). Returns `null` for unknown ids or
936
+ * when no sample sits near `time`. Used by the YLabel intro count-up start.
937
+ */
938
+ getStackedValueAtTime(t, i) {
939
+ const e = this.#t.find((n) => n.id === t);
940
+ if (!e) return null;
941
+ const s = e.renderer.getStackedValueAtTime?.(i, this.#r);
942
+ if (s != null) return s;
943
+ const r = e.renderer.getDataAtTime?.(i, this.#r);
944
+ return r ? "close" in r ? r.close : r.value : null;
823
945
  }
824
946
  /**
825
947
  * Per-layer last snapshots with each layer's own `time`. Returns `null`
@@ -843,7 +965,7 @@ class Ot extends st {
843
965
  /** Get the primary display color for a series. */
844
966
  getSeriesColor(t) {
845
967
  const i = this.#t.find((e) => e.id === t);
846
- return i ? i.renderer.getLayerColors()[0] ?? this.#r.line.color : null;
968
+ return i ? i.renderer.getLayerColors()[0] ?? this.#n.line.color : null;
847
969
  }
848
970
  /** The series' primary name (first layer). Used for single-layer overlays. */
849
971
  getSeriesLabel(t) {
@@ -861,7 +983,7 @@ class Ot extends st {
861
983
  */
862
984
  setSeriesLabels(t, i) {
863
985
  const e = this.#t.find((s) => s.id === t);
864
- !e || I(e.labels, i) || (e.labels = i, this.#b());
986
+ !e || H(e.labels, i) || (e.labels = i, this.#p());
865
987
  }
866
988
  /**
867
989
  * Set the per-layer color overrides carried in `data` (sparse — `undefined`
@@ -872,7 +994,7 @@ class Ot extends st {
872
994
  */
873
995
  setLayerColors(t, i) {
874
996
  const e = this.#t.find((s) => s.id === t);
875
- !e || ft(e.colorOverrides, i) || (e.colorOverrides = i, e.renderer.setColorOverrides?.(i), this.#i.markDirty(), this.#b());
997
+ !e || St(e.colorOverrides, i) || (e.colorOverrides = i, e.renderer.setColorOverrides?.(i), this.#e.markDirty(), this.#p());
876
998
  }
877
999
  /** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
878
1000
  getSeriesLayers(t) {
@@ -885,29 +1007,43 @@ class Ot extends st {
885
1007
  }
886
1008
  /** Get all slices with computed colors and percentages. Returns null for series without slice data (e.g. candlestick, line, bar). */
887
1009
  getSliceInfo(t) {
888
- return this.#t.find((i) => i.id === t)?.renderer.getSliceInfo?.(this.#r) ?? null;
1010
+ return this.#t.find((i) => i.id === t)?.renderer.getSliceInfo?.(this.#n) ?? null;
889
1011
  }
890
1012
  /** Get hover info (label/value/percent/color) for the currently hovered element, or null. */
891
1013
  getHoverInfo(t) {
892
- return this.#t.find((i) => i.id === t)?.renderer.getHoverInfo?.(this.#r) ?? null;
1014
+ return this.#t.find((i) => i.id === t)?.renderer.getHoverInfo?.(this.#n) ?? null;
893
1015
  }
894
1016
  /** Apply a new theme and update series colors where appropriate. */
895
1017
  setTheme(t) {
896
- const i = this.#r;
897
- this.#r = t;
1018
+ const i = this.#n;
1019
+ this.#n = t;
898
1020
  for (const e of this.#t)
899
1021
  e.renderer.applyTheme(t, i);
900
- this.#i.markDirty(), this.#b();
1022
+ this.#e.markDirty(), this.#p();
901
1023
  }
902
1024
  getTheme() {
903
- return this.#r;
1025
+ return this.#n;
1026
+ }
1027
+ /** BCP 47 locale applied to the built-in time-axis / tooltip / crosshair formatting. */
1028
+ setLocale(t) {
1029
+ this.timeScale.setLocale(t), this.#p();
1030
+ }
1031
+ getLocale() {
1032
+ return this.timeScale.getLocale();
1033
+ }
1034
+ /** IANA timezone applied to the same built-in date-time formatting. */
1035
+ setTimeZone(t) {
1036
+ this.timeScale.setTimeZone(t), this.#p();
1037
+ }
1038
+ getTimeZone() {
1039
+ return this.timeScale.getTimeZone();
904
1040
  }
905
1041
  /** Update axis configuration and re-render. */
906
1042
  setAxis(t) {
907
1043
  const i = this.yAxisWidth, e = this.xAxisHeight;
908
- this.#I = t, this.#h = { min: t.y?.min, max: t.y?.max }, this.#u = !0;
1044
+ this.#H = t, this.#l = { min: t.y?.min, max: t.y?.max }, this.#f = !0;
909
1045
  const s = performance.now();
910
- this.#e.onAxisReconfig(s), this.#p(s), (this.yAxisWidth !== i || this.xAxisHeight !== e) && this.syncScales(), this.#i.markDirty();
1046
+ this.#s.onAxisReconfig(s), this.#v(s), (this.yAxisWidth !== i || this.xAxisHeight !== e) && this.syncScales(), this.#e.markDirty();
911
1047
  }
912
1048
  /**
913
1049
  * Apply label-density knobs driven by `<YAxis labelCount=… minLabelSpacing=…>`
@@ -917,19 +1053,19 @@ class Ot extends st {
917
1053
  */
918
1054
  setYAxisLabelDensity(t) {
919
1055
  let i = !1;
920
- "labelCount" in t && (this.yScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.yScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
1056
+ "labelCount" in t && (this.yScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.yScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#e.markDirty());
921
1057
  }
922
1058
  /** Label-density knobs for the time axis — mirror of {@link setYAxisLabelDensity}. */
923
1059
  setTimeAxisLabelDensity(t) {
924
1060
  let i = !1;
925
- "labelCount" in t && (this.timeScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.timeScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
1061
+ "labelCount" in t && (this.timeScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.timeScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#e.markDirty());
926
1062
  }
927
1063
  getMediaSize() {
928
- return this.#n.size.media;
1064
+ return this.#a.size.media;
929
1065
  }
930
1066
  /** Returns layout metrics for the chart area, Y axis, and time axis. */
931
1067
  getLayout() {
932
- const t = this.#n.size.media, i = this.yAxisWidth, e = this.xAxisHeight;
1068
+ const t = this.#a.size.media, i = this.yAxisWidth, e = this.xAxisHeight;
933
1069
  return {
934
1070
  chartArea: { x: 0, y: 0, width: t.width - i, height: t.height - e },
935
1071
  yAxisWidth: i,
@@ -937,7 +1073,7 @@ class Ot extends st {
937
1073
  };
938
1074
  }
939
1075
  getDataInterval() {
940
- return this.#a;
1076
+ return this.#r;
941
1077
  }
942
1078
  /**
943
1079
  * Update viewport padding at runtime. Refits the visible time range to
@@ -949,25 +1085,25 @@ class Ot extends st {
949
1085
  */
950
1086
  setPadding(t) {
951
1087
  const i = this.#c;
952
- this.#c = D(t);
953
- const e = this.#c, s = !T(i.left, e.left) || !T(i.right, e.right), r = i.top !== e.top || i.bottom !== e.bottom;
1088
+ this.#c = C(t);
1089
+ const e = this.#c, s = !R(i.left, e.left) || !R(i.right, e.right), r = i.top !== e.top || i.bottom !== e.bottom;
954
1090
  if (s) {
955
- const { first: n, last: a } = this.getDataBounds();
956
- if (n !== void 0 && a !== void 0) {
957
- const h = this.#n.size.media.width - this.yAxisWidth;
958
- this.#R(n, a, h);
1091
+ const { first: n, last: h } = this.getDataBounds();
1092
+ if (n !== void 0 && h !== void 0) {
1093
+ const a = this.#a.size.media.width - this.yAxisWidth;
1094
+ this.#P(n, h, a);
959
1095
  }
960
1096
  }
961
1097
  if (s || r) {
962
- this.#u = !0;
1098
+ this.#f = !0;
963
1099
  const n = performance.now();
964
- this.#e.onAxisReconfig(n), this.#p(n);
1100
+ this.#s.onAxisReconfig(n), this.#v(n);
965
1101
  }
966
- this.syncScales(), this.#i.markDirty(), r && this.emit("viewportChange");
1102
+ this.syncScales(), this.#e.markDirty(), r && this.emit("viewportChange");
967
1103
  }
968
1104
  /** Show or hide the background grid. Takes effect on the next render frame. */
969
1105
  setGrid(t) {
970
- this.#E = t.visible, this.#i.markDirty();
1106
+ this.#O = t.visible, this.#e.markDirty();
971
1107
  }
972
1108
  /**
973
1109
  * Set the visual state for one side of the chart. Typically called in
@@ -979,11 +1115,61 @@ class Ot extends st {
979
1115
  * symmetry and currently renders identically to `idle`.
980
1116
  */
981
1117
  setEdgeState(t, i) {
982
- this.#x[t] !== i && (this.#x[t] = i, this.#l.markDirty());
1118
+ if (this.#S[t] === i) return;
1119
+ const e = this.#S[t] === "loading", s = this.#R[t];
1120
+ this.#R[t] = !1, this.#V[t] = e && !s && (i === "idle" || i === "has-more") ? performance.now() : null, this.#S[t] = i, this.#h.markDirty();
983
1121
  }
984
1122
  /** Read the current host-declared state for a given edge. */
985
1123
  getEdgeState(t) {
986
- return this.#x[t];
1124
+ return this.#S[t];
1125
+ }
1126
+ /**
1127
+ * Alpha for a side's `loading` exit fade, or `null` when none is in
1128
+ * flight. Self-cleaning: an elapsed fade clears its start stamp.
1129
+ */
1130
+ #ut(t) {
1131
+ const i = this.#V[t];
1132
+ if (i === null) return null;
1133
+ const e = (performance.now() - i) / ft;
1134
+ return e >= 1 ? (this.#V[t] = null, null) : 1 - L(Math.max(0, e));
1135
+ }
1136
+ /**
1137
+ * Swap the painter used for a side's `loading` spinner — the same
1138
+ * pluggable-function contract as the series intro animations. `null`
1139
+ * (default) falls back to the built-in three-dot spinner. Takes effect
1140
+ * immediately if that side is currently in the `loading` state.
1141
+ */
1142
+ setEdgeIndicator(t, i) {
1143
+ this.#nt[t] = i, this.#S[t] === "loading" && this.#h.markDirty();
1144
+ }
1145
+ /**
1146
+ * Resolve the boundary point's Y position and series kind on the primary
1147
+ * visible time series, for a custom edge indicator that wants to start
1148
+ * exactly where the real chart leaves off and match its shape (candle vs.
1149
+ * plain bar/line). `left` continues from the first visible candle's
1150
+ * `open` (the value immediately preceding the boundary); `right` from the
1151
+ * last visible candle's `close`.
1152
+ */
1153
+ #Dt(t, i) {
1154
+ const e = this.getSeriesIdsByType("time", { visibleOnly: !0, singleLayerOnly: !0 })[0] ?? this.getSeriesIdsByType("time", { visibleOnly: !0 })[0];
1155
+ if (!e) return null;
1156
+ const s = this.getDataAtTime(e, i);
1157
+ if (!s) return null;
1158
+ const r = this.#t.find((n) => n.id === e);
1159
+ return r ? {
1160
+ valueY: this.yScale.valueToY(O(t, s)),
1161
+ seriesKind: r.renderer.kind
1162
+ } : null;
1163
+ }
1164
+ /**
1165
+ * Resolve the media-pixel spacing between adjacent bars at the chart's
1166
+ * current zoom level, for a custom edge indicator that wants to size its
1167
+ * placeholder shapes to match the real bars that will render once data
1168
+ * lands. `null` when the viewport has no meaningful span yet.
1169
+ */
1170
+ #Lt() {
1171
+ const t = this.timeScale.barWidthMedia(this.#r);
1172
+ return Number.isFinite(t) && t > 0 ? t : null;
987
1173
  }
988
1174
  /**
989
1175
  * Notify chart that a YLabel overlay is mounted / unmounted. Currently a
@@ -995,24 +1181,46 @@ class Ot extends st {
995
1181
  }
996
1182
  /**
997
1183
  * Dispatch a crosshair move to every renderer that supports spatial hover
998
- * (currently: pie). Any change in hover index schedules a main-layer redraw.
1184
+ * (pie, heatmap — anything implementing hitTest/setHoverIndex). Any change
1185
+ * in hover index schedules a main-layer redraw. Keep this loop capability-
1186
+ * probed, not kind-gated: a kind check here silently kills hover for the
1187
+ * next spatial renderer.
999
1188
  */
1000
- #_(t) {
1001
- const i = this.#n.size, e = this.#c, s = {
1189
+ #G(t) {
1190
+ const i = this.#a.size, e = this.#c, s = {
1002
1191
  top: e.top * i.verticalPixelRatio,
1003
1192
  bottom: e.bottom * i.verticalPixelRatio
1004
1193
  };
1005
- let r = !1;
1006
- for (const n of this.#t) {
1007
- if (!n.renderer.hitTest || !n.renderer.setHoverIndex) continue;
1194
+ let r = !1, n = null;
1195
+ for (const h of this.#t) {
1196
+ if (!h.renderer.hitTest || !h.renderer.setHoverIndex) continue;
1008
1197
  let a = -1;
1009
1198
  if (t) {
1010
- const h = t.mediaX * i.horizontalPixelRatio, l = t.mediaY * i.verticalPixelRatio;
1011
- a = n.renderer.hitTest(h, l, i.bitmap.width, i.bitmap.height, s);
1199
+ const l = t.mediaX * i.horizontalPixelRatio, o = t.mediaY * i.verticalPixelRatio;
1200
+ a = h.renderer.hitTest(l, o, i.bitmap.width, i.bitmap.height, s);
1012
1201
  }
1013
- n.renderer.setHoverIndex(a) && (r = !0);
1202
+ h.renderer.setHoverIndex(a) && (r = !0), a >= 0 && !n && (n = { seriesId: h.id, index: a, info: h.renderer.getHoverInfo?.(this.#n) ?? null });
1203
+ }
1204
+ r && (this.#e.markDirty(), this.emit("seriesHover", n));
1205
+ }
1206
+ /**
1207
+ * Read-only counterpart to {@link #updateHover} for `pointClick` — same
1208
+ * capability-probed `hitTest` scan, but doesn't touch `setHoverIndex` (a
1209
+ * click shouldn't move the hover state the last pointer move already set).
1210
+ * Returns the first spatial series (pie, heatmap, or a custom spatial kind)
1211
+ * whose `hitTest` claims the position, or `null` if none does.
1212
+ */
1213
+ #ft(t) {
1214
+ const i = this.#a.size, e = this.#c, s = {
1215
+ top: e.top * i.verticalPixelRatio,
1216
+ bottom: e.bottom * i.verticalPixelRatio
1217
+ }, r = t.mediaX * i.horizontalPixelRatio, n = t.mediaY * i.verticalPixelRatio;
1218
+ for (const h of this.#t) {
1219
+ if (!h.renderer.hitTest) continue;
1220
+ const a = h.renderer.hitTest(r, n, i.bitmap.width, i.bitmap.height, s);
1221
+ if (a >= 0) return { seriesId: h.id, index: a };
1014
1222
  }
1015
- r && this.#i.markDirty();
1223
+ return null;
1016
1224
  }
1017
1225
  /**
1018
1226
  * Re-derive the crosshair after the viewport moved beneath it. A streaming
@@ -1034,40 +1242,40 @@ class Ot extends st {
1034
1242
  * viewport didn't move, the re-derived values are bit-identical and the
1035
1243
  * method exits without emitting.
1036
1244
  */
1037
- #yt() {
1245
+ #Ct() {
1038
1246
  const t = this.#d;
1039
1247
  if (t === null) return;
1040
- const { from: i, to: e } = this.timeScale.getRange(), s = this.yScale.getRange(), r = Math.abs(e - i) * 1e-9, n = Math.abs(s.max - s.min) * 1e-9, a = 1e-6;
1041
- let h;
1042
- if (this.#F === "pointer") {
1248
+ const { from: i, to: e } = this.timeScale.getRange(), s = this.yScale.getRange(), r = Math.abs(e - i) * 1e-9, n = Math.abs(s.max - s.min) * 1e-9, h = 1e-6;
1249
+ let a;
1250
+ if (this.#Y === "pointer") {
1043
1251
  const l = this.timeScale.xToTime(t.mediaX), o = this.yScale.yToValue(t.mediaY);
1044
1252
  if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.time) <= r && Math.abs(o - t.y) <= n) return;
1045
- h = { mediaX: t.mediaX, mediaY: t.mediaY, time: l, y: o };
1253
+ a = { mediaX: t.mediaX, mediaY: t.mediaY, time: l, y: o };
1046
1254
  } else {
1047
1255
  if (t.time < i || t.time > e) {
1048
- this.#d = null, this.#l.markDirty(), this.emit("crosshairMove", null), this.#_(null);
1256
+ this.#d = null, this.#h.markDirty(), this.emit("crosshairMove", null), this.#G(null);
1049
1257
  return;
1050
1258
  }
1051
1259
  const l = this.timeScale.timeToX(t.time), o = this.yScale.valueToY(t.y);
1052
- if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.mediaX) <= a && Math.abs(o - t.mediaY) <= a) return;
1053
- h = { mediaX: l, mediaY: o, time: t.time, y: t.y };
1260
+ if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.mediaX) <= h && Math.abs(o - t.mediaY) <= h) return;
1261
+ a = { mediaX: l, mediaY: o, time: t.time, y: t.y };
1054
1262
  }
1055
- this.#d = h, this.#l.markDirty(), this.emit("crosshairMove", h), this.#_(h);
1263
+ this.#d = a, this.#h.markDirty(), this.emit("crosshairMove", a), this.#G(a);
1056
1264
  }
1057
1265
  /** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
1058
1266
  destroy() {
1059
1267
  for (const t of this.#t) t.renderer.dispose();
1060
- this.#t = [], this.#m = [], this.#v = [], this.#g = [], this.#A = null, this.#i.destroy(), this.#l.destroy(), this.#W?.destroy(), this.#n.destroy(), this.#X?.destroy(), this.#X = null, this.#B && this.#S?.destroy(), this.#S = null, this.#B = !1, this.removeAllListeners();
1268
+ this.#t = [], this.#m = [], this.#b = [], this.#y = [], this.#T = null, this.#e.destroy(), this.#h.destroy(), this.#C?.destroy(), this.#a.destroy(), this.#U?.destroy(), this.#U = null, this.#_ && this.#x?.destroy(), this.#x = null, this.#_ = !1, this.removeAllListeners();
1061
1269
  }
1062
1270
  /** The attached performance monitor, or `null` when instrumentation is disabled. */
1063
1271
  getPerfMonitor() {
1064
- return this.#S;
1272
+ return this.#x;
1065
1273
  }
1066
1274
  /** Compute the earliest and latest timestamps across all series. */
1067
1275
  getDataBounds() {
1068
1276
  let t, i;
1069
1277
  for (const e of this.#t) {
1070
- if (!e.visible || e.renderer.kind === "pie") continue;
1278
+ if (!e.visible || !S(e.renderer)) continue;
1071
1279
  const s = e.renderer.getTimeBounds();
1072
1280
  s !== null && ((t === void 0 || s.first < t) && (t = s.first), (i === void 0 || s.last > i) && (i = s.last));
1073
1281
  }
@@ -1076,28 +1284,28 @@ class Ot extends st {
1076
1284
  /** Set by `setSeriesData`/bulk-replace paths so the next `onDataChanged`
1077
1285
  * snaps the Y range (instead of easing) and `yScale.getRange()` reflects
1078
1286
  * the new domain synchronously. Cleared by `onDataChanged` after each run. */
1079
- #$ = !1;
1287
+ #q = !1;
1080
1288
  onDataChanged() {
1081
- if (this.#w > 0) {
1082
- this.#O = !0;
1289
+ if (this.#A > 0) {
1290
+ this.#X = !0;
1083
1291
  return;
1084
1292
  }
1085
1293
  this.updateDataInterval();
1086
1294
  const { first: t, last: i } = this.getDataBounds();
1087
- t !== void 0 && (this.#k = t), i !== void 0 && (this.#P = this.#s, this.#s = i);
1088
- const e = this.#$;
1089
- this.#$ = !1;
1090
- const s = !this.#u && t !== void 0;
1091
- e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#tt = e;
1295
+ t !== void 0 && (this.#k = t), i !== void 0 && (this.#z = this.#i, this.#i = i);
1296
+ const e = this.#q;
1297
+ this.#q = !1;
1298
+ const s = !this.#f && t !== void 0;
1299
+ e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#ht = e;
1092
1300
  const r = performance.now();
1093
- e || s ? (this.#D.seed(r), this.#e.onDataReplaced(r)) : this.#e.onPointAppended(r), this.#e.lastXTarget !== null && (this.#M = !0), t !== void 0 && (this.#u = !0), this.#p(r), this.syncScales(), s ? this.#G() : this.#i.markDirty(), this.emit("dataUpdate"), this.#b();
1301
+ e || s ? (this.#D.seed(r), this.#s.onDataReplaced(r)) : this.#s.onPointAppended(r), this.#s.lastXTarget !== null && (this.#I = !0), t !== void 0 && (this.#f = !0), this.#v(r), this.syncScales(), s ? this.#J() : this.#e.markDirty(), this.emit("dataUpdate"), this.#p();
1094
1302
  }
1095
1303
  updateDataInterval() {
1096
1304
  for (const t of this.#t) {
1097
- if (t.renderer.kind === "pie") continue;
1305
+ if (!S(t.renderer)) continue;
1098
1306
  const i = t.renderer.sampleTimes(20);
1099
1307
  if (i.length >= 2) {
1100
- this.#a = lt(i);
1308
+ this.#r = ut(i);
1101
1309
  break;
1102
1310
  }
1103
1311
  }
@@ -1107,13 +1315,13 @@ class Ot extends st {
1107
1315
  * data regardless of the caller's requested kind. Also reset by paths
1108
1316
  * that intentionally invalidate the current Y baseline (axis bound
1109
1317
  * change, empty viewport). */
1110
- #u = !1;
1318
+ #f = !1;
1111
1319
  /** Whether the engine's X slot has received its first emit. Pre-init X
1112
1320
  * emits are forced to `instant` so first paint snaps to the fitted
1113
1321
  * range — without this guard the engine would ease from `{0,0}` into
1114
1322
  * the new target and the very first frame would render with a
1115
1323
  * zero-width X domain. */
1116
- #M = !1;
1324
+ #I = !1;
1117
1325
  /**
1118
1326
  * One-shot `setVisibleRange` argument applied on the first data arrival.
1119
1327
  * Captured at chart construction from {@link ChartOptions.viewport.initialRange};
@@ -1127,8 +1335,8 @@ class Ot extends st {
1127
1335
  * axis labels subscribe to that event and re-renders on every tick are
1128
1336
  * expensive. Initialised to NaN so the first real Y emit always passes
1129
1337
  * the change check. */
1130
- #U = Number.NaN;
1131
- #Z = Number.NaN;
1338
+ #K = Number.NaN;
1339
+ #j = Number.NaN;
1132
1340
  /**
1133
1341
  * Sample the visible data window and return the resolved Y bounds, or
1134
1342
  * `null` when no series has data inside the X destination range.
@@ -1137,11 +1345,11 @@ class Ot extends st {
1137
1345
  * converge together. Clears {@link #yInited} on empty so the next emit
1138
1346
  * with valid data lands as an instant snap.
1139
1347
  */
1140
- #nt(t) {
1141
- const i = t ?? this.#o, e = this.#h.min !== void 0 && this.#h.min !== "auto" && typeof this.#h.min != "number" || this.#h.max !== void 0 && this.#h.max !== "auto" && typeof this.#h.max != "number" ? [] : null, s = q(i, this.#t, e);
1348
+ #gt(t) {
1349
+ const i = t ?? this.#o, e = this.#l.min !== void 0 && this.#l.min !== "auto" && typeof this.#l.min != "number" || this.#l.max !== void 0 && this.#l.max !== "auto" && typeof this.#l.max != "number" ? [] : null, s = Q(i, this.#t, e);
1142
1350
  if (s === null)
1143
- return this.#u = !1, null;
1144
- const r = M(this.#h.min, s.min, s.max, e ?? [], "min"), n = M(this.#h.max, s.max, s.min, e ?? [], "max");
1351
+ return this.#f = !1, null;
1352
+ const r = I(this.#l.min, s.min, s.max, e ?? [], "min"), n = I(this.#l.max, s.max, s.min, e ?? [], "max");
1145
1353
  return { min: r, max: n };
1146
1354
  }
1147
1355
  /**
@@ -1162,24 +1370,29 @@ class Ot extends st {
1162
1370
  * - **autoscroll off** → `null` (no X retarget; user has panned away
1163
1371
  * from the tail).
1164
1372
  */
1165
- #pt() {
1166
- if (this.#k === null || this.#s === null) return null;
1167
- const { from: t, to: i } = this.#o, e = t === 0 && i === 0, s = this.#n.size.media.width - this.yAxisWidth;
1168
- if (e)
1169
- return this.#R(this.#k, this.#s, s), this.#N !== void 0 && (this.setVisibleRange(this.#N), this.#N = void 0), this.#o;
1170
- if (!this.#y) return null;
1171
- if (this.#tt)
1172
- return this.#R(this.#k, this.#s, s), this.#o;
1173
- const r = j({
1373
+ #Rt() {
1374
+ if (this.#k === null || this.#i === null) return null;
1375
+ const { from: t, to: i } = this.#o, e = t === 0 && i === 0, s = this.#a.size.media.width - this.yAxisWidth;
1376
+ if (e) {
1377
+ if (this.#P(this.#k, this.#i, s), this.#N !== void 0 && (this.setVisibleRange(this.#N), this.#N = void 0, !this.#u && this.#i !== null)) {
1378
+ const { to: n } = this.#o;
1379
+ this.#i >= n && this.#i - n <= this.#r * 1.5 && (this.#u = !0);
1380
+ }
1381
+ return this.#o;
1382
+ }
1383
+ if (!this.#u) return null;
1384
+ if (this.#ht)
1385
+ return this.#P(this.#k, this.#i, s), this.#o;
1386
+ const r = J({
1174
1387
  currentLogical: this.#o,
1175
- lastTime: this.#s,
1176
- prevDataEnd: this.#P,
1177
- dataInterval: this.#a,
1388
+ lastTime: this.#i,
1389
+ prevDataEnd: this.#z,
1390
+ dataInterval: this.#r,
1178
1391
  paddingRight: this.#c.right,
1179
1392
  chartWidth: s,
1180
- holdUntilFilled: this.#L
1393
+ holdUntilFilled: this.#w
1181
1394
  });
1182
- return r.releaseHold && (this.#L = !1), r.reengageAutoScroll && (this.#y = !0), r.newLogical === null ? null : (this.#D.observe(performance.now()), this.#e.setXSettleMs(this.#D.pickSettleMs(this.#f.axis.x.settleMs)), this.#T(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#P = this.#s, r.newLogical);
1395
+ return r.releaseHold && (this.#w = !1), r.reengageAutoScroll && (this.#u = !0), r.newLogical === null ? null : (this.#D.observe(performance.now()), this.#s.setXSettleMs(this.#D.pickSettleMs(this.#g.axis.x.settleMs)), this.#L(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#z = this.#i, r.newLogical);
1183
1396
  }
1184
1397
  /**
1185
1398
  * Fit the viewport's logical X range around a data span. Mirrors the
@@ -1187,16 +1400,16 @@ class Ot extends st {
1187
1400
  * forces autoscroll on (data tail is in the new window by construction),
1188
1401
  * commits the new range via `viewport.setRange` (which emits `change`).
1189
1402
  */
1190
- #R(t, i, e) {
1191
- const s = this.#c, r = O({
1403
+ #P(t, i, e) {
1404
+ const s = this.#c, r = X({
1192
1405
  firstTime: t,
1193
1406
  lastTime: i,
1194
- dataInterval: this.#a,
1195
- maxVisibleBars: this.#it,
1407
+ dataInterval: this.#r,
1408
+ maxVisibleBars: this.#ot,
1196
1409
  chartWidth: e,
1197
1410
  padding: { left: s.left, right: s.right }
1198
1411
  });
1199
- this.#L = !1, this.#y = !0, this.#T(r, { emitChange: !0 });
1412
+ this.#w = !1, this.#u = !0, this.#L(r, { emitChange: !0 });
1200
1413
  }
1201
1414
  /**
1202
1415
  * Commit a programmatic zoom — `fitContent` or `setVisibleRange`. Reads
@@ -1212,15 +1425,15 @@ class Ot extends st {
1212
1425
  * passes `xEase: true` so X eases instead — viewport and axis converge
1213
1426
  * on the same frame.
1214
1427
  */
1215
- #H(t) {
1428
+ #W(t) {
1216
1429
  const i = this.#o;
1217
1430
  if (i.to <= i.from) return;
1218
1431
  const e = performance.now();
1219
- if (!this.#M || !this.#u) {
1220
- this.#at(e);
1432
+ if (!this.#I || !this.#f) {
1433
+ this.#pt(e);
1221
1434
  return;
1222
1435
  }
1223
- this.#e.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#p(e);
1436
+ this.#s.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#v(e);
1224
1437
  }
1225
1438
  /**
1226
1439
  * Commit the current logical X to the engine on the *gesture* profile:
@@ -1234,15 +1447,15 @@ class Ot extends st {
1234
1447
  * instead of lagging on the long sticky contract. First paint still snaps
1235
1448
  * (same as the programmatic path) so synchronous readers see final values.
1236
1449
  */
1237
- #vt() {
1450
+ #Mt() {
1238
1451
  const t = this.#o;
1239
1452
  if (t.to <= t.from) return;
1240
1453
  const i = performance.now();
1241
- if (!this.#M || !this.#u) {
1242
- this.#at(i);
1454
+ if (!this.#I || !this.#f) {
1455
+ this.#pt(i);
1243
1456
  return;
1244
1457
  }
1245
- this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#p(i);
1458
+ this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#v(i);
1246
1459
  }
1247
1460
  /**
1248
1461
  * First-paint snap: land both axes on the current logical X immediately
@@ -1251,9 +1464,9 @@ class Ot extends st {
1251
1464
  * commits — both must snap before the chart has painted once so a
1252
1465
  * synchronous `getYRange()` / `getVisibleRange()` read sees final values.
1253
1466
  */
1254
- #at(t) {
1255
- const i = this.#o, e = this.#nt(i);
1256
- this.#M = !0, e !== null && (this.#u = !0), this.#e.snap({ x: i, y: e ?? void 0 }, t), this.#p(t);
1467
+ #pt(t) {
1468
+ const i = this.#o, e = this.#gt(i);
1469
+ this.#I = !0, e !== null && (this.#f = !0), this.#s.snap({ x: i, y: e ?? void 0 }, t), this.#v(t);
1257
1470
  }
1258
1471
  /**
1259
1472
  * Validate and commit a new logical X range. Mirrors the old
@@ -1273,9 +1486,9 @@ class Ot extends st {
1273
1486
  * see `setVisibleRange`'s gesture path, where a per-frame echo would
1274
1487
  * otherwise re-arm and stall the in-flight Y ease.
1275
1488
  */
1276
- #T(t, i) {
1489
+ #L(t, i) {
1277
1490
  const { from: e, to: s } = t;
1278
- return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#a < 2 || this.#o.from === e && this.#o.to === s ? !1 : (this.#o = { from: e, to: s }, i.emitChange && (this.syncScales(), this.#i?.markDirty(), this.emit("viewportChange")), !0);
1491
+ return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#r < 2 || this.#o.from === e && this.#o.to === s ? !1 : (this.#o = { from: e, to: s }, i.emitChange && (this.syncScales(), this.#e?.markDirty(), this.emit("viewportChange")), !0);
1279
1492
  }
1280
1493
  /**
1281
1494
  * Apply pixel-padding to the engine's raw Y target and store the
@@ -1283,11 +1496,11 @@ class Ot extends st {
1283
1496
  * symmetric pad top / bottom from `#padding`, suppressed on the side
1284
1497
  * that has an explicit (fixed) axis bound.
1285
1498
  */
1286
- #ht(t, i, e, s, r) {
1287
- const n = i - t, a = e > 0 ? this.#c.top / e * n : 0, h = e > 0 ? this.#c.bottom / e * n : 0;
1288
- this.#V = {
1289
- min: s ? t : t - h,
1290
- max: r ? i : i + a
1499
+ #yt(t, i, e, s, r) {
1500
+ const n = i - t, h = e > 0 ? this.#c.top / e * n : 0, a = e > 0 ? this.#c.bottom / e * n : 0;
1501
+ this.#F = {
1502
+ min: s ? t : t - a,
1503
+ max: r ? i : i + h
1291
1504
  };
1292
1505
  }
1293
1506
  /**
@@ -1300,18 +1513,18 @@ class Ot extends st {
1300
1513
  * as a gesture for visual easing. Public so consumers can drive pan
1301
1514
  * programmatically.
1302
1515
  */
1303
- pan(t, i = this.#z) {
1304
- i > 0 && (this.#z = i), this.#L = !1;
1305
- const e = U({
1516
+ pan(t, i = this.#E) {
1517
+ i > 0 && (this.#E = i), this.#w = !1;
1518
+ const e = q({
1306
1519
  currentLogical: this.#o,
1307
1520
  timeDelta: t,
1308
1521
  chartWidth: i,
1309
- dataInterval: this.#a,
1522
+ dataInterval: this.#r,
1310
1523
  padding: { left: this.#c.left, right: this.#c.right },
1311
1524
  dataStart: this.#k,
1312
- dataEnd: this.#s
1525
+ dataEnd: this.#i
1313
1526
  });
1314
- e.newLogical !== null && (this.#y = !e.autoScrollOff, this.#T(e.newLogical, { emitChange: !0 }), e.edgeReached !== null && (this.#J[e.edgeReached.side] = e.edgeReached.boundaryTime, this.#K?.(e.edgeReached)), this.#D.pause(), this.#ot());
1527
+ e.newLogical !== null && (this.#u = !e.autoScrollOff, this.#L(e.newLogical, { emitChange: !0 }), e.edgeReached !== null && (this.#st[e.edgeReached.side] = e.edgeReached.boundaryTime, this.#rt?.(e.edgeReached)), this.#D.pause(), this.#vt());
1315
1528
  }
1316
1529
  /**
1317
1530
  * Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
@@ -1328,37 +1541,37 @@ class Ot extends st {
1328
1541
  * and produce a visible jump). Cursor-anchored zoom is intentionally
1329
1542
  * sacrificed in follow-live mode — pan first to inspect history.
1330
1543
  */
1331
- zoomAt(t, i, e = this.#z) {
1332
- e > 0 && (this.#z = e), this.#L = !1;
1333
- const s = Z({
1544
+ zoomAt(t, i, e = this.#E) {
1545
+ e > 0 && (this.#E = e), this.#w = !1;
1546
+ const s = K({
1334
1547
  currentLogical: this.#o,
1335
1548
  centerTime: t,
1336
1549
  factor: i,
1337
1550
  chartWidth: e,
1338
- dataInterval: this.#a,
1551
+ dataInterval: this.#r,
1339
1552
  padding: { left: this.#c.left, right: this.#c.right },
1340
1553
  dataStart: this.#k,
1341
- dataEnd: this.#s
1554
+ dataEnd: this.#i
1342
1555
  });
1343
1556
  if (s.newLogical === null) return;
1344
1557
  let r = s.newLogical;
1345
- if (this.#y && this.#s !== null) {
1346
- const n = r.to - r.from, a = C(this.#c.right, n, this.#a, e), h = this.#s + a;
1347
- r = { from: h - n, to: h }, this.#P = this.#s;
1558
+ if (this.#u && this.#i !== null) {
1559
+ const n = r.to - r.from, h = M(this.#c.right, n, this.#r, e), a = this.#i + h;
1560
+ r = { from: a - n, to: a }, this.#z = this.#i;
1348
1561
  }
1349
- this.#T(r, { emitChange: !0 }), this.#D.pause(), this.#ot();
1562
+ this.#L(r, { emitChange: !0 }), this.#D.pause(), this.#vt();
1350
1563
  }
1351
1564
  /**
1352
1565
  * Common tail for `pan` / `zoomAt`: routes the just-committed `#logical`
1353
1566
  * to the engine as a gesture so the X spring retargets and the Y spring
1354
1567
  * eases over `animations.axis.y.gesture`.
1355
1568
  */
1356
- #ot() {
1569
+ #vt() {
1357
1570
  const t = this.#o;
1358
1571
  if (t.to <= t.from) return;
1359
- this.#M = !0, this.#u = !0;
1572
+ this.#I = !0, this.#f = !0;
1360
1573
  const i = performance.now();
1361
- this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#p(i);
1574
+ this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#v(i);
1362
1575
  }
1363
1576
  /**
1364
1577
  * Tick the engine at the given wall clock and push the resolved X / Y
@@ -1373,11 +1586,11 @@ class Ot extends st {
1373
1586
  * `effectiveNow === startWall` and the prune step keeps zero-duration
1374
1587
  * events long enough for the slot processor to snap them.
1375
1588
  */
1376
- #p(t) {
1377
- const i = this.#e.tick(t), e = this.#n.size;
1589
+ #v(t) {
1590
+ const i = this.#s.tick(t), e = this.#a.size;
1378
1591
  if (e.media.width === 0 || e.media.height === 0) return;
1379
- const s = this.#h.min !== void 0 && this.#h.min !== "auto", r = this.#h.max !== void 0 && this.#h.max !== "auto", n = e.media.height - this.xAxisHeight;
1380
- this.#ht(i.yRange.min, i.yRange.max, n, s, r), this.#U = i.yRange.min, this.#Z = i.yRange.max, this.syncScales();
1592
+ const s = this.#l.min !== void 0 && this.#l.min !== "auto", r = this.#l.max !== void 0 && this.#l.max !== "auto", n = e.media.height - this.xAxisHeight;
1593
+ this.#yt(i.yRange.min, i.yRange.max, n, s, r), this.#K = i.yRange.min, this.#j = i.yRange.max, this.syncScales();
1381
1594
  }
1382
1595
  /**
1383
1596
  * Lightweight scale sync: updates timeScale/yScale from current viewport state
@@ -1385,78 +1598,78 @@ class Ot extends st {
1385
1598
  * handler, so DOM axis components always read fresh coordinates on re-render.
1386
1599
  */
1387
1600
  syncScales() {
1388
- const t = this.#n.size;
1601
+ const t = this.#a.size;
1389
1602
  if (t.media.width === 0 || t.media.height === 0) return;
1390
1603
  const i = t.media.width - this.yAxisWidth, e = t.media.height - this.xAxisHeight;
1391
1604
  this.timeScale.update(
1392
- this.#e.getAnimationState().xRange,
1605
+ this.#s.getAnimationState().xRange,
1393
1606
  i,
1394
1607
  t.horizontalPixelRatio,
1395
- this.#a
1396
- ), this.yScale.update(this.#V, e, t.verticalPixelRatio);
1608
+ this.#r
1609
+ ), this.yScale.update(this.#F, e, t.verticalPixelRatio);
1397
1610
  }
1398
1611
  /** Expensive: background, grid, all series. Only on data/viewport/resize change. */
1399
1612
  renderMain(t) {
1400
- const i = this.#n.size;
1613
+ const i = this.#a.size;
1401
1614
  if (i.media.width === 0 || i.media.height === 0) return;
1402
- const e = typeof t == "number" ? t : performance.now(), s = this.#e.tick(e);
1403
- s.animating && this.#i.markDirty();
1404
- const r = s.yRange.min, n = s.yRange.max, a = r !== this.#U || n !== this.#Z;
1405
- if (a) {
1406
- const d = this.#h.min !== void 0 && this.#h.min !== "auto", c = this.#h.max !== void 0 && this.#h.max !== "auto", f = i.media.height - this.xAxisHeight;
1407
- this.#ht(r, n, f, d, c), this.#U = r, this.#Z = n;
1615
+ const e = typeof t == "number" ? t : performance.now(), s = this.#s.tick(e);
1616
+ s.animating && this.#e.markDirty();
1617
+ const r = s.yRange.min, n = s.yRange.max, h = r !== this.#K || n !== this.#j;
1618
+ if (h) {
1619
+ const d = this.#l.min !== void 0 && this.#l.min !== "auto", c = this.#l.max !== void 0 && this.#l.max !== "auto", p = i.media.height - this.xAxisHeight;
1620
+ this.#yt(r, n, p, d, c), this.#K = r, this.#j = n;
1408
1621
  }
1409
- if (this.syncScales(), this.#yt(), this.#s !== null && !this.#y) {
1410
- const d = this.#e.lastXTarget ?? this.#o, c = d.to - d.from;
1622
+ if (this.syncScales(), this.#Ct(), this.#i !== null && !this.#u) {
1623
+ const d = this.#s.lastXTarget ?? this.#o, c = d.to - d.from;
1411
1624
  if (c > 0) {
1412
- const f = this.#n.size.media.width - this.yAxisWidth, p = C(this.#c.right, c, this.#a, f), v = this.#a * 0.5;
1413
- Math.abs(d.to - (this.#s + p)) < v && (this.#y = !0);
1625
+ const p = this.#a.size.media.width - this.yAxisWidth, v = M(this.#c.right, c, this.#r, p), m = this.#r * 0.5;
1626
+ Math.abs(d.to - (this.#i + v)) < m && (this.#u = !0);
1414
1627
  }
1415
1628
  }
1416
- a && this.emit("viewportChange");
1417
- let h = !1, l = !1;
1418
- this.#n.useMainLayer((d) => {
1419
- const { context: c, bitmapSize: f } = d, p = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, v = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
1420
- c.clearRect(0, 0, f.width, f.height), c.save(), c.beginPath(), c.rect(0, 0, p, v), c.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#a).ticks), this.yScale.tickTracker.tick(e), this.timeScale.tickTracker.tick(e);
1421
- const b = this.yScale.tickTracker.snapshot(), x = this.timeScale.tickTracker.snapshot();
1422
- h = b.isAnimating, l = x.isAnimating, (b.isAnimating || x.isAnimating || s.animating) && this.emit("tickFrame"), this.#E && K({
1629
+ h && this.emit("viewportChange");
1630
+ let a = !1, l = !1;
1631
+ this.#a.useMainLayer((d) => {
1632
+ const { context: c, bitmapSize: p } = d, v = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, m = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
1633
+ c.clearRect(0, 0, p.width, p.height), c.save(), c.beginPath(), c.rect(0, 0, v, m), c.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#r).ticks), this.yScale.tickTracker.tick(e), this.timeScale.tickTracker.tick(e);
1634
+ const g = this.yScale.tickTracker.snapshot(), k = this.timeScale.tickTracker.snapshot();
1635
+ a = g.isAnimating, l = k.isAnimating, (g.isAnimating || k.isAnimating || s.animating) && this.emit("tickFrame"), this.#O && it({
1423
1636
  scope: d,
1424
1637
  timeScale: this.timeScale,
1425
1638
  yScale: this.yScale,
1426
- theme: this.#r,
1427
- yTicks: b,
1428
- timeTicks: x
1429
- }), this.#ut(d, p, v);
1430
- const k = this.#c, P = { top: k.top, bottom: k.bottom }, w = this.#S;
1431
- for (const y of this.#t) {
1432
- const S = y.renderer.getAlpha?.() ?? (y.visible ? 1 : 0);
1433
- if (S <= 0) continue;
1434
- const L = {
1639
+ theme: this.#n,
1640
+ yTicks: g,
1641
+ timeTicks: k
1642
+ }), this.#At(d, v, m);
1643
+ const A = this.#c, V = { top: A.top, bottom: A.bottom }, w = this.#x;
1644
+ for (const b of this.#t) {
1645
+ const x = b.renderer.getAlpha?.() ?? (b.visible ? 1 : 0);
1646
+ if (x <= 0) continue;
1647
+ const T = {
1435
1648
  scope: d,
1436
1649
  timeScale: this.timeScale,
1437
1650
  yScale: this.yScale,
1438
- theme: this.#r,
1439
- dataInterval: this.#a,
1440
- padding: P,
1651
+ theme: this.#n,
1652
+ dataInterval: this.#r,
1653
+ padding: V,
1441
1654
  state: s,
1442
- seriesId: y.id
1443
- }, A = c.globalAlpha;
1444
- if (S < 1 && (c.globalAlpha = A * S), w) {
1445
- const V = performance.now();
1446
- y.renderer.render(L), w.recordSeries(y.id, performance.now() - V, e);
1655
+ seriesId: b.id
1656
+ }, D = c.globalAlpha;
1657
+ if (x < 1 && (c.globalAlpha = D * x), w) {
1658
+ const z = performance.now();
1659
+ b.renderer.render(T), w.recordSeries(b.id, performance.now() - z, e);
1447
1660
  } else
1448
- y.renderer.render(L);
1449
- S < 1 && (c.globalAlpha = A);
1661
+ b.renderer.render(T);
1662
+ x < 1 && (c.globalAlpha = D);
1450
1663
  }
1451
1664
  c.restore();
1452
- });
1665
+ }), this.#$();
1453
1666
  let o = !1;
1454
1667
  for (const d of this.#t)
1455
1668
  if (d.renderer.needsAnimation) {
1456
- o = !0, this.#i.markDirty();
1669
+ o = !0, this.#e.markDirty();
1457
1670
  break;
1458
1671
  }
1459
- const u = h || l;
1672
+ const u = a || l;
1460
1673
  !s.animating && !o && !u && (this.yScale.tickTracker.markArmed(), this.timeScale.tickTracker.markArmed());
1461
1674
  }
1462
1675
  /**
@@ -1468,13 +1681,13 @@ class Ot extends st {
1468
1681
  * `markDirty` calls made *during* `renderOverlay` (pulse, edge spinner)
1469
1682
  * land after the consume and re-arm cleanly for the next frame.
1470
1683
  */
1471
- #G(t) {
1472
- this.#j("main", t, () => this.renderMain(t)), this.#l.consume(), this.#j("overlay", t, () => this.renderOverlay(t));
1684
+ #J(t) {
1685
+ this.#Q("main", t, () => this.renderMain(t)), this.#h.consume(), this.#Q("overlay", t, () => this.renderOverlay(t));
1473
1686
  }
1474
1687
  /** Run a layer's paint, booking duration + draw calls to the perf monitor
1475
1688
  * when one is attached — keeps overlay time out of the `main` samples. */
1476
- #j(t, i, e) {
1477
- const s = this.#S;
1689
+ #Q(t, i, e) {
1690
+ const s = this.#x;
1478
1691
  if (!s) {
1479
1692
  e();
1480
1693
  return;
@@ -1485,66 +1698,80 @@ class Ot extends st {
1485
1698
  }
1486
1699
  /** Cheap overlay: crosshair, nearest-point dots, pulse animation, edge indicator. */
1487
1700
  renderOverlay(t) {
1488
- const i = this.#n.size;
1701
+ const i = this.#a.size;
1489
1702
  if (i.media.width === 0 || i.media.height === 0) return;
1490
1703
  let e = !1;
1491
- for (const a of this.#t)
1492
- if (a.visible && a.renderer.overlayNeedsAnimation) {
1704
+ for (const o of this.#t)
1705
+ if (o.visible && o.renderer.overlayNeedsAnimation) {
1493
1706
  e = !0;
1494
1707
  break;
1495
1708
  }
1496
- this.#m.some((a) => a.pulse) && (e = !0);
1497
- const s = this.#x.left === "loading" || this.#x.right === "loading", r = this.#x.left !== "idle" || this.#x.right !== "idle", n = !!this.#d || e || r || this.#m.length > 0 || this.#g.length > 0;
1498
- if (!(!n && !this.#q)) {
1499
- if (this.#q = n, this.#n.useOverlayLayer((a) => {
1500
- if (!n) return;
1501
- const h = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, l = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
1502
- if (a.context.save(), a.context.beginPath(), a.context.rect(0, 0, h, l), a.context.clip(), this.#d && this.#gt()) {
1503
- const c = this.#d.mediaX * i.horizontalPixelRatio, f = this.#d.mediaY * i.verticalPixelRatio;
1504
- J(a, c, f, this.#r);
1709
+ this.#m.some((o) => o.pulse) && (e = !0);
1710
+ const s = {
1711
+ left: this.#R.left && this.#S.left === "loading" ? "idle" : this.#S.left,
1712
+ right: this.#R.right && this.#S.right === "loading" ? "idle" : this.#S.right
1713
+ }, r = {
1714
+ left: this.#ut("left"),
1715
+ right: this.#ut("right")
1716
+ }, n = r.left !== null || r.right !== null, h = s.left === "loading" || s.right === "loading" || n, a = s.left !== "idle" || s.right !== "idle" || n, l = !!this.#d || e || a || this.#m.length > 0 || this.#y.length > 0;
1717
+ if (!(!l && !this.#et)) {
1718
+ if (this.#et = l, this.#a.useOverlayLayer((o) => {
1719
+ if (!l) return;
1720
+ const u = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, d = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
1721
+ if (o.context.save(), o.context.beginPath(), o.context.rect(0, 0, u, d), o.context.clip(), this.#d && this.#Tt()) {
1722
+ const m = this.#d.mediaX * i.horizontalPixelRatio, g = this.#d.mediaY * i.verticalPixelRatio;
1723
+ tt(o, m, g, this.#n);
1505
1724
  }
1506
- const o = this.#c, u = { top: o.top, bottom: o.bottom }, d = this.#e.getAnimationState();
1507
- for (const c of this.#t)
1508
- c.visible && c.renderer.drawOverlay?.({
1509
- scope: a,
1725
+ const c = this.#c, p = { top: c.top, bottom: c.bottom }, v = this.#s.getAnimationState();
1726
+ for (const m of this.#t)
1727
+ m.visible && m.renderer.drawOverlay?.({
1728
+ scope: o,
1510
1729
  timeScale: this.timeScale,
1511
1730
  yScale: this.yScale,
1512
- theme: this.#r,
1513
- dataInterval: this.#a,
1514
- padding: u,
1731
+ theme: this.#n,
1732
+ dataInterval: this.#r,
1733
+ padding: p,
1515
1734
  crosshair: this.#d,
1516
- state: d,
1517
- seriesId: c.id
1735
+ state: v,
1736
+ seriesId: m.id
1518
1737
  });
1519
- this.#ft(a, h, l), this.#mt(a), r && E({
1520
- scope: a,
1738
+ this.#wt(o, u, d), this.#kt(o, u, d), a && Y({
1739
+ scope: o,
1740
+ chartMediaWidth: i.media.width - this.yAxisWidth,
1521
1741
  chartMediaHeight: i.media.height - this.xAxisHeight,
1522
1742
  timeScale: this.timeScale,
1523
- theme: this.#r,
1524
- edgeStates: this.#x,
1525
- resolveBoundary: (c) => F(c, this.#J[c], this.getDataBounds())
1526
- }), a.context.restore();
1743
+ theme: this.#n,
1744
+ edgeStates: s,
1745
+ edgeExitFades: r,
1746
+ edgeIndicatorFns: this.#nt,
1747
+ resolveBoundary: (m) => B(m, this.#st[m], this.getDataBounds()),
1748
+ resolveEdgeAnchor: (m, g) => this.#Dt(m, g),
1749
+ resolveEdgeBarSpacing: () => this.#Lt(),
1750
+ reportPlaceholders: (m, g) => {
1751
+ this.#B[m] = { bars: g, at: performance.now() };
1752
+ }
1753
+ }), o.context.restore();
1527
1754
  }), e) {
1528
- const { from: a, to: h } = this.timeScale.getRange();
1529
- let l = !1;
1530
- for (const o of this.#t)
1531
- if (!(!o.visible || !o.renderer.overlayNeedsAnimation) && (o.renderer.hasOverlayContentInRange?.(a, h) ?? !0)) {
1532
- l = !0;
1755
+ const { from: o, to: u } = this.timeScale.getRange();
1756
+ let d = !1;
1757
+ for (const c of this.#t)
1758
+ if (!(!c.visible || !c.renderer.overlayNeedsAnimation) && (c.renderer.hasOverlayContentInRange?.(o, u) ?? !0)) {
1759
+ d = !0;
1533
1760
  break;
1534
1761
  }
1535
- if (!l) {
1536
- for (const o of this.#m)
1537
- if (o.pulse && o.time >= a && o.time <= h) {
1538
- l = !0;
1762
+ if (!d) {
1763
+ for (const c of this.#m)
1764
+ if (c.pulse && c.time >= o && c.time <= u) {
1765
+ d = !0;
1539
1766
  break;
1540
1767
  }
1541
1768
  }
1542
- l && this.#l.markDirty();
1769
+ d && this.#h.markDirty();
1543
1770
  }
1544
- s && this.#l.markDirty();
1771
+ h && this.#h.markDirty();
1545
1772
  }
1546
1773
  }
1547
1774
  }
1548
1775
  export {
1549
- Ot as ChartInstance
1776
+ qt as ChartInstance
1550
1777
  };