@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
package/dist/index.cjs CHANGED
@@ -9896,7 +9896,7 @@ function attachChromeTooltip(anchor, opts) {
9896
9896
  } else {
9897
9897
  const left = Math.min(a.left - h.left, Math.max(0, h.width - tip.offsetWidth - 4));
9898
9898
  tip.style.left = `${Math.max(0, left)}px`;
9899
- tip.style.top = `${a.bottom - h.top + 6}px`;
9899
+ tip.style.top = opts.placement === "above" ? `${Math.max(0, a.top - h.top - tip.offsetHeight - 6)}px` : `${a.bottom - h.top + 6}px`;
9900
9900
  }
9901
9901
  };
9902
9902
  const arm = (e) => {
@@ -10022,6 +10022,23 @@ var MENU_CSS = `
10022
10022
  background: var(--vela-hover-strong);
10023
10023
  color: var(--vela-fg-bright);
10024
10024
  }
10025
+ /* Checkmark mode (context/action menus): every row reserves the leading mark column so
10026
+ labels align; a checked row fills it with a \u2713 and brightens its ink \u2014 the row surface
10027
+ stays free for the hover wash. */
10028
+ .vela-menu-item .vela-menu-mark {
10029
+ width: 14px;
10030
+ flex: none;
10031
+ display: inline-flex;
10032
+ align-items: center;
10033
+ justify-content: center;
10034
+ }
10035
+ .vela-menu-item .vela-menu-mark .vela-icon {
10036
+ width: 14px;
10037
+ height: 14px;
10038
+ font-size: 14px;
10039
+ color: var(--vela-fg-bright);
10040
+ }
10041
+ .vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
10025
10042
  /* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
10026
10043
  same control language as the settings dialog's toggles. */
10027
10044
  .vela-menu-switch {
@@ -10090,6 +10107,7 @@ var Surface = class _Surface {
10090
10107
  this.host = opts.host;
10091
10108
  this.onSelect = opts.onSelect;
10092
10109
  this.onFavorite = opts.onFavorite;
10110
+ this.checkmarks = opts.checkmarks === true;
10093
10111
  this.positioner = doc.createElement("div");
10094
10112
  this.positioner.className = "vela-ui-layer";
10095
10113
  this.list = doc.createElement("ul");
@@ -10168,6 +10186,7 @@ var Surface = class _Surface {
10168
10186
  build() {
10169
10187
  const doc = this.doc;
10170
10188
  this.list.replaceChildren();
10189
+ const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
10171
10190
  for (const item of this.items) {
10172
10191
  if (item.separatorBefore) {
10173
10192
  const sep = doc.createElement("li");
@@ -10181,7 +10200,16 @@ var Surface = class _Surface {
10181
10200
  li.dataset.veiId = item.id;
10182
10201
  if (item.toggle) {
10183
10202
  li.dataset.toggle = "1";
10184
- } else if (!branch && item.checked) {
10203
+ }
10204
+ if (markable) {
10205
+ const mark = doc.createElement("span");
10206
+ mark.className = "vela-menu-mark";
10207
+ if (!branch && !item.toggle && item.checked) {
10208
+ mark.appendChild(iconEl("check", doc));
10209
+ li.dataset.checkmark = "1";
10210
+ }
10211
+ li.appendChild(mark);
10212
+ } else if (!branch && !item.toggle && item.checked) {
10185
10213
  li.dataset.checked = "1";
10186
10214
  }
10187
10215
  if (item.icon) li.appendChild(iconEl(item.icon, doc));
@@ -10230,6 +10258,7 @@ var Surface = class _Surface {
10230
10258
  placement: "right-start",
10231
10259
  onSelect: this.onSelect,
10232
10260
  onFavorite: this.onFavorite,
10261
+ checkmarks: this.checkmarks,
10233
10262
  id: `${this.mid}--${item.id}`
10234
10263
  });
10235
10264
  sub.setItems(item.submenu ?? []);
@@ -10254,7 +10283,8 @@ var Menu = class {
10254
10283
  triggerId: opts.triggerId,
10255
10284
  id: opts.id,
10256
10285
  minWidth: opts.minWidth,
10257
- onFavorite: opts.onFavorite
10286
+ onFavorite: opts.onFavorite,
10287
+ checkmarks: opts.checkmarks
10258
10288
  });
10259
10289
  this.root.setItems(opts.items);
10260
10290
  }
@@ -10282,6 +10312,13 @@ var Menu = class {
10282
10312
  }
10283
10313
  };
10284
10314
 
10315
+ // src/core/model/inputs.ts
10316
+ function inputVisible(when, values) {
10317
+ if (!when) return true;
10318
+ const conds = Array.isArray(when) ? when : [when];
10319
+ return conds.every((c) => c.anyOf ? c.anyOf.some((x) => x === values[c.key]) : values[c.key] === c.equals);
10320
+ }
10321
+
10285
10322
  // src/ui/tokens.ts
10286
10323
  var STATIC_ID = "vela-ui-tokens";
10287
10324
  var STATIC_DECLS = Object.entries(STATIC_TOKENS).map(([k, v]) => ` ${k}: ${v};`).join("\n");
@@ -10290,8 +10327,13 @@ var STATIC_CSS = `
10290
10327
  ${STATIC_DECLS}
10291
10328
  font-family: var(--vela-font, -apple-system, system-ui, sans-serif);
10292
10329
  box-sizing: border-box;
10330
+ /* Chrome text (titles, buttons, menus, readouts) is UI, not copy \u2014 never selectable. */
10331
+ user-select: none;
10332
+ -webkit-user-select: none;
10293
10333
  }
10294
10334
  .vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
10335
+ /* Text ENTRY is the one exception: selection is part of editing. */
10336
+ .vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
10295
10337
  .vela-icon { display: inline-flex; align-items: center; flex: none; }
10296
10338
  .vela-icon svg { display: block; }
10297
10339
  `;
@@ -11288,6 +11330,9 @@ var NUMBER_CSS = `
11288
11330
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
11289
11331
  -moz-appearance: textfield;
11290
11332
  appearance: textfield;
11333
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
11334
+ user-select: text;
11335
+ -webkit-user-select: text;
11291
11336
  }
11292
11337
  .vela-num input::-webkit-inner-spin-button, .vela-num input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
11293
11338
  .vela-num input:hover { border-color: var(--vela-fg-muted); }
