@luxalgo/vela 0.6.10 → 0.6.12

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 (42) hide show
  1. package/README.md +110 -46
  2. package/dist/{DataProvider-CWmp31dA.d.ts → DataProvider-DhzstpQb.d.ts} +1 -1
  3. package/dist/{DataProvider-l_eLMly_.d.cts → DataProvider-DlMtrwqM.d.cts} +1 -1
  4. package/dist/{chunk-4KZVZ7QQ.js → chunk-F4M24ANM.js} +130 -1
  5. package/dist/{chunk-NMTQXNT4.js → chunk-IZV3CW5N.js} +245 -43
  6. package/dist/{chunk-FFOP37FQ.js → chunk-KV7FDWSL.js} +748 -70
  7. package/dist/{contributions-BCz6Dr6a.d.ts → contributions-37nni40G.d.ts} +20 -4
  8. package/dist/{contributions-D9vTzm5p.d.cts → contributions-lPojhTxI.d.cts} +20 -4
  9. package/dist/index.cjs +866 -58
  10. package/dist/index.d.cts +12 -6
  11. package/dist/index.d.ts +12 -6
  12. package/dist/index.js +3 -3
  13. package/dist/{options-DSqHsQyN.d.cts → options-CYS5Wmlx.d.cts} +78 -2
  14. package/dist/{options-DSqHsQyN.d.ts → options-CYS5Wmlx.d.ts} +78 -2
  15. package/dist/{plugin-Bt8hLR8Z.d.cts → plugin-Cwikpz1m.d.cts} +10 -3
  16. package/dist/{plugin-CH7pfMrE.d.ts → plugin-DHyaoMjW.d.ts} +10 -3
  17. package/dist/plugin.cjs +119 -0
  18. package/dist/plugin.d.cts +4 -4
  19. package/dist/plugin.d.ts +4 -4
  20. package/dist/plugin.js +2 -2
  21. package/dist/providers/binance.d.cts +2 -2
  22. package/dist/providers/binance.d.ts +2 -2
  23. package/dist/providers/coinbase.d.cts +2 -2
  24. package/dist/providers/coinbase.d.ts +2 -2
  25. package/dist/providers/hyperliquid.d.cts +2 -2
  26. package/dist/providers/hyperliquid.d.ts +2 -2
  27. package/dist/{statusline-5K7y4Ya2.d.ts → statusline-model-CiJm-riV.d.cts} +17 -87
  28. package/dist/{statusline-TYLQmoeh.d.cts → statusline-model-D-q_OOx9.d.ts} +17 -87
  29. package/dist/ui.d.cts +1 -1
  30. package/dist/ui.d.ts +1 -1
  31. package/dist/vela.global.js +866 -58
  32. package/dist/vela.global.min.js +52 -52
  33. package/dist/widget.cjs +1109 -100
  34. package/dist/widget.d.cts +118 -7
  35. package/dist/widget.d.ts +118 -7
  36. package/dist/widget.js +4 -4
  37. package/dist/workspace.cjs +1109 -100
  38. package/dist/workspace.d.cts +20 -11
  39. package/dist/workspace.d.ts +20 -11
  40. package/dist/workspace.js +3 -3
  41. package/package.json +1 -1
  42. /package/dist/{chunk-AOGZBKUE.js → chunk-H26BEHF4.js} +0 -0
@@ -1,8 +1,8 @@
1
- import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-FFOP37FQ.js';
2
- import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType } from './chunk-4KZVZ7QQ.js';
1
+ import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-KV7FDWSL.js';
2
+ import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType, inputDeltas } from './chunk-F4M24ANM.js';
3
3
  import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-G77Y7LK2.js';
4
4
  import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-MHY7MVXH.js';
5
- import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-KG4YT3TI.js';
5
+ import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-KG4YT3TI.js';
6
6
  import { baseOf } from './chunk-W4EJWLEO.js';
7
7
 
8
8
  // src/widget/timeframe.ts
@@ -3757,11 +3757,12 @@ async function resolveIndicators(config, fetchImpl = fetch) {
3757
3757
  }
3758
3758
  return out;
3759
3759
  }
3760
+ var ledgerEntryName = (e) => typeof e === "string" ? e : e.name;
3760
3761
  function indicatorLedger(i) {
3761
3762
  const natives = [...i.present];
3762
3763
  if (i.volumePending && !natives.includes("volume")) natives.push("volume");
3763
3764
  return {
3764
- manifest: i.manifestSettled ? [...i.instanceNames] : [...i.pendingManifest ?? i.instanceNames],
3765
+ manifest: i.manifestSettled ? [...i.instanceEntries] : [...i.pendingManifest ?? i.instanceEntries],
3765
3766
  natives
3766
3767
  };
3767
3768
  }
