@wick-charts/vue 0.5.1 → 0.6.1

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