@@ -11500,6 +11545,9 @@ var TEXT_CSS = `
11500
11545
  overflow: hidden;
11501
11546
  text-overflow: ellipsis;
11502
11547
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
11548
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
11549
+ user-select: text;
11550
+ -webkit-user-select: text;
11503
11551
  }
11504
11552
  .vela-text-field:hover { border-color: var(--vela-fg-muted); }
11505
11553
  .vela-text-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
@@ -11593,6 +11641,9 @@ var TEXTAREA_CSS = `
11593
11641
  resize: vertical;
11594
11642
  outline: none;
11595
11643
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
11644
+ /* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
11645
+ user-select: text;
11646
+ -webkit-user-select: text;
11596
11647
  }
11597
11648
  .vela-textarea-field:hover { border-color: var(--vela-fg-muted); }
11598
11649
  .vela-textarea-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
@@ -12121,6 +12172,8 @@ var FIELD_CSS = `
12121
12172
  font-size: 14px;
12122
12173
  }
12123
12174
  .vela-field-label[data-size='sm'] { font-size: inherit; opacity: 0.85; }
12175
+ /* Titles are fully inert: spans, not label[for] \u2014 native labels propagate :hover and
12176
+ clicks onto their control, and both belong to the input alone. */
12124
12177
  .vela-field-cell { display: flex; align-items: center; gap: 8px; justify-self: start; }
12125
12178
  .vela-field-bool { display: flex; align-items: center; gap: 8px; min-height: 22px; }
12126
12179
  .vela-field-stacked { display: flex; flex-direction: column; gap: 8px; }
@@ -12179,11 +12232,17 @@ function fieldSeparator() {
12179
12232
  return el;
12180
12233
  }
12181
12234
  function labelEl(text, opts) {
12182
- const el = opts.id ? document.createElement("label") : document.createElement("span");
12183
- if (opts.id && el instanceof HTMLLabelElement) el.htmlFor = opts.id;
12235
+ const el = document.createElement("span");
12184
12236
  el.className = "vela-field-label";
12185
12237
  if (opts.size) el.dataset.size = opts.size;
12186
12238
  el.textContent = text;
12239
+ const id = opts.id;
12240
+ if (id) {
12241
+ el.id = `${id}--title`;
12242
+ queueMicrotask(() => {
12243
+ el.ownerDocument.getElementById(id)?.setAttribute("aria-labelledby", el.id);
12244
+ });
12245
+ }
12187
12246
  return el;
12188
12247
  }