@@ -3843,7 +3844,17 @@ function sanitizeCell(raw) {
3843
3844
  if (c.drawings != null && typeof c.drawings === "object") out.drawings = c.drawings;
3844
3845
  const ind = c.indicators;
3845
3846
  if (ind != null && typeof ind === "object") {
3846
- const manifest = Array.isArray(ind.manifest) ? ind.manifest.filter((n) => typeof n === "string") : [];
3847
+ const manifest = Array.isArray(ind.manifest) ? ind.manifest.flatMap((n) => {
3848
+ if (typeof n === "string") return [n];
3849
+ if (n != null && typeof n === "object" && typeof n.name === "string") {
3850
+ const e = n;
3851
+ const bag = (v) => v != null && typeof v === "object" && !Array.isArray(v) ? v : void 0;
3852
+ const inputs = bag(e.inputs);
3853
+ const props = bag(e.props);
3854
+ return [inputs || props ? { name: e.name, ...inputs ? { inputs } : {}, ...props ? { props } : {} } : e.name];
3855
+ }
3856
+ return [];
3857
+ }) : [];
3847
3858
  const natives = Array.isArray(ind.natives) ? ind.natives.filter((n) => typeof n === "string") : [];
3848
3859
  out.indicators = { manifest, natives };
3849
3860
  }
@@ -3977,6 +3988,21 @@ function fmtChange(open, close) {
3977
3988
  return `${sign}${fmtPrice(diff, decimalsFor(close))} (${sign}${pct.toFixed(2)}%)`;
3978
3989
  }
3979
3990
 
3991
+ // src/widget/statusline-model.ts
3992
+ function segmentVisibility(parts, chartHidden = false) {
3993
+ return { avatar: parts.logo, symbol: parts.name, meta: parts.name, market: parts.market, ohlc: parts.ohlc && !chartHidden, change: parts.change && !chartHidden, eye: chartHidden };
3994
+ }
3995
+ function statuslineMenuItems(parts, chartVisible) {
3996
+ return [
3997
+ { id: "part:logo", label: "Symbol logo", checked: parts.logo },
3998
+ { id: "part:name", label: "Symbol name", checked: parts.name },
3999
+ { id: "part:market", label: "Market status", checked: parts.market },
4000
+ { id: "part:ohlc", label: "OHLC values", checked: parts.ohlc },
4001
+ { id: "part:change", label: "Bar change values", checked: parts.change },
4002
+ { id: "chart", label: chartVisible ? "Hide chart" : "Show chart", separatorBefore: true }
4003
+ ];
4004
+ }
4005
+
3980
4006
  // src/widget/statusline.ts
3981
4007
  var STYLE_ID10 = "vela-widget-statusline";
3982
4008
  var CSS10 = `
@@ -4006,7 +4032,13 @@ var CSS10 = `
4006
4032
  padding: 2px 7px;
4007
4033
  margin-left: -7px;
4008
4034
  }
