@luxalgo/vela 0.7.3 → 0.7.5

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 (37) hide show
  1. package/dist/{DataProvider-BALR0qPq.d.ts → DataProvider-DLeFs1gE.d.ts} +1 -1
  2. package/dist/{DataProvider-D5uMiJYz.d.cts → DataProvider-Dut6lXGM.d.cts} +1 -1
  3. package/dist/{chunk-2EO74GIE.js → chunk-MCLI6B3S.js} +261 -68
  4. package/dist/{chunk-DH2VF5X7.js → chunk-MNG5XPLV.js} +6 -1
  5. package/dist/{contributions-BbhbZd3c.d.ts → contributions-CEkgJKTU.d.ts} +7 -3
  6. package/dist/{contributions-CcBFe-91.d.cts → contributions-DYtiRtES.d.cts} +7 -3
  7. package/dist/index.cjs +261 -68
  8. package/dist/index.d.cts +23 -6
  9. package/dist/index.d.ts +23 -6
  10. package/dist/index.js +1 -1
  11. package/dist/{options-C7b_Qssu.d.cts → options-DNBoMKkX.d.cts} +10 -1
  12. package/dist/{options-C7b_Qssu.d.ts → options-DNBoMKkX.d.ts} +10 -1
  13. package/dist/{plugin-Cnix5ZBh.d.ts → plugin-CLn--oP6.d.ts} +3 -3
  14. package/dist/{plugin-D6MlX4--.d.cts → plugin-DtkeQVAO.d.cts} +3 -3
  15. package/dist/plugin.d.cts +4 -4
  16. package/dist/plugin.d.ts +4 -4
  17. package/dist/providers/binance.d.cts +2 -2
  18. package/dist/providers/binance.d.ts +2 -2
  19. package/dist/providers/coinbase.d.cts +2 -2
  20. package/dist/providers/coinbase.d.ts +2 -2
  21. package/dist/providers/hyperliquid.d.cts +2 -2
  22. package/dist/providers/hyperliquid.d.ts +2 -2
  23. package/dist/{statusline-model-D8NL7o4L.d.ts → statusline-model-DypGXjtr.d.ts} +3 -3
  24. package/dist/{statusline-model-DUKt7_TQ.d.cts → statusline-model-gA__yaog.d.cts} +3 -3
  25. package/dist/ui.d.cts +1 -1
  26. package/dist/ui.d.ts +1 -1
  27. package/dist/vela.global.js +261 -68
  28. package/dist/vela.global.min.js +53 -49
  29. package/dist/widget.cjs +266 -68
  30. package/dist/widget.d.cts +6 -6
  31. package/dist/widget.d.ts +6 -6
  32. package/dist/widget.js +3 -3
  33. package/dist/workspace.cjs +266 -68
  34. package/dist/workspace.d.cts +5 -5
  35. package/dist/workspace.d.ts +5 -5
  36. package/dist/workspace.js +2 -2
  37. package/package.json +1 -1
package/dist/widget.cjs CHANGED
@@ -5115,6 +5115,17 @@ function hasOwnCandlePaint(style) {
5115
5115
  for (const t of chartTypes()) if (t.id === style) return (t.basePainting ?? "candles") === "candles";
5116
5116
  return false;
5117
5117
  }
