@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
package/dist/widget.js CHANGED
@@ -1,7 +1,7 @@
1
- import { VelaWorkspace } from './chunk-NMTQXNT4.js';
2
- export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, localStorageAdapter, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, timeframeLabel, timeframeMs } from './chunk-NMTQXNT4.js';
3
- export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-FFOP37FQ.js';
4
- export { DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, OVERRIDABLE_TOPBAR_IDS, SidePanel, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, clampPanelWidth, pinnedTopbarActionIds, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, resolveTopbarComposition, sidePanels, statePersistenceHandlers, symbolRanking, topbarActionOverride, topbarHas, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments } from './chunk-4KZVZ7QQ.js';
1
+ import { VelaWorkspace } from './chunk-IO3NYSQV.js';
2
+ export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, localStorageAdapter, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, timeframeLabel, timeframeMs } from './chunk-IO3NYSQV.js';
3
+ export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-73PEA4MU.js';
4
+ export { DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, OVERRIDABLE_TOPBAR_IDS, SidePanel, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, clampPanelWidth, pinnedTopbarActionIds, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, resolveTopbarComposition, sidePanels, statePersistenceHandlers, symbolRanking, topbarActionOverride, topbarHas, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments } from './chunk-MTLJKZDZ.js';
5
5
  import './chunk-G77Y7LK2.js';
6
6
  import './chunk-MHY7MVXH.js';
7
7
  import './chunk-KG4YT3TI.js';
@@ -16798,11 +16798,12 @@ async function resolveIndicators(config, fetchImpl = fetch) {
16798
16798
  }
16799
16799
  return out;
16800
16800
  }
16801
+ var ledgerEntryName = (e) => typeof e === "string" ? e : e.name;
16801
16802
  function indicatorLedger(i) {
16802
16803
  const natives = [...i.present];
16803
16804
  if (i.volumePending && !natives.includes("volume")) natives.push("volume");
16804
16805
  return {
16805
- manifest: i.manifestSettled ? [...i.instanceNames] : [...i.pendingManifest ?? i.instanceNames],
16806
+ manifest: i.manifestSettled ? [...i.instanceEntries] : [...i.pendingManifest ?? i.instanceEntries],
16806
16807
  natives
16807
16808
  };
16808
16809
  }