4009
- .vela-statusline:hover { background: var(--vela-bg); }
4035
+ /* Hovering opens the chip the same way a legend row opens: solid chart background
4036
+ * plus the same inset neutral outline the indicator rows wear (InputsUI's
4037
+ * setRowHighlighted) \u2014 the two columns read as one family. */
4038
+ .vela-statusline:hover { background: var(--vela-bg); box-shadow: inset 0 0 0 1px var(--vela-border); }
4039
+ /* Chart hidden (the price series' eye \u2014 renderer 'candleVisible'): the line dims to
4040
+ * the same 0.5 wash a hidden indicator's legend row wears. */
4041
+ .vela-statusline.vela-sl-chart-hidden { opacity: 0.5; }
4010
4042
  .vela-statusline .vela-sl-avatar {
4011
4043
  width: 18px;
4012
4044
  height: 18px;
@@ -4031,6 +4063,24 @@ var CSS10 = `
4031
4063
  * these are the pre-ink fallbacks only. */
4032
4064
  .vela-statusline .vela-sl-change[data-dir='up'] { color: var(--vela-up); }
4033
4065
  .vela-statusline .vela-sl-change[data-dir='down'] { color: var(--vela-down); }
4066
+ /* The show-chart eye \u2014 out only while the chart is hidden (syncParts drives display),
4067
+ * replacing the value readout it took away. Same footprint as a legend action button. */
4068
+ .vela-statusline .vela-sl-eye {
4069
+ align-self: center;
4070
+ align-items: center;
4071
+ justify-content: center;
4072
+ width: 18px;
4073
+ height: 18px;
4074
+ padding: 0;
4075
+ border: none;
4076
+ border-radius: 3px;
4077
+ background: none;
4078
+ color: var(--vela-fg-muted);
4079
+ cursor: pointer;
4080
+ line-height: 0;
4081
+ flex: none;
4082
+ }
4083
+ .vela-statusline .vela-sl-eye:hover { color: var(--vela-fg); background: color-mix(in srgb, var(--vela-fg) 12%, transparent); }
4034
4084
  /* Stack the TOP pane's legend below the status line (lower study panes stay put).
4035
4085
  * The renderer marks whichever legend sits at the plot's top edge \u2014 the price pane
4036
4086
  * normally, or a maximized study pane filling the plot \u2014 so the legend never merges
@@ -4140,7 +4190,12 @@ var Statusline = class {
4140
4190
  constructor(host, symbol, iconFor) {
4141
4191
  this.host = host;
4142
4192
  this.iconFor = iconFor;
4143
- this.parts = { name: true, market: true, ohlc: true, change: true };
4193
+ this.parts = { logo: true, name: true, market: true, ohlc: true, change: true };
4194
+ /** The right-click action menu — present once a host wires it via {@link attachMenu}. */
4195
+ this.menu = null;
4196
+ this.menuHooks = null;
4197
+ /** Mirror of the renderer's `candleVisible` — see {@link setChartHidden}. */
4198
+ this.chartHidden = false;
4144
4199
  this.lastBar = null;
4145
4200
  this.hoverBar = null;
4146
4201
  this.unsubs = [];
@@ -4156,6 +4211,15 @@ var Statusline = class {
4156
4211
  /** Fit mode (multi-chart cells): one row, overflowing segments hidden — see {@link setFitMode}. */
4157
4212
  this.fitMode = false;
4158
4213
  this.fitRO = null;
4214
+ this.onContextMenu = (e) => {
4215
+ if (!this.menu || !this.menuHooks) return;
4216
+ e.preventDefault();
4217
+ e.stopPropagation();
4218
+ const chartVisible = this.menuHooks.chartVisible();
4219
+ this.setChartHidden(!chartVisible);
4220
+ this.menu.setItems(statuslineMenuItems(this.parts, chartVisible));
4221
+ this.menu.openAt(e.clientX, e.clientY);
4222
+ };
4159
4223
  const doc = host.ownerDocument;
4160
4224
  injectStyles(STYLE_ID10, CSS10, doc);
4161
4225
  host.classList.add("vela-has-statusline");
@@ -4182,9 +4246,21 @@ var Statusline = class {
4182
4246
  this.ohlcEl.className = "vela-sl-ohlc";
4183
4247
  this.changeEl = doc.createElement("span");
4184
4248
  this.changeEl.className = "vela-sl-change";
4185
- this.el.append(this.avatarEl, this.symbolEl, this.metaEl, this.marketEl, this.ohlcEl, this.changeEl);
4249
+ this.eyeEl = doc.createElement("button");
4250
+ this.eyeEl.type = "button";
4251
+ this.eyeEl.className = "vela-sl-eye";
4252
+ this.eyeEl.innerHTML = iconAt("eye-off", 14);
4253
+ this.eyeEl.setAttribute("aria-label", "Show chart");
4254
+ this.eyeEl.style.display = "none";
4255
+ this.eyeEl.addEventListener("click", (e) => {
4256
+ e.stopPropagation();
4257
+ this.menuHooks?.setChartVisible(true);
4258
+ this.setChartHidden(false);
4259
+ });
4260
+ this.el.append(this.avatarEl, this.symbolEl, this.metaEl, this.marketEl, this.ohlcEl, this.changeEl, this.eyeEl);
4186
4261
  host.appendChild(this.el);
4187
4262
  this.marketTip = new Tooltip(this.marketEl, { content: MARKET_LABELS.open, placement: "bottom" });
4263
+ this.eyeTip = new Tooltip(this.eyeEl, { content: "Show chart", placement: "bottom" });
4188
4264
  this.setMarketStatus("open");
4189
4265
  this.render();
4190
4266
  }
@@ -4194,6 +4270,7 @@ var Statusline = class {
4194
4270
  const fresh = tickerIconEl(this.el.ownerDocument, baseOfTicker(ticker), ticker, "vela-sl-avatar", this.iconFor?.(symbol));
4195
4271
  this.avatarEl.replaceWith(fresh);
4196
4272
  this.avatarEl = fresh;
4273
+ this.syncParts();
4197
4274
  this.fit();
4198
4275
  }
4199
4276
  /**
@@ -4221,20 +4298,25 @@ var Statusline = class {
4221
4298
  }
4222
4299
  /** Project the parts config onto the segments (the baseline fit() prunes from). */
4223
4300
  syncParts() {
4224
- this.symbolEl.style.display = this.parts.name ? "" : "none";
4225
- this.marketEl.style.display = this.parts.market ? "" : "none";
4226
- this.ohlcEl.style.display = this.parts.ohlc ? "" : "none";
4227
- this.changeEl.style.display = this.parts.change ? "" : "none";
4301
+ const seg = segmentVisibility(this.parts, this.chartHidden);
4302
+ this.avatarEl.style.display = seg.avatar ? "" : "none";
4303
+ this.symbolEl.style.display = seg.symbol ? "" : "none";
4304
+ this.metaEl.style.display = seg.meta ? "" : "none";
4305
+ this.marketEl.style.display = seg.market ? "" : "none";
4306
+ this.ohlcEl.style.display = seg.ohlc ? "" : "none";
4307
+ this.changeEl.style.display = seg.change ? "" : "none";
4308
+ this.eyeEl.style.display = seg.eye ? "inline-flex" : "none";
4228
4309
  }
4229
4310
  /** Hide overflowing segments until the row fits its max-width (fit mode only). */
4230
4311
  fit() {
4231
4312
  if (!this.fitMode) return;
4232
4313
  this.syncParts();
4314
+ const seg = segmentVisibility(this.parts, this.chartHidden);
4233
4315
  const order = [
4234
- [this.ohlcEl, this.parts.ohlc],
4235
- [this.changeEl, this.parts.change],
4236
- [this.metaEl, true],
4237
- [this.marketEl, this.parts.market]
4316
+ [this.ohlcEl, seg.ohlc],
4317
+ [this.changeEl, seg.change],
4318
+ [this.metaEl, seg.meta],
4319
+ [this.marketEl, seg.market]
4238
4320
  ];
4239
4321
  for (const [el, shown] of order) {
4240
4322
  if (this.el.scrollWidth <= this.el.clientWidth) break;
@@ -4255,7 +4337,6 @@ var Statusline = class {
4255
4337
  this.readout = readout;
4256
4338
  this.render();
4257
4339
  }
4258
- /** Show/hide one part (the settings dialog's Status line tab drives these). */
4259
4340
  /** The "· BINANCE · 1h" segment after the symbol — venue first, then resolution. */
4260
4341
  setMeta(timeframe, provider) {
4261
4342
  this.metaEl.textContent = `${provider ? `\xB7 ${provider.toUpperCase()} ` : ""}\xB7 ${timeframeLabel(timeframe)}`;
@@ -4274,6 +4355,9 @@ var Statusline = class {
4274
4355
  });
4275
4356
  this.marketTip.setContent(MARKET_LABELS[status]);
4276
4357
  }
4358
+ /** Show/hide one part — the settings dialog's Status line tab and the right-click
4359
+ * menu both drive these. 'name' owns the venue/timeframe meta too (see
4360
+ * {@link segmentVisibility}). */
4277
4361
  setPartVisible(part, visible) {
4278
4362
  this.parts[part] = visible;
4279
4363
  this.syncParts();
@@ -4282,6 +4366,47 @@ var Statusline = class {
4282
4366
  partVisible(part) {
4283
4367
  return this.parts[part];
4284
4368
  }
4369
+ /** Mirror the chart's (price series') visibility: dim the whole line like a hidden
4370
+ * indicator's legend row, drop the value readout (OHLC + bar change — values of a
4371
+ * series that isn't painted), and put the show-chart eye out in its place. The
4372
+ * parts config is untouched, so showing the chart restores the readout exactly as
4373
+ * configured. Idempotent; {@link render} re-syncs it from the live renderer, so
4374
+ * toggles made elsewhere (the object tree's eye) converge too. */
4375
+ setChartHidden(hidden) {
4376
+ if (hidden === this.chartHidden) return;
4377
+ this.chartHidden = hidden;
4378
+ this.el.classList.toggle("vela-sl-chart-hidden", hidden);
4379
+ this.syncParts();
4380
+ this.fit();
4381
+ }
4382
+ /** Wire the right-click action menu: one checkable toggle per part plus hide/show
4383
+ * for the chart itself. The menu is built once; later calls just swap the hooks. */
4384
+ attachMenu(hooks) {
4385
+ this.menuHooks = hooks;
4386
+ if (this.menu) return;
4387
+ this.menu = new Menu({
4388
+ host: this.host,
4389
+ items: [],
4390
+ placement: "bottom-start",
4391
+ // Pointer-anchored action menu — checked state reads as a leading ✓ (the
4392
+ // same shape as the chart's own context menu).
4393
+ checkmarks: true,
4394
+ onSelect: (id) => this.runMenuItem(id)
4395
+ });
4396
+ this.el.addEventListener("contextmenu", this.onContextMenu);
4397
+ }
4398
+ runMenuItem(id) {
4399
+ const hooks = this.menuHooks;
4400
+ if (!hooks) return;
4401
+ if (id.startsWith("part:")) {
4402
+ const part = id.slice("part:".length);
4403
+ hooks.setPart(part, !this.parts[part]);
4404
+ } else if (id === "chart") {
4405
+ const next = !hooks.chartVisible();
4406
+ hooks.setChartVisible(next);
4407
+ this.setChartHidden(!next);
4408
+ }
4409
+ }
4285
4410
  /** (Re)bind to a chart instance — called after every widget rebuild. */
4286
4411
  onChart(chart) {
4287
4412
  this.detach();
@@ -4303,7 +4428,11 @@ var Statusline = class {
4303
4428
  this.detach();
4304
4429
  this.fitRO?.disconnect();
4305
4430
  this.fitRO = null;
4431
+ this.el.removeEventListener("contextmenu", this.onContextMenu);
4432
+ this.menu?.destroy();
4433
+ this.menu = null;
4306
4434
  this.marketTip.destroy();
4435
+ this.eyeTip.destroy();
4307
4436
  this.marketBubble.destroy();
4308
4437
  this.host.classList.remove("vela-has-statusline", "vela-sl-fit-host");
4309
4438
  this.el.remove();
@@ -4313,6 +4442,7 @@ var Statusline = class {
4313
4442
  this.unsubs = [];
4314
4443
  }
4315
4444
  render() {
4445
+ if (this.menuHooks) this.setChartHidden(!this.menuHooks.chartVisible());
4316
4446
  const bar = this.hoverBar ?? this.lastBar;
4317
4447
  if (!bar) {
4318
4448
  this.ohlcEl.replaceChildren();
@@ -4969,6 +5099,7 @@ var TIME_AXIS_H2 = 22;
4969
5099
  var CONTROLS_BOTTOM_PX = TIME_AXIS_H2 + 12;
4970
5100
  var CLUSTER_H = 24;
4971
5101
  var CLUSTER_PILL = "rgba(0,0,0,0.65)";
5102
+ var CLUSTER_LEFT_CSS = "calc((100% + var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px)) / 2)";
4972
5103
  var STYLE_ID12 = "vela-cell-controls";
4973
5104
  var CSS12 = `
4974
5105
  .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;}
