@wick-charts/vue 0.5.0 → 0.6.0

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