12189
12248
  function asList(control) {
@@ -12212,9 +12271,7 @@ function fieldRow(opts) {
12212
12271
  it.className = "vela-field-inline-item";
12213
12272
  if (item.toggleFirst) it.style.flexDirection = "row-reverse";
12214
12273
  if (item.label) {
12215
- const lbl = labelEl(item.label, { id: item.id, size });
12216
- if (item.toggleFirst) lbl.style.cursor = "pointer";
12217
- it.appendChild(lbl);
12274
+ it.appendChild(labelEl(item.label, { id: item.id, size }));
12218
12275
  }
12219
12276
  if (item.fit) it.appendChild(item.control);
12220
12277
  else {
@@ -12239,7 +12296,7 @@ function fieldRow(opts) {
12239
12296
  tone: "bright",
12240
12297
  onChange: (v) => opts.toggle?.onChange(v)
12241
12298
  });
12242
- wrap2.append(sw.el, labelEl(opts.label, { size }));
12299
+ wrap2.append(sw.el, labelEl(opts.label, { id: opts.toggle?.id ?? opts.id, size }));
12243
12300
  if (opts.info) wrap2.appendChild(opts.info);
12244
12301
  if (opts.toggle?.get) {
12245
12302
  const get = opts.toggle.get;
@@ -12278,7 +12335,7 @@ function fieldRow(opts) {
12278
12335
  if (controls.length > 0) dimControls(controls, v);
12279
12336
  }
12280
12337
  });
12281
- left.append(sw.el, labelEl(opts.label, { size }));
12338
+ left.append(sw.el, labelEl(opts.label, { id: opts.toggle.id ?? opts.id, size }));
12282
12339
  wrap.appendChild(left);
12283
12340
  if (controls.length > 0) dimControls(controls, opts.toggle.checked);
12284
12341
  if (opts.toggle.get) {
@@ -12432,6 +12489,8 @@ var IndicatorInputsDialog = class {
12432
12489
  this.row = null;
12433
12490
  this.snapshot = null;
12434
12491
  this.dialogTips = [];
12492
+ /** Re-applies every `when` gate against current values; set while the dialog is open. */
12493
+ this.refreshVisibility = null;
12435
12494
  this.choicePop = null;
12436
12495
  this.calendarPop = null;
12437
12496
  this.calendarAnchor = null;
@@ -12494,14 +12553,17 @@ var IndicatorInputsDialog = class {
12494
12553
  tabs.style.cssText = `display:flex;gap:12px;padding:0 20px;border-bottom:1px solid ${border};flex:0 0 auto;`;
12495
12554
  const tabEls = [];
12496
12555
  const bodies = [];
12556
+ const gates = [];
12557
+ let active = 0;
12497
12558
  const activate = (idx) => {
12559
+ active = idx;
12498
12560
  for (let i = 0; i < tabEls.length; i += 1) {
12499
- const active = i === idx;
12561
+ const on = i === idx;
12500
12562
  const el = tabEls[i];
12501
- el.style.borderBottomColor = active ? fg : "transparent";
12502
- el.style.color = active ? "inherit" : "var(--vela-fg-muted)";
12503
- el.classList.toggle("vela-ind-tab-active", active);
12504
- bodies[i].style.display = active ? "grid" : "none";
12563
+ el.style.borderBottomColor = on ? fg : "transparent";
12564
+ el.style.color = on ? "inherit" : "var(--vela-fg-muted)";
12565
+ el.classList.toggle("vela-ind-tab-active", on);
12566
+ bodies[i].style.display = on ? "grid" : "none";
12505
12567
  }
12506
12568
  };
12507
12569
  for (const [idx, def] of tabDefs.entries()) {
@@ -12512,11 +12574,24 @@ var IndicatorInputsDialog = class {
12512
12574
  tab.addEventListener("click", () => activate(idx));
12513
12575
  tabs.appendChild(tab);
12514
12576
  tabEls.push(tab);
12515
- bodies.push(this.buildTabBody(def.inputs, row));
12577
+ bodies.push(this.buildTabBody(def.inputs, row, gates));
12578
+ if (def.inputs.every((d) => d.when)) {
12579
+ gates.push({ el: tab, visible: (bag) => def.inputs.some((d) => inputVisible(d.when, bag)) });
12580
+ }
12516
12581
  }
12517
12582
  ui.body.appendChild(tabs);
12518
12583
  for (const b of bodies) ui.body.appendChild(b);
12519
12584
  activate(0);
12585
+ const refresh = () => {
12586
+ const bag = valuesBag(row);
12587
+ for (const g of gates) g.el.style.display = g.visible(bag) ? "" : "none";
12588
+ if (tabEls[active]?.style.display === "none") {
12589
+ const next = tabEls.findIndex((el) => el.style.display !== "none");
12590
+ if (next >= 0) activate(next);
12591
+ }
12592
+ };
12593
+ this.refreshVisibility = refresh;
12594
+ refresh();
12520
12595
  const onBackdropDown = (e) => {
12521
12596
  if (e.target !== ui.backdrop && e.target !== ui.positioner) return;
12522
12597
  if (isPopoverOpen() || eventDismissedPopover(e)) {
@@ -12536,16 +12611,27 @@ var IndicatorInputsDialog = class {
12536
12611
  }
12537
12612
  /** One tab's scrollable body — one shared grid so every group's controls share a
12538
12613
  * left edge (a 100px number field lines up with a wider session/time pair). */
12539
- buildTabBody(inputs, row) {
12614
+ buildTabBody(inputs, row, gates) {
12540
12615
  const body = document.createElement("div");
12541
12616
  body.style.cssText = inputDialogBodyStyle(this.host.mobile());
12542
12617
  for (const group of groupInputs(inputs)) {
12543
12618
  const section = document.createElement("div");
12544
12619
  section.style.cssText = "display:contents;";
12620
+ let header = null;
12545
12621
  if (group.name) {
12546
- section.appendChild(fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 }));
12622
+ header = fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 });
12623
+ section.appendChild(header);
12624
+ }
12625
+ for (const inputRow of group.rows) {
12626
+ const el = this.buildInputRow(section, inputRow, row);
12627
+ if (inputRow.some((d) => d.when)) {
12628
+ gates.push({ el, visible: (bag) => rowVisible(inputRow, bag) });
12629
+ }
12630
+ }
12631
+ if (header && group.rows.every((r) => r.every((d) => d.when))) {
12632
+ const rows = group.rows;
12633
+ gates.push({ el: header, visible: (bag) => rows.some((r) => rowVisible(r, bag)) });
12547
12634
  }
12548
- for (const inputRow of group.rows) this.buildInputRow(section, inputRow, row);
12549
12635
  body.appendChild(section);
12550
12636
  }
12551
12637
  return body;
@@ -12553,6 +12639,7 @@ var IndicatorInputsDialog = class {
12553
12639
  close() {
12554
12640
  document.removeEventListener("keydown", this.onDialogKey);
12555
12641
  closeOpenPopovers();
12642
+ this.refreshVisibility = null;
12556
12643
  this.choicePop = null;
12557
12644
  this.calendarPop = null;
12558
12645
  this.calendarAnchor = null;
@@ -12582,11 +12669,21 @@ var IndicatorInputsDialog = class {
12582
12669
  }
12583
12670
  this.close();
12584
12671
  }
12672
+ /** Write one edit through: store it, notify the host, and re-apply the `when` gates. */
12673
+ commit(row, key, value) {
12674
+ row.values[key] = value;
12675
+ this.host.onChange?.({ indicatorId: row.id, key, value });
12676
+ this.refreshVisibility?.();
12677
+ }
12585
12678
  /** One settings row (or several `inline=` inputs) placed into a section's grid. */
12586
12679
  buildInputRow(grid, decls, row) {
12587
12680
  const idOf = (inp) => `vela-inp-${row.id}-${inp.key}`;
12681
+ const append = (el) => {
12682
+ grid.appendChild(el);
12683
+ return el;
12684
+ };
12588
12685
  if (decls.length > 1) {
12589
- grid.appendChild(fieldRow({
12686
+ return append(fieldRow({
12590
12687
  label: "",
12591
12688
  inline: decls.map((d) => ({
12592
12689
  label: nameOf(d) || void 0,
@@ -12597,14 +12694,13 @@ var IndicatorInputsDialog = class {
12597
12694
  })),
12598
12695
  info: [...decls].reverse().find((d) => d.tooltip)?.tooltip ? this.infoButton([...decls].reverse().find((d) => d.tooltip).tooltip) : void 0
12599
12696
  }));
12600
- return;
12601
12697
  }
12602
12698
  const lead = decls[0];
12603
12699
  const id = idOf(lead);
12604
12700
  const info = lead.tooltip ? this.infoButton(lead.tooltip) : void 0;
12605
12701
  if (lead.type === "bool") {
12606
12702
  const current = Boolean(row.values[lead.key] ?? lead.defval);
12607
- grid.appendChild(fieldRow({
12703
+ return append(fieldRow({
12608
12704
  label: nameOf(lead),
12609
12705
  id,
12610
12706
  bool: true,
@@ -12612,26 +12708,21 @@ var IndicatorInputsDialog = class {
12612
12708
  toggle: {
12613
12709
  id,
12614
12710
  checked: current,
12615
- onChange: (v) => {
12616
- row.values[lead.key] = v;
12617
- this.host.onChange?.({ indicatorId: row.id, key: lead.key, value: v });
12618
- }
12711
+ onChange: (v) => this.commit(row, lead.key, v)
12619
12712
  }
12620
12713
  }));
12621
- return;
12622
12714
  }
12623
12715
  if (lead.type === "text_area") {
12624
- grid.appendChild(fieldRow({
12716
+ return append(fieldRow({
12625
12717
  label: nameOf(lead),
12626
12718
  id,
12627
12719
  stacked: true,
12628
12720
  info,
12629
12721
  control: this.buildControl(row, lead, id)
12630
12722
  }));
12631
- return;
12632
12723
  }
12633
12724
  const fit = lead.type === "color" || lead.type === "session" || lead.type === "time";
12634
- grid.appendChild(fieldRow({
12725
+ return append(fieldRow({
12635
12726
  label: nameOf(lead),
12636
12727
  id,
12637
12728
  fit,
@@ -12643,10 +12734,7 @@ var IndicatorInputsDialog = class {
12643
12734
  /** Build the typed control for one input, committing edits live via `onChange`. */
12644
12735
  buildControl(row, inp, id) {
12645
12736
  const current = row.values[inp.key] ?? inp.defval;
12646
- const emit = (value) => {
12647
- row.values[inp.key] = value;
12648
- this.host.onChange?.({ indicatorId: row.id, key: inp.key, value });
12649
- };
12737
+ const emit = (value) => this.commit(row, inp.key, value);
12650
12738
  if (inp.type === "bool") {
12651
12739
  return buildFieldControl({ kind: "switch", id, checked: Boolean(current), onChange: (v) => emit(v) }).el;
12652
12740
  }
@@ -13067,6 +13155,14 @@ function tabInputs(inputs) {
13067
13155
  }
13068
13156
  return names.map((name) => ({ name, inputs: byTab.get(name) }));
13069
13157
  }
13158
+ function rowVisible(decls, bag) {
13159
+ return decls.some((d) => inputVisible(d.when, bag));
13160
+ }
13161
+ function valuesBag(row) {
13162
+ const bag = {};
13163
+ for (const inp of row.inputs) bag[inp.key] = row.values[inp.key] ?? inp.defval;
13164
+ return bag;
13165
+ }
13070
13166
  function groupInputs(inputs) {
13071
13167
  const order = [];
13072
13168
  const byGroup = /* @__PURE__ */ new Map();
@@ -13529,6 +13625,8 @@ var InputsUI = class {
13529
13625
  host: this.container,
13530
13626
  items,
13531
13627
  placement: "bottom-start",
13628
+ // Right-click action menu — checked state reads as a leading ✓ (see the kit Menu).
13629
+ checkmarks: true,
13532
13630
  onSelect: (itemId) => {
13533
13631
  if (itemId !== "values" || !this.rowMenuId) return;
13534
13632
  const target = this.rows.get(this.rowMenuId);
@@ -13864,6 +13962,8 @@ var InputsUI = class {
13864
13962
  syncRowActions(row) {
13865
13963
  const open2 = row.highlighted;
13866
13964
  row.controlsEl.style.display = open2 || row.hidden ? "inline-flex" : "none";
13965
+ if (open2) row.el.appendChild(row.statusEl);
13966
+ else row.el.insertBefore(row.statusEl, row.valuesEl);
13867
13967
  for (const child of Array.from(row.controlsEl.children)) {
13868
13968
  if (!(child instanceof HTMLElement) || child === row.eyeEl) continue;
13869
13969
  if (child === row.extrasEl) {
@@ -14107,6 +14207,148 @@ var PaneControls = class {
14107
14207
  }
14108
14208
  };
14109
14209
 
14210
+ // src/renderers/native/chrome/axisLayout.ts
14211
+ var AXIS_MASTER_W = 64;
14212
+ var AXIS_MERGED_W = 56;
14213
+ var PANE_SEPARATOR_PX = 3;
14214
+ function axisColumnX(dataW, column) {
14215
+ return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
14216
+ }
14217
+
14218
+ // src/renderers/native/chrome/AxisScaleButtons.ts
14219
+ var STYLE_ID2 = "vela-axis-scale-buttons";
14220
+ var BTN_PX = 18;
14221
+ var BTN_GAP = 4;
14222
+ var AXIS_BORDER = 1;
14223
+ var CLUSTER_W = AXIS_MASTER_W - AXIS_BORDER;
14224
+ var PAD_TOP = 8;
14225
+ var PAD_BOTTOM = 6;
14226
+ var CLUSTER_H = BTN_PX + PAD_TOP + PAD_BOTTOM;
14227
+ var MIN_PANE_H = 48;
14228
+ function ensureStyles2() {
14229
+ if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
14230
+ const st = document.createElement("style");
14231
+ st.id = STYLE_ID2;
14232
+ st.textContent = `
14233
+ .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);}
14234
+ .vela-axis-btn:hover{background:var(--vela-active);color:var(--vela-fg-bright);}
14235
+ .vela-axis-on,.vela-axis-on:hover{background:var(--vela-selected-bg);color:var(--vela-selected-fg);}
14236
+ `;
14237
+ document.head.appendChild(st);
14238
+ }
14239
+ var AxisScaleButtons = class {
14240
+ constructor(plot, theme, deps) {
14241
+ this.plot = plot;
14242
+ this.theme = theme;
14243
+ this.deps = deps;
14244
+ this.tipDisposers = [];
14245
+ this.hoverPaneId = null;
14246
+ /** Reveal over the master scale column of the pane under the cursor (mouse only —
14247
+ * touch has no hover, and a drag-rescale on the axis must not sprout buttons). */
14248
+ this.onPlotMove = (e) => {
14249
+ if (e.pointerType !== "mouse") return;
14250
+ const rect = this.plot.getBoundingClientRect();
14251
+ const x = e.clientX - rect.left;
14252
+ const gutterLeft = rect.width - this.deps.rightAxis();
14253
+ let hit = null;
14254
+ if (x >= gutterLeft && x <= gutterLeft + AXIS_MASTER_W) {
14255
+ const y = e.clientY - rect.top;
14256
+ for (const p of this.deps.panes()) {
14257
+ if (p.collapsed || p.height < MIN_PANE_H) continue;
14258
+ if (y >= p.top && y <= p.top + p.height) {
14259
+ hit = p.id;
14260
+ break;
14261
+ }
14262
+ }
14263
+ }
14264
+ this.setHoverPane(hit);
14265
+ if (hit) this.sync();
14266
+ };
14267
+ this.onPlotLeave = () => this.setHoverPane(null);
14268
+ ensureStyles2();
14269
+ this.cluster = document.createElement("div");
14270
+ Object.assign(this.cluster.style, {
14271
+ position: "absolute",
14272
+ display: "none",
14273
+ justifyContent: "center",
14274
+ alignItems: "center",
14275
+ gap: `${BTN_GAP}px`,
14276
+ padding: `${PAD_TOP}px 0 ${PAD_BOTTOM}px`,
14277
+ width: `${CLUSTER_W}px`,
14278
+ boxSizing: "border-box",
14279
+ zIndex: "6",
14280
+ pointerEvents: "auto"
14281
+ });
14282
+ this.applyTheme();
14283
+ this.autoBtn = this.button("A", "Auto (fits data to screen)", () => this.deps.onToggleAuto(this.hoverPaneId ?? ""));
14284
+ this.logBtn = this.button("L", "Logarithmic scale", () => this.deps.onToggleLog(this.hoverPaneId ?? ""));
14285
+ this.cluster.append(this.autoBtn, this.logBtn);
14286
+ this.plot.appendChild(this.cluster);
14287
+ this.plot.addEventListener("pointermove", this.onPlotMove);
14288
+ this.plot.addEventListener("pointerleave", this.onPlotLeave);
14289
+ }
14290
+ button(label, title, onClick) {
14291
+ const b = document.createElement("button");
14292
+ b.type = "button";
14293
+ b.setAttribute("aria-label", title);
14294
+ b.textContent = label;
14295
+ b.className = "vela-axis-btn";
14296
+ Object.assign(b.style, { width: `${BTN_PX}px`, height: `${BTN_PX}px` });
14297
+ this.tipDisposers.push(attachChromeTooltip(b, {
14298
+ host: this.plot,
14299
+ theme: () => this.theme,
14300
+ text: () => title,
14301
+ placement: "above"
14302
+ }));
14303
+ b.addEventListener("click", (e) => {
14304
+ e.stopPropagation();
14305
+ onClick();
14306
+ this.sync();
14307
+ });
14308
+ return b;
14309
+ }
14310
+ /** Re-tint the chip when the plot background/border change (theme swap, config edit). */
14311
+ setTheme(theme) {
14312
+ this.theme = theme;
14313
+ this.applyTheme();
14314
+ }
14315
+ /** The strip is SOLID chart background: the buttons cover axis labels, never blend with them. */
14316
+ applyTheme() {
14317
+ this.cluster.style.background = this.theme.background;
14318
+ }
14319
+ setHoverPane(paneId) {
14320
+ if (this.hoverPaneId === paneId) return;
14321
+ this.hoverPaneId = paneId;
14322
+ this.reposition();
14323
+ }
14324
+ /** Re-place (or hide) the cluster after a hover or pane-layout change. */
14325
+ reposition() {
14326
+ const pane = this.hoverPaneId ? this.deps.panes().find((p) => p.id === this.hoverPaneId) : void 0;
14327
+ if (!pane || pane.collapsed || pane.height < MIN_PANE_H) {
14328
+ this.cluster.style.display = "none";
14329
+ return;
14330
+ }
14331
+ this.cluster.style.right = `${this.deps.rightAxis() - AXIS_MASTER_W}px`;
14332
+ this.cluster.style.top = `${pane.top + pane.height - CLUSTER_H}px`;
14333
+ this.cluster.style.display = "flex";
14334
+ this.sync();
14335
+ }
14336
+ /** Reflect the hovered pane's current auto/log state on the buttons. */
14337
+ sync() {
14338
+ const pane = this.deps.panes().find((p) => p.id === this.hoverPaneId);
14339
+ if (!pane) return;
14340
+ this.autoBtn.classList.toggle("vela-axis-on", pane.auto);
14341
+ this.logBtn.classList.toggle("vela-axis-on", pane.log);
14342
+ }
14343
+ destroy() {
14344
+ this.plot.removeEventListener("pointermove", this.onPlotMove);
14345
+ this.plot.removeEventListener("pointerleave", this.onPlotLeave);
14346
+ for (const dispose of this.tipDisposers) dispose();
14347
+ this.tipDisposers.length = 0;
14348
+ this.cluster.remove();
14349
+ }
14350
+ };
14351
+
14110
14352
  // src/renderers/shared/TableOverlay.ts
14111
14353
  var SIZE_PX3 = {
14112
14354
  auto: 13,
@@ -14464,6 +14706,8 @@ var BASELINE_BOTTOM_LINE = BEARISH;
14464
14706
  var BASELINE_FILL_ALPHA = 0.25;
14465
14707
  var BASELINE_FILL_ALPHA_FAR = 0.05;
14466
14708
  var BASELINE_LEVEL_DEFAULT = 50;
14709
+ var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
14710
+ var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
14467
14711
  function defaultChartStyle() {
14468
14712
  return {
14469
14713
  chartTypes: {},
@@ -14494,7 +14738,8 @@ function defaultChartStyle() {
14494
14738
  bottomFillColor2: null,
14495
14739
  width: 2,
14496
14740
  baselineLevel: BASELINE_LEVEL_DEFAULT
14497
- }
14741
+ },
14742
+ sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE }
14498
14743
  };
14499
14744
  }
14500
14745
  var LINE_STYLES = ["solid", "dashed", "dotted"];
@@ -14628,6 +14873,7 @@ function mergeConfig(base, patch) {
14628
14873
  const area = asObject(p.area);
14629
14874
  const baseline = asObject(p.baseline);
14630
14875
  const series = asObject(p.series);
14876
+ const sessions = asObject(p.sessions);
14631
14877
  const stacking = asObject(p.stacking);
14632
14878
  const stackSeries = asObject(stacking.series);
14633
14879
  return {
@@ -14720,6 +14966,10 @@ function mergeConfig(base, patch) {
14720
14966
  baseline: series.baseline === null ? null : isNum(series.baseline) ? series.baseline : base.series.baseline,
14721
14967
  spacing: isNum(series.spacing) ? clampSpacing(series.spacing) : base.series.spacing
14722
14968
  },
14969
+ sessions: {
14970
+ premarketColor: isColor(sessions.premarketColor) ? sessions.premarketColor : base.sessions.premarketColor,
14971
+ postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor
14972
+ },
14723
14973
  stacking: {
14724
14974
  candles: isNum(stacking.candles) ? stacking.candles : base.stacking.candles,
14725
14975
  // Additive like `chartTypes`: a patch names only the ids it carries; the rest keep
@@ -14966,6 +15216,8 @@ var SceneGraph = class {
14966
15216
  this.tradeMarkers = defaultTradeMarkersState();
14967
15217
  /** Renderer-owned shaded time bands (session highlighting), behind grid + data. */
14968
15218
  this.highlights = [];
15219
+ /** Pre/post-market bands pushed by the host (`sessionZones` feature); null ⇒ no sessions. */
15220
+ this.sessionZones = null;
14969
15221
  /** Draw-order key of the price candles, relative to indicator series z (see `seriesZ`).
14970
15222
  * Indicators with z below this draw BEHIND the candles; at/above draw in front.
14971
15223
  * Default 0 with indicators mounting at z < 0 ⇒ the price reads on top of every overlay,
@@ -15000,6 +15252,15 @@ var SceneGraph = class {
15000
15252
  if (!this.orderedCache) this.orderedCache = [...this.panes.values()].sort((a, b) => a.order - b.order);
15001
15253
  return this.orderedCache;
15002
15254
  }
15255
+ /** The session zones resolved into colored bands (pre/post-market washes from the
15256
+ * config's session colors) — consumed by the same painting path as {@link highlights}. */
15257
+ sessionHighlightBands() {
15258
+ if (!this.sessionZones) return [];
15259
+ const out = [];
15260
+ for (const [from, to] of this.sessionZones.pre) out.push({ from, to, color: this.style.sessions.premarketColor });
15261
+ for (const [from, to] of this.sessionZones.post) out.push({ from, to, color: this.style.sessions.postmarketColor });
15262
+ return out;
15263
+ }
15003
15264
  indicatorsForPane(paneId) {
15004
15265
  const out = [];
15005
15266
  for (const model of this.indicators.values()) if (model.paneId === paneId) out.push(model);
@@ -15930,10 +16191,12 @@ var WebGL2Backend = class {
15930
16191
  }
15931
16192
  }
15932
16193
  }
15933
- /** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height). */
16194
+ /** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
16195
+ * Session-zone washes (pre/post-market) paint first, host highlights on top. */
15934
16196
  emitHighlights(b, scene, pane, coords) {
15935
- if (scene.highlights.length === 0) return;
15936
- for (const band of scene.highlights) {
16197
+ const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
16198
+ if (bands.length === 0) return;
16199
+ for (const band of bands) {
15937
16200
  const x1 = coords.timeToX(band.from);
15938
16201
  const x2 = coords.timeToX(band.to);
15939
16202
  if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
@@ -17907,10 +18170,12 @@ var Canvas2dBackend = class {
17907
18170
  ctx.fillStyle = bg.color;
17908
18171
  ctx.fillRect(x1, pane.bounds.top, x2 - x1, pane.bounds.height);
17909
18172
  }
17910
- /** Renderer-owned session highlight bands: full-height (all panes), behind grid + data. */
18173
+ /** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
18174
+ * Session-zone washes (pre/post-market) paint first, host highlights on top. */
17911
18175
  drawHighlights(ctx, scene, coords) {
17912
- if (scene.highlights.length === 0) return;
17913
- for (const band of scene.highlights) {
18176
+ const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
18177
+ if (bands.length === 0) return;
18178
+ for (const band of bands) {
17914
18179
  const x1 = coords.timeToX(band.from);
17915
18180
  const x2 = coords.timeToX(band.to);
17916
18181
  if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
@@ -18684,14 +18949,6 @@ var DrawingSceneRenderer = class {
18684
18949
  }
18685
18950
  };
18686
18951
 
18687
- // src/renderers/native/chrome/axisLayout.ts
18688
- var AXIS_MASTER_W = 64;
18689
- var AXIS_MERGED_W = 56;
18690
- var PANE_SEPARATOR_PX = 3;
18691
- function axisColumnX(dataW, column) {
18692
- return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
18693
- }
18694
-
18695
18952
  // src/renderers/native/chrome/ChromeRenderer.ts
18696
18953
  var ChromeRenderer = class {
18697
18954
  constructor() {
@@ -19574,6 +19831,7 @@ var SettingsDialog = class {
19574
19831
  for (const hr of hs.rows) {
19575
19832
  if (hr.kind === "heading") body.append(this.sectionTitle(hr.label));
19576
19833
  else if (hr.kind === "toggle") body.append(this.boolRow(hr.label, hr.get(), (v) => hr.set(v)));
19834
+ else if (hr.kind === "color") body.append(this.colorRow(hr.label, hr.get(), (v) => hr.set(v)));
19577
19835
  else body.append(this.selectRowLabeled(hr.label, hr.get(), hr.options.map((o) => [o, o]), (v) => hr.set(v)));
19578
19836
  }
19579
19837
  }
@@ -22298,14 +22556,14 @@ function btn(label, primary, onClick) {
22298
22556
  // src/renderers/native/drawings/DrawingSettingsPopup.ts
22299
22557
  var BTN = 30;
22300
22558
  var ICON = 21;
22301
- var STYLE_ID2 = "vela-drawing-popup-styles";
22559
+ var STYLE_ID3 = "vela-drawing-popup-styles";
22302
22560
  var STYLE_REV = "3";
22303
- function ensureStyles2() {
22561
+ function ensureStyles3() {
22304
22562
  if (typeof document === "undefined") return;
22305
- const existing = document.getElementById(STYLE_ID2);
22563
+ const existing = document.getElementById(STYLE_ID3);
22306
22564
  if (existing?.dataset.rev === STYLE_REV) return;
22307
22565
  const s = existing ?? document.createElement("style");
22308
- s.id = STYLE_ID2;
22566
+ s.id = STYLE_ID3;
22309
22567
  s.dataset.rev = STYLE_REV;
22310
22568
  s.textContent = `
22311
22569
  .vela-dpop-btn{background:transparent;color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
@@ -22362,7 +22620,7 @@ var DrawingSettingsPopup = class {
22362
22620
  * `onClose` fires on an outside-click dismissal (not a programmatic close). */
22363
22621
  open(drawing, anchor, actions, onClose) {
22364
22622
  this.close();
22365
- ensureStyles2();
22623
+ ensureStyles3();
22366
22624
  this.onClose = onClose ?? null;
22367
22625
  const t = this.theme;
22368
22626
  const schema = drawing.schema();
@@ -23090,7 +23348,7 @@ var DrawingToolbar = class {
23090
23348
  this.onCollapse = options.onCollapse ?? (() => {
23091
23349
  });
23092
23350
  this.onDrawingsSync = options.onDrawingsSync ?? null;
23093
- ensureStyles3();
23351
+ ensureStyles4();
23094
23352
  this.root = document.createElement("div");
23095
23353
  this.root.className = "vela-dtb";
23096
23354
  this.styleRoot();
@@ -23573,13 +23831,13 @@ var DrawingToolbar = class {
23573
23831
  }
23574
23832
  };
23575
23833
  var MAGNET_ID = "__magnet__";
23576
- var STYLE_ID3 = "vela-drawing-toolbar-styles";
23577
- function ensureStyles3() {
23834
+ var STYLE_ID4 = "vela-drawing-toolbar-styles";
23835
+ function ensureStyles4() {
23578
23836
  if (typeof document === "undefined") return;
23579
- let s = document.getElementById(STYLE_ID3);
23837
+ let s = document.getElementById(STYLE_ID4);
23580
23838
  if (!s) {
23581
23839
  s = document.createElement("style");
23582
- s.id = STYLE_ID3;
23840
+ s.id = STYLE_ID4;
23583
23841
  document.head.appendChild(s);
23584
23842
  }
23585
23843
  s.textContent = `
@@ -24850,7 +25108,7 @@ var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path
24850
25108
 
24851
25109
  // src/renderers/native/chrome/AttributionMark.ts
24852
25110
  var ATTRIBUTION_URL = "https://luxalgo.com/vela";
24853
- var STYLE_ID4 = "vela-attribution-styles";
25111
+ var STYLE_ID5 = "vela-attribution-styles";
24854
25112
  var CSS2 = `
24855
25113
  .vela-attribution {
24856
25114
  text-decoration: none;
@@ -24913,11 +25171,11 @@ var CSS2 = `
24913
25171
  [data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
24914
25172
  [data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
24915
25173
  `;
24916
- function ensureStyles4(doc) {
24917
- let s = doc.getElementById(STYLE_ID4);
25174
+ function ensureStyles5(doc) {
25175
+ let s = doc.getElementById(STYLE_ID5);
24918
25176
  if (!s) {
24919
25177
  s = doc.createElement("style");
24920
- s.id = STYLE_ID4;
25178
+ s.id = STYLE_ID5;
24921
25179
  doc.head.appendChild(s);
24922
25180
  }
24923
25181
  s.textContent = CSS2;
@@ -24946,7 +25204,7 @@ function createCustomMark(doc, html, background) {
24946
25204
  return el;
24947
25205
  }
24948
25206
  function createAttributionMark(doc, background) {
24949
- ensureStyles4(doc);
25207
+ ensureStyles5(doc);
24950
25208
  const a = doc.createElement("a");
24951
25209
  a.className = "vela-attribution";
24952
25210
  a.href = ATTRIBUTION_URL;
@@ -25458,6 +25716,8 @@ var NativeRenderer = class {
25458
25716
  // total right-gutter width (master column + merged-scale columns)
25459
25717
  this.paneControls = null;
25460
25718
  // per-pane hover button cluster (top-right)
25719
+ this.axisScaleButtons = null;
25720
+ // A/L hover buttons at the bottom of each pane's price scale
25461
25721
  this.paneActionCbs = /* @__PURE__ */ new Set();
25462
25722
  // ── data window (crosshair OHLC + per-series readout, pulled by host panels) ──
25463
25723
  this.hoverLogical = null;
@@ -25501,7 +25761,7 @@ var NativeRenderer = class {
25501
25761
  this.priceStyleCbs = /* @__PURE__ */ new Set();
25502
25762
  this.tableOverlays = /* @__PURE__ */ new Map();
25503
25763
  this.name = "native";
25504
- 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"];
25764
+ 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"];
25505
25765
  /** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
25506
25766
  * so moving onto the button doesn't count as leaving). */
25507
25767
  this.onScrollProximityMove = (e) => {
@@ -25617,6 +25877,9 @@ var NativeRenderer = class {
25617
25877
  case "highlights":
25618
25878
  this.scene.highlights = sanitizeHighlights(value);
25619
25879
  break;
25880
+ case "sessionZones":
25881
+ this.scene.sessionZones = sanitizeSessionZones(value);
25882
+ break;
25620
25883
  case "gridlines":
25621
25884
  this.scene.showGrid = Boolean(value);
25622
25885
  break;
@@ -25733,6 +25996,8 @@ var NativeRenderer = class {
25733
25996
  return this.scene.indicatorZOrder();
25734
25997
  case "highlights":
25735
25998
  return this.scene.highlights;
25999
+ case "sessionZones":
26000
+ return this.scene.sessionZones;
25736
26001
  case "gridlines":
25737
26002
  return this.scene.showGrid;
25738
26003
  case "axisLabels":
@@ -25942,7 +26207,8 @@ var NativeRenderer = class {
25942
26207
  baselineLevel: s.baseline.baselineLevel
25943
26208
  };
25944
26209
  })(),
25945
- series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale }
26210
+ series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale },
26211
+ sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor }
25946
26212
  };
25947
26213
  }
25948
26214
  /**
@@ -26024,6 +26290,7 @@ var NativeRenderer = class {
26024
26290
  width: next.baseline.width,
26025
26291
  baselineLevel: next.baseline.baselineLevel
26026
26292
  };
26293
+ s.sessions = { premarketColor: next.sessions.premarketColor, postmarketColor: next.sessions.postmarketColor };
26027
26294
  this.setPriceStyle(next.series.style);
26028
26295
  this.scene.candleOverride = candleOverrideFor(this.scene.priceStyle, s.chartTypes);
26029
26296
  this.scene.baselineValue = next.series.baseline;
@@ -26037,6 +26304,7 @@ var NativeRenderer = class {
26037
26304
  }
26038
26305
  this.inputsUI?.setTheme(this.theme);
26039
26306
  this.paneControls?.setTheme(this.theme);
26307
+ this.axisScaleButtons?.setTheme(this.theme);
26040
26308
  this.userDrawings?.setTheme(this.chromeTheme());
26041
26309
  this.refreshScrollButtonTheme();
26042
26310
  this.refreshAttributionColor();
@@ -26375,7 +26643,7 @@ var NativeRenderer = class {
26375
26643
  this.surfaceBackground = theme.background;
26376
26644
  this.surfaceTextColor = theme.textColor;
26377
26645
  this.wrapper = document.createElement("div");
26378
- Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair" });
26646
+ Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair", userSelect: "none", webkitUserSelect: "none" });
26379
26647
  applyChromeTokens(this.wrapper, this.chromeTheme());
26380
26648
  this.volumeCanvas = document.createElement("canvas");
26381
26649
  Object.assign(this.volumeCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
@@ -26544,6 +26812,15 @@ var NativeRenderer = class {
26544
26812
  this.emitPaneAction({ type: "maximize", paneId, maximized });
26545
26813
  }
26546
26814
  });
26815
+ this.axisScaleButtons = new AxisScaleButtons(this.plot, theme, {
26816
+ panes: () => this.axisScaleViews(),
26817
+ rightAxis: () => this.rightAxisW,
26818
+ onToggleAuto: (paneId) => this.togglePaneAuto(paneId),
26819
+ onToggleLog: (paneId) => {
26820
+ const pane = this.scene.panes.get(paneId);
26821
+ if (pane) this.setPaneLog(paneId, !paneLogScale(this.scene, pane));
26822
+ }
26823
+ });
26547
26824
  this.resizeObserver = new ResizeObserver(() => this.resize());
26548
26825
  this.resizeObserver.observe(this.wrapper);
26549
26826
  this.watchDpr();
@@ -26620,6 +26897,7 @@ var NativeRenderer = class {
26620
26897
  this.applyBackground();
26621
26898
  this.inputsUI.setTheme(theme);
26622
26899
  this.paneControls?.setTheme(this.theme);
26900
+ this.axisScaleButtons?.setTheme(this.theme);
26623
26901
  this.settingsDialog?.refreshConfig(this.getConfig());
26624
26902
  this.syncThemeControl();
26625
26903
  this.settingsDialog?.setTheme(this.theme);
@@ -26668,6 +26946,7 @@ var NativeRenderer = class {
26668
26946
  this.liveRegion = null;
26669
26947
  this.inputsUI?.destroy();
26670
26948
  this.paneControls?.destroy();
26949
+ this.axisScaleButtons?.destroy();
26671
26950
  for (const overlay of this.tableOverlays.values()) overlay.destroy();
26672
26951
  this.tableOverlays.clear();
26673
26952
  this.resizeObserver?.disconnect();
@@ -28062,6 +28341,25 @@ var NativeRenderer = class {
28062
28341
  invert: paneInvert(this.scene, p)
28063
28342
  }));
28064
28343
  }
28344
+ /** Thin projection of the panes for the axis A/L hover buttons (top-to-bottom order). */
28345
+ axisScaleViews() {
28346
+ return this.scene.orderedPanes().map((p) => ({
28347
+ id: p.id,
28348
+ top: p.bounds.top,
28349
+ height: p.bounds.height,
28350
+ collapsed: p.collapsed,
28351
+ auto: p.manualScale == null,
28352
+ log: paneLogScale(this.scene, p)
28353
+ }));
28354
+ }
28355
+ /** Toggle one pane's autoscale (the A axis button): off freezes the current window into
28356
+ * manual mode, on drops it — the per-pane twin of the chart-level `autoScale` feature. */
28357
+ togglePaneAuto(paneId) {
28358
+ const pane = this.scene.panes.get(paneId);
28359
+ if (!pane) return;
28360
+ pane.manualScale = pane.manualScale == null ? { ...pane.scale } : null;
28361
+ this.scheduler?.invalidate(4 /* Full */);
28362
+ }
28065
28363
  /** Set one pane's axis mode. The price pane routes to the scene-level setting (persisted +
28066
28364
  * keyboard shortcuts); a study pane keeps its own, so panes never affect each other. */
28067
28365
  setPaneScaleMode(paneId, mode) {
@@ -28073,19 +28371,18 @@ var NativeRenderer = class {
28073
28371
  }
28074
28372
  this.scheduler?.invalidate(4 /* Full */);
28075
28373
  }
28076
- /** Set one pane's logarithmic flag. Clears that pane's frozen (manual) window so the new
28077
- * space re-fits, and routes the price pane to the scene-level flag. */
28374
+ /** Set one pane's logarithmic flag; the price pane routes to the scene-level flag.
28375
+ * Log and auto are INDEPENDENT: a frozen (manual) window is kept — its price bounds
28376
+ * stay put and only its `log` tag flips, so the same range re-renders in the new
28377
+ * space instead of snapping back to autoscale. */
28078
28378
  setPaneLog(paneId, log) {
28079
- if (paneId === PRICE_PANE_ID) {
28080
- this.scene.logScale = log;
28081
- const price = this.scene.orderedPanes().find((p) => p.kind === "price");
28082
- if (price) price.manualScale = null;
28083
- } else {
28084
- const p = this.scene.panes.get(paneId);
28085
- if (!p) return;
28086
- p.logScale = log;
28087
- p.manualScale = null;
28379
+ const pane = paneId === PRICE_PANE_ID ? this.scene.orderedPanes().find((p) => p.kind === "price") : this.scene.panes.get(paneId);
28380
+ if (paneId === PRICE_PANE_ID) this.scene.logScale = log;
28381
+ else {
28382
+ if (!pane) return;
28383
+ pane.logScale = log;
28088
28384
  }
28385
+ if (pane?.manualScale) pane.manualScale.log = log;
28089
28386
  this.scheduler?.invalidate(4 /* Full */);
28090
28387
  }
28091
28388
  /** Flip one pane's axis (high at the bottom). The price pane routes to the scene-level flag
@@ -28184,6 +28481,7 @@ var NativeRenderer = class {
28184
28481
  }
28185
28482
  this.inputsUI?.reposition();
28186
28483
  this.paneControls?.reposition();
28484
+ this.axisScaleButtons?.reposition();
28187
28485
  this.repositionScrollButton();
28188
28486
  this.positionAttribution();
28189
28487
  this.publishPricePaneBounds();
@@ -28202,6 +28500,7 @@ var NativeRenderer = class {
28202
28500
  }
28203
28501
  this.inputsUI?.reposition();
28204
28502
  this.paneControls?.reposition();
28503
+ this.axisScaleButtons?.reposition();
28205
28504
  this.repositionScrollButton();
28206
28505
  this.positionAttribution();
28207
28506
  this.publishPricePaneBounds();
@@ -28409,6 +28708,22 @@ function sanitizeHighlights(value) {
28409
28708
  }
28410
28709
  return out.sort((a, b) => a.from - b.from);
28411
28710
  }
28711
+ function sanitizeSessionZones(value) {
28712
+ if (value == null || typeof value !== "object") return null;
28713
+ const v = value;
28714
+ const windows = (raw) => {
28715
+ if (!Array.isArray(raw)) return [];
28716
+ const out = [];
28717
+ for (const w of raw) {
28718
+ if (!Array.isArray(w)) continue;
28719
+ const from = Number(w[0]);
28720
+ const to = Number(w[1]);
28721
+ if (Number.isFinite(from) && Number.isFinite(to) && to > from) out.push([from, to]);
28722
+ }
28723
+ return out.sort((a, b) => a[0] - b[0]);
28724
+ };
28725
+ return { pre: windows(v.pre), post: windows(v.post) };
28726
+ }
28412
28727
  function clamp012(v) {
28413
28728
  return Math.max(0, Math.min(1, v));
28414
28729
  }
@@ -29078,6 +29393,7 @@ exports.drawingTypes = drawingTypes;
29078
29393
  exports.getDrawingType = getDrawingType;
29079
29394
  exports.getNativeIndicator = getNativeIndicator;
29080
29395
  exports.iconMarkup = iconMarkup;
29396
+ exports.inputVisible = inputVisible;
29081
29397
  exports.legendActions = legendActions;
29082
29398
  exports.nativeIndicatorDescriptors = nativeIndicatorDescriptors;
29083
29399
  exports.nativeIndicatorTypes = nativeIndicatorTypes;