@luxalgo/vela 0.6.2 → 0.6.4

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 (45) hide show
  1. package/dist/{DataProvider-DHX4x6-r.d.ts → DataProvider-BmHkcwvJ.d.ts} +1 -1
  2. package/dist/{DataProvider-CNmk84SH.d.cts → DataProvider-DrN7ZIou.d.cts} +1 -1
  3. package/dist/{chunk-TP56QRMK.js → chunk-2R7BANEM.js} +1 -1
  4. package/dist/{chunk-U5KCQHAC.js → chunk-3QX6QOCX.js} +80 -12
  5. package/dist/{chunk-7D6YIF34.js → chunk-6FA4TDUF.js} +325 -67
  6. package/dist/{chunk-7Y7CJ7EN.js → chunk-BEJ57HP4.js} +8 -1
  7. package/dist/{chunk-QHZ7IXFL.js → chunk-OICPLNU7.js} +59 -9
  8. package/dist/{contributions-DDpv7hEL.d.cts → contributions-2bEmCF9S.d.cts} +2 -2
  9. package/dist/{contributions-ciV6Neyd.d.ts → contributions-DatoAzMx.d.ts} +2 -2
  10. package/dist/{history-CFqZm5re.d.ts → history-BmyjaVir.d.cts} +6 -6
  11. package/dist/{history-ChoT34nz.d.cts → history-CvOtsjT5.d.ts} +6 -6
  12. package/dist/index.cjs +394 -78
  13. package/dist/index.d.cts +36 -9
  14. package/dist/index.d.ts +36 -9
  15. package/dist/index.js +4 -4
  16. package/dist/{options-BqGeFHtp.d.ts → options-YdiaaVjt.d.cts} +20 -1
  17. package/dist/{options-BqGeFHtp.d.cts → options-YdiaaVjt.d.ts} +20 -1
  18. package/dist/{plugin-C97gUCVf.d.ts → plugin-BeCMF1VQ.d.ts} +3 -3
  19. package/dist/{plugin-CSb_sTiN.d.cts → plugin-CkiStRaI.d.cts} +3 -3
  20. package/dist/plugin.cjs +8 -0
  21. package/dist/plugin.d.cts +4 -4
  22. package/dist/plugin.d.ts +4 -4
  23. package/dist/plugin.js +2 -2
  24. package/dist/providers/binance.d.cts +2 -2
  25. package/dist/providers/binance.d.ts +2 -2
  26. package/dist/providers/coinbase.d.cts +2 -2
  27. package/dist/providers/coinbase.d.ts +2 -2
  28. package/dist/providers/hyperliquid.d.cts +2 -2
  29. package/dist/providers/hyperliquid.d.ts +2 -2
  30. package/dist/ui.cjs +59 -9
  31. package/dist/ui.d.cts +10 -2
  32. package/dist/ui.d.ts +10 -2
  33. package/dist/ui.js +2 -2
  34. package/dist/vela.global.js +394 -78
  35. package/dist/vela.global.min.js +84 -47
  36. package/dist/widget.cjs +646 -191
  37. package/dist/widget.d.cts +29 -8
  38. package/dist/widget.d.ts +29 -8
  39. package/dist/widget.js +145 -73
  40. package/dist/workspace.cjs +601 -142
  41. package/dist/workspace.d.cts +29 -5
  42. package/dist/workspace.d.ts +29 -5
  43. package/dist/workspace.js +143 -67
  44. package/package.json +1 -1
  45. /package/dist/{chunk-Q3XQHLIH.js → chunk-AOGZBKUE.js} +0 -0
@@ -9872,7 +9872,7 @@ var Vela = (function (exports) {
9872
9872
  } else {
9873
9873
  const left = Math.min(a.left - h.left, Math.max(0, h.width - tip.offsetWidth - 4));
9874
9874
  tip.style.left = `${Math.max(0, left)}px`;
9875
- tip.style.top = `${a.bottom - h.top + 6}px`;
9875
+ tip.style.top = opts.placement === "above" ? `${Math.max(0, a.top - h.top - tip.offsetHeight - 6)}px` : `${a.bottom - h.top + 6}px`;
9876
9876
  }
9877
9877
  };