5118
+ var CANDLE_OVERRIDE_KEYS = [
5119
+ "candleUpColor",
5120
+ "candleDownColor",
5121
+ "candleBodyVisible",
5122
+ "candleBorderVisible",
5123
+ "candleBorderUpColor",
5124
+ "candleBorderDownColor",
5125
+ "candleWickVisible",
5126
+ "candleWickUpColor",
5127
+ "candleWickDownColor"
5128
+ ];
5118
5129
  function candleOverrideFor(style, bags) {
5119
5130
  if (!hasOwnCandlePaint(style)) return null;
5120
5131
  const bag = bags[style] ?? {};
@@ -5186,7 +5197,7 @@ function clampLevel(v) {
5186
5197
  function clampSpacing(v) {
5187
5198
  return v < 0.1 ? 0.1 : v > 10 ? 10 : v;
5188
5199
  }
5189
- function factoryResetConfig(factory) {
5200
+ function factoryResetConfig(factory, priceStyle = factory.series.style) {
5190
5201
  const bag = {};
5191
5202
  for (const t of chartTypes()) {
5192
5203
  const section = t.settings;
@@ -5208,10 +5219,16 @@ function factoryResetConfig(factory) {
5208
5219
  if (!section.instances) addRows(section.rows);
5209
5220
  bag[t.id] = defaults2;
5210
5221
  }
5222
+ for (const t of chartTypes()) {
5223
+ if (!hasOwnCandlePaint(t.id)) continue;
5224
+ const defaults2 = bag[t.id] ?? {};
5225
+ for (const key of CANDLE_OVERRIDE_KEYS) defaults2[key] = null;
5226
+ bag[t.id] = defaults2;
5227
+ }
5211
5228
  for (const [typeId, vals] of Object.entries(factory.chartTypes)) {
5212
5229
  bag[typeId] = { ...bag[typeId] ?? {}, ...vals };
5213
5230
  }
5214
- return { ...factory, chartTypes: bag };
5231
+ return { ...factory, chartTypes: bag, series: { ...factory.series, style: priceStyle } };
5215
5232
  }
5216
5233
  function mergeConfig(base, patch) {
5217
5234
  const p = asObject(patch);
@@ -18653,6 +18670,42 @@ function presetToRange(preset, bars) {
18653
18670
  return { from: Math.max(first, from), to: last };
18654
18671
  }
18655
18672
 
18673
+ // src/core/marks/visibility.ts
18674
+ function markGroupOwnVisible(choices, groupId, groups) {
18675
+ const chosen = choices[groupId];
18676
+ if (typeof chosen === "boolean") return chosen;
18677
+ return groups.find((g) => g.id === groupId)?.visible !== false;
18678
+ }
18679
+ function markGroupVisible(choices, groupId, groups) {
18680
+ if (groupId === void 0) return true;
18681
+ const seen = /* @__PURE__ */ new Set();
18682
+ let id = groupId;
18683
+ while (id !== void 0 && !seen.has(id)) {
18684
+ seen.add(id);
18685
+ if (!markGroupOwnVisible(choices, id, groups)) return false;
18686
+ const parent = groups.find((g) => g.id === id)?.parent;
18687
+ id = parent !== void 0 && groups.some((g) => g.id === parent) ? parent : void 0;
18688
+ }
18689
+ return true;
18690
+ }
18691
+ function markGroupRows(groups) {
18692
+ const ids = new Set(groups.map((g) => g.id));
18693
+ const out = [];
18694
+ const placed = /* @__PURE__ */ new Set();
18695
+ const place = (group, depth) => {
18696
+ if (placed.has(group.id)) return;
18697
+ placed.add(group.id);
18698
+ out.push({ group, depth });
18699
+ for (const child of groups) if (child.parent === group.id && child.id !== group.id) place(child, depth + 1);
18700
+ };
18701
+ for (const g of groups) {
18702
+ const parentKnown = g.parent !== void 0 && ids.has(g.parent) && g.parent !== g.id;
18703
+ if (!parentKnown) place(g, 0);
18704
+ }
18705
+ for (const g of groups) place(g, 0);
18706
+ return out;
18707
+ }
18708
+
18656
18709
  // src/core/marks/MarksController.ts
18657
18710
  var MarksController = class {
18658
18711
  constructor(renderer, events) {
@@ -18697,6 +18750,7 @@ var MarksController = class {
18697
18750
  defineGroup(group) {
18698
18751
  if (!group || typeof group.id !== "string" || group.id.length === 0) throw new Error("[vela] marks.defineGroup: `id` must be a non-empty string");
18699
18752
  if (typeof group.label !== "string") throw new Error(`[vela] marks.defineGroup: group "${group.id}" needs a string \`label\``);
18753
+ if (group.parent !== void 0 && (typeof group.parent !== "string" || group.parent.length === 0)) throw new Error(`[vela] marks.defineGroup: group "${group.id}" has a \`parent\` that is not a group id`);
18700
18754
  this.groups.set(group.id, { ...group });
18701
18755
  this.sync();
18702
18756
  }
@@ -18716,12 +18770,16 @@ var MarksController = class {
18716
18770
  }
18717
18771
  this.renderer.applyFeature("marks", { groups: { [id]: visible } });
18718
18772
  }
18719
- /** A group's effective visibility: the user's (persisted) choice, else the group's declared default, else visible. */
18773
+ /**
18774
+ * A group's effective visibility: its own switch — the user's (persisted) choice, else
18775
+ * the declared default, else visible — AND every ancestor's, so a child under a
18776
+ * switched-off parent reads hidden whatever its own choice says.
18777
+ */
18720
18778
  isGroupVisible(id) {
18721
18779
  const state = this.renderer.readFeature("marks");
18722
- const chosen = state?.groups?.[id];
18723
- if (typeof chosen === "boolean") return chosen;
18724
- return this.groups.get(id)?.visible !== false;
18780
+ const groups = {};
18781
+ for (const [gid, v] of Object.entries(state?.groups ?? {})) if (typeof v === "boolean") groups[gid] = v;
18782
+ return markGroupVisible(groups, id, [...this.groups.values()]);
18725
18783
  }
18726
18784
  destroy() {
18727
18785
  for (const unsub of this.subs) unsub();
@@ -25710,11 +25768,8 @@ function mergeMarksState(base, patch) {
25710
25768
  for (const [id, v] of Object.entries(g)) if (typeof v === "boolean") groups[id] = v;
25711
25769
  return { visible: typeof p.visible === "boolean" ? p.visible : base.visible, groups };
25712
25770
  }
25713
- function markGroupVisible(state, groupId, groups) {
25714
- if (groupId === void 0) return true;
25715
- const chosen = state.groups[groupId];
25716
- if (typeof chosen === "boolean") return chosen;
25717
- return groups.find((g) => g.id === groupId)?.visible !== false;
25771
+ function markGroupVisible2(state, groupId, groups) {
25772
+ return markGroupVisible(state.groups, groupId, groups);
25718
25773
  }
25719
25774
 
25720
25775
  // src/renderers/native/core/SceneGraph.ts
@@ -27726,6 +27781,32 @@ function clusterMarks(marks, barTimes, intervalMs2, hidden) {
27726
27781
  }
27727
27782
  return out;
27728
27783
  }
27784
+ function foldOverlappingClusters(clusters, xOf, bucketPx = MARK_CLUSTER_PX) {
27785
+ const byGroup = /* @__PURE__ */ new Map();
27786
+ for (const c of clusters) {
27787
+ const list = byGroup.get(c.group);
27788
+ if (list) list.push(c);
27789
+ else byGroup.set(c.group, [c]);
27790
+ }
27791
+ const out = [];
27792
+ for (const list of byGroup.values()) {
27793
+ list.sort((a, b) => a.bar - b.bar);
27794
+ const runs = [];
27795
+ let anchorX = Number.NaN;
27796
+ for (const c of list) {
27797
+ const x = xOf(c.bar);
27798
+ const run = runs[runs.length - 1];
27799
+ if (run && Number.isFinite(x) && Number.isFinite(anchorX) && x - anchorX < bucketPx) {
27800
+ run.marks.push(...c.marks);
27801
+ } else {
27802
+ runs.push({ key: c.key, bar: c.bar, group: c.group, marks: [...c.marks] });
27803
+ anchorX = x;
27804
+ }
27805
+ }
27806
+ out.push(...runs);
27807
+ }
27808
+ return out;
27809
+ }
27729
27810
  function groupRank(groups, clusters) {
27730
27811
  const rank = /* @__PURE__ */ new Map();
27731
27812
  groups.forEach((g, i) => rank.set(g.id, i));
@@ -27735,7 +27816,7 @@ function groupRank(groups, clusters) {
27735
27816
  return (group) => group === void 0 ? Number.MAX_SAFE_INTEGER : rank.get(group) ?? Number.MAX_SAFE_INTEGER - 1;
27736
27817
  }
27737
27818
  function layoutMarkLane(input) {
27738
- const clusters = clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden);
27819
+ const clusters = foldOverlappingClusters(clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden), input.xOf);
27739
27820
  const rankOf = groupRank(input.groups, clusters);
27740
27821
  const byBar = /* @__PURE__ */ new Map();
27741
27822
  for (const c of clusters) {
@@ -27864,13 +27945,29 @@ function paintMarkLane(ctx, layout, deps) {
27864
27945
  const img = deps.icons.get(mark.glyph.icon, ink, symbolPx, deps.dpr);
27865
27946
  if (img) ctx.drawImage(img, center.x - symbolPx / 2, center.y - symbolPx / 2, symbolPx, symbolPx);
27866
27947
  } else if (mark.glyph.letter) {
27948
+ const letter = mark.glyph.letter.slice(0, 2);
27867
27949
  ctx.fillStyle = ink;
27868
- ctx.font = `600 ${Math.round(size * 0.58)}px ${deps.fontFamily}`;
27869
- ctx.fillText(mark.glyph.letter.slice(0, 2), center.x, center.y + 0.5);
27950
+ let px = letterFontPx(size, letter);
27951
+ ctx.font = `600 ${px}px ${deps.fontFamily}`;
27952
+ if (letter.length > 1) {
27953
+ px = fitLetterPx(px, ctx.measureText(letter).width, symbolInnerWidth(shape, size));
27954
+ ctx.font = `600 ${px}px ${deps.fontFamily}`;
27955
+ }
27956
+ ctx.fillText(letter, center.x, center.y + 0.5);
27870
27957
  }
27871
27958
  }
27872
27959
  ctx.restore();
27873
27960
  }
27961
+ function letterFontPx(size, letter) {
27962
+ return Math.round(size * (letter.length > 1 ? 0.38 : 0.58));
27963
+ }
27964
+ function symbolInnerWidth(shape, size) {
27965
+ return (shape === "pin" ? size * 0.82 : size) - 3;
27966
+ }
27967
+ function fitLetterPx(px, width, inner) {
27968
+ if (!(width > inner) || !(width > 0)) return px;
27969
+ return Math.max(4, Math.floor(px * inner / width));
27970
+ }
27874
27971
  function traceShape(ctx, shape, x, y, size) {
27875
27972
  const r = size / 2;
27876
27973
  ctx.beginPath();
@@ -28072,7 +28169,7 @@ var ChromeRenderer = class {
28072
28169
  this.markLayout = layoutMarkLane({
28073
28170
  marks: scene.timelineMarks,
28074
28171
  groups: scene.markGroups,
28075
- hidden: (groupId) => !markGroupVisible(scene.marks, groupId, scene.markGroups),
28172
+ hidden: (groupId) => !markGroupVisible2(scene.marks, groupId, scene.markGroups),
28076
28173
  barTimes: this.barTimes(scene),
28077
28174
  intervalMs: coords.barInterval,
28078
28175
  xOf: (bar) => coords.logicalToX(bar),
@@ -28762,6 +28859,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
28762
28859
  muted and non-interactive. Applied to each row's children so it survives display:contents;
28763
28860
  !important beats the inline opacity on labels. */
28764
28861
  .vela-sd-soft>*{opacity:0.4 !important;pointer-events:none !important;}
28862
+ /* A mark group nested under a parent group on the Events tab: indented one step per level
28863
+ (--vela-sd-depth). The indent rides the first child (the switch) as a MARGIN \u2014 padding
28864
+ would push the switch's own tick out of its box. */
28865
+ .vela-sd-nested>*:first-child{margin-left:calc(var(--vela-sd-depth,1)*24px);}
28765
28866
  /* \u2500\u2500 mobile presentation (.vela-sd-mobile on the scrim; structural sizes are inline in open()) \u2500\u2500
28766
28867
  The tab rail becomes a burger-opened overlay sidebar; the group TOC becomes a sticky
28767
28868
  row of horizontally scrollable tabs; the instance strip scrolls instead of wrapping;
@@ -28810,6 +28911,8 @@ var SettingsDialog = class {
28810
28911
  this.hostSections = [];
28811
28912
  /** The timeline-mark groups (defined + named by marks) — one checkbox each on the Events tab. */
28812
28913
  this.markGroups = [];
28914
+ /** A group's OWN switch (what its checkbox shows) — a child under an off parent keeps its own state. */
28915
+ this.markGroupOwnVisible = () => true;
28813
28916
  this.markGroupVisible = () => true;
28814
28917
  /** The Canvas → Theme row: current app theme + where a pick is raised. The row is a
28815
28918
  * host callback, NOT a config patch — the app theme stays out of the persisted
@@ -28827,6 +28930,9 @@ var SettingsDialog = class {
28827
28930
  this.activeSection = null;
28828
28931
  /** Mobile chrome: fullscreen card, burger-opened section sidebar, TOC as top tabs. */
28829
28932
  this.mobileLayout = false;
28933
+ /** Opens/closes the mobile section sidebar of the CURRENT build (the burger in the
28934
+ * shell header outlives pane rebuilds, so it reaches the rail through here). */
28935
+ this.toggleRail = null;
28830
28936
  /** The visibility policy: setting ids hidden by the host (subtree semantics). */
28831
28937
  this.hiddenSettings = /* @__PURE__ */ new Set();
28832
28938
  if (getComputedStyle(container).position === "static") container.style.position = "relative";
@@ -28836,7 +28942,8 @@ var SettingsDialog = class {
28836
28942
  this.hostSections = sections;
28837
28943
  }
28838
28944
  /** The timeline-mark groups and their current visibility — the Events tab's rows on next open. */
28839
- setMarkGroups(groups, visible) {
28945
+ setMarkGroups(groups, visible, ownVisible = visible) {
28946
+ this.markGroupOwnVisible = ownVisible;
28840
28947
  this.markGroups = groups;
28841
28948
  this.markGroupVisible = visible;
28842
28949
  }
@@ -28902,7 +29009,6 @@ var SettingsDialog = class {
28902
29009
  this.onReset = onReset ?? null;
28903
29010
  ensureControlStyles();
28904
29011
  const mobile = this.mobileLayout;
28905
- let toggleRail = null;
28906
29012
  let burger;
28907
29013
  if (mobile) {
28908
29014
  burger = document.createElement("button");
@@ -28910,8 +29016,83 @@ var SettingsDialog = class {
28910
29016
  burger.className = "vela-sd-burger";
28911
29017
  burger.innerHTML = iconAt("burger", 16);
28912
29018
  burger.title = "Sections";
28913
- burger.addEventListener("click", () => toggleRail?.());
29019
+ burger.addEventListener("click", () => this.toggleRail?.());
28914
29020
  }
29021
+ const ui = new Dialog({
29022
+ host: this.container,
29023
+ title: "Chart settings",
29024
+ // Non-modal: a live-edit dialog must leave the page interactive — a modal
29025
+ // machine locks pointer events on the whole body, killing the chart, the
29026
+ // legend, and the body-portaled popovers (color picker, select lists).
29027
+ modal: false,
29028
+ contained: true,
29029
+ align: "top",
29030
+ draggable: !mobile,
29031
+ flush: true,
29032
+ className: "vela-dialog--settings",
29033
+ headerStart: burger,
29034
+ closeOnBackdrop: true,
29035
+ footer: (foot) => {
29036
+ foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
29037
+ const resetBtn = document.createElement("button");
29038
+ resetBtn.type = "button";
29039
+ resetBtn.textContent = "Reset defaults";
29040
+ resetBtn.className = "vela-sd-btn";
29041
+ resetBtn.addEventListener("click", () => this.onReset?.());
29042
+ foot.appendChild(resetBtn);
29043
+ },
29044
+ // A close signal may only close ITS OWN dialog: the machine reports the exit
29045
+ // asynchronously, so a close-then-reopen in one tick would otherwise see the
29046
+ // old instance's signal tear down the freshly opened one.
29047
+ onOpenChange: (open2) => {
29048
+ if (!open2 && this.ui === ui) this.close();
29049
+ }
29050
+ });
29051
+ if (mobile) ui.positioner.classList.add("vela-sd-mobile");
29052
+ ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
29053
+ this.root = ui.positioner;
29054
+ this.ui = ui;
29055
+ const panes = this.buildContent(ui, config, section, mobile);
29056
+ ui.show();
29057
+ this.layoutPanes(panes);
29058
+ }
29059
+ /**
29060
+ * Re-seed every control of an OPEN dialog from `config`, in place: the shell stays
29061
+ * (no close/open transition), only the tab rail and panes rebuild, landing back on
29062
+ * the current tab. The reset path — the restored values must show without the
29063
+ * dialog re-entering. No-op while closed.
29064
+ */
29065
+ refresh(config) {
29066
+ const ui = this.ui;
29067
+ if (!ui) return;
29068
+ closeOpenPopovers();
29069
+ closeWidthPopover();
29070
+ for (const dispose of this.hintTips) dispose();
29071
+ this.hintTips = [];
29072
+ this.config = config;
29073
+ ui.body.replaceChildren();
29074
+ this.layoutPanes(this.buildContent(ui, config, this.activeSection ?? void 0, this.mobileLayout));
29075
+ }
29076
+ /** Structured chart-type panes (instance strip / group TOC) own their layout and
29077
+ * tag their rows hosts instead; each host gets its own field grid. Runs on a SHOWN
29078
+ * dialog — the grids measure their labels. */
29079
+ layoutPanes(panes) {
29080
+ for (const el of panes) {
29081
+ const hosts = [...el.querySelectorAll("[data-sd-rows-host]")];
29082
+ if (hosts.length === 0) {
29083
+ this.layoutSettingsGrids(el);
29084
+ continue;
29085
+ }
29086
+ for (const h of hosts) this.layoutSettingsGrids(h);
29087
+ }
29088
+ }
29089
+ /**
29090
+ * Build the tab rail + one pane per section into `ui.body` (the linear `body` of
29091
+ * section markers and rows is split afterwards), select `section` (the active
29092
+ * style's own tab when none is asked for), and return the pane elements for
29093
+ * {@link layoutPanes}.
29094
+ */
29095
+ buildContent(ui, config, section, mobile) {
28915
29096
  const body = document.createElement("div");
28916
29097
  body.style.cssText = "display:flex;flex-direction:column;gap:0;";
28917
29098
  const sid = (el, id) => {
@@ -29115,9 +29296,28 @@ var SettingsDialog = class {
29115
29296
  if (this.markGroups.length > 0) {
29116
29297
  body.append(sid(this.section("Events"), MARKS_SETTINGS_ID));
29117
29298
  body.append(sid(this.sectionTitle("Visible events"), MARKS_GROUPS_SETTINGS_ID));
29118
- for (const g of this.markGroups) {
29119
- body.append(sid(this.boolRow(g.label, this.markGroupVisible(g.id), (v) => this.emit({ marks: { groups: { [g.id]: v } } })), markGroupSettingsId(g.id)));
29299
+ const rowsById = /* @__PURE__ */ new Map();
29300
+ const rows = markGroupRows(this.markGroups);
29301
+ const refreshDimming = () => {
29302
+ for (const { group } of rows) {
29303
+ const el = rowsById.get(group.id);
29304
+ if (!el || group.parent === void 0) continue;
29305
+ el.classList.toggle("vela-sd-soft", !this.markGroupVisible(group.parent));
29306
+ }
29307
+ };
29308
+ for (const { group: g, depth } of rows) {
29309
+ const row = this.boolRow(g.label, this.markGroupOwnVisible(g.id), (v) => {
29310
+ this.emit({ marks: { groups: { [g.id]: v } } });
29311
+ refreshDimming();
29312
+ });
29313
+ if (depth > 0) {
29314
+ row.classList.add("vela-sd-nested");
29315
+ row.style.setProperty("--vela-sd-depth", String(depth));
29316
+ }
29317
+ rowsById.set(g.id, row);
29318
+ body.append(sid(row, markGroupSettingsId(g.id)));
29120
29319
  }
29320
+ refreshDimming();
29121
29321
  }
29122
29322
  renderChartTypeSections("end");
29123
29323
  renderHostSections("end");
@@ -29145,8 +29345,8 @@ var SettingsDialog = class {
29145
29345
  if (mobile) {
29146
29346
  railScrim = document.createElement("div");
29147
29347
  railScrim.className = "vela-sd-railscrim";
29148
- railScrim.addEventListener("click", () => toggleRail?.());
29149
- toggleRail = (open2) => {
29348
+ railScrim.addEventListener("click", () => this.toggleRail?.());
29349
+ this.toggleRail = (open2) => {
29150
29350
  const on = open2 ?? !rail.classList.contains("open");
29151
29351
  rail.classList.toggle("open", on);
29152
29352
  railScrim?.classList.toggle("open", on);
@@ -29186,35 +29386,6 @@ var SettingsDialog = class {
29186
29386
  });
29187
29387
  if (hidActive) activate(0);
29188
29388
  };
29189
- const ui = new Dialog({
29190
- host: this.container,
29191
- title: "Chart settings",
29192
- // Non-modal: a live-edit dialog must leave the page interactive — a modal
29193
- // machine locks pointer events on the whole body, killing the chart, the
29194
- // legend, and the body-portaled popovers (color picker, select lists).
29195
- modal: false,
29196
- contained: true,
29197
- align: "top",
29198
- draggable: !mobile,
29199
- flush: true,
29200
- className: "vela-dialog--settings",
29201
- headerStart: burger,
29202
- closeOnBackdrop: true,
29203
- footer: (foot) => {
29204
- foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
29205
- const resetBtn = document.createElement("button");
29206
- resetBtn.type = "button";
29207
- resetBtn.textContent = "Reset defaults";
29208
- resetBtn.className = "vela-sd-btn";
29209
- resetBtn.addEventListener("click", () => this.onReset?.());
29210
- foot.appendChild(resetBtn);
29211
- },
29212
- onOpenChange: (open2) => {
29213
- if (!open2) this.close();
29214
- }
29215
- });
29216
- if (mobile) ui.positioner.classList.add("vela-sd-mobile");
29217
- ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
29218
29389
  const activate = (idx) => {
29219
29390
  panes.forEach((p, i) => {
29220
29391
  p.el.style.display = i === idx ? "block" : "none";
@@ -29223,7 +29394,7 @@ var SettingsDialog = class {
29223
29394
  this.activeSection = panes[idx]?.title ?? null;
29224
29395
  if (mobile) {
29225
29396
  ui.titleEl.textContent = panes[idx]?.title ?? "Chart settings";
29226
- toggleRail?.(false);
29397
+ this.toggleRail?.(false);
29227
29398
  }
29228
29399
  };
29229
29400
  panes.forEach((p, i) => {
@@ -29238,17 +29409,7 @@ var SettingsDialog = class {
29238
29409
  shell.append(rail, paneHost);
29239
29410
  if (railScrim) shell.append(railScrim);
29240
29411
  ui.body.appendChild(shell);
29241
- this.root = ui.positioner;
29242
- this.ui = ui;
29243
- ui.show();
29244
- for (const p of panes) {
29245
- const hosts = [...p.el.querySelectorAll("[data-sd-rows-host]")];
29246
- if (hosts.length === 0) {
29247
- this.layoutSettingsGrids(p.el);
29248
- continue;
29249
- }
29250
- for (const h of hosts) this.layoutSettingsGrids(h);
29251
- }
29412
+ return panes.map((p) => p.el);
29252
29413
  }
29253
29414
  close() {
29254
29415
  closeOpenPopovers();
@@ -29256,6 +29417,7 @@ var SettingsDialog = class {
29256
29417
  const ui = this.ui;
29257
29418
  this.ui = null;
29258
29419
  this.root = null;
29420
+ this.toggleRail = null;
29259
29421
  this.tabs = [];
29260
29422
  for (const dispose of this.hintTips) dispose();
29261
29423
  this.hintTips = [];
@@ -36574,7 +36736,11 @@ var NativeRenderer = class {
36574
36736
  }
36575
36737
  this.settingsDialog.setTheme(this.theme);
36576
36738
  this.settingsDialog.setHostSections(this.hostSettingsSections);
36577
- this.settingsDialog.setMarkGroups(this.markGroupsInUse(), (id) => markGroupVisible(this.scene.marks, id, this.scene.markGroups));
36739
+ this.settingsDialog.setMarkGroups(
36740
+ this.markGroupsForEventsTab(),
36741
+ (id) => markGroupVisible2(this.scene.marks, id, this.scene.markGroups),
36742
+ (id) => markGroupOwnVisible(this.scene.marks.groups, id, this.scene.markGroups)
36743
+ );
36578
36744
  this.settingsDialog.setHiddenSettings(this.hiddenSettings);
36579
36745
  this.syncThemeControl();
36580
36746
  this.settingsDialog.toggle(
@@ -36582,13 +36748,24 @@ var NativeRenderer = class {
36582
36748
  (patch) => this.applyConfig(patch),
36583
36749
  (json) => this.applyConfig(json),
36584
36750
  () => {
36585
- if (this.factoryConfig) this.applyConfig(factoryResetConfig(this.factoryConfig));
36586
- this.settingsDialog?.close();
36587
- this.openSettingsDialog();
36751
+ if (this.factoryConfig) this.applyConfig(this.factoryResetDocument(this.factoryConfig));
36752
+ this.settingsDialog?.refresh(this.getConfig());
36588
36753
  },
36589
36754
  section
36590
36755
  );
36591
36756
  }
36757
+ /**
36758
+ * The document "Reset defaults" applies: every setting back to its first-run value,
36759
+ * with two things that are NOT settings held or resolved here — the price style
36760
+ * stays the one the user is looking at, and the timeline-mark groups (an additive
36761
+ * merge, like the type bags) are named back to their host-declared visibility.
36762
+ */
36763
+ factoryResetDocument(factory) {
36764
+ const doc = factoryResetConfig(factory, this.scene.priceStyle);
36765
+ const groups = { ...doc.marks.groups };
36766
+ for (const g of this.markGroupsInUse()) groups[g.id] = g.visible !== false;
36767
+ return { ...doc, marks: { ...doc.marks, groups } };
36768
+ }
36592
36769
  /** Close the in-chart dialogs (indicator settings + chart-settings gear). No-op when none are open. */
36593
36770
  closeDialogs() {
36594
36771
  this.inputsUI?.closeOpenDialog();
@@ -37651,6 +37828,15 @@ var NativeRenderer = class {
37651
37828
  markGroupsInUse() {
37652
37829
  return effectiveMarkGroups(this.scene.timelineMarks, this.scene.markGroups);
37653
37830
  }
37831
+ /**
37832
+ * The groups as the Events tab lists them: nested only under a DEFINED parent. The
37833
+ * painter's visibility chain resolves parents against the defined groups alone, so a
37834
+ * parent that marks merely name must not nest (and dim) a child the painter still shows.
37835
+ */
37836
+ markGroupsForEventsTab() {
37837
+ const defined = new Set(this.scene.markGroups.map((g) => g.id));
37838
+ return this.markGroupsInUse().map((g) => g.parent !== void 0 && !defined.has(g.parent) ? { ...g, parent: void 0 } : g);
37839
+ }
37654
37840
  onViewportChange(cb) {
37655
37841
  this.viewportCbs.add(cb);
37656
37842
  return () => this.viewportCbs.delete(cb);
@@ -38609,7 +38795,7 @@ var NativeRenderer = class {
38609
38795
  const or = overlaySeriesRange(this.scene.indicators.values(), i0, i1, (id) => this.scene.offsetOf(id));
38610
38796
  if (or) dr = dr ? { min: Math.min(dr.min, or.min), max: Math.max(dr.max, or.max) } : or;
38611
38797
  }
38612
- const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels));
38798
+ const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels) || !this.paneHasMeasurableContent(masterModels, dr));
38613
38799
  pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
38614
38800
  pane.percentBaseline = pane.kind === "price" ? this.bars[i0]?.close ?? 0 : this.firstVisibleValue(masterModels, i0);
38615
38801
  pane.axisFormat = void 0;
@@ -38859,6 +39045,13 @@ var NativeRenderer = class {
38859
39045
  priceLayersAnchoredToBars(masterModels) {
38860
39046
  return masterModels.some((m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type));
38861
39047
  }
39048
+ /** True when the pane's master content contributes SOMETHING to its autoscale besides
39049
+ * the candles: a series painted on the pane (force_overlay ones scale elsewhere), a
39050
+ * price line, or a measured drawings range. Mirrors what `computePaneScale` considers. */
39051
+ paneHasMeasurableContent(masterModels, drawings) {
39052
+ if (drawings) return true;
39053
+ return masterModels.some((m) => m.priceLines.length > 0 || m.series.some((s) => s.overlay !== true));
39054
+ }
38862
39055
  /** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
38863
39056
  * band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
38864
39057
  * Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
@@ -43536,6 +43729,11 @@ var ChartCell = class {
43536
43729
  if (typeof zone === "string" && normalizeTimezone(zone) !== normalizeTimezone(this.deps.timezone())) {
43537
43730
  this.deps.setTimezone(normalizeTimezone(zone));
43538
43731
  }
43732
+ const style = this.priceStyle;
43733
+ if (style !== (this.state.priceStyle ?? "candles")) {
43734
+ this.state.priceStyle = style;
43735
+ this.deps.onPriceStyleChanged(this.id);
43736
+ }
43539
43737
  this.syncStatuslineColors();
43540
43738
  this.syncPlotOverlayTokens();
43541
43739
  });
package/dist/widget.d.cts CHANGED
@@ -1,10 +1,10 @@
1
- import { V as Vela, W as WidgetContext, K as SidePanelButton } from './contributions-CcBFe-91.cjs';
2
- export { C as CellStateContext, aA as DEFAULT_PANEL_ORDER, E as ExternalIndicatorEntry, O as OVERRIDABLE_TOPBAR_IDS, Q as SidePanelDescriptor, T as SidePanelHandle, X as StatePersistenceHandler, $ as SymbolRankingHook, a4 as WidgetActionDescriptor, a5 as WidgetActionTarget, a6 as WidgetAttachment, ag as registerSidePanel, ah as registerStatePersistence, ai as registerSymbolRanking, aj as registerWidgetAction, ak as registerWidgetAttachment, am as sidePanels, an as statePersistenceHandlers, ao as symbolRanking, ap as topbarActionOverride, au as unregisterSidePanel, av as unregisterStatePersistence, aw as unregisterWidgetAction, ax as unregisterWidgetAttachment, ay as widgetActions, az as widgetAttachments } from './contributions-CcBFe-91.cjs';
3
- import { b as VelaOptions, d as MarketSession, T as ThemeName, c as VelaTheme, v as DataWindowReadout } from './options-C7b_Qssu.cjs';
4
- import { c as VelaShellOptions, W as WidgetHistory, b as RangePreset, f as WorkspaceState, j as TopbarComposition, a as StatuslinePart, P as PanelsState } from './statusline-model-DUKt7_TQ.cjs';
5
- export { B as Bottombar, k as BottombarOptions, C as CellState, g as ChartState, I as IndicatorLoader, l as IndicatorManifest, m as IndicatorManifestEntry, n as RANGE_PRESETS, R as ResolvedIndicator, o as ResolvedTopbarComposition, p as TOPBAR_BUILTIN_IDS, q as TOPBAR_DEFAULT_LEFT, r as TOPBAR_DEFAULT_RIGHT, V as VelaStorage, t as WidgetStorage, h as decodeState, i as encodeState, u as localStorageAdapter, v as pinnedTopbarActionIds, w as resolveIndicators, x as resolveTopbarComposition, s as sanitizeState, y as topbarHas } from './statusline-model-DUKt7_TQ.cjs';
1
+ import { V as Vela, W as WidgetContext, K as SidePanelButton } from './contributions-DYtiRtES.cjs';
2
+ export { C as CellStateContext, aA as DEFAULT_PANEL_ORDER, E as ExternalIndicatorEntry, O as OVERRIDABLE_TOPBAR_IDS, Q as SidePanelDescriptor, T as SidePanelHandle, X as StatePersistenceHandler, $ as SymbolRankingHook, a4 as WidgetActionDescriptor, a5 as WidgetActionTarget, a6 as WidgetAttachment, ag as registerSidePanel, ah as registerStatePersistence, ai as registerSymbolRanking, aj as registerWidgetAction, ak as registerWidgetAttachment, am as sidePanels, an as statePersistenceHandlers, ao as symbolRanking, ap as topbarActionOverride, au as unregisterSidePanel, av as unregisterStatePersistence, aw as unregisterWidgetAction, ax as unregisterWidgetAttachment, ay as widgetActions, az as widgetAttachments } from './contributions-DYtiRtES.cjs';
3
+ import { b as VelaOptions, d as MarketSession, T as ThemeName, c as VelaTheme, v as DataWindowReadout } from './options-DNBoMKkX.cjs';
4
+ import { c as VelaShellOptions, W as WidgetHistory, b as RangePreset, f as WorkspaceState, j as TopbarComposition, a as StatuslinePart, P as PanelsState } from './statusline-model-gA__yaog.cjs';
5
+ export { B as Bottombar, k as BottombarOptions, C as CellState, g as ChartState, I as IndicatorLoader, l as IndicatorManifest, m as IndicatorManifestEntry, n as RANGE_PRESETS, R as ResolvedIndicator, o as ResolvedTopbarComposition, p as TOPBAR_BUILTIN_IDS, q as TOPBAR_DEFAULT_LEFT, r as TOPBAR_DEFAULT_RIGHT, V as VelaStorage, t as WidgetStorage, h as decodeState, i as encodeState, u as localStorageAdapter, v as pinnedTopbarActionIds, w as resolveIndicators, x as resolveTopbarComposition, s as sanitizeState, y as topbarHas } from './statusline-model-gA__yaog.cjs';
6
6
  import { K as KeymapManager } from './keymap-CGOz5F5f.cjs';
7
- import { a as SymbolDescriptor } from './DataProvider-D5uMiJYz.cjs';
7
+ import { a as SymbolDescriptor } from './DataProvider-Dut6lXGM.cjs';
8
8
  import { d as SidePanel } from './side-panel-HF0IAzwf.cjs';
9
9
  export { D as DEFAULT_PANEL_MAX_WIDTH, a as DEFAULT_PANEL_MIN_WIDTH, b as DEFAULT_PANEL_WIDTH, S as SidePanelOptions, c as clampPanelWidth } from './side-panel-HF0IAzwf.cjs';
10
10
 
package/dist/widget.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { V as Vela, W as WidgetContext, K as SidePanelButton } from './contributions-BbhbZd3c.js';
2
- export { C as CellStateContext, aA as DEFAULT_PANEL_ORDER, E as ExternalIndicatorEntry, O as OVERRIDABLE_TOPBAR_IDS, Q as SidePanelDescriptor, T as SidePanelHandle, X as StatePersistenceHandler, $ as SymbolRankingHook, a4 as WidgetActionDescriptor, a5 as WidgetActionTarget, a6 as WidgetAttachment, ag as registerSidePanel, ah as registerStatePersistence, ai as registerSymbolRanking, aj as registerWidgetAction, ak as registerWidgetAttachment, am as sidePanels, an as statePersistenceHandlers, ao as symbolRanking, ap as topbarActionOverride, au as unregisterSidePanel, av as unregisterStatePersistence, aw as unregisterWidgetAction, ax as unregisterWidgetAttachment, ay as widgetActions, az as widgetAttachments } from './contributions-BbhbZd3c.js';
3
- import { b as VelaOptions, d as MarketSession, T as ThemeName, c as VelaTheme, v as DataWindowReadout } from './options-C7b_Qssu.js';
4
- import { c as VelaShellOptions, W as WidgetHistory, b as RangePreset, f as WorkspaceState, j as TopbarComposition, a as StatuslinePart, P as PanelsState } from './statusline-model-D8NL7o4L.js';
5
- export { B as Bottombar, k as BottombarOptions, C as CellState, g as ChartState, I as IndicatorLoader, l as IndicatorManifest, m as IndicatorManifestEntry, n as RANGE_PRESETS, R as ResolvedIndicator, o as ResolvedTopbarComposition, p as TOPBAR_BUILTIN_IDS, q as TOPBAR_DEFAULT_LEFT, r as TOPBAR_DEFAULT_RIGHT, V as VelaStorage, t as WidgetStorage, h as decodeState, i as encodeState, u as localStorageAdapter, v as pinnedTopbarActionIds, w as resolveIndicators, x as resolveTopbarComposition, s as sanitizeState, y as topbarHas } from './statusline-model-D8NL7o4L.js';
1
+ import { V as Vela, W as WidgetContext, K as SidePanelButton } from './contributions-CEkgJKTU.js';
2
+ export { C as CellStateContext, aA as DEFAULT_PANEL_ORDER, E as ExternalIndicatorEntry, O as OVERRIDABLE_TOPBAR_IDS, Q as SidePanelDescriptor, T as SidePanelHandle, X as StatePersistenceHandler, $ as SymbolRankingHook, a4 as WidgetActionDescriptor, a5 as WidgetActionTarget, a6 as WidgetAttachment, ag as registerSidePanel, ah as registerStatePersistence, ai as registerSymbolRanking, aj as registerWidgetAction, ak as registerWidgetAttachment, am as sidePanels, an as statePersistenceHandlers, ao as symbolRanking, ap as topbarActionOverride, au as unregisterSidePanel, av as unregisterStatePersistence, aw as unregisterWidgetAction, ax as unregisterWidgetAttachment, ay as widgetActions, az as widgetAttachments } from './contributions-CEkgJKTU.js';
3
+ import { b as VelaOptions, d as MarketSession, T as ThemeName, c as VelaTheme, v as DataWindowReadout } from './options-DNBoMKkX.js';
4
+ import { c as VelaShellOptions, W as WidgetHistory, b as RangePreset, f as WorkspaceState, j as TopbarComposition, a as StatuslinePart, P as PanelsState } from './statusline-model-DypGXjtr.js';
5
+ export { B as Bottombar, k as BottombarOptions, C as CellState, g as ChartState, I as IndicatorLoader, l as IndicatorManifest, m as IndicatorManifestEntry, n as RANGE_PRESETS, R as ResolvedIndicator, o as ResolvedTopbarComposition, p as TOPBAR_BUILTIN_IDS, q as TOPBAR_DEFAULT_LEFT, r as TOPBAR_DEFAULT_RIGHT, V as VelaStorage, t as WidgetStorage, h as decodeState, i as encodeState, u as localStorageAdapter, v as pinnedTopbarActionIds, w as resolveIndicators, x as resolveTopbarComposition, s as sanitizeState, y as topbarHas } from './statusline-model-DypGXjtr.js';
6
6
  import { K as KeymapManager } from './keymap-CGOz5F5f.js';
7
- import { a as SymbolDescriptor } from './DataProvider-BALR0qPq.js';
7
+ import { a as SymbolDescriptor } from './DataProvider-DLeFs1gE.js';
8
8
  import { d as SidePanel } from './side-panel-HF0IAzwf.js';
9
9
  export { D as DEFAULT_PANEL_MAX_WIDTH, a as DEFAULT_PANEL_MIN_WIDTH, b as DEFAULT_PANEL_WIDTH, S as SidePanelOptions, c as clampPanelWidth } from './side-panel-HF0IAzwf.js';
10
10
 
package/dist/widget.js CHANGED
@@ -1,6 +1,6 @@
1
- import { VelaWorkspace } from './chunk-DH2VF5X7.js';
2
- export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, localStorageAdapter, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, timeframeLabel, timeframeMs } from './chunk-DH2VF5X7.js';
3
- export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-2EO74GIE.js';
1
+ import { VelaWorkspace } from './chunk-MNG5XPLV.js';
2
+ export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, localStorageAdapter, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, timeframeLabel, timeframeMs } from './chunk-MNG5XPLV.js';
3
+ export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-MCLI6B3S.js';
4
4
  export { DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, OVERRIDABLE_TOPBAR_IDS, SidePanel, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, clampPanelWidth, pinnedTopbarActionIds, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, resolveTopbarComposition, sidePanels, statePersistenceHandlers, symbolRanking, topbarActionOverride, topbarHas, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments } from './chunk-BFA32GOU.js';
5
5
  import './chunk-ZADTVRUO.js';
6
6
  import './chunk-BKHSQ4YM.js';