@@ -4978,8 +5109,11 @@ var CSS12 = `
4978
5109
  .vela-cc-grip{cursor:grab;touch-action:none;}
4979
5110
  .vela-cc-grip:active{cursor:grabbing;}
4980
5111
  `;
4981
- function nearBottomCenter(x, y, width, height, proximityPx = CELL_CONTROLS_PROXIMITY_PX) {
4982
- const cx = width / 2;
5112
+ function plotCenterX(width, toolbarGutter = 0, scaleGutter = 0) {
5113
+ return (width + toolbarGutter - scaleGutter) / 2;
5114
+ }
5115
+ function nearBottomCenter(x, y, width, height, proximityPx = CELL_CONTROLS_PROXIMITY_PX, gutters = {}) {
5116
+ const cx = plotCenterX(width, gutters.toolbar ?? 0, gutters.scale ?? 0);
4983
5117
  const cy = height - CONTROLS_BOTTOM_PX - CLUSTER_H / 2;
4984
5118
  return Math.hypot(x - cx, y - cy) <= proximityPx;
4985
5119
  }
@@ -4998,7 +5132,7 @@ var CellControls = class {
4998
5132
  if (this.suspended) return;
4999
5133
  if (this.dragging) return;
5000
5134
  const rect = this.host.getBoundingClientRect();
5001
- this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height));
5135
+ this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height, CELL_CONTROLS_PROXIMITY_PX, this.hostGutters()));
5002
5136
  };
