@luxalgo/vela 0.6.10 → 0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/{DataProvider-CWmp31dA.d.ts → DataProvider-BBf-jc6W.d.ts} +1 -1
  2. package/dist/{DataProvider-l_eLMly_.d.cts → DataProvider-p0TEyhlX.d.cts} +1 -1
  3. package/dist/{chunk-FFOP37FQ.js → chunk-73PEA4MU.js} +131 -33
  4. package/dist/{chunk-NMTQXNT4.js → chunk-IO3NYSQV.js} +65 -22
  5. package/dist/{chunk-4KZVZ7QQ.js → chunk-MTLJKZDZ.js} +9 -1
  6. package/dist/{contributions-BCz6Dr6a.d.ts → contributions-Bbe2R-mQ.d.ts} +16 -2
  7. package/dist/{contributions-D9vTzm5p.d.cts → contributions-CO01zWve.d.cts} +16 -2
  8. package/dist/index.cjs +139 -32
  9. package/dist/index.d.cts +12 -6
  10. package/dist/index.d.ts +12 -6
  11. package/dist/index.js +3 -3
  12. package/dist/{options-DSqHsQyN.d.cts → options-yp7sA96q.d.cts} +8 -1
  13. package/dist/{options-DSqHsQyN.d.ts → options-yp7sA96q.d.ts} +8 -1
  14. package/dist/{plugin-Bt8hLR8Z.d.cts → plugin-CkkH8QnX.d.cts} +3 -3
  15. package/dist/{plugin-CH7pfMrE.d.ts → plugin-DwxjM3Ni.d.ts} +3 -3
  16. package/dist/plugin.cjs +9 -0
  17. package/dist/plugin.d.cts +4 -4
  18. package/dist/plugin.d.ts +4 -4
  19. package/dist/plugin.js +2 -2
  20. package/dist/providers/binance.d.cts +2 -2
  21. package/dist/providers/binance.d.ts +2 -2
  22. package/dist/providers/coinbase.d.cts +2 -2
  23. package/dist/providers/coinbase.d.ts +2 -2
  24. package/dist/providers/hyperliquid.d.cts +2 -2
  25. package/dist/providers/hyperliquid.d.ts +2 -2
  26. package/dist/{statusline-5K7y4Ya2.d.ts → statusline-CHPDuKNp.d.ts} +13 -5
  27. package/dist/{statusline-TYLQmoeh.d.cts → statusline-DTHZUFqK.d.cts} +13 -5
  28. package/dist/ui.d.cts +1 -1
  29. package/dist/ui.d.ts +1 -1
  30. package/dist/vela.global.js +139 -32
  31. package/dist/vela.global.min.js +43 -43
  32. package/dist/widget.cjs +201 -52
  33. package/dist/widget.d.cts +6 -6
  34. package/dist/widget.d.ts +6 -6
  35. package/dist/widget.js +4 -4
  36. package/dist/workspace.cjs +201 -52
  37. package/dist/workspace.d.cts +20 -11
  38. package/dist/workspace.d.ts +20 -11
  39. package/dist/workspace.js +3 -3
  40. package/package.json +1 -1
  41. /package/dist/{chunk-AOGZBKUE.js → chunk-H26BEHF4.js} +0 -0