9878
9878
  const arm = (e) => {
@@ -16247,6 +16247,23 @@ var Vela = (function (exports) {
16247
16247
  background: var(--vela-hover-strong);
16248
16248
  color: var(--vela-fg-bright);
16249
16249
  }
16250
+ /* Checkmark mode (context/action menus): every row reserves the leading mark column so
16251
+ labels align; a checked row fills it with a \u2713 and brightens its ink \u2014 the row surface
16252
+ stays free for the hover wash. */
16253
+ .vela-menu-item .vela-menu-mark {
16254
+ width: 14px;
16255
+ flex: none;
16256
+ display: inline-flex;
16257
+ align-items: center;
16258
+ justify-content: center;
16259
+ }
16260
+ .vela-menu-item .vela-menu-mark .vela-icon {
16261
+ width: 14px;
16262
+ height: 14px;
16263
+ font-size: 14px;
16264
+ color: var(--vela-fg-bright);
16265
+ }
16266
+ .vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
16250
16267
  /* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
16251
16268
  same control language as the settings dialog's toggles. */
16252
16269
  .vela-menu-switch {
@@ -16317,6 +16334,7 @@ var Vela = (function (exports) {
16317
16334
  this.host = opts.host;
16318
16335
  this.onSelect = opts.onSelect;
16319
16336
  this.onFavorite = opts.onFavorite;
16337
+ this.checkmarks = opts.checkmarks === true;
16320
16338
  this.positioner = doc.createElement("div");
16321
16339
  this.positioner.className = "vela-ui-layer";
16322
16340
  this.list = doc.createElement("ul");
@@ -16395,6 +16413,7 @@ var Vela = (function (exports) {
16395
16413
  build() {
16396
16414
  const doc = this.doc;
16397
16415
  this.list.replaceChildren();
16416
+ const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
16398
16417
  for (const item of this.items) {
16399
16418
  if (item.separatorBefore) {
16400
16419
  const sep = doc.createElement("li");
@@ -16408,7 +16427,16 @@ var Vela = (function (exports) {
16408
16427
  li.dataset.veiId = item.id;
16409
16428
  if (item.toggle) {
16410
16429
  li.dataset.toggle = "1";
16411
- } else if (!branch && item.checked) {
16430
+ }
16431
+ if (markable) {
16432
+ const mark = doc.createElement("span");
16433
+ mark.className = "vela-menu-mark";
16434
+ if (!branch && !item.toggle && item.checked) {
16435
+ mark.appendChild(iconEl("check", doc));
16436
+ li.dataset.checkmark = "1";
16437
+ }
16438
+ li.appendChild(mark);
16439
+ } else if (!branch && !item.toggle && item.checked) {
16412
16440
  li.dataset.checked = "1";
16413
16441
  }
16414
16442
  if (item.icon) li.appendChild(iconEl(item.icon, doc));
@@ -16457,6 +16485,7 @@ var Vela = (function (exports) {
16457
16485
  placement: "right-start",
16458
16486
  onSelect: this.onSelect,
16459
16487
  onFavorite: this.onFavorite,
16488
+ checkmarks: this.checkmarks,
16460
16489
  id: `${this.mid}--${item.id}`
16461
16490
  });
16462
16491
  sub.setItems(item.submenu ?? []);
@@ -16481,7 +16510,8 @@ var Vela = (function (exports) {
16481
16510
  triggerId: opts.triggerId,
16482
16511
  id: opts.id,
16483
16512
  minWidth: opts.minWidth,
16484
- onFavorite: opts.onFavorite
16513
+ onFavorite: opts.onFavorite,
16514
+ checkmarks: opts.checkmarks
16485
16515
  });
16486
16516
  this.root.setItems(opts.items);
16487
16517
  }
@@ -16509,6 +16539,13 @@ var Vela = (function (exports) {
16509
16539
  }
16510
16540
  };
16511
16541
 
16542
+ // src/core/model/inputs.ts
16543
+ function inputVisible(when, values) {
16544
+ if (!when) return true;
16545
+ const conds = Array.isArray(when) ? when : [when];
16546
+ return conds.every((c) => c.anyOf ? c.anyOf.some((x) => x === values[c.key]) : values[c.key] === c.equals);
16547
+ }
16548
+
16512
16549
  // src/ui/tokens.ts
16513
16550
  var STATIC_ID = "vela-ui-tokens";
16514
16551
  var STATIC_DECLS = Object.entries(STATIC_TOKENS).map(([k, v]) => ` ${k}: ${v};`).join("\n");
@@ -16517,8 +16554,13 @@ var Vela = (function (exports) {
16517
16554
  ${STATIC_DECLS}
16518
16555
  font-family: var(--vela-font, -apple-system, system-ui, sans-serif);
16519
16556
  box-sizing: border-box;
16557
+ /* Chrome text (titles, buttons, menus, readouts) is UI, not copy \u2014 never selectable. */
16558
+ user-select: none;
16559
+ -webkit-user-select: none;
16520
16560
  }
16521
16561
  .vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
16562
+ /* Text ENTRY is the one exception: selection is part of editing. */
16563
+ .vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
16522
16564
  .vela-icon { display: inline-flex; align-items: center; flex: none; }
16523
16565
  .vela-icon svg { display: block; }
16524
16566
  `;
@@ -18704,6 +18746,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
18704
18746
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
18705
18747
  -moz-appearance: textfield;
18706
18748
  appearance: textfield;
18749
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
18750
+ user-select: text;
18751
+ -webkit-user-select: text;
18707
18752
  }
18708
18753
  .vela-num input::-webkit-inner-spin-button, .vela-num input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
18709
18754
  .vela-num input:hover { border-color: var(--vela-fg-muted); }
@@ -18916,6 +18961,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
18916
18961
  overflow: hidden;
18917
18962
  text-overflow: ellipsis;
18918
18963
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
18964
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
18965
+ user-select: text;
18966
+ -webkit-user-select: text;
18919
18967
  }
18920
18968
  .vela-text-field:hover { border-color: var(--vela-fg-muted); }
18921
18969
  .vela-text-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
@@ -19009,6 +19057,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
19009
19057
  resize: vertical;
19010
19058
  outline: none;
19011
19059
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
19060
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
19061
+ user-select: text;
19062
+ -webkit-user-select: text;
19012
19063
  }
19013
19064
  .vela-textarea-field:hover { border-color: var(--vela-fg-muted); }
19014
19065
  .vela-textarea-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
@@ -19537,6 +19588,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
19537
19588
  font-size: 14px;
19538
19589
  }
19539
19590
  .vela-field-label[data-size='sm'] { font-size: inherit; opacity: 0.85; }
19591
+ /* Titles are fully inert: spans, not label[for] \u2014 native labels propagate :hover and
19592
+ clicks onto their control, and both belong to the input alone. */
19540
19593
  .vela-field-cell { display: flex; align-items: center; gap: 8px; justify-self: start; }
19541
19594
  .vela-field-bool { display: flex; align-items: center; gap: 8px; min-height: 22px; }
19542
19595
  .vela-field-stacked { display: flex; flex-direction: column; gap: 8px; }
@@ -19595,11 +19648,17 @@ ${overlayScrollbarCss(".vela-dialog *")}
19595
19648
  return el;
19596
19649
  }
19597
19650
  function labelEl(text, opts) {
19598
- const el = opts.id ? document.createElement("label") : document.createElement("span");
19599
- if (opts.id && el instanceof HTMLLabelElement) el.htmlFor = opts.id;
19651
+ const el = document.createElement("span");
19600
19652
  el.className = "vela-field-label";
19601
19653
  if (opts.size) el.dataset.size = opts.size;
19602
19654
  el.textContent = text;
19655
+ const id = opts.id;
19656
+ if (id) {
19657
+ el.id = `${id}--title`;
19658
+ queueMicrotask(() => {
19659
+ el.ownerDocument.getElementById(id)?.setAttribute("aria-labelledby", el.id);
19660
+ });
19661
+ }
19603
19662
  return el;
19604
19663
  }
19605
19664
  function asList(control) {
@@ -19628,9 +19687,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
19628
19687
  it.className = "vela-field-inline-item";
19629
19688
  if (item.toggleFirst) it.style.flexDirection = "row-reverse";
19630
19689
  if (item.label) {
19631
- const lbl = labelEl(item.label, { id: item.id, size: size3 });
19632
- if (item.toggleFirst) lbl.style.cursor = "pointer";
19633
- it.appendChild(lbl);
19690
+ it.appendChild(labelEl(item.label, { id: item.id, size: size3 }));
19634
19691
  }
19635
19692
  if (item.fit) it.appendChild(item.control);
19636
19693
  else {
@@ -19655,7 +19712,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
19655
19712
  tone: "bright",
19656
19713
  onChange: (v) => opts.toggle?.onChange(v)
19657
19714
  });
19658
- wrap3.append(sw.el, labelEl(opts.label, { size: size3 }));
19715
+ wrap3.append(sw.el, labelEl(opts.label, { id: opts.toggle?.id ?? opts.id, size: size3 }));
19659
19716
  if (opts.info) wrap3.appendChild(opts.info);
19660
19717
  if (opts.toggle?.get) {
19661
19718
  const get = opts.toggle.get;
@@ -19694,7 +19751,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
19694
19751
  if (controls.length > 0) dimControls(controls, v);
19695
19752
  }
19696
19753
  });
19697
- left.append(sw.el, labelEl(opts.label, { size: size3 }));
19754
+ left.append(sw.el, labelEl(opts.label, { id: opts.toggle.id ?? opts.id, size: size3 }));
19698
19755
  wrap2.appendChild(left);
19699
19756
  if (controls.length > 0) dimControls(controls, opts.toggle.checked);
19700
19757
  if (opts.toggle.get) {
@@ -19848,6 +19905,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
19848
19905
  this.row = null;
19849
19906
  this.snapshot = null;
19850
19907
  this.dialogTips = [];
19908
+ /** Re-applies every `when` gate against current values; set while the dialog is open. */
19909
+ this.refreshVisibility = null;
19851
19910
  this.choicePop = null;
19852
19911
  this.calendarPop = null;
19853
19912
  this.calendarAnchor = null;
@@ -19910,14 +19969,17 @@ ${overlayScrollbarCss(".vela-dialog *")}
19910
19969
  tabs.style.cssText = `display:flex;gap:12px;padding:0 20px;border-bottom:1px solid ${border};flex:0 0 auto;`;
19911
19970
  const tabEls = [];
19912
19971
  const bodies = [];
19972
+ const gates = [];
19973
+ let active = 0;
19913
19974
  const activate = (idx) => {
19975
+ active = idx;
19914
19976
  for (let i = 0; i < tabEls.length; i += 1) {
19915
- const active = i === idx;
19977
+ const on = i === idx;
19916
19978
  const el = tabEls[i];
19917
- el.style.borderBottomColor = active ? fg : "transparent";
19918
- el.style.color = active ? "inherit" : "var(--vela-fg-muted)";
19919
- el.classList.toggle("vela-ind-tab-active", active);
19920
- bodies[i].style.display = active ? "grid" : "none";
19979
+ el.style.borderBottomColor = on ? fg : "transparent";
19980
+ el.style.color = on ? "inherit" : "var(--vela-fg-muted)";
19981
+ el.classList.toggle("vela-ind-tab-active", on);
19982
+ bodies[i].style.display = on ? "grid" : "none";
19921
19983
  }
19922
19984
  };
19923
19985
  for (const [idx, def] of tabDefs.entries()) {
@@ -19928,11 +19990,24 @@ ${overlayScrollbarCss(".vela-dialog *")}
19928
19990
  tab.addEventListener("click", () => activate(idx));
19929
19991
  tabs.appendChild(tab);
19930
19992
  tabEls.push(tab);
19931
- bodies.push(this.buildTabBody(def.inputs, row));
19993
+ bodies.push(this.buildTabBody(def.inputs, row, gates));
19994
+ if (def.inputs.every((d) => d.when)) {
19995
+ gates.push({ el: tab, visible: (bag) => def.inputs.some((d) => inputVisible(d.when, bag)) });
19996
+ }
19932
19997
  }
19933
19998
  ui.body.appendChild(tabs);
19934
19999
  for (const b of bodies) ui.body.appendChild(b);
19935
20000
  activate(0);
20001
+ const refresh = () => {
20002
+ const bag = valuesBag(row);
20003
+ for (const g of gates) g.el.style.display = g.visible(bag) ? "" : "none";
20004
+ if (tabEls[active]?.style.display === "none") {
20005
+ const next2 = tabEls.findIndex((el) => el.style.display !== "none");
20006
+ if (next2 >= 0) activate(next2);
20007
+ }
20008
+ };
20009
+ this.refreshVisibility = refresh;
20010
+ refresh();
19936
20011
  const onBackdropDown = (e) => {
19937
20012
  if (e.target !== ui.backdrop && e.target !== ui.positioner) return;
19938
20013
  if (isPopoverOpen() || eventDismissedPopover(e)) {
@@ -19952,16 +20027,27 @@ ${overlayScrollbarCss(".vela-dialog *")}
19952
20027
  }
19953
20028
  /** One tab's scrollable body — one shared grid so every group's controls share a
19954
20029
  * left edge (a 100px number field lines up with a wider session/time pair). */
19955
- buildTabBody(inputs, row) {
20030
+ buildTabBody(inputs, row, gates) {
19956
20031
  const body = document.createElement("div");
19957
20032
  body.style.cssText = inputDialogBodyStyle(this.host.mobile());
19958
20033
  for (const group of groupInputs(inputs)) {
19959
20034
  const section = document.createElement("div");
19960
20035
  section.style.cssText = "display:contents;";
20036
+ let header = null;
19961
20037
  if (group.name) {
19962
- section.appendChild(fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 }));
20038
+ header = fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 });
20039
+ section.appendChild(header);
20040
+ }
20041
+ for (const inputRow of group.rows) {
20042
+ const el = this.buildInputRow(section, inputRow, row);
20043
+ if (inputRow.some((d) => d.when)) {
20044
+ gates.push({ el, visible: (bag) => rowVisible(inputRow, bag) });
20045
+ }
20046
+ }
20047
+ if (header && group.rows.every((r) => r.every((d) => d.when))) {
20048
+ const rows = group.rows;
20049
+ gates.push({ el: header, visible: (bag) => rows.some((r) => rowVisible(r, bag)) });
19963
20050
  }
19964
- for (const inputRow of group.rows) this.buildInputRow(section, inputRow, row);
19965
20051
  body.appendChild(section);
19966
20052
  }
19967
20053
  return body;
@@ -19969,6 +20055,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
19969
20055
  close() {
19970
20056
  document.removeEventListener("keydown", this.onDialogKey);
19971
20057
  closeOpenPopovers();
20058
+ this.refreshVisibility = null;
19972
20059
  this.choicePop = null;
19973
20060
  this.calendarPop = null;
19974
20061
  this.calendarAnchor = null;
@@ -19998,11 +20085,21 @@ ${overlayScrollbarCss(".vela-dialog *")}
19998
20085
  }
19999
20086
  this.close();
20000
20087
  }
20088
+ /** Write one edit through: store it, notify the host, and re-apply the `when` gates. */
20089
+ commit(row, key, value) {
20090
+ row.values[key] = value;
20091
+ this.host.onChange?.({ indicatorId: row.id, key, value });
20092
+ this.refreshVisibility?.();
20093
+ }
20001
20094
  /** One settings row (or several `inline=` inputs) placed into a section's grid. */
20002
20095
  buildInputRow(grid, decls, row) {
20003
20096
  const idOf = (inp) => `vela-inp-${row.id}-${inp.key}`;
20097
+ const append = (el) => {
20098
+ grid.appendChild(el);
20099
+ return el;
20100
+ };
20004
20101
  if (decls.length > 1) {
20005
- grid.appendChild(fieldRow({
20102
+ return append(fieldRow({
20006
20103
  label: "",
20007
20104
  inline: decls.map((d) => ({
20008
20105
  label: nameOf(d) || void 0,
@@ -20013,14 +20110,13 @@ ${overlayScrollbarCss(".vela-dialog *")}
20013
20110
  })),
20014
20111
  info: [...decls].reverse().find((d) => d.tooltip)?.tooltip ? this.infoButton([...decls].reverse().find((d) => d.tooltip).tooltip) : void 0
20015
20112
  }));
20016
- return;
20017
20113
  }
20018
20114
  const lead = decls[0];
20019
20115
  const id = idOf(lead);
20020
20116
  const info = lead.tooltip ? this.infoButton(lead.tooltip) : void 0;
20021
20117
  if (lead.type === "bool") {
20022
20118
  const current = Boolean(row.values[lead.key] ?? lead.defval);
20023
- grid.appendChild(fieldRow({
20119
+ return append(fieldRow({
20024
20120
  label: nameOf(lead),
20025
20121
  id,
20026
20122
  bool: true,
@@ -20028,26 +20124,21 @@ ${overlayScrollbarCss(".vela-dialog *")}
20028
20124
  toggle: {
20029
20125
  id,
20030
20126
  checked: current,
20031
- onChange: (v) => {
20032
- row.values[lead.key] = v;
20033
- this.host.onChange?.({ indicatorId: row.id, key: lead.key, value: v });
20034
- }
20127
+ onChange: (v) => this.commit(row, lead.key, v)
20035
20128
  }
20036
20129
  }));
20037
- return;
20038
20130
  }
20039
20131
  if (lead.type === "text_area") {
20040
- grid.appendChild(fieldRow({
20132
+ return append(fieldRow({
20041
20133
  label: nameOf(lead),
20042
20134
  id,
20043
20135
  stacked: true,
20044
20136
  info,
20045
20137
  control: this.buildControl(row, lead, id)
20046
20138
  }));
20047
- return;
20048
20139
  }
20049
20140
  const fit = lead.type === "color" || lead.type === "session" || lead.type === "time";
20050
- grid.appendChild(fieldRow({
20141
+ return append(fieldRow({
20051
20142
  label: nameOf(lead),
20052
20143
  id,
20053
20144
  fit,
@@ -20059,10 +20150,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
20059
20150
  /** Build the typed control for one input, committing edits live via `onChange`. */
20060
20151
  buildControl(row, inp, id) {
20061
20152
  const current = row.values[inp.key] ?? inp.defval;
20062
- const emit = (value) => {
20063
- row.values[inp.key] = value;
20064
- this.host.onChange?.({ indicatorId: row.id, key: inp.key, value });
20065
- };
20153
+ const emit = (value) => this.commit(row, inp.key, value);
20066
20154
  if (inp.type === "bool") {
20067
20155
  return buildFieldControl({ kind: "switch", id, checked: Boolean(current), onChange: (v) => emit(v) }).el;
20068
20156
  }
@@ -20483,6 +20571,14 @@ ${overlayScrollbarCss(".vela-dialog *")}
20483
20571
  }
20484
20572
  return names.map((name) => ({ name, inputs: byTab.get(name) }));
20485
20573
  }
20574
+ function rowVisible(decls, bag) {
20575
+ return decls.some((d) => inputVisible(d.when, bag));
20576
+ }
20577
+ function valuesBag(row) {
20578
+ const bag = {};
20579
+ for (const inp of row.inputs) bag[inp.key] = row.values[inp.key] ?? inp.defval;
20580
+ return bag;
20581
+ }
20486
20582
  function groupInputs(inputs) {
20487
20583
  const order = [];
20488
20584
  const byGroup = /* @__PURE__ */ new Map();
@@ -20945,6 +21041,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
20945
21041
  host: this.container,
20946
21042
  items,
20947
21043
  placement: "bottom-start",
21044
+ // Right-click action menu — checked state reads as a leading ✓ (see the kit Menu).
21045
+ checkmarks: true,
20948
21046
  onSelect: (itemId) => {
20949
21047
  if (itemId !== "values" || !this.rowMenuId) return;
20950
21048
  const target = this.rows.get(this.rowMenuId);
@@ -21280,6 +21378,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
21280
21378
  syncRowActions(row) {
21281
21379
  const open2 = row.highlighted;
21282
21380
  row.controlsEl.style.display = open2 || row.hidden ? "inline-flex" : "none";
21381
+ if (open2) row.el.appendChild(row.statusEl);
21382
+ else row.el.insertBefore(row.statusEl, row.valuesEl);
21283
21383
  for (const child of Array.from(row.controlsEl.children)) {
21284
21384
  if (!(child instanceof HTMLElement) || child === row.eyeEl) continue;
21285
21385
  if (child === row.extrasEl) {
@@ -21523,6 +21623,148 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
21523
21623
  }
21524
21624
  };
21525
21625
 
21626
+ // src/renderers/native/chrome/axisLayout.ts
21627
+ var AXIS_MASTER_W = 64;
21628
+ var AXIS_MERGED_W = 56;
21629
+ var PANE_SEPARATOR_PX = 3;
21630
+ function axisColumnX(dataW, column) {
21631
+ return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
21632
+ }
21633
+
21634
+ // src/renderers/native/chrome/AxisScaleButtons.ts
21635
+ var STYLE_ID2 = "vela-axis-scale-buttons";
21636
+ var BTN_PX = 18;
21637
+ var BTN_GAP = 4;
21638
+ var AXIS_BORDER = 1;
21639
+ var CLUSTER_W = AXIS_MASTER_W - AXIS_BORDER;
21640
+ var PAD_TOP = 8;
21641
+ var PAD_BOTTOM = 6;
21642
+ var CLUSTER_H = BTN_PX + PAD_TOP + PAD_BOTTOM;
21643
+ var MIN_PANE_H = 48;
21644
+ function ensureStyles2() {
21645
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
21646
+ const st = document.createElement("style");
21647
+ st.id = STYLE_ID2;
21648
+ st.textContent = `
21649
+ .vela-axis-btn{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--vela-radius-sm);background:transparent;cursor:pointer;font:600 10px/1 -apple-system,Segoe UI,sans-serif;color:var(--vela-fg-muted);}
21650
+ .vela-axis-btn:hover{background:var(--vela-active);color:var(--vela-fg-bright);}
21651
+ .vela-axis-on,.vela-axis-on:hover{background:var(--vela-selected-bg);color:var(--vela-selected-fg);}
21652
+ `;
21653
+ document.head.appendChild(st);
21654
+ }
21655
+ var AxisScaleButtons = class {
21656
+ constructor(plot, theme, deps) {
21657
+ this.plot = plot;
21658
+ this.theme = theme;
21659
+ this.deps = deps;
21660
+ this.tipDisposers = [];
21661
+ this.hoverPaneId = null;
21662
+ /** Reveal over the master scale column of the pane under the cursor (mouse only —
21663
+ * touch has no hover, and a drag-rescale on the axis must not sprout buttons). */
21664
+ this.onPlotMove = (e) => {
21665
+ if (e.pointerType !== "mouse") return;
21666
+ const rect = this.plot.getBoundingClientRect();
21667
+ const x = e.clientX - rect.left;
21668
+ const gutterLeft = rect.width - this.deps.rightAxis();
21669
+ let hit = null;
21670
+ if (x >= gutterLeft && x <= gutterLeft + AXIS_MASTER_W) {
21671
+ const y = e.clientY - rect.top;
21672
+ for (const p of this.deps.panes()) {
21673
+ if (p.collapsed || p.height < MIN_PANE_H) continue;
21674
+ if (y >= p.top && y <= p.top + p.height) {
21675
+ hit = p.id;
21676
+ break;
21677
+ }
21678
+ }
21679
+ }
21680
+ this.setHoverPane(hit);
21681
+ if (hit) this.sync();
21682
+ };
21683
+ this.onPlotLeave = () => this.setHoverPane(null);
21684
+ ensureStyles2();
21685
+ this.cluster = document.createElement("div");
21686
+ Object.assign(this.cluster.style, {
21687
+ position: "absolute",
21688
+ display: "none",
21689
+ justifyContent: "center",
21690
+ alignItems: "center",
21691
+ gap: `${BTN_GAP}px`,
21692
+ padding: `${PAD_TOP}px 0 ${PAD_BOTTOM}px`,
21693
+ width: `${CLUSTER_W}px`,
21694
+ boxSizing: "border-box",
21695
+ zIndex: "6",
21696
+ pointerEvents: "auto"
21697
+ });
21698
+ this.applyTheme();
21699
+ this.autoBtn = this.button("A", "Auto (fits data to screen)", () => this.deps.onToggleAuto(this.hoverPaneId ?? ""));
21700
+ this.logBtn = this.button("L", "Logarithmic scale", () => this.deps.onToggleLog(this.hoverPaneId ?? ""));
21701
+ this.cluster.append(this.autoBtn, this.logBtn);
21702
+ this.plot.appendChild(this.cluster);
21703
+ this.plot.addEventListener("pointermove", this.onPlotMove);
21704
+ this.plot.addEventListener("pointerleave", this.onPlotLeave);
21705
+ }
21706
+ button(label, title, onClick) {
21707
+ const b = document.createElement("button");
21708
+ b.type = "button";
21709
+ b.setAttribute("aria-label", title);
21710
+ b.textContent = label;
21711
+ b.className = "vela-axis-btn";
21712
+ Object.assign(b.style, { width: `${BTN_PX}px`, height: `${BTN_PX}px` });
21713
+ this.tipDisposers.push(attachChromeTooltip(b, {
21714
+ host: this.plot,
21715
+ theme: () => this.theme,
21716
+ text: () => title,
21717
+ placement: "above"
21718
+ }));
21719
+ b.addEventListener("click", (e) => {
21720
+ e.stopPropagation();
21721
+ onClick();
21722
+ this.sync();
21723
+ });
21724
+ return b;
21725
+ }
21726
+ /** Re-tint the chip when the plot background/border change (theme swap, config edit). */
21727
+ setTheme(theme) {
21728
+ this.theme = theme;
21729
+ this.applyTheme();
21730
+ }
21731
+ /** The strip is SOLID chart background: the buttons cover axis labels, never blend with them. */
21732
+ applyTheme() {
21733
+ this.cluster.style.background = this.theme.background;
21734
+ }
21735
+ setHoverPane(paneId) {
21736
+ if (this.hoverPaneId === paneId) return;
21737
+ this.hoverPaneId = paneId;
21738
+ this.reposition();
21739
+ }
21740
+ /** Re-place (or hide) the cluster after a hover or pane-layout change. */
21741
+ reposition() {
21742
+ const pane = this.hoverPaneId ? this.deps.panes().find((p) => p.id === this.hoverPaneId) : void 0;
21743
+ if (!pane || pane.collapsed || pane.height < MIN_PANE_H) {
21744
+ this.cluster.style.display = "none";
21745
+ return;
21746
+ }
21747
+ this.cluster.style.right = `${this.deps.rightAxis() - AXIS_MASTER_W}px`;
21748
+ this.cluster.style.top = `${pane.top + pane.height - CLUSTER_H}px`;
21749
+ this.cluster.style.display = "flex";
21750
+ this.sync();
21751
+ }
21752
+ /** Reflect the hovered pane's current auto/log state on the buttons. */
21753
+ sync() {
21754
+ const pane = this.deps.panes().find((p) => p.id === this.hoverPaneId);
21755
+ if (!pane) return;
21756
+ this.autoBtn.classList.toggle("vela-axis-on", pane.auto);
21757
+ this.logBtn.classList.toggle("vela-axis-on", pane.log);
21758
+ }
21759
+ destroy() {
21760
+ this.plot.removeEventListener("pointermove", this.onPlotMove);
21761
+ this.plot.removeEventListener("pointerleave", this.onPlotLeave);
21762
+ for (const dispose of this.tipDisposers) dispose();
21763
+ this.tipDisposers.length = 0;
21764
+ this.cluster.remove();
21765
+ }
21766
+ };
21767
+
21526
21768
  // src/renderers/shared/TableOverlay.ts
21527
21769
  var SIZE_PX3 = {
21528
21770
  auto: 13,
@@ -21880,6 +22122,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
21880
22122
  var BASELINE_FILL_ALPHA = 0.25;
21881
22123
  var BASELINE_FILL_ALPHA_FAR = 0.05;
21882
22124
  var BASELINE_LEVEL_DEFAULT = 50;
22125
+ var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
22126
+ var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
21883
22127
  function defaultChartStyle() {
21884
22128
  return {
21885
22129
  chartTypes: {},
@@ -21910,7 +22154,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
21910
22154
  bottomFillColor2: null,
21911
22155
  width: 2,
21912
22156
  baselineLevel: BASELINE_LEVEL_DEFAULT
21913
- }
22157
+ },
22158
+ sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE }
21914
22159
  };
21915
22160
  }
21916
22161
  var LINE_STYLES = ["solid", "dashed", "dotted"];
@@ -22044,6 +22289,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22044
22289
  const area = asObject(p.area);
22045
22290
  const baseline = asObject(p.baseline);
22046
22291
  const series = asObject(p.series);
22292
+ const sessions = asObject(p.sessions);
22047
22293
  const stacking = asObject(p.stacking);
22048
22294
  const stackSeries = asObject(stacking.series);
22049
22295
  return {
@@ -22136,6 +22382,10 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22136
22382
  baseline: series.baseline === null ? null : isNum(series.baseline) ? series.baseline : base.series.baseline,
22137
22383
  spacing: isNum(series.spacing) ? clampSpacing(series.spacing) : base.series.spacing
22138
22384
  },
22385
+ sessions: {
22386
+ premarketColor: isColor(sessions.premarketColor) ? sessions.premarketColor : base.sessions.premarketColor,
22387
+ postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor
22388
+ },
22139
22389
  stacking: {
22140
22390
  candles: isNum(stacking.candles) ? stacking.candles : base.stacking.candles,
22141
22391
  // Additive like `chartTypes`: a patch names only the ids it carries; the rest keep
@@ -22382,6 +22632,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22382
22632
  this.tradeMarkers = defaultTradeMarkersState();
22383
22633
  /** Renderer-owned shaded time bands (session highlighting), behind grid + data. */
22384
22634
  this.highlights = [];
22635
+ /** Pre/post-market bands pushed by the host (`sessionZones` feature); null ⇒ no sessions. */
22636
+ this.sessionZones = null;
22385
22637
  /** Draw-order key of the price candles, relative to indicator series z (see `seriesZ`).
22386
22638
  * Indicators with z below this draw BEHIND the candles; at/above draw in front.
22387
22639
  * Default 0 with indicators mounting at z < 0 ⇒ the price reads on top of every overlay,
@@ -22416,6 +22668,15 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22416
22668
  if (!this.orderedCache) this.orderedCache = [...this.panes.values()].sort((a, b) => a.order - b.order);
22417
22669
  return this.orderedCache;
22418
22670
  }
22671
+ /** The session zones resolved into colored bands (pre/post-market washes from the
22672
+ * config's session colors) — consumed by the same painting path as {@link highlights}. */
22673
+ sessionHighlightBands() {
22674
+ if (!this.sessionZones) return [];
22675
+ const out = [];
22676
+ for (const [from, to] of this.sessionZones.pre) out.push({ from, to, color: this.style.sessions.premarketColor });
22677
+ for (const [from, to] of this.sessionZones.post) out.push({ from, to, color: this.style.sessions.postmarketColor });
22678
+ return out;
22679
+ }
22419
22680
  indicatorsForPane(paneId) {
22420
22681
  const out = [];
22421
22682
  for (const model of this.indicators.values()) if (model.paneId === paneId) out.push(model);
@@ -23346,10 +23607,12 @@ void main() {
23346
23607
  }
23347
23608
  }
23348
23609
  }
23349
- /** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height). */
23610
+ /** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
23611
+ * Session-zone washes (pre/post-market) paint first, host highlights on top. */
23350
23612
  emitHighlights(b, scene, pane, coords) {
23351
- if (scene.highlights.length === 0) return;
23352
- for (const band of scene.highlights) {
23613
+ const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
23614
+ if (bands.length === 0) return;
23615
+ for (const band of bands) {
23353
23616
  const x1 = coords.timeToX(band.from);
23354
23617
  const x2 = coords.timeToX(band.to);
23355
23618
  if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
@@ -25323,10 +25586,12 @@ void main() {
25323
25586
  ctx.fillStyle = bg.color;
25324
25587
  ctx.fillRect(x1, pane.bounds.top, x2 - x1, pane.bounds.height);
25325
25588
  }
25326
- /** Renderer-owned session highlight bands: full-height (all panes), behind grid + data. */
25589
+ /** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
25590
+ * Session-zone washes (pre/post-market) paint first, host highlights on top. */
25327
25591
  drawHighlights(ctx, scene, coords) {
25328
- if (scene.highlights.length === 0) return;
25329
- for (const band of scene.highlights) {
25592
+ const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
25593
+ if (bands.length === 0) return;
25594
+ for (const band of bands) {
25330
25595
  const x1 = coords.timeToX(band.from);
25331
25596
  const x2 = coords.timeToX(band.to);
25332
25597
  if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
@@ -26100,14 +26365,6 @@ void main() {
26100
26365
  }
26101
26366
  };
26102
26367
 
26103
- // src/renderers/native/chrome/axisLayout.ts
26104
- var AXIS_MASTER_W = 64;
26105
- var AXIS_MERGED_W = 56;
26106
- var PANE_SEPARATOR_PX = 3;
26107
- function axisColumnX(dataW, column) {
26108
- return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
26109
- }
26110
-
26111
26368
  // src/renderers/native/chrome/ChromeRenderer.ts
26112
26369
  var ChromeRenderer = class {
26113
26370
  constructor() {
@@ -26990,6 +27247,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
26990
27247
  for (const hr of hs.rows) {
26991
27248
  if (hr.kind === "heading") body.append(this.sectionTitle(hr.label));
26992
27249
  else if (hr.kind === "toggle") body.append(this.boolRow(hr.label, hr.get(), (v) => hr.set(v)));
27250
+ else if (hr.kind === "color") body.append(this.colorRow(hr.label, hr.get(), (v) => hr.set(v)));
26993
27251
  else body.append(this.selectRowLabeled(hr.label, hr.get(), hr.options.map((o) => [o, o]), (v) => hr.set(v)));
26994
27252
  }
26995
27253
  }
@@ -29714,14 +29972,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
29714
29972
  // src/renderers/native/drawings/DrawingSettingsPopup.ts
29715
29973
  var BTN = 30;
29716
29974
  var ICON = 21;
29717
- var STYLE_ID2 = "vela-drawing-popup-styles";
29975
+ var STYLE_ID3 = "vela-drawing-popup-styles";
29718
29976
  var STYLE_REV = "3";
29719
- function ensureStyles2() {
29977
+ function ensureStyles3() {
29720
29978
  if (typeof document === "undefined") return;
29721
- const existing = document.getElementById(STYLE_ID2);
29979
+ const existing = document.getElementById(STYLE_ID3);
29722
29980
  if (existing?.dataset.rev === STYLE_REV) return;
29723
29981
  const s = existing ?? document.createElement("style");
29724
- s.id = STYLE_ID2;
29982
+ s.id = STYLE_ID3;
29725
29983
  s.dataset.rev = STYLE_REV;
29726
29984
  s.textContent = `
29727
29985
  .vela-dpop-btn{background:transparent;color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
@@ -29778,7 +30036,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
29778
30036
  * `onClose` fires on an outside-click dismissal (not a programmatic close). */
29779
30037
  open(drawing, anchor, actions, onClose) {
29780
30038
  this.close();
29781
- ensureStyles2();
30039
+ ensureStyles3();
29782
30040
  this.onClose = onClose ?? null;
29783
30041
  const t = this.theme;
29784
30042
  const schema = drawing.schema();
@@ -30506,7 +30764,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
30506
30764
  this.onCollapse = options.onCollapse ?? (() => {
30507
30765
  });
30508
30766
  this.onDrawingsSync = options.onDrawingsSync ?? null;
30509
- ensureStyles3();
30767
+ ensureStyles4();
30510
30768
  this.root = document.createElement("div");
30511
30769
  this.root.className = "vela-dtb";
30512
30770
  this.styleRoot();
@@ -30989,13 +31247,13 @@ ${overlayScrollbarCss(".vela-sd-pane")}
30989
31247
  }
30990
31248
  };
30991
31249
  var MAGNET_ID = "__magnet__";
30992
- var STYLE_ID3 = "vela-drawing-toolbar-styles";
30993
- function ensureStyles3() {
31250
+ var STYLE_ID4 = "vela-drawing-toolbar-styles";
31251
+ function ensureStyles4() {
30994
31252
  if (typeof document === "undefined") return;
30995
- let s = document.getElementById(STYLE_ID3);
31253
+ let s = document.getElementById(STYLE_ID4);
30996
31254
  if (!s) {
30997
31255
  s = document.createElement("style");
30998
- s.id = STYLE_ID3;
31256
+ s.id = STYLE_ID4;
30999
31257
  document.head.appendChild(s);
31000
31258
  }
31001
31259
  s.textContent = `
@@ -32266,7 +32524,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32266
32524
 
32267
32525
  // src/renderers/native/chrome/AttributionMark.ts
32268
32526
  var ATTRIBUTION_URL = "https://luxalgo.com/vela";
32269
- var STYLE_ID4 = "vela-attribution-styles";
32527
+ var STYLE_ID5 = "vela-attribution-styles";
32270
32528
  var CSS2 = `
32271
32529
  .vela-attribution {
32272
32530
  text-decoration: none;
@@ -32329,11 +32587,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32329
32587
  [data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
32330
32588
  [data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
32331
32589
  `;
32332
- function ensureStyles4(doc) {
32333
- let s = doc.getElementById(STYLE_ID4);
32590
+ function ensureStyles5(doc) {
32591
+ let s = doc.getElementById(STYLE_ID5);
32334
32592
  if (!s) {
32335
32593
  s = doc.createElement("style");
32336
- s.id = STYLE_ID4;
32594
+ s.id = STYLE_ID5;
32337
32595
  doc.head.appendChild(s);
32338
32596
  }
32339
32597
  s.textContent = CSS2;
@@ -32362,7 +32620,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32362
32620
  return el;
32363
32621
  }
32364
32622
  function createAttributionMark(doc, background) {
32365
- ensureStyles4(doc);
32623
+ ensureStyles5(doc);
32366
32624
  const a = doc.createElement("a");
32367
32625
  a.className = "vela-attribution";
32368
32626
  a.href = ATTRIBUTION_URL;
@@ -32874,6 +33132,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32874
33132
  // total right-gutter width (master column + merged-scale columns)
32875
33133
  this.paneControls = null;
32876
33134
  // per-pane hover button cluster (top-right)
33135
+ this.axisScaleButtons = null;
33136
+ // A/L hover buttons at the bottom of each pane's price scale
32877
33137
  this.paneActionCbs = /* @__PURE__ */ new Set();
32878
33138
  // ── data window (crosshair OHLC + per-series readout, pulled by host panels) ──
32879
33139
  this.hoverLogical = null;
@@ -32917,7 +33177,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
32917
33177
  this.priceStyleCbs = /* @__PURE__ */ new Set();
32918
33178
  this.tableOverlays = /* @__PURE__ */ new Map();
32919
33179
  this.name = "native";
32920
- this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
33180
+ this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
32921
33181
  /** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
32922
33182
  * so moving onto the button doesn't count as leaving). */
32923
33183
  this.onScrollProximityMove = (e) => {
@@ -33033,6 +33293,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33033
33293
  case "highlights":
33034
33294
  this.scene.highlights = sanitizeHighlights(value);
33035
33295
  break;
33296
+ case "sessionZones":
33297
+ this.scene.sessionZones = sanitizeSessionZones(value);
33298
+ break;
33036
33299
  case "gridlines":
33037
33300
  this.scene.showGrid = Boolean(value);
33038
33301
  break;
@@ -33149,6 +33412,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33149
33412
  return this.scene.indicatorZOrder();
33150
33413
  case "highlights":
33151
33414
  return this.scene.highlights;
33415
+ case "sessionZones":
33416
+ return this.scene.sessionZones;
33152
33417
  case "gridlines":
33153
33418
  return this.scene.showGrid;
33154
33419
  case "axisLabels":
@@ -33358,7 +33623,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33358
33623
  baselineLevel: s.baseline.baselineLevel
33359
33624
  };
33360
33625
  })(),
33361
- series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale }
33626
+ series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale },
33627
+ sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor }
33362
33628
  };
33363
33629
  }
33364
33630
  /**
@@ -33440,6 +33706,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33440
33706
  width: next2.baseline.width,
33441
33707
  baselineLevel: next2.baseline.baselineLevel
33442
33708
  };
33709
+ s.sessions = { premarketColor: next2.sessions.premarketColor, postmarketColor: next2.sessions.postmarketColor };
33443
33710
  this.setPriceStyle(next2.series.style);
33444
33711
  this.scene.candleOverride = candleOverrideFor(this.scene.priceStyle, s.chartTypes);
33445
33712
  this.scene.baselineValue = next2.series.baseline;
@@ -33453,6 +33720,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33453
33720
  }
33454
33721
  this.inputsUI?.setTheme(this.theme);
33455
33722
  this.paneControls?.setTheme(this.theme);
33723
+ this.axisScaleButtons?.setTheme(this.theme);
33456
33724
  this.userDrawings?.setTheme(this.chromeTheme());
33457
33725
  this.refreshScrollButtonTheme();
33458
33726
  this.refreshAttributionColor();
@@ -33791,7 +34059,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33791
34059
  this.surfaceBackground = theme.background;
33792
34060
  this.surfaceTextColor = theme.textColor;
33793
34061
  this.wrapper = document.createElement("div");
33794
- Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair" });
34062
+ Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair", userSelect: "none", webkitUserSelect: "none" });
33795
34063
  applyChromeTokens(this.wrapper, this.chromeTheme());
33796
34064
  this.volumeCanvas = document.createElement("canvas");
33797
34065
  Object.assign(this.volumeCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
@@ -33960,6 +34228,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
33960
34228
  this.emitPaneAction({ type: "maximize", paneId, maximized });
33961
34229
  }
33962
34230
  });
34231
+ this.axisScaleButtons = new AxisScaleButtons(this.plot, theme, {
34232
+ panes: () => this.axisScaleViews(),
34233
+ rightAxis: () => this.rightAxisW,
34234
+ onToggleAuto: (paneId) => this.togglePaneAuto(paneId),
34235
+ onToggleLog: (paneId) => {
34236
+ const pane = this.scene.panes.get(paneId);
34237
+ if (pane) this.setPaneLog(paneId, !paneLogScale(this.scene, pane));
34238
+ }
34239
+ });
33963
34240
  this.resizeObserver = new ResizeObserver(() => this.resize());
33964
34241
  this.resizeObserver.observe(this.wrapper);
33965
34242
  this.watchDpr();
@@ -34036,6 +34313,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34036
34313
  this.applyBackground();
34037
34314
  this.inputsUI.setTheme(theme);
34038
34315
  this.paneControls?.setTheme(this.theme);
34316
+ this.axisScaleButtons?.setTheme(this.theme);
34039
34317
  this.settingsDialog?.refreshConfig(this.getConfig());
34040
34318
  this.syncThemeControl();
34041
34319
  this.settingsDialog?.setTheme(this.theme);
@@ -34084,6 +34362,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34084
34362
  this.liveRegion = null;
34085
34363
  this.inputsUI?.destroy();
34086
34364
  this.paneControls?.destroy();
34365
+ this.axisScaleButtons?.destroy();
34087
34366
  for (const overlay of this.tableOverlays.values()) overlay.destroy();
34088
34367
  this.tableOverlays.clear();
34089
34368
  this.resizeObserver?.disconnect();
@@ -35478,6 +35757,25 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35478
35757
  invert: paneInvert(this.scene, p)
35479
35758
  }));
35480
35759
  }
35760
+ /** Thin projection of the panes for the axis A/L hover buttons (top-to-bottom order). */
35761
+ axisScaleViews() {
35762
+ return this.scene.orderedPanes().map((p) => ({
35763
+ id: p.id,
35764
+ top: p.bounds.top,
35765
+ height: p.bounds.height,
35766
+ collapsed: p.collapsed,
35767
+ auto: p.manualScale == null,
35768
+ log: paneLogScale(this.scene, p)
35769
+ }));
35770
+ }
35771
+ /** Toggle one pane's autoscale (the A axis button): off freezes the current window into
35772
+ * manual mode, on drops it — the per-pane twin of the chart-level `autoScale` feature. */
35773
+ togglePaneAuto(paneId) {
35774
+ const pane = this.scene.panes.get(paneId);
35775
+ if (!pane) return;
35776
+ pane.manualScale = pane.manualScale == null ? { ...pane.scale } : null;
35777
+ this.scheduler?.invalidate(4 /* Full */);
35778
+ }
35481
35779
  /** Set one pane's axis mode. The price pane routes to the scene-level setting (persisted +
35482
35780
  * keyboard shortcuts); a study pane keeps its own, so panes never affect each other. */
35483
35781
  setPaneScaleMode(paneId, mode) {
@@ -35489,19 +35787,18 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35489
35787
  }
35490
35788
  this.scheduler?.invalidate(4 /* Full */);
35491
35789
  }
35492
- /** Set one pane's logarithmic flag. Clears that pane's frozen (manual) window so the new
35493
- * space re-fits, and routes the price pane to the scene-level flag. */
35790
+ /** Set one pane's logarithmic flag; the price pane routes to the scene-level flag.
35791
+ * Log and auto are INDEPENDENT: a frozen (manual) window is kept — its price bounds
35792
+ * stay put and only its `log` tag flips, so the same range re-renders in the new
35793
+ * space instead of snapping back to autoscale. */
35494
35794
  setPaneLog(paneId, log) {
35495
- if (paneId === PRICE_PANE_ID) {
35496
- this.scene.logScale = log;
35497
- const price = this.scene.orderedPanes().find((p) => p.kind === "price");
35498
- if (price) price.manualScale = null;
35499
- } else {
35500
- const p = this.scene.panes.get(paneId);
35501
- if (!p) return;
35502
- p.logScale = log;
35503
- p.manualScale = null;
35795
+ const pane = paneId === PRICE_PANE_ID ? this.scene.orderedPanes().find((p) => p.kind === "price") : this.scene.panes.get(paneId);
35796
+ if (paneId === PRICE_PANE_ID) this.scene.logScale = log;
35797
+ else {
35798
+ if (!pane) return;
35799
+ pane.logScale = log;
35504
35800
  }
35801
+ if (pane?.manualScale) pane.manualScale.log = log;
35505
35802
  this.scheduler?.invalidate(4 /* Full */);
35506
35803
  }
35507
35804
  /** Flip one pane's axis (high at the bottom). The price pane routes to the scene-level flag
@@ -35600,6 +35897,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35600
35897
  }
35601
35898
  this.inputsUI?.reposition();
35602
35899
  this.paneControls?.reposition();
35900
+ this.axisScaleButtons?.reposition();
35603
35901
  this.repositionScrollButton();
35604
35902
  this.positionAttribution();
35605
35903
  this.publishPricePaneBounds();
@@ -35618,6 +35916,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35618
35916
  }
35619
35917
  this.inputsUI?.reposition();
35620
35918
  this.paneControls?.reposition();
35919
+ this.axisScaleButtons?.reposition();
35621
35920
  this.repositionScrollButton();
35622
35921
  this.positionAttribution();
35623
35922
  this.publishPricePaneBounds();
@@ -35825,6 +36124,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35825
36124
  }
35826
36125
  return out.sort((a, b) => a.from - b.from);
35827
36126
  }
36127
+ function sanitizeSessionZones(value) {
36128
+ if (value == null || typeof value !== "object") return null;
36129
+ const v = value;
36130
+ const windows = (raw) => {
36131
+ if (!Array.isArray(raw)) return [];
36132
+ const out = [];
36133
+ for (const w of raw) {
36134
+ if (!Array.isArray(w)) continue;
36135
+ const from = Number(w[0]);
36136
+ const to = Number(w[1]);
36137
+ if (Number.isFinite(from) && Number.isFinite(to) && to > from) out.push([from, to]);
36138
+ }
36139
+ return out.sort((a, b) => a[0] - b[0]);
36140
+ };
36141
+ return { pre: windows(v.pre), post: windows(v.post) };
36142
+ }
35828
36143
  function clamp012(v) {
35829
36144
  return Math.max(0, Math.min(1, v));
35830
36145
  }
@@ -37703,6 +38018,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
37703
38018
  exports.getDrawingType = getDrawingType;
37704
38019
  exports.getNativeIndicator = getNativeIndicator;
37705
38020
  exports.iconMarkup = iconMarkup;
38021
+ exports.inputVisible = inputVisible;
37706
38022
  exports.legendActions = legendActions;
37707
38023
  exports.nativeIndicatorDescriptors = nativeIndicatorDescriptors;
37708
38024
  exports.nativeIndicatorTypes = nativeIndicatorTypes;