@@ -17736,7 +17737,17 @@ function sanitizeCell(raw) {
17736
17737
  if (c.drawings != null && typeof c.drawings === "object") out.drawings = c.drawings;
17737
17738
  const ind = c.indicators;
17738
17739
  if (ind != null && typeof ind === "object") {
17739
- const manifest = Array.isArray(ind.manifest) ? ind.manifest.filter((n) => typeof n === "string") : [];
17740
+ const manifest = Array.isArray(ind.manifest) ? ind.manifest.flatMap((n) => {
17741
+ if (typeof n === "string") return [n];
17742
+ if (n != null && typeof n === "object" && typeof n.name === "string") {
17743
+ const e = n;
17744
+ const bag = (v) => v != null && typeof v === "object" && !Array.isArray(v) ? v : void 0;
17745
+ const inputs = bag(e.inputs);
17746
+ const props = bag(e.props);
17747
+ return [inputs || props ? { name: e.name, ...inputs ? { inputs } : {}, ...props ? { props } : {} } : e.name];
17748
+ }
17749
+ return [];
17750
+ }) : [];
17740
17751
  const natives = Array.isArray(ind.natives) ? ind.natives.filter((n) => typeof n === "string") : [];
17741
17752
  out.indicators = { manifest, natives };
17742
17753
  }
@@ -17935,6 +17946,12 @@ var IndicatorHandleImpl = class {
17935
17946
  get visible() {
17936
17947
  return this.visibleState;
17937
17948
  }
17949
+ inputValues() {
17950
+ return this.controller.inputValuesOf(this.id);
17951
+ }
17952
+ propValues() {
17953
+ return this.controller.propValuesOf(this.id);
17954
+ }
17938
17955
  setInput(key, value) {
17939
17956
  this.controller.applyInputs(this.id, { [key]: value });
17940
17957
  }
@@ -18074,6 +18091,7 @@ var RUN_EMIT_THROTTLE_MS = 1e3;
18074
18091
  var PREVIEW_BARS = 300;
18075
18092
  var SINGLE_LOAD_BARS = 5e3;
18076
18093
  var CHUNK_BARS = 1e4;
18094
+ var FIRST_PAINT_BARS = 100;
18077
18095
  var GAP_FACTOR = 1.5;
18078
18096
  var HEAL_COOLDOWN_MS = 5e3;
18079
18097
  var EngineOrchestrator = class _EngineOrchestrator {
@@ -18343,6 +18361,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
18343
18361
  let painted = false;
18344
18362
  const paint = (bars, final) => {
18345
18363
  if (this.generation !== gen || !final && bars.length === 0) return;
18364
+ if (!painted && !final && bars.length < Math.min(requested, FIRST_PAINT_BARS)) return;
18346
18365
  this.setBarSeries(bars, painted ? { preserveView: true } : void 0);
18347
18366
  if (!painted && bars.length > 0) {
18348
18367
  painted = true;
@@ -18973,6 +18992,15 @@ var EngineOrchestrator = class _EngineOrchestrator {
18973
18992
  record.pendingCause = "inputs";
18974
18993
  if (record.session) record.session.update(record.inputValues);
18975
18994
  else if (record.native && !record.hidden) record.native.instance.setInputs(record.inputValues);
18995
+ this.events.emit("indicator:inputs", { id });
18996
+ }
18997
+ /** IndicatorController: the CURRENT stored input values (defaults merged with edits). */
18998
+ inputValuesOf(id) {
18999
+ return { ...this.registry.get(id)?.inputValues };
19000
+ }
19001
+ /** IndicatorController: the CURRENT declaration-prop overrides. */
19002
+ propValuesOf(id) {
19003
+ return { ...this.registry.get(id)?.propValues };
18976
19004
  }
18977
19005
  /** IndicatorController: re-run an indicator with merged declaration-prop overrides.
18978
19006
  * Same lifecycle as {@link applyInputs} — a prop change replays the whole script.
@@ -18986,6 +19014,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
18986
19014
  if (!record.hidden) this.setLoading(record, true);
18987
19015
  record.pendingCause = "inputs";
18988
19016
  record.session.update(record.inputValues, record.propValues);
19017
+ this.events.emit("indicator:inputs", { id });
18989
19018
  }
18990
19019
  /** IndicatorController: tear down an indicator and (if now empty) its pane. */
18991
19020
  /** Live handles of every indicator on the chart (script + native), insertion order. */
@@ -20207,6 +20236,14 @@ function inputVisible(when, values) {
20207
20236
  const conds = Array.isArray(when) ? when : [when];
20208
20237
  return conds.every((c) => c.anyOf ? c.anyOf.some((x) => x === values[c.key]) : values[c.key] === c.equals);
20209
20238
  }
20239
+ function inputDeltas(schema, values) {
20240
+ const out = {};
20241
+ for (const s of schema) {
20242
+ const v = values[s.key];
20243
+ if (v !== void 0 && JSON.stringify(v) !== JSON.stringify(s.defval)) out[s.key] = v;
20244
+ }
20245
+ return Object.keys(out).length > 0 ? out : void 0;
20246
+ }
20210
20247
 
20211
20248
  // src/renderers/shared/IndicatorInputsDialog.ts
20212
20249
  var PROPS_TAB = "Properties";
@@ -23794,6 +23831,7 @@ var DOUBLE_TAP_MS = 350;
23794
23831
  var DOUBLE_TAP_SLOP = 30;
23795
23832
  var TIME_SCALE_K = 4e-3;
23796
23833
  var WHEEL_ZOOM_K = 4e-3;
23834
+ var WHEEL_PRICE_DRAG_PX = 0.25;
23797
23835
  function wheelZoomAnchor(coords, cursorX, rightEdge) {
23798
23836
  if (rightEdge) return { logical: coords.rightEdgeLogical, x: coords.width };
23799
23837
  return { logical: coords.xToLogical(cursorX), x: cursorX };
@@ -23924,7 +23962,7 @@ var InputController = class {
23924
23962
  this.capture(e.pointerId);
23925
23963
  return;
23926
23964
  }
23927
- if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y)) {
23965
+ if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
23928
23966
  this.region = "drawing";
23929
23967
  this.capture(e.pointerId);
23930
23968
  return;
@@ -24038,7 +24076,7 @@ var InputController = class {
24038
24076
  const wasTouch = e.pointerType === "touch";
24039
24077
  const tapRelease = this.dragging && !this.moved && (!wasTouch || Math.hypot(x - this.startX, y - this.startY) <= TOUCH_TAP_SLOP);
24040
24078
  if (this.dragging && this.region === "drawing") {
24041
- this.deps.drawingsPointerUp?.(x, y);
24079
+ this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
24042
24080
  } else if (tapRelease && this.region === "data") {
24043
24081
  this.deps.onClick(x, y);
24044
24082
  } else if (this.dragging && this.region === "data") {
@@ -24059,7 +24097,7 @@ var InputController = class {
24059
24097
  if (e.pointerType === "touch") this.touches.delete(e.pointerId);
24060
24098
  this.cancelLongPress();
24061
24099
  if (!this.dragging) return;
24062
- if (this.region === "drawing" && !Number.isNaN(this.cursorX)) this.deps.drawingsPointerUp?.(this.cursorX, this.cursorY);
24100
+ if (this.region === "drawing" && !Number.isNaN(this.cursorX)) this.deps.drawingsPointerUp?.(this.cursorX, this.cursorY, this.snapMode(e));
24063
24101
  if (this.region === "crosshair" || e.pointerType === "touch") this.deps.onPointerMove(null, null);
24064
24102
  this.endGesture(e);
24065
24103
  };
@@ -24075,6 +24113,12 @@ var InputController = class {
24075
24113
  this.onWheel = (e) => {
24076
24114
  e.preventDefault();
24077
24115
  this.deps.drawingsClearTransient?.();
24116
+ const { x, y } = this.local(e);
24117
+ if (this.regionAt(x, y) === "price" && e.deltaY !== 0) {
24118
+ this.deps.beginPriceScale(x, y);
24119
+ this.deps.priceScaleBy(e.deltaY * WHEEL_PRICE_DRAG_PX);
24120
+ return;
24121
+ }
24078
24122
  const coords = this.deps.getCoords();
24079
24123
  const vp = coords.getViewport();
24080
24124
  const pan = wheelPanDelta(e.deltaX, e.deltaY, e.shiftKey);
@@ -24082,9 +24126,8 @@ var InputController = class {
24082
24126
  this.deps.apply({ barSpacing: vp.barSpacing, rightOffset: wheelPanRightOffset(vp.rightOffset, pan, coords.pxPerBar()) });
24083
24127
  return;
24084
24128
  }
24085
- const cursorX = this.local(e).x;
24086
24129
  const rightEdge = this.rightEdgeZoom && !(e.ctrlKey || e.metaKey);
24087
- const anchor = wheelZoomAnchor(coords, cursorX, rightEdge);
24130
+ const anchor = wheelZoomAnchor(coords, x, rightEdge);
24088
24131
  const target = clampBarSpacing(vp.barSpacing * Math.exp(-e.deltaY * WHEEL_ZOOM_K));
24089
24132
  this.deps.zoomTo(target, anchor.logical, anchor.x);
24090
24133
  };
@@ -25566,6 +25609,15 @@ function drawingSetEmpty(s) {
25566
25609
  function fontSizePx(size) {
25567
25610
  return size === "auto" ? 12 : namedFontSize(size);
25568
25611
  }
25612
+ function lineCoversWindow(a, b, extend, lo, hi) {
25613
+ const minX = Math.min(a, b);
25614
+ const maxX = Math.max(a, b);
25615
+ if (a === b) return a >= lo && a <= hi;
25616
+ if (extend === "both") return true;
25617
+ if (extend === "left") return maxX >= lo;
25618
+ if (extend === "right") return minX <= hi;
25619
+ return maxX >= lo && minX <= hi;
25620
+ }
25569
25621
  var DrawingSceneRenderer = class {
25570
25622
  constructor(deps, set = EMPTY_DRAWING_SET) {
25571
25623
  this.deps = deps;
@@ -25630,7 +25682,7 @@ var DrawingSceneRenderer = class {
25630
25682
  };
25631
25683
  for (const ln of this.set.lines) {
25632
25684
  if (ln.invisible) continue;
25633
- if (!visible(this.logicalOf(ln.xloc, ln.x1), this.logicalOf(ln.xloc, ln.x2), ln.extend)) continue;
25685
+ if (!lineCoversWindow(this.logicalOf(ln.xloc, ln.x1), this.logicalOf(ln.xloc, ln.x2), ln.extend, lo, hi)) continue;
25634
25686
  fold(ln.y1);
25635
25687
  fold(ln.y2);
25636
25688
  }
@@ -29480,6 +29532,22 @@ var DrawingInteraction = class {
29480
29532
  this.snapAt = changed ? { point: snapped, paneId } : null;
29481
29533
  return snapped;
29482
29534
  }
29535
+ /**
29536
+ * Resolve a cursor pixel through the magnet and return the snapped pixel — the same
29537
+ * conversion drawing placement uses. Updates the snap-ring marker. The measure
29538
+ * ruler goes through this so its endpoints follow weak/strong/Ctrl magnet too.
29539
+ */
29540
+ snapCursor(x, y, mode) {
29541
+ const proj = this.deps.projector();
29542
+ const paneId = proj.paneIdAtY(y) ?? "price";
29543
+ const point = this.resolve(x, y, paneId, mode);
29544
+ const sy = proj.yOf(point.price, paneId);
29545
+ return { x: proj.xOf(point.time), y: sy ?? y };
29546
+ }
29547
+ /** Drop the snap-ring marker (a transient mode ended without going through `up`). */
29548
+ clearSnapMarker() {
29549
+ this.snapAt = null;
29550
+ }
29483
29551
  /** Resolve a pixel to a data point with the segment angle locked to 45° steps around
29484
29552
  * `pivot` (Shift held on a line tool). Works in PIXEL space — the user reasons about
29485
29553
  * the angle they see, not about time/price units. The magnet is bypassed: snapping
@@ -30885,30 +30953,39 @@ var MeasureOverlay = class {
30885
30953
  isFinished() {
30886
30954
  return this.state === "finished";
30887
30955
  }
30888
- /** A press: begin the measurement, or finish it on the second click. */
30889
- down(x, y) {
30956
+ /** A press: begin the measurement, or finish it on the second click.
30957
+ * `x,y` are the raw cursor (drag-slop vs click-move-click). `gx,gy` are the
30958
+ * graphic endpoints — magnet-snapped when the magnet is on, else the same as `x,y`. */
30959
+ down(x, y, gx = x, gy = y) {
30890
30960
  if (this.state === "measuring") {
30891
- this.end = { x, y };
30961
+ this.end = { x: gx, y: gy };
30892
30962
  this.state = "finished";
30893
30963
  return;
30894
30964
  }
30895
- this.start = { x, y };
30896
- this.end = { x, y };
30965
+ this.start = { x: gx, y: gy };
30966
+ this.end = { x: gx, y: gy };
30897
30967
  this.pressX = x;
30898
30968
  this.pressY = y;
30899
30969
  this.state = "measuring";
30900
30970
  }
30971
+ /** Size the in-progress ruler. `x,y` are the graphic (magnet-snapped) cursor. */
30901
30972
  move(x, y) {
30902
30973
  if (this.state === "measuring") this.end = { x, y };
30903
30974
  }
30904
30975
  /** A release: finish if the press was actually dragged (press-drag-release), else wait
30905
- * for the second click (click-move-click). */
30906
- up(x, y) {
30976
+ * for the second click (click-move-click). `x,y` are the raw cursor (slop); `gx,gy`
30977
+ * are the graphic end (magnet-snapped when the magnet is on). */
30978
+ up(x, y, gx = x, gy = y) {
30907
30979
  if (this.state === "measuring" && Math.hypot(x - this.pressX, y - this.pressY) > DRAG_SLOP4) {
30908
- this.end = { x, y };
30980
+ this.end = { x: gx, y: gy };
30909
30981
  this.state = "finished";
30910
30982
  }
30911
30983
  }
30984
+ /** Current graphic endpoints in media pixels, or null when idle. */
30985
+ points() {
30986
+ if (!this.start || !this.end) return null;
30987
+ return { start: this.start, end: this.end };
30988
+ }
30912
30989
  clear() {
30913
30990
  this.state = "idle";
30914
30991
  this.start = null;
@@ -31285,11 +31362,13 @@ var UserDrawingController = class {
31285
31362
  }
31286
31363
  /** Shift+press on the empty plot: arm the measure ruler AND start it at (x, y) in one
31287
31364
  * gesture — the equivalent of clicking the toolbar's Measure button, then pressing.
31288
- * Returns false when a mode/tool is already active (the normal press path owns it). */
31289
- beginMeasureAt(x, y) {
31365
+ * `snap` is the effective magnet (sticky mode, or Ctrl/Cmd-forced strong). Returns
31366
+ * false when a mode/tool is already active (the normal press path owns it). */
31367
+ beginMeasureAt(x, y, snap = "off") {
31290
31368
  if (this.measureMode || this.eraserMode || this.activeTool != null) return false;
31291
31369
  this.withModeIntent(() => this.toggleMeasure());
31292
- this.measure.down(x, y);
31370
+ const g = this.interaction.snapCursor(x, y, snap);
31371
+ this.measure.down(x, y, g.x, g.y);
31293
31372
  this.render();
31294
31373
  return true;
31295
31374
  }
@@ -31311,7 +31390,8 @@ var UserDrawingController = class {
31311
31390
  return;
31312
31391
  }
31313
31392
  if (this.measureMode) {
31314
- this.measure.down(x, y);
31393
+ const g = this.interaction.snapCursor(x, y, snap);
31394
+ this.measure.down(x, y, g.x, g.y);
31315
31395
  if (this.measure.isFinished()) this.withModeIntent(() => this.exitMeasure(false));
31316
31396
  this.render();
31317
31397
  return;
@@ -31324,7 +31404,8 @@ var UserDrawingController = class {
31324
31404
  return;
31325
31405
  }
31326
31406
  if (this.measureMode) {
31327
- this.measure.move(x, y);
31407
+ const g = this.interaction.snapCursor(x, y, snap);
31408
+ this.measure.move(g.x, g.y);
31328
31409
  this.render();
31329
31410
  return;
31330
31411
  }
@@ -31342,13 +31423,14 @@ var UserDrawingController = class {
31342
31423
  this.render();
31343
31424
  }
31344
31425
  }
31345
- pointerUp(x, y) {
31426
+ pointerUp(x, y, snap = "off") {
31346
31427
  if (this.eraserMode) {
31347
31428
  this.erasing = false;
31348
31429
  return;
31349
31430
  }
31350
31431
  if (this.measureMode) {
31351
- this.measure.up(x, y);
31432
+ const g = this.interaction.snapCursor(x, y, snap);
31433
+ this.measure.up(x, y, g.x, g.y);
31352
31434
  if (this.measure.isFinished()) this.withModeIntent(() => this.exitMeasure(false));
31353
31435
  this.render();
31354
31436
  return;
@@ -31393,6 +31475,7 @@ var UserDrawingController = class {
31393
31475
  /** Leave ruler mode. `clearGraphic` keeps a just-finished measurement on screen (false). */
31394
31476
  exitMeasure(clearGraphic = true) {
31395
31477
  this.measureMode = false;
31478
+ this.interaction.clearSnapMarker();
31396
31479
  if (clearGraphic) this.measure.clear();
31397
31480
  this.toolbar.setMeasureActive(false);
31398
31481
  this.render();
@@ -31533,15 +31616,28 @@ var UserDrawingController = class {
31533
31616
  if (this.eraserMode) return "pointer";
31534
31617
  return this.interaction.cursorAt(x, y);
31535
31618
  }
31536
- /** Right-click while placing: cancel the in-progress drawing and revert to the
31537
- * pointer the gesture is an explicit escape, so it disarms even in
31538
- * stay-in-drawing-mode (where Escape would leave the tool armed). Returns whether
31539
- * the press was consumed; false lets the host's context menu open normally. */
31619
+ /** Right-click: an explicit escape back to the pointer. Cancels an in-progress
31620
+ * placement or measurement, and also plain-disarms an armed-but-idle drawing
31621
+ * tool or the eraser — so a right-click ALWAYS reverts to the pointer, even in
31622
+ * stay-in-drawing-mode (where Escape would leave a drawing tool armed).
31623
+ * Persistent toggles (magnet, stay-mode, favorites) are untouched. Returns
31624
+ * whether the press was consumed; false lets the host's context menu open
31625
+ * normally. */
31540
31626
  cancelPlacement() {
31541
- if (!this.interaction.isPlacing()) return false;
31542
- this.interaction.cancel();
31543
- if (this.activeTool != null) this.emit({ kind: "arm", type: null });
31544
- return true;
31627
+ if (this.measureMode || this.eraserMode) {
31628
+ this.withModeIntent(() => this.measureMode ? this.exitMeasure() : this.exitEraser());
31629
+ return true;
31630
+ }
31631
+ if (this.interaction.isPlacing()) {
31632
+ this.interaction.cancel();
31633
+ if (this.activeTool != null) this.emit({ kind: "arm", type: null });
31634
+ return true;
31635
+ }
31636
+ if (this.activeTool != null) {
31637
+ this.emit({ kind: "arm", type: null });
31638
+ return true;
31639
+ }
31640
+ return false;
31545
31641
  }
31546
31642
  /** Double-click over a drawing → suppress the chart's view reset (single-click already
31547
31643
  * opens settings). Returns true only when a drawing is under the cursor. */
@@ -31567,6 +31663,10 @@ var UserDrawingController = class {
31567
31663
  return true;
31568
31664
  }
31569
31665
  if (this.interaction.cancel()) return true;
31666
+ if (this.measureMode) {
31667
+ this.withModeIntent(() => this.exitMeasure());
31668
+ return true;
31669
+ }
31570
31670
  if (this.selectedIds.size) {
31571
31671
  this.clearSelection();
31572
31672
  return true;
@@ -33991,13 +34091,13 @@ var NativeRenderer = class {
33991
34091
  resetView: () => this.resetView(),
33992
34092
  // User drawings claim a gesture before pan when armed / over a drawing.
33993
34093
  drawingsClaim: (x, y) => this.userDrawings?.claim(x, y) ?? false,
33994
- drawingsMeasureStart: (x, y) => this.userDrawings?.beginMeasureAt(x, y) ?? false,
34094
+ drawingsMeasureStart: (x, y, snap) => this.userDrawings?.beginMeasureAt(x, y, snap) ?? false,
33995
34095
  drawingsDeleteAt: (x, y) => this.userDrawings?.deleteAt(x, y) ?? false,
33996
34096
  drawingsCancelPlacement: () => this.userDrawings?.cancelPlacement() ?? false,
33997
34097
  drawingsSnapMode: () => this.snapMode,
33998
34098
  drawingsPointerDown: (x, y, snap, shift) => this.userDrawings?.pointerDown(x, y, snap, shift),
33999
34099
  drawingsPointerMove: (x, y, snap, shift) => this.userDrawings?.pointerMove(x, y, snap, shift),
34000
- drawingsPointerUp: (x, y) => this.userDrawings?.pointerUp(x, y),
34100
+ drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
34001
34101
  drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? null,
34002
34102
  drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
34003
34103
  drawingsClearTransient: () => this.userDrawings?.clearTransient()
@@ -34312,7 +34412,8 @@ var NativeRenderer = class {
34312
34412
  }
34313
34413
  const skipFit = opts?.preserveView === true && this.didInitialFit;
34314
34414
  if (this.coords.width > 0 && !skipFit) {
34315
- this.fitContent();
34415
+ if (this.didInitialFit) this.reframeKeepZoom();
34416
+ else this.fitContent();
34316
34417
  this.didInitialFit = true;
34317
34418
  }
34318
34419
  if (!this.introPlayed && this.bars.length > 0) {
@@ -34960,6 +35061,7 @@ var NativeRenderer = class {
34960
35061
  this.scaleDragHeight = res.height;
34961
35062
  this.scaleDragStart = { ...res.holder.scale };
34962
35063
  res.holder.manualScale = { ...res.holder.scale };
35064
+ this.axisScaleButtons?.reposition();
34963
35065
  this.scheduler.invalidate(4 /* Full */);
34964
35066
  }
34965
35067
  /** Rescale the grabbed scale around its center by the total drag (down ⇒ zoom out). */
@@ -34991,6 +35093,7 @@ var NativeRenderer = class {
34991
35093
  const res = this.resolveScaleHolder(x, y);
34992
35094
  if (!res) return;
34993
35095
  res.holder.manualScale = null;
35096
+ this.axisScaleButtons?.reposition();
34994
35097
  this.scheduler.invalidate(4 /* Full */);
34995
35098
  }
34996
35099
  /**
@@ -35628,6 +35731,20 @@ var NativeRenderer = class {
35628
35731
  this.coords.setViewport(v);
35629
35732
  this.targetBarSpacing = v.barSpacing;
35630
35733
  }
35734
+ /** Re-frame after a series replacement (a symbol/timeframe switch): keep the user's
35735
+ * zoom (bar spacing), re-anchor the newest bars at the default right offset.
35736
+ * `clampViewport`'s fit-all-bars floor deliberately does NOT apply — a progressive
35737
+ * head may still be backfilling toward the previous depth, and raising the spacing
35738
+ * to its temporary bar count would lose the zoom this exists to keep. */
35739
+ reframeKeepZoom() {
35740
+ this.animator?.stop();
35741
+ this.panVelocity = 0;
35742
+ for (const pane of this.scene.panes.values()) pane.manualScale = null;
35743
+ for (const sl of this.scene.indicatorScales.values()) sl.manualScale = null;
35744
+ const v = { barSpacing: clampBarSpacing(this.coords.getViewport().barSpacing), rightOffset: defaultViewport().rightOffset };
35745
+ this.coords.setViewport(v);
35746
+ this.targetBarSpacing = v.barSpacing;
35747
+ }
35631
35748
  paneBoundsFor(paneId) {
35632
35749
  const p = this.scene.panes.get(paneId);
35633
35750
  return { top: p?.bounds.top ?? 0, height: p?.bounds.height ?? 0, rightAxis: this.rightAxisW };
@@ -37329,6 +37446,7 @@ var TIME_AXIS_H2 = 22;
37329
37446
  var CONTROLS_BOTTOM_PX = TIME_AXIS_H2 + 12;
37330
37447
  var CLUSTER_H2 = 24;
37331
37448
  var CLUSTER_PILL2 = "rgba(0,0,0,0.65)";
37449
+ var CLUSTER_LEFT_CSS = "calc((100% + var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px)) / 2)";
37332
37450
  var STYLE_ID26 = "vela-cell-controls";
37333
37451
  var CSS23 = `
37334
37452
  .vela-cc-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:var(--vela-radius-sm);background:transparent;line-height:0;font-size:12px;color:var(--vela-fg-muted);cursor:pointer;}
@@ -37338,8 +37456,11 @@ var CSS23 = `
37338
37456
  .vela-cc-grip{cursor:grab;touch-action:none;}
37339
37457
  .vela-cc-grip:active{cursor:grabbing;}
37340
37458
  `;
37341
- function nearBottomCenter(x, y, width, height, proximityPx = CELL_CONTROLS_PROXIMITY_PX) {
37342
- const cx = width / 2;
37459
+ function plotCenterX(width, toolbarGutter = 0, scaleGutter = 0) {
37460
+ return (width + toolbarGutter - scaleGutter) / 2;
37461
+ }
37462
+ function nearBottomCenter(x, y, width, height, proximityPx = CELL_CONTROLS_PROXIMITY_PX, gutters = {}) {
37463
+ const cx = plotCenterX(width, gutters.toolbar ?? 0, gutters.scale ?? 0);
37343
37464
  const cy = height - CONTROLS_BOTTOM_PX - CLUSTER_H2 / 2;
37344
37465
  return Math.hypot(x - cx, y - cy) <= proximityPx;
37345
37466
  }
@@ -37358,7 +37479,7 @@ var CellControls = class {
37358
37479
  if (this.suspended) return;
37359
37480
  if (this.dragging) return;
37360
37481
  const rect = this.host.getBoundingClientRect();
37361
- this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height));
37482
+ this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height, CELL_CONTROLS_PROXIMITY_PX, this.hostGutters()));
37362
37483
  };
37363
37484
  this.onHostLeave = () => {
37364
37485
  if (this.dragging) return;
@@ -37369,7 +37490,7 @@ var CellControls = class {
37369
37490
  this.root = host.ownerDocument.createElement("div");
37370
37491
  Object.assign(this.root.style, {
37371
37492
  position: "absolute",
37372
- left: "50%",
37493
+ left: CLUSTER_LEFT_CSS,
37373
37494
  bottom: `${CONTROLS_BOTTOM_PX}px`,
37374
37495
  transform: "translateX(-50%)",
37375
37496
  zIndex: "6",
@@ -37469,6 +37590,16 @@ var CellControls = class {
37469
37590
  this.suspended = on;
37470
37591
  if (on) this.setNear(false);
37471
37592
  }
37593
+ /** Live renderer gutters on the cell host (0 when unpublished — a test stub). */
37594
+ hostGutters() {
37595
+ const view = this.host.ownerDocument.defaultView;
37596
+ if (!view) return { toolbar: 0, scale: 0 };
37597
+ const cs = view.getComputedStyle(this.host);
37598
+ return {
37599
+ toolbar: Number.parseFloat(cs.getPropertyValue("--vela-toolbar-gutter")) || 0,
37600
+ scale: Number.parseFloat(cs.getPropertyValue("--vela-scale-gutter")) || 0
37601
+ };
37602
+ }
37472
37603
  setNear(near) {
37473
37604
  if (near === this.near) return;
37474
37605
  this.near = near;
@@ -37709,6 +37840,12 @@ function cellDrawings(opt) {
37709
37840
  if (opt === true || opt == null) return { toolbar: false };
37710
37841
  return { ...opt, toolbar: false };
37711
37842
  }
37843
+ function instanceDeltas(handle) {
37844
+ if (!handle) return void 0;
37845
+ const inputs = inputDeltas(handle.inputs, handle.inputValues());
37846
+ const props = inputDeltas(handle.props, handle.propValues());
37847
+ return inputs || props ? { ...inputs ? { inputs } : {}, ...props ? { props } : {} } : void 0;
37848
+ }
37712
37849
  var ChartCell = class {
37713
37850
  constructor(id, gridHost, seed, deps) {
37714
37851
  this.id = id;
@@ -37884,6 +38021,7 @@ var ChartCell = class {
37884
38021
  this.syncPresentNatives();
37885
38022
  this.refreshNativeCatalog();
37886
38023
  });
38024
+ this.inner.on("indicator:inputs", () => this.deps.onStateDirty());
37887
38025
  this.inner.on("indicator:removed", ({ id: id2 }) => {
37888
38026
  if (this.destroyed) return;
37889
38027
  const idx = this.instances.findIndex((it) => it.handle?.id === id2);
@@ -37892,7 +38030,7 @@ var ChartCell = class {
37892
38030
  this.instances.splice(idx, 1);
37893
38031
  this.history.push({
37894
38032
  undo: () => {
37895
- snapshot.handle = this.addToChart(snapshot.entry);
38033
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
37896
38034
  this.instances.push(snapshot);
37897
38035
  this.deps.onIndicatorsChanged(this.id);
37898
38036
  },
@@ -38281,9 +38419,9 @@ var ChartCell = class {
38281
38419
  this.manifest = list;
38282
38420
  if (this.pendingManifestNames) {
38283
38421
  if (list.length === 0) return;
38284
- for (const name of this.pendingManifestNames) {
38285
- const entry = list.find((e) => e.name === name);
38286
- if (entry) this.addManifestInstance(entry, { record: false });
38422
+ for (const led of this.pendingManifestNames) {
38423
+ const entry = list.find((e) => e.name === ledgerEntryName(led));
38424
+ if (entry) this.addManifestInstance(entry, { record: false, ...typeof led === "object" ? { inputs: led.inputs, props: led.props } : {} });
38287
38425
  }
38288
38426
  this.pendingManifestNames = null;
38289
38427
  return;
@@ -38313,9 +38451,9 @@ var ChartCell = class {
38313
38451
  }
38314
38452
  for (const it of [...this.instances]) this.dropInstance(it);
38315
38453
  if (this.manifest.length > 0) {
38316
- for (const name of led.manifest) {
38317
- const entry = this.manifest.find((e) => e.name === name);
38318
- if (entry) this.addManifestInstance(entry, { record: false });
38454
+ for (const item of led.manifest) {
38455
+ const entry = this.manifest.find((e) => e.name === ledgerEntryName(item));
38456
+ if (entry) this.addManifestInstance(entry, { record: false, ...typeof item === "object" ? { inputs: item.inputs, props: item.props } : {} });
38319
38457
  }
38320
38458
  this.pendingManifestNames = null;
38321
38459
  } else if (!this.deps.manifestSettled()) {
@@ -38364,12 +38502,13 @@ var ChartCell = class {
38364
38502
  * a persistence handler's `restore` runs silently, a user-driven call records.
38365
38503
  */
38366
38504
  addExternalIndicator(entry) {
38367
- this.addManifestInstance({ ...entry, enabled: true }, { external: true });
38505
+ this.addManifestInstance({ ...entry, enabled: true }, { external: true, ...entry.inputs ? { inputs: entry.inputs } : {}, ...entry.props ? { props: entry.props } : {} });
38368
38506
  }
38369
38507
  /** Add ONE instance of a manifest entry (repeatable — duplicates are legitimate). */
38370
38508
  addManifestInstance(entry, opts = {}) {
38371
38509
  if (this.destroyed) return;
38372
- const it = { entry, handle: this.addToChart(entry), ...opts.external ? { external: true } : {} };
38510
+ const values = opts.inputs || opts.props ? { inputs: opts.inputs, props: opts.props } : void 0;
38511
+ const it = { entry, handle: this.addToChart(entry, values), ...opts.external ? { external: true } : {}, ...values ? { values } : {} };
38373
38512
  this.instances.push(it);
38374
38513
  this.deps.onIndicatorsChanged(this.id);
38375
38514
  if (opts.record === false) return;
@@ -38377,7 +38516,7 @@ var ChartCell = class {
38377
38516
  this.history.push({
38378
38517
  undo: () => this.dropInstance(snapshot),
38379
38518
  redo: () => {
38380
- snapshot.handle = this.addToChart(snapshot.entry);
38519
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
38381
38520
  this.instances.push(snapshot);
38382
38521
  this.deps.onIndicatorsChanged(this.id);
38383
38522
  }
@@ -38390,7 +38529,7 @@ var ChartCell = class {
38390
38529
  const snapshot = it;
38391
38530
  this.history.push({
38392
38531
  undo: () => {
38393
- snapshot.handle = this.addToChart(snapshot.entry);
38532
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
38394
38533
  this.instances.push(snapshot);
38395
38534
  this.deps.onIndicatorsChanged(this.id);
38396
38535
  },
@@ -38400,6 +38539,9 @@ var ChartCell = class {
38400
38539
  dropInstance(it) {
38401
38540
  const idx = this.instances.indexOf(it);
38402
38541
  if (idx >= 0) this.instances.splice(idx, 1);
38542
+ const captured = instanceDeltas(it.handle);
38543
+ if (captured) it.values = captured;
38544
+ else delete it.values;
38403
38545
  try {
38404
38546
  it.handle?.remove();
38405
38547
  } catch {
@@ -38442,9 +38584,13 @@ var ChartCell = class {
38442
38584
  this.deps.onIndicatorsChanged(this.id);
38443
38585
  });
38444
38586
  }
38445
- addToChart(entry) {
38587
+ addToChart(entry, values) {
38446
38588
  try {
38447
- return this.inner?.addIndicator(entry.script, entry.language !== void 0 ? { language: entry.language } : void 0) ?? null;
38589
+ return this.inner?.addIndicator(entry.script, {
38590
+ ...entry.language !== void 0 ? { language: entry.language } : {},
38591
+ ...values?.inputs ? { inputs: values.inputs } : {},
38592
+ ...values?.props ? { props: values.props } : {}
38593
+ }) ?? null;
38448
38594
  } catch (err) {
38449
38595
  console.warn(`[vela] indicator "${entry.name}" failed to add:`, err);
38450
38596
  return null;
@@ -38558,7 +38704,10 @@ var ChartCell = class {
38558
38704
  // manifest — their plugin persists them via the `ext` seam instead.
38559
38705
  indicators: indicatorLedger({
38560
38706
  present: this.inner ? this.inner.presentNativeIndicators() : [],
38561
- instanceNames: this.instances.filter((it) => !it.external).map((it) => it.entry.name),
38707
+ instanceEntries: this.instances.filter((it) => !it.external).map((it) => {
38708
+ const d = it.handle ? instanceDeltas(it.handle) : it.values;
38709
+ return d ? { name: it.entry.name, ...d } : it.entry.name;
38710
+ }),
38562
38711
  pendingManifest: this.pendingManifestNames,
38563
38712
  manifestSettled: this.deps.manifestSettled(),
38564
38713
  volumePending: this.volumeMayBePending && this.volumeIntent