@wick-charts/vue 0.6.0 → 0.6.2

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