@@ -130,6 +130,12 @@ var Vela = (function (exports) {
130
130
  get visible() {
131
131
  return this.visibleState;
132
132
  }
133
+ inputValues() {
134
+ return this.controller.inputValuesOf(this.id);
135
+ }
136
+ propValues() {
137
+ return this.controller.propValuesOf(this.id);
138
+ }
133
139
  setInput(key, value) {
134
140
  this.controller.applyInputs(this.id, { [key]: value });
135
141
  }
@@ -7272,6 +7278,7 @@ var Vela = (function (exports) {
7272
7278
  var PREVIEW_BARS = 300;
7273
7279
  var SINGLE_LOAD_BARS = 5e3;
7274
7280
  var CHUNK_BARS = 1e4;
7281
+ var FIRST_PAINT_BARS = 100;
7275
7282
  var GAP_FACTOR = 1.5;
7276
7283
  var HEAL_COOLDOWN_MS = 5e3;
7277
7284
  var EngineOrchestrator = class _EngineOrchestrator {
@@ -7541,6 +7548,7 @@ var Vela = (function (exports) {
7541
7548
  let painted = false;
7542
7549
  const paint = (bars, final) => {
7543
7550
  if (this.generation !== gen || !final && bars.length === 0) return;
7551
+ if (!painted && !final && bars.length < Math.min(requested, FIRST_PAINT_BARS)) return;
7544
7552
  this.setBarSeries(bars, painted ? { preserveView: true } : void 0);
7545
7553
  if (!painted && bars.length > 0) {
7546
7554
  painted = true;
@@ -8171,6 +8179,15 @@ var Vela = (function (exports) {
8171
8179
  record.pendingCause = "inputs";
8172
8180
  if (record.session) record.session.update(record.inputValues);
8173
8181
  else if (record.native && !record.hidden) record.native.instance.setInputs(record.inputValues);
8182
+ this.events.emit("indicator:inputs", { id });
8183
+ }
8184
+ /** IndicatorController: the CURRENT stored input values (defaults merged with edits). */
8185
+ inputValuesOf(id) {
8186
+ return { ...this.registry.get(id)?.inputValues };
8187
+ }
8188
+ /** IndicatorController: the CURRENT declaration-prop overrides. */
8189
+ propValuesOf(id) {
8190
+ return { ...this.registry.get(id)?.propValues };
8174
8191
  }
8175
8192
  /** IndicatorController: re-run an indicator with merged declaration-prop overrides.
8176
8193
  * Same lifecycle as {@link applyInputs} — a prop change replays the whole script.
@@ -8184,6 +8201,7 @@ var Vela = (function (exports) {
8184
8201
  if (!record.hidden) this.setLoading(record, true);
8185
8202
  record.pendingCause = "inputs";
8186
8203
  record.session.update(record.inputValues, record.propValues);
8204
+ this.events.emit("indicator:inputs", { id });
8187
8205
  }
8188
8206
  /** IndicatorController: tear down an indicator and (if now empty) its pane. */
8189
8207
  /** Live handles of every indicator on the chart (script + native), insertion order. */
@@ -17222,6 +17240,14 @@ ${STATIC_DECLS}
17222
17240
  const conds = Array.isArray(when) ? when : [when];
17223
17241
  return conds.every((c) => c.anyOf ? c.anyOf.some((x) => x === values[c.key]) : values[c.key] === c.equals);
17224
17242
  }
17243
+ function inputDeltas(schema, values) {
17244
+ const out = {};
17245
+ for (const s of schema) {
17246
+ const v = values[s.key];
17247
+ if (v !== void 0 && JSON.stringify(v) !== JSON.stringify(s.defval)) out[s.key] = v;
17248
+ }
17249
+ return Object.keys(out).length > 0 ? out : void 0;
17250
+ }
17225
17251
 
17226
17252
  // src/ui/components/select/controller.ts
17227
17253
  function selectController(opts) {
@@ -24194,6 +24220,7 @@ void main() {
24194
24220
  var DOUBLE_TAP_SLOP = 30;
24195
24221
  var TIME_SCALE_K = 4e-3;
24196
24222
  var WHEEL_ZOOM_K = 4e-3;
24223
+ var WHEEL_PRICE_DRAG_PX = 0.25;
24197
24224
  function wheelZoomAnchor(coords, cursorX, rightEdge) {
24198
24225
  if (rightEdge) return { logical: coords.rightEdgeLogical, x: coords.width };
24199
24226
  return { logical: coords.xToLogical(cursorX), x: cursorX };
@@ -24324,7 +24351,7 @@ void main() {
24324
24351
  this.capture(e.pointerId);
24325
24352
  return;
24326
24353
  }
24327
- if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y)) {
24354
+ if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
24328
24355
  this.region = "drawing";
24329
24356
  this.capture(e.pointerId);
24330
24357
  return;
@@ -24438,7 +24465,7 @@ void main() {
24438
24465
  const wasTouch = e.pointerType === "touch";
24439
24466
  const tapRelease = this.dragging && !this.moved && (!wasTouch || Math.hypot(x - this.startX, y - this.startY) <= TOUCH_TAP_SLOP);
24440
24467
  if (this.dragging && this.region === "drawing") {
24441
- this.deps.drawingsPointerUp?.(x, y);
24468
+ this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
24442
24469
  } else if (tapRelease && this.region === "data") {
24443
24470
  this.deps.onClick(x, y);
24444
24471
  } else if (this.dragging && this.region === "data") {
@@ -24459,7 +24486,7 @@ void main() {
24459
24486
  if (e.pointerType === "touch") this.touches.delete(e.pointerId);
24460
24487
  this.cancelLongPress();
24461
24488
  if (!this.dragging) return;
24462
- if (this.region === "drawing" && !Number.isNaN(this.cursorX)) this.deps.drawingsPointerUp?.(this.cursorX, this.cursorY);
24489
+ if (this.region === "drawing" && !Number.isNaN(this.cursorX)) this.deps.drawingsPointerUp?.(this.cursorX, this.cursorY, this.snapMode(e));
24463
24490
  if (this.region === "crosshair" || e.pointerType === "touch") this.deps.onPointerMove(null, null);
24464
24491
  this.endGesture(e);
24465
24492
  };
@@ -24475,6 +24502,12 @@ void main() {
24475
24502
  this.onWheel = (e) => {
24476
24503
  e.preventDefault();
24477
24504
  this.deps.drawingsClearTransient?.();
24505
+ const { x, y } = this.local(e);
24506
+ if (this.regionAt(x, y) === "price" && e.deltaY !== 0) {
24507
+ this.deps.beginPriceScale(x, y);
24508
+ this.deps.priceScaleBy(e.deltaY * WHEEL_PRICE_DRAG_PX);
24509
+ return;
24510
+ }
24478
24511
  const coords = this.deps.getCoords();
24479
24512
  const vp = coords.getViewport();
24480
24513
  const pan = wheelPanDelta(e.deltaX, e.deltaY, e.shiftKey);
@@ -24482,9 +24515,8 @@ void main() {
24482
24515
  this.deps.apply({ barSpacing: vp.barSpacing, rightOffset: wheelPanRightOffset(vp.rightOffset, pan, coords.pxPerBar()) });
24483
24516
  return;
24484
24517
  }
24485
- const cursorX = this.local(e).x;
24486
24518
  const rightEdge = this.rightEdgeZoom && !(e.ctrlKey || e.metaKey);
24487
- const anchor = wheelZoomAnchor(coords, cursorX, rightEdge);
24519
+ const anchor = wheelZoomAnchor(coords, x, rightEdge);
24488
24520
  const target = clampBarSpacing(vp.barSpacing * Math.exp(-e.deltaY * WHEEL_ZOOM_K));
24489
24521
  this.deps.zoomTo(target, anchor.logical, anchor.x);
24490
24522
  };
@@ -25966,6 +25998,15 @@ void main() {
25966
25998
  function fontSizePx(size3) {
25967
25999
  return size3 === "auto" ? 12 : namedFontSize(size3);
25968
26000
  }
26001
+ function lineCoversWindow(a, b, extend, lo, hi) {
26002
+ const minX = Math.min(a, b);
26003
+ const maxX = Math.max(a, b);
26004
+ if (a === b) return a >= lo && a <= hi;
26005
+ if (extend === "both") return true;
26006
+ if (extend === "left") return maxX >= lo;
26007
+ if (extend === "right") return minX <= hi;
26008
+ return maxX >= lo && minX <= hi;
26009
+ }
25969
26010
  var DrawingSceneRenderer = class {
25970
26011
  constructor(deps, set = EMPTY_DRAWING_SET) {
25971
26012
  this.deps = deps;
@@ -26030,7 +26071,7 @@ void main() {
26030
26071
  };
26031
26072
  for (const ln of this.set.lines) {
26032
26073
  if (ln.invisible) continue;
26033
- if (!visible(this.logicalOf(ln.xloc, ln.x1), this.logicalOf(ln.xloc, ln.x2), ln.extend)) continue;
26074
+ if (!lineCoversWindow(this.logicalOf(ln.xloc, ln.x1), this.logicalOf(ln.xloc, ln.x2), ln.extend, lo, hi)) continue;
26034
26075
  fold(ln.y1);
26035
26076
  fold(ln.y2);
26036
26077
  }
@@ -29970,6 +30011,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
29970
30011
  this.snapAt = changed ? { point: snapped, paneId } : null;
29971
30012
  return snapped;
29972
30013
  }
30014
+ /**
30015
+ * Resolve a cursor pixel through the magnet and return the snapped pixel — the same
30016
+ * conversion drawing placement uses. Updates the snap-ring marker. The measure
30017
+ * ruler goes through this so its endpoints follow weak/strong/Ctrl magnet too.
30018
+ */
30019
+ snapCursor(x, y, mode) {
30020
+ const proj = this.deps.projector();
30021
+ const paneId = proj.paneIdAtY(y) ?? "price";
30022
+ const point = this.resolve(x, y, paneId, mode);
30023
+ const sy = proj.yOf(point.price, paneId);
30024
+ return { x: proj.xOf(point.time), y: sy ?? y };
30025
+ }
30026
+ /** Drop the snap-ring marker (a transient mode ended without going through `up`). */
30027
+ clearSnapMarker() {
30028
+ this.snapAt = null;
30029
+ }
29973
30030
  /** Resolve a pixel to a data point with the segment angle locked to 45° steps around
29974
30031
  * `pivot` (Shift held on a line tool). Works in PIXEL space — the user reasons about
29975
30032
  * the angle they see, not about time/price units. The magnet is bypassed: snapping
@@ -32007,30 +32064,39 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32007
32064
  isFinished() {
32008
32065
  return this.state === "finished";
32009
32066
  }
32010
- /** A press: begin the measurement, or finish it on the second click. */
32011
- down(x, y) {
32067
+ /** A press: begin the measurement, or finish it on the second click.
32068
+ * `x,y` are the raw cursor (drag-slop vs click-move-click). `gx,gy` are the
32069
+ * graphic endpoints — magnet-snapped when the magnet is on, else the same as `x,y`. */
32070
+ down(x, y, gx = x, gy = y) {
32012
32071
  if (this.state === "measuring") {
32013
- this.end = { x, y };
32072
+ this.end = { x: gx, y: gy };
32014
32073
  this.state = "finished";
32015
32074
  return;
32016
32075
  }
32017
- this.start = { x, y };
32018
- this.end = { x, y };
32076
+ this.start = { x: gx, y: gy };
32077
+ this.end = { x: gx, y: gy };
32019
32078
  this.pressX = x;
32020
32079
  this.pressY = y;
32021
32080
  this.state = "measuring";
32022
32081
  }
32082
+ /** Size the in-progress ruler. `x,y` are the graphic (magnet-snapped) cursor. */
32023
32083
  move(x, y) {
32024
32084
  if (this.state === "measuring") this.end = { x, y };
32025
32085
  }
32026
32086
  /** A release: finish if the press was actually dragged (press-drag-release), else wait
32027
- * for the second click (click-move-click). */
32028
- up(x, y) {
32087
+ * for the second click (click-move-click). `x,y` are the raw cursor (slop); `gx,gy`
32088
+ * are the graphic end (magnet-snapped when the magnet is on). */
32089
+ up(x, y, gx = x, gy = y) {
32029
32090
  if (this.state === "measuring" && Math.hypot(x - this.pressX, y - this.pressY) > DRAG_SLOP3) {
32030
- this.end = { x, y };
32091
+ this.end = { x: gx, y: gy };
32031
32092
  this.state = "finished";
32032
32093
  }
32033
32094
  }
32095
+ /** Current graphic endpoints in media pixels, or null when idle. */
32096
+ points() {
32097
+ if (!this.start || !this.end) return null;
32098
+ return { start: this.start, end: this.end };
32099
+ }
32034
32100
  clear() {
32035
32101
  this.state = "idle";
32036
32102
  this.start = null;
@@ -32407,11 +32473,13 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32407
32473
  }
32408
32474
  /** Shift+press on the empty plot: arm the measure ruler AND start it at (x, y) in one
32409
32475
  * gesture — the equivalent of clicking the toolbar's Measure button, then pressing.
32410
- * Returns false when a mode/tool is already active (the normal press path owns it). */
32411
- beginMeasureAt(x, y) {
32476
+ * `snap` is the effective magnet (sticky mode, or Ctrl/Cmd-forced strong). Returns
32477
+ * false when a mode/tool is already active (the normal press path owns it). */
32478
+ beginMeasureAt(x, y, snap = "off") {
32412
32479
  if (this.measureMode || this.eraserMode || this.activeTool != null) return false;
32413
32480
  this.withModeIntent(() => this.toggleMeasure());
32414
- this.measure.down(x, y);
32481
+ const g = this.interaction.snapCursor(x, y, snap);
32482
+ this.measure.down(x, y, g.x, g.y);
32415
32483
  this.render();
32416
32484
  return true;
32417
32485
  }
@@ -32433,7 +32501,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32433
32501
  return;
32434
32502
  }
32435
32503
  if (this.measureMode) {
32436
- this.measure.down(x, y);
32504
+ const g = this.interaction.snapCursor(x, y, snap);
32505
+ this.measure.down(x, y, g.x, g.y);
32437
32506
  if (this.measure.isFinished()) this.withModeIntent(() => this.exitMeasure(false));
32438
32507
  this.render();
32439
32508
  return;
@@ -32446,7 +32515,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32446
32515
  return;
32447
32516
  }
32448
32517
  if (this.measureMode) {
32449
- this.measure.move(x, y);
32518
+ const g = this.interaction.snapCursor(x, y, snap);
32519
+ this.measure.move(g.x, g.y);
32450
32520
  this.render();
32451
32521
  return;
32452
32522
  }
@@ -32464,13 +32534,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32464
32534
  this.render();
32465
32535
  }
32466
32536
  }
32467
- pointerUp(x, y) {
32537
+ pointerUp(x, y, snap = "off") {
32468
32538
  if (this.eraserMode) {
32469
32539
  this.erasing = false;
32470
32540
  return;
32471
32541
  }
32472
32542
  if (this.measureMode) {
32473
- this.measure.up(x, y);
32543
+ const g = this.interaction.snapCursor(x, y, snap);
32544
+ this.measure.up(x, y, g.x, g.y);
32474
32545
  if (this.measure.isFinished()) this.withModeIntent(() => this.exitMeasure(false));
32475
32546
  this.render();
32476
32547
  return;
@@ -32515,6 +32586,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32515
32586
  /** Leave ruler mode. `clearGraphic` keeps a just-finished measurement on screen (false). */
32516
32587
  exitMeasure(clearGraphic = true) {
32517
32588
  this.measureMode = false;
32589
+ this.interaction.clearSnapMarker();
32518
32590
  if (clearGraphic) this.measure.clear();
32519
32591
  this.toolbar.setMeasureActive(false);
32520
32592
  this.render();
@@ -32655,15 +32727,28 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32655
32727
  if (this.eraserMode) return "pointer";
32656
32728
  return this.interaction.cursorAt(x, y);
32657
32729
  }
32658
- /** Right-click while placing: cancel the in-progress drawing and revert to the
32659
- * pointer the gesture is an explicit escape, so it disarms even in
32660
- * stay-in-drawing-mode (where Escape would leave the tool armed). Returns whether
32661
- * the press was consumed; false lets the host's context menu open normally. */
32730
+ /** Right-click: an explicit escape back to the pointer. Cancels an in-progress
32731
+ * placement or measurement, and also plain-disarms an armed-but-idle drawing
32732
+ * tool or the eraser — so a right-click ALWAYS reverts to the pointer, even in
32733
+ * stay-in-drawing-mode (where Escape would leave a drawing tool armed).
32734
+ * Persistent toggles (magnet, stay-mode, favorites) are untouched. Returns
32735
+ * whether the press was consumed; false lets the host's context menu open
32736
+ * normally. */
32662
32737
  cancelPlacement() {
32663
- if (!this.interaction.isPlacing()) return false;
32664
- this.interaction.cancel();
32665
- if (this.activeTool != null) this.emit({ kind: "arm", type: null });
32666
- return true;
32738
+ if (this.measureMode || this.eraserMode) {
32739
+ this.withModeIntent(() => this.measureMode ? this.exitMeasure() : this.exitEraser());
32740
+ return true;
32741
+ }
32742
+ if (this.interaction.isPlacing()) {
32743
+ this.interaction.cancel();
32744
+ if (this.activeTool != null) this.emit({ kind: "arm", type: null });
32745
+ return true;
32746
+ }
32747
+ if (this.activeTool != null) {
32748
+ this.emit({ kind: "arm", type: null });
32749
+ return true;
32750
+ }
32751
+ return false;
32667
32752
  }
32668
32753
  /** Double-click over a drawing → suppress the chart's view reset (single-click already
32669
32754
  * opens settings). Returns true only when a drawing is under the cursor. */
@@ -32689,6 +32774,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32689
32774
  return true;
32690
32775
  }
32691
32776
  if (this.interaction.cancel()) return true;
32777
+ if (this.measureMode) {
32778
+ this.withModeIntent(() => this.exitMeasure());
32779
+ return true;
32780
+ }
32692
32781
  if (this.selectedIds.size) {
32693
32782
  this.clearSelection();
32694
32783
  return true;
@@ -35247,13 +35336,13 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35247
35336
  resetView: () => this.resetView(),
35248
35337
  // User drawings claim a gesture before pan when armed / over a drawing.
35249
35338
  drawingsClaim: (x, y) => this.userDrawings?.claim(x, y) ?? false,
35250
- drawingsMeasureStart: (x, y) => this.userDrawings?.beginMeasureAt(x, y) ?? false,
35339
+ drawingsMeasureStart: (x, y, snap) => this.userDrawings?.beginMeasureAt(x, y, snap) ?? false,
35251
35340
  drawingsDeleteAt: (x, y) => this.userDrawings?.deleteAt(x, y) ?? false,
35252
35341
  drawingsCancelPlacement: () => this.userDrawings?.cancelPlacement() ?? false,
35253
35342
  drawingsSnapMode: () => this.snapMode,
35254
35343
  drawingsPointerDown: (x, y, snap, shift3) => this.userDrawings?.pointerDown(x, y, snap, shift3),
35255
35344
  drawingsPointerMove: (x, y, snap, shift3) => this.userDrawings?.pointerMove(x, y, snap, shift3),
35256
- drawingsPointerUp: (x, y) => this.userDrawings?.pointerUp(x, y),
35345
+ drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
35257
35346
  drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? null,
35258
35347
  drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
35259
35348
  drawingsClearTransient: () => this.userDrawings?.clearTransient()
@@ -35568,7 +35657,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35568
35657
  }
35569
35658
  const skipFit = opts?.preserveView === true && this.didInitialFit;
35570
35659
  if (this.coords.width > 0 && !skipFit) {
35571
- this.fitContent();
35660
+ if (this.didInitialFit) this.reframeKeepZoom();
35661
+ else this.fitContent();
35572
35662
  this.didInitialFit = true;
35573
35663
  }
35574
35664
  if (!this.introPlayed && this.bars.length > 0) {
@@ -36216,6 +36306,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
36216
36306
  this.scaleDragHeight = res.height;
36217
36307
  this.scaleDragStart = { ...res.holder.scale };
36218
36308
  res.holder.manualScale = { ...res.holder.scale };
36309
+ this.axisScaleButtons?.reposition();
36219
36310
  this.scheduler.invalidate(4 /* Full */);
36220
36311
  }
36221
36312
  /** Rescale the grabbed scale around its center by the total drag (down ⇒ zoom out). */
@@ -36247,6 +36338,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
36247
36338
  const res = this.resolveScaleHolder(x, y);
36248
36339
  if (!res) return;
36249
36340
  res.holder.manualScale = null;
36341
+ this.axisScaleButtons?.reposition();
36250
36342
  this.scheduler.invalidate(4 /* Full */);
36251
36343
  }
36252
36344
  /**
@@ -36884,6 +36976,20 @@ ${overlayScrollbarCss(".vela-sd-pane")}
36884
36976
  this.coords.setViewport(v);
36885
36977
  this.targetBarSpacing = v.barSpacing;
36886
36978
  }
36979
+ /** Re-frame after a series replacement (a symbol/timeframe switch): keep the user's
36980
+ * zoom (bar spacing), re-anchor the newest bars at the default right offset.
36981
+ * `clampViewport`'s fit-all-bars floor deliberately does NOT apply — a progressive
36982
+ * head may still be backfilling toward the previous depth, and raising the spacing
36983
+ * to its temporary bar count would lose the zoom this exists to keep. */
36984
+ reframeKeepZoom() {
36985
+ this.animator?.stop();
36986
+ this.panVelocity = 0;
36987
+ for (const pane of this.scene.panes.values()) pane.manualScale = null;
36988
+ for (const sl of this.scene.indicatorScales.values()) sl.manualScale = null;
36989
+ const v = { barSpacing: clampBarSpacing(this.coords.getViewport().barSpacing), rightOffset: defaultViewport().rightOffset };
36990
+ this.coords.setViewport(v);
36991
+ this.targetBarSpacing = v.barSpacing;
36992
+ }
36887
36993
  paneBoundsFor(paneId) {
36888
36994
  const p = this.scene.panes.get(paneId);
36889
36995
  return { top: p?.bounds.top ?? 0, height: p?.bounds.height ?? 0, rightAxis: this.rightAxisW };
@@ -39313,6 +39419,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
39313
39419
  exports.getDrawingType = getDrawingType;
39314
39420
  exports.getNativeIndicator = getNativeIndicator;
39315
39421
  exports.iconMarkup = iconMarkup;
39422
+ exports.inputDeltas = inputDeltas;
39316
39423
  exports.inputVisible = inputVisible;
39317
39424
  exports.legendActions = legendActions;
39318
39425
  exports.legendCallouts = legendCallouts;