5003
5137
  this.onHostLeave = () => {
5004
5138
  if (this.dragging) return;
@@ -5009,7 +5143,7 @@ var CellControls = class {
5009
5143
  this.root = host.ownerDocument.createElement("div");
5010
5144
  Object.assign(this.root.style, {
5011
5145
  position: "absolute",
5012
- left: "50%",
5146
+ left: CLUSTER_LEFT_CSS,
5013
5147
  bottom: `${CONTROLS_BOTTOM_PX}px`,
5014
5148
  transform: "translateX(-50%)",
5015
5149
  zIndex: "6",
@@ -5109,6 +5243,16 @@ var CellControls = class {
5109
5243
  this.suspended = on;
5110
5244
  if (on) this.setNear(false);
5111
5245
  }
5246
+ /** Live renderer gutters on the cell host (0 when unpublished — a test stub). */
5247
+ hostGutters() {
5248
+ const view = this.host.ownerDocument.defaultView;
5249
+ if (!view) return { toolbar: 0, scale: 0 };
5250
+ const cs = view.getComputedStyle(this.host);
5251
+ return {
5252
+ toolbar: Number.parseFloat(cs.getPropertyValue("--vela-toolbar-gutter")) || 0,
5253
+ scale: Number.parseFloat(cs.getPropertyValue("--vela-scale-gutter")) || 0
5254
+ };
5255
+ }
5112
5256
  setNear(near) {
5113
5257
  if (near === this.near) return;
5114
5258
  this.near = near;
@@ -5223,6 +5367,12 @@ function cellDrawings(opt) {
5223
5367
  if (opt === true || opt == null) return { toolbar: false };
5224
5368
  return { ...opt, toolbar: false };
5225
5369
  }
5370
+ function instanceDeltas(handle) {
5371
+ if (!handle) return void 0;
5372
+ const inputs = inputDeltas(handle.inputs, handle.inputValues());
5373
+ const props = inputDeltas(handle.props, handle.propValues());
5374
+ return inputs || props ? { ...inputs ? { inputs } : {}, ...props ? { props } : {} } : void 0;
5375
+ }
5226
5376
  var ChartCell = class {
5227
5377
  constructor(id, gridHost, seed, deps) {
5228
5378
  this.id = id;
@@ -5273,7 +5423,14 @@ var ChartCell = class {
5273
5423
  this.destroyed = false;
5274
5424
  this.appTheme = deps.theme;
5275
5425
  const symbol = prefixedSymbol(seed);
5276
- this.state = { symbol, provider: parseSymbol(symbol ?? "").provider ?? void 0, timeframe: seed.timeframe, priceStyle: seed.priceStyle, bars: seed.bars, session: normalizeSession(seed.session) };
5426
+ this.state = {
5427
+ symbol,
5428
+ provider: parseSymbol(symbol ?? "").provider ?? void 0,
5429
+ timeframe: seed.timeframe,
5430
+ priceStyle: seed.priceStyle,
5431
+ bars: seed.bars,
5432
+ session: normalizeSession(seed.session)
5433
+ };
5277
5434
  const doc = gridHost.ownerDocument;
5278
5435
  this.host = doc.createElement("div");
5279
5436
  this.host.className = "vela-cell";
@@ -5361,6 +5518,11 @@ var ChartCell = class {
5361
5518
  this.statusline = deps.statusline ? new Statusline(this.host, symbol ?? "", (sym) => this.inner?.data.symbolIcon(sym)) : null;
5362
5519
  this.statusline?.setMeta(seed.timeframe ?? "60", this.state.provider ?? "");
5363
5520
  this.statusline?.onChart(this.inner);
5521
+ this.statusline?.attachMenu({
5522
+ setPart: (part, visible) => this.setStatuslinePart(part, visible),
5523
+ chartVisible: () => this.inner?.renderer.get("candleVisible") !== false,
5524
+ setChartVisible: (visible) => this.inner?.renderer.set("candleVisible", visible)
5525
+ });
5364
5526
  this.marketStatus = this.statusline ? new MarketStatusTracker((s) => this.statusline?.setMarketStatus(s)) : null;
5365
5527
  void this.inner.data.ready().then(() => {
5366
5528
  if (this.inner && this.state.symbol) {
@@ -5398,6 +5560,7 @@ var ChartCell = class {
5398
5560
  this.syncPresentNatives();
5399
5561
  this.refreshNativeCatalog();
5400
5562
  });
5563
+ this.inner.on("indicator:inputs", () => this.deps.onStateDirty());
5401
5564
  this.inner.on("indicator:removed", ({ id: id2 }) => {
5402
5565
  if (this.destroyed) return;
5403
5566
  const idx = this.instances.findIndex((it) => it.handle?.id === id2);
@@ -5406,7 +5569,7 @@ var ChartCell = class {
5406
5569
  this.instances.splice(idx, 1);
5407
5570
  this.history.push({
5408
5571
  undo: () => {
5409
- snapshot.handle = this.addToChart(snapshot.entry);
5572
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
5410
5573
  this.instances.push(snapshot);
5411
5574
  this.deps.onIndicatorsChanged(this.id);
5412
5575
  },
@@ -5567,7 +5730,7 @@ var ChartCell = class {
5567
5730
  kind: "select",
5568
5731
  label: "Bars to fetch",
5569
5732
  id: "bars",
5570
- options: ["500", "1000", "2000", "5000", "10000", "20000"],
5733
+ options: ["500", "1000", "2000", "5000", "10000", "20000", "50000", "60000", "80000", "100000"],
5571
5734
  get: () => String(this.state.bars ?? 1e3),
5572
5735
  set: (v) => {
5573
5736
  this.state.bars = Number(v);
@@ -5599,10 +5762,34 @@ var ChartCell = class {
5599
5762
  id: "status-line",
5600
5763
  rows: [
5601
5764
  { kind: "heading", label: "Status line", id: "parts" },
5602
- { kind: "toggle", label: "Symbol name", id: "name", get: () => sl.partVisible("name"), set: (v) => this.setStatuslinePart("name", v) },
5603
- { kind: "toggle", label: "Market status", id: "market", get: () => sl.partVisible("market"), set: (v) => this.setStatuslinePart("market", v) },
5604
- { kind: "toggle", label: "OHLC values", id: "ohlc", get: () => sl.partVisible("ohlc"), set: (v) => this.setStatuslinePart("ohlc", v) },
5605
- { kind: "toggle", label: "Bar change values", id: "change", get: () => sl.partVisible("change"), set: (v) => this.setStatuslinePart("change", v) },
5765
+ {
5766
+ kind: "toggle",
5767
+ label: "Symbol name",
5768
+ id: "name",
5769
+ get: () => sl.partVisible("name"),
5770
+ set: (v) => this.setStatuslinePart("name", v)
5771
+ },
5772
+ {
5773
+ kind: "toggle",
5774
+ label: "Market status",
5775
+ id: "market",
5776
+ get: () => sl.partVisible("market"),
5777
+ set: (v) => this.setStatuslinePart("market", v)
5778
+ },
5779
+ {
5780
+ kind: "toggle",
5781
+ label: "OHLC values",
5782
+ id: "ohlc",
5783
+ get: () => sl.partVisible("ohlc"),
5784
+ set: (v) => this.setStatuslinePart("ohlc", v)
5785
+ },
5786
+ {
5787
+ kind: "toggle",
5788
+ label: "Bar change values",
5789
+ id: "change",
5790
+ get: () => sl.partVisible("change"),
5791
+ set: (v) => this.setStatuslinePart("change", v)
5792
+ },
5606
5793
  { kind: "heading", label: "Indicators", id: "indicators" },
5607
5794
  {
5608
5795
  kind: "toggle",
@@ -5655,7 +5842,7 @@ var ChartCell = class {
5655
5842
  statusPrefs() {
5656
5843
  const sl = this.statusline;
5657
5844
  return {
5658
- parts: sl ? { name: sl.partVisible("name"), market: sl.partVisible("market"), ohlc: sl.partVisible("ohlc"), change: sl.partVisible("change") } : null,
5845
+ parts: sl ? { logo: sl.partVisible("logo"), name: sl.partVisible("name"), market: sl.partVisible("market"), ohlc: sl.partVisible("ohlc"), change: sl.partVisible("change") } : null,
5659
5846
  indicatorTitles: this.indicatorTitlesOn,
5660
5847
  indicatorValues: this.indicatorValuesOn
5661
5848
  };
@@ -5795,9 +5982,9 @@ var ChartCell = class {
5795
5982
  this.manifest = list;
5796
5983
  if (this.pendingManifestNames) {
5797
5984
  if (list.length === 0) return;
5798
- for (const name of this.pendingManifestNames) {
5799
- const entry = list.find((e) => e.name === name);
5800
- if (entry) this.addManifestInstance(entry, { record: false });
5985
+ for (const led of this.pendingManifestNames) {
5986
+ const entry = list.find((e) => e.name === ledgerEntryName(led));
5987
+ if (entry) this.addManifestInstance(entry, { record: false, ...typeof led === "object" ? { inputs: led.inputs, props: led.props } : {} });
5801
5988
  }
5802
5989
  this.pendingManifestNames = null;
5803
5990
  return;
@@ -5827,9 +6014,10 @@ var ChartCell = class {
5827
6014
  }
5828
6015
  for (const it of [...this.instances]) this.dropInstance(it);
5829
6016
  if (this.manifest.length > 0) {
5830
- for (const name of led.manifest) {
5831
- const entry = this.manifest.find((e) => e.name === name);
5832
- if (entry) this.addManifestInstance(entry, { record: false });
6017
+ for (const item of led.manifest) {
6018
+ const entry = this.manifest.find((e) => e.name === ledgerEntryName(item));
6019
+ if (entry)
6020
+ this.addManifestInstance(entry, { record: false, ...typeof item === "object" ? { inputs: item.inputs, props: item.props } : {} });
5833
6021
  }
5834
6022
  this.pendingManifestNames = null;
5835
6023
  } else if (!this.deps.manifestSettled()) {
@@ -5878,12 +6066,16 @@ var ChartCell = class {
5878
6066
  * a persistence handler's `restore` runs silently, a user-driven call records.
5879
6067
  */
5880
6068
  addExternalIndicator(entry) {
5881
- this.addManifestInstance({ ...entry, enabled: true }, { external: true });
6069
+ this.addManifestInstance(
6070
+ { ...entry, enabled: true },
6071
+ { external: true, ...entry.inputs ? { inputs: entry.inputs } : {}, ...entry.props ? { props: entry.props } : {} }
6072
+ );
5882
6073
  }
5883
6074
  /** Add ONE instance of a manifest entry (repeatable — duplicates are legitimate). */
5884
6075
  addManifestInstance(entry, opts = {}) {
5885
6076
  if (this.destroyed) return;
5886
- const it = { entry, handle: this.addToChart(entry), ...opts.external ? { external: true } : {} };
6077
+ const values = opts.inputs || opts.props ? { inputs: opts.inputs, props: opts.props } : void 0;
6078
+ const it = { entry, handle: this.addToChart(entry, values), ...opts.external ? { external: true } : {}, ...values ? { values } : {} };
5887
6079
  this.instances.push(it);
5888
6080
  this.deps.onIndicatorsChanged(this.id);
5889
6081
  if (opts.record === false) return;
@@ -5891,7 +6083,7 @@ var ChartCell = class {
5891
6083
  this.history.push({
5892
6084
  undo: () => this.dropInstance(snapshot),
5893
6085
  redo: () => {
5894
- snapshot.handle = this.addToChart(snapshot.entry);
6086
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
5895
6087
  this.instances.push(snapshot);
5896
6088
  this.deps.onIndicatorsChanged(this.id);
5897
6089
  }
@@ -5904,7 +6096,7 @@ var ChartCell = class {
5904
6096
  const snapshot = it;
5905
6097
  this.history.push({
5906
6098
  undo: () => {
5907
- snapshot.handle = this.addToChart(snapshot.entry);
6099
+ snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
5908
6100
  this.instances.push(snapshot);
5909
6101
  this.deps.onIndicatorsChanged(this.id);
5910
6102
  },
@@ -5914,6 +6106,9 @@ var ChartCell = class {
5914
6106
  dropInstance(it) {
5915
6107
  const idx = this.instances.indexOf(it);
5916
6108
  if (idx >= 0) this.instances.splice(idx, 1);
6109
+ const captured = instanceDeltas(it.handle);
6110
+ if (captured) it.values = captured;
6111
+ else delete it.values;
5917
6112
  try {
5918
6113
  it.handle?.remove();
5919
6114
  } catch {
@@ -5956,9 +6151,13 @@ var ChartCell = class {
5956
6151
  this.deps.onIndicatorsChanged(this.id);
5957
6152
  });
5958
6153
  }
5959
- addToChart(entry) {
6154
+ addToChart(entry, values) {
5960
6155
  try {
5961
- return this.inner?.addIndicator(entry.script, entry.language !== void 0 ? { language: entry.language } : void 0) ?? null;
6156
+ return this.inner?.addIndicator(entry.script, {
6157
+ ...entry.language !== void 0 ? { language: entry.language } : {},
6158
+ ...values?.inputs ? { inputs: values.inputs } : {},
6159
+ ...values?.props ? { props: values.props } : {}
6160
+ }) ?? null;
5962
6161
  } catch (err) {
5963
6162
  console.warn(`[vela] indicator "${entry.name}" failed to add:`, err);
5964
6163
  return null;
@@ -6072,7 +6271,10 @@ var ChartCell = class {
6072
6271
  // manifest — their plugin persists them via the `ext` seam instead.
6073
6272
  indicators: indicatorLedger({
6074
6273
  present: this.inner ? this.inner.presentNativeIndicators() : [],
6075
- instanceNames: this.instances.filter((it) => !it.external).map((it) => it.entry.name),
6274
+ instanceEntries: this.instances.filter((it) => !it.external).map((it) => {
6275
+ const d = it.handle ? instanceDeltas(it.handle) : it.values;
6276
+ return d ? { name: it.entry.name, ...d } : it.entry.name;
6277
+ }),
6076
6278
  pendingManifest: this.pendingManifestNames,
6077
6279
  manifestSettled: this.deps.manifestSettled(),
6078
6280
  volumePending: this.volumeMayBePending && this.volumeIntent
@@ -7618,7 +7820,7 @@ function buildContext(host) {
7618
7820
  }
7619
7821
 
7620
7822
  // src/workspace/VelaWorkspace.ts
7621
- var DEFAULT_TIMEFRAMES = ["1", "5", "15", "60", "240", "D", "W"];
7823
+ var DEFAULT_TIMEFRAMES = ["1", "5", "15", "30", "60", "240", "D", "W", "M"];
7622
7824
  var GAP_PX = 2;
7623
7825
  var POOL_CAP = 16;
7624
7826
  var TIME_AXIS_H3 = 22;