@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.
- package/dist/{DataProvider-BALR0qPq.d.ts → DataProvider-DLeFs1gE.d.ts} +1 -1
- package/dist/{DataProvider-D5uMiJYz.d.cts → DataProvider-Dut6lXGM.d.cts} +1 -1
- package/dist/{chunk-2EO74GIE.js → chunk-MCLI6B3S.js} +261 -68
- package/dist/{chunk-DH2VF5X7.js → chunk-MNG5XPLV.js} +6 -1
- package/dist/{contributions-BbhbZd3c.d.ts → contributions-CEkgJKTU.d.ts} +7 -3
- package/dist/{contributions-CcBFe-91.d.cts → contributions-DYtiRtES.d.cts} +7 -3
- package/dist/index.cjs +261 -68
- package/dist/index.d.cts +23 -6
- package/dist/index.d.ts +23 -6
- package/dist/index.js +1 -1
- package/dist/{options-C7b_Qssu.d.cts → options-DNBoMKkX.d.cts} +10 -1
- package/dist/{options-C7b_Qssu.d.ts → options-DNBoMKkX.d.ts} +10 -1
- package/dist/{plugin-Cnix5ZBh.d.ts → plugin-CLn--oP6.d.ts} +3 -3
- package/dist/{plugin-D6MlX4--.d.cts → plugin-DtkeQVAO.d.cts} +3 -3
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/{statusline-model-D8NL7o4L.d.ts → statusline-model-DypGXjtr.d.ts} +3 -3
- package/dist/{statusline-model-DUKt7_TQ.d.cts → statusline-model-gA__yaog.d.cts} +3 -3
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/vela.global.js +261 -68
- package/dist/vela.global.min.js +53 -49
- package/dist/widget.cjs +266 -68
- package/dist/widget.d.cts +6 -6
- package/dist/widget.d.ts +6 -6
- package/dist/widget.js +3 -3
- package/dist/workspace.cjs +266 -68
- package/dist/workspace.d.cts +5 -5
- package/dist/workspace.d.ts +5 -5
- package/dist/workspace.js +2 -2
- package/package.json +1 -1
package/dist/vela.global.js
CHANGED
|
@@ -7116,6 +7116,42 @@ var Vela = (function (exports) {
|
|
|
7116
7116
|
}
|
|
7117
7117
|
};
|
|
7118
7118
|
|
|
7119
|
+
// src/core/marks/visibility.ts
|
|
7120
|
+
function markGroupOwnVisible(choices, groupId, groups) {
|
|
7121
|
+
const chosen = choices[groupId];
|
|
7122
|
+
if (typeof chosen === "boolean") return chosen;
|
|
7123
|
+
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
7124
|
+
}
|
|
7125
|
+
function markGroupVisible(choices, groupId, groups) {
|
|
7126
|
+
if (groupId === void 0) return true;
|
|
7127
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7128
|
+
let id = groupId;
|
|
7129
|
+
while (id !== void 0 && !seen.has(id)) {
|
|
7130
|
+
seen.add(id);
|
|
7131
|
+
if (!markGroupOwnVisible(choices, id, groups)) return false;
|
|
7132
|
+
const parent = groups.find((g) => g.id === id)?.parent;
|
|
7133
|
+
id = parent !== void 0 && groups.some((g) => g.id === parent) ? parent : void 0;
|
|
7134
|
+
}
|
|
7135
|
+
return true;
|
|
7136
|
+
}
|
|
7137
|
+
function markGroupRows(groups) {
|
|
7138
|
+
const ids = new Set(groups.map((g) => g.id));
|
|
7139
|
+
const out = [];
|
|
7140
|
+
const placed = /* @__PURE__ */ new Set();
|
|
7141
|
+
const place = (group, depth) => {
|
|
7142
|
+
if (placed.has(group.id)) return;
|
|
7143
|
+
placed.add(group.id);
|
|
7144
|
+
out.push({ group, depth });
|
|
7145
|
+
for (const child of groups) if (child.parent === group.id && child.id !== group.id) place(child, depth + 1);
|
|
7146
|
+
};
|
|
7147
|
+
for (const g of groups) {
|
|
7148
|
+
const parentKnown = g.parent !== void 0 && ids.has(g.parent) && g.parent !== g.id;
|
|
7149
|
+
if (!parentKnown) place(g, 0);
|
|
7150
|
+
}
|
|
7151
|
+
for (const g of groups) place(g, 0);
|
|
7152
|
+
return out;
|
|
7153
|
+
}
|
|
7154
|
+
|
|
7119
7155
|
// src/core/marks/MarksController.ts
|
|
7120
7156
|
var MarksController = class {
|
|
7121
7157
|
constructor(renderer, events) {
|
|
@@ -7160,6 +7196,7 @@ var Vela = (function (exports) {
|
|
|
7160
7196
|
defineGroup(group) {
|
|
7161
7197
|
if (!group || typeof group.id !== "string" || group.id.length === 0) throw new Error("[vela] marks.defineGroup: `id` must be a non-empty string");
|
|
7162
7198
|
if (typeof group.label !== "string") throw new Error(`[vela] marks.defineGroup: group "${group.id}" needs a string \`label\``);
|
|
7199
|
+
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`);
|
|
7163
7200
|
this.groups.set(group.id, { ...group });
|
|
7164
7201
|
this.sync();
|
|
7165
7202
|
}
|
|
@@ -7179,12 +7216,16 @@ var Vela = (function (exports) {
|
|
|
7179
7216
|
}
|
|
7180
7217
|
this.renderer.applyFeature("marks", { groups: { [id]: visible } });
|
|
7181
7218
|
}
|
|
7182
|
-
/**
|
|
7219
|
+
/**
|
|
7220
|
+
* A group's effective visibility: its own switch — the user's (persisted) choice, else
|
|
7221
|
+
* the declared default, else visible — AND every ancestor's, so a child under a
|
|
7222
|
+
* switched-off parent reads hidden whatever its own choice says.
|
|
7223
|
+
*/
|
|
7183
7224
|
isGroupVisible(id) {
|
|
7184
7225
|
const state = this.renderer.readFeature("marks");
|
|
7185
|
-
const
|
|
7186
|
-
if (typeof
|
|
7187
|
-
return this.groups.
|
|
7226
|
+
const groups = {};
|
|
7227
|
+
for (const [gid, v] of Object.entries(state?.groups ?? {})) if (typeof v === "boolean") groups[gid] = v;
|
|
7228
|
+
return markGroupVisible(groups, id, [...this.groups.values()]);
|
|
7188
7229
|
}
|
|
7189
7230
|
destroy() {
|
|
7190
7231
|
for (const unsub of this.subs) unsub();
|
|
@@ -23374,6 +23415,17 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23374
23415
|
for (const t of chartTypes()) if (t.id === style) return (t.basePainting ?? "candles") === "candles";
|
|
23375
23416
|
return false;
|
|
23376
23417
|
}
|
|
23418
|
+
var CANDLE_OVERRIDE_KEYS = [
|
|
23419
|
+
"candleUpColor",
|
|
23420
|
+
"candleDownColor",
|
|
23421
|
+
"candleBodyVisible",
|
|
23422
|
+
"candleBorderVisible",
|
|
23423
|
+
"candleBorderUpColor",
|
|
23424
|
+
"candleBorderDownColor",
|
|
23425
|
+
"candleWickVisible",
|
|
23426
|
+
"candleWickUpColor",
|
|
23427
|
+
"candleWickDownColor"
|
|
23428
|
+
];
|
|
23377
23429
|
function candleOverrideFor(style, bags) {
|
|
23378
23430
|
if (!hasOwnCandlePaint(style)) return null;
|
|
23379
23431
|
const bag = bags[style] ?? {};
|
|
@@ -23445,7 +23497,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23445
23497
|
function clampSpacing(v) {
|
|
23446
23498
|
return v < 0.1 ? 0.1 : v > 10 ? 10 : v;
|
|
23447
23499
|
}
|
|
23448
|
-
function factoryResetConfig(factory) {
|
|
23500
|
+
function factoryResetConfig(factory, priceStyle = factory.series.style) {
|
|
23449
23501
|
const bag = {};
|
|
23450
23502
|
for (const t of chartTypes()) {
|
|
23451
23503
|
const section = t.settings;
|
|
@@ -23467,10 +23519,16 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23467
23519
|
if (!section.instances) addRows(section.rows);
|
|
23468
23520
|
bag[t.id] = defaults2;
|
|
23469
23521
|
}
|
|
23522
|
+
for (const t of chartTypes()) {
|
|
23523
|
+
if (!hasOwnCandlePaint(t.id)) continue;
|
|
23524
|
+
const defaults2 = bag[t.id] ?? {};
|
|
23525
|
+
for (const key of CANDLE_OVERRIDE_KEYS) defaults2[key] = null;
|
|
23526
|
+
bag[t.id] = defaults2;
|
|
23527
|
+
}
|
|
23470
23528
|
for (const [typeId, vals] of Object.entries(factory.chartTypes)) {
|
|
23471
23529
|
bag[typeId] = { ...bag[typeId] ?? {}, ...vals };
|
|
23472
23530
|
}
|
|
23473
|
-
return { ...factory, chartTypes: bag };
|
|
23531
|
+
return { ...factory, chartTypes: bag, series: { ...factory.series, style: priceStyle } };
|
|
23474
23532
|
}
|
|
23475
23533
|
function mergeConfig(base, patch) {
|
|
23476
23534
|
const p = asObject(patch);
|
|
@@ -25849,11 +25907,8 @@ void main() {
|
|
|
25849
25907
|
for (const [id, v] of Object.entries(g)) if (typeof v === "boolean") groups[id] = v;
|
|
25850
25908
|
return { visible: typeof p.visible === "boolean" ? p.visible : base.visible, groups };
|
|
25851
25909
|
}
|
|
25852
|
-
function
|
|
25853
|
-
|
|
25854
|
-
const chosen = state.groups[groupId];
|
|
25855
|
-
if (typeof chosen === "boolean") return chosen;
|
|
25856
|
-
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
25910
|
+
function markGroupVisible2(state, groupId, groups) {
|
|
25911
|
+
return markGroupVisible(state.groups, groupId, groups);
|
|
25857
25912
|
}
|
|
25858
25913
|
|
|
25859
25914
|
// src/renderers/native/core/SceneGraph.ts
|
|
@@ -27865,6 +27920,32 @@ void main() {
|
|
|
27865
27920
|
}
|
|
27866
27921
|
return out;
|
|
27867
27922
|
}
|
|
27923
|
+
function foldOverlappingClusters(clusters, xOf, bucketPx = MARK_CLUSTER_PX) {
|
|
27924
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
27925
|
+
for (const c of clusters) {
|
|
27926
|
+
const list = byGroup.get(c.group);
|
|
27927
|
+
if (list) list.push(c);
|
|
27928
|
+
else byGroup.set(c.group, [c]);
|
|
27929
|
+
}
|
|
27930
|
+
const out = [];
|
|
27931
|
+
for (const list of byGroup.values()) {
|
|
27932
|
+
list.sort((a, b) => a.bar - b.bar);
|
|
27933
|
+
const runs = [];
|
|
27934
|
+
let anchorX = Number.NaN;
|
|
27935
|
+
for (const c of list) {
|
|
27936
|
+
const x = xOf(c.bar);
|
|
27937
|
+
const run = runs[runs.length - 1];
|
|
27938
|
+
if (run && Number.isFinite(x) && Number.isFinite(anchorX) && x - anchorX < bucketPx) {
|
|
27939
|
+
run.marks.push(...c.marks);
|
|
27940
|
+
} else {
|
|
27941
|
+
runs.push({ key: c.key, bar: c.bar, group: c.group, marks: [...c.marks] });
|
|
27942
|
+
anchorX = x;
|
|
27943
|
+
}
|
|
27944
|
+
}
|
|
27945
|
+
out.push(...runs);
|
|
27946
|
+
}
|
|
27947
|
+
return out;
|
|
27948
|
+
}
|
|
27868
27949
|
function groupRank(groups, clusters) {
|
|
27869
27950
|
const rank = /* @__PURE__ */ new Map();
|
|
27870
27951
|
groups.forEach((g, i) => rank.set(g.id, i));
|
|
@@ -27874,7 +27955,7 @@ void main() {
|
|
|
27874
27955
|
return (group) => group === void 0 ? Number.MAX_SAFE_INTEGER : rank.get(group) ?? Number.MAX_SAFE_INTEGER - 1;
|
|
27875
27956
|
}
|
|
27876
27957
|
function layoutMarkLane(input) {
|
|
27877
|
-
const clusters = clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden);
|
|
27958
|
+
const clusters = foldOverlappingClusters(clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden), input.xOf);
|
|
27878
27959
|
const rankOf = groupRank(input.groups, clusters);
|
|
27879
27960
|
const byBar = /* @__PURE__ */ new Map();
|
|
27880
27961
|
for (const c of clusters) {
|
|
@@ -28003,13 +28084,29 @@ void main() {
|
|
|
28003
28084
|
const img = deps.icons.get(mark.glyph.icon, ink, symbolPx, deps.dpr);
|
|
28004
28085
|
if (img) ctx.drawImage(img, center.x - symbolPx / 2, center.y - symbolPx / 2, symbolPx, symbolPx);
|
|
28005
28086
|
} else if (mark.glyph.letter) {
|
|
28087
|
+
const letter = mark.glyph.letter.slice(0, 2);
|
|
28006
28088
|
ctx.fillStyle = ink;
|
|
28007
|
-
|
|
28008
|
-
ctx.
|
|
28089
|
+
let px = letterFontPx(size3, letter);
|
|
28090
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
28091
|
+
if (letter.length > 1) {
|
|
28092
|
+
px = fitLetterPx(px, ctx.measureText(letter).width, symbolInnerWidth(shape, size3));
|
|
28093
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
28094
|
+
}
|
|
28095
|
+
ctx.fillText(letter, center.x, center.y + 0.5);
|
|
28009
28096
|
}
|
|
28010
28097
|
}
|
|
28011
28098
|
ctx.restore();
|
|
28012
28099
|
}
|
|
28100
|
+
function letterFontPx(size3, letter) {
|
|
28101
|
+
return Math.round(size3 * (letter.length > 1 ? 0.38 : 0.58));
|
|
28102
|
+
}
|
|
28103
|
+
function symbolInnerWidth(shape, size3) {
|
|
28104
|
+
return (shape === "pin" ? size3 * 0.82 : size3) - 3;
|
|
28105
|
+
}
|
|
28106
|
+
function fitLetterPx(px, width, inner) {
|
|
28107
|
+
if (!(width > inner) || !(width > 0)) return px;
|
|
28108
|
+
return Math.max(4, Math.floor(px * inner / width));
|
|
28109
|
+
}
|
|
28013
28110
|
function traceShape(ctx, shape, x, y, size3) {
|
|
28014
28111
|
const r = size3 / 2;
|
|
28015
28112
|
ctx.beginPath();
|
|
@@ -28211,7 +28308,7 @@ void main() {
|
|
|
28211
28308
|
this.markLayout = layoutMarkLane({
|
|
28212
28309
|
marks: scene.timelineMarks,
|
|
28213
28310
|
groups: scene.markGroups,
|
|
28214
|
-
hidden: (groupId) => !
|
|
28311
|
+
hidden: (groupId) => !markGroupVisible2(scene.marks, groupId, scene.markGroups),
|
|
28215
28312
|
barTimes: this.barTimes(scene),
|
|
28216
28313
|
intervalMs: coords.barInterval,
|
|
28217
28314
|
xOf: (bar) => coords.logicalToX(bar),
|
|
@@ -28978,6 +29075,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
28978
29075
|
muted and non-interactive. Applied to each row's children so it survives display:contents;
|
|
28979
29076
|
!important beats the inline opacity on labels. */
|
|
28980
29077
|
.vela-sd-soft>*{opacity:0.4 !important;pointer-events:none !important;}
|
|
29078
|
+
/* A mark group nested under a parent group on the Events tab: indented one step per level
|
|
29079
|
+
(--vela-sd-depth). The indent rides the first child (the switch) as a MARGIN \u2014 padding
|
|
29080
|
+
would push the switch's own tick out of its box. */
|
|
29081
|
+
.vela-sd-nested>*:first-child{margin-left:calc(var(--vela-sd-depth,1)*24px);}
|
|
28981
29082
|
/* \u2500\u2500 mobile presentation (.vela-sd-mobile on the scrim; structural sizes are inline in open()) \u2500\u2500
|
|
28982
29083
|
The tab rail becomes a burger-opened overlay sidebar; the group TOC becomes a sticky
|
|
28983
29084
|
row of horizontally scrollable tabs; the instance strip scrolls instead of wrapping;
|
|
@@ -29026,6 +29127,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29026
29127
|
this.hostSections = [];
|
|
29027
29128
|
/** The timeline-mark groups (defined + named by marks) — one checkbox each on the Events tab. */
|
|
29028
29129
|
this.markGroups = [];
|
|
29130
|
+
/** A group's OWN switch (what its checkbox shows) — a child under an off parent keeps its own state. */
|
|
29131
|
+
this.markGroupOwnVisible = () => true;
|
|
29029
29132
|
this.markGroupVisible = () => true;
|
|
29030
29133
|
/** The Canvas → Theme row: current app theme + where a pick is raised. The row is a
|
|
29031
29134
|
* host callback, NOT a config patch — the app theme stays out of the persisted
|
|
@@ -29043,6 +29146,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29043
29146
|
this.activeSection = null;
|
|
29044
29147
|
/** Mobile chrome: fullscreen card, burger-opened section sidebar, TOC as top tabs. */
|
|
29045
29148
|
this.mobileLayout = false;
|
|
29149
|
+
/** Opens/closes the mobile section sidebar of the CURRENT build (the burger in the
|
|
29150
|
+
* shell header outlives pane rebuilds, so it reaches the rail through here). */
|
|
29151
|
+
this.toggleRail = null;
|
|
29046
29152
|
/** The visibility policy: setting ids hidden by the host (subtree semantics). */
|
|
29047
29153
|
this.hiddenSettings = /* @__PURE__ */ new Set();
|
|
29048
29154
|
if (getComputedStyle(container).position === "static") container.style.position = "relative";
|
|
@@ -29052,7 +29158,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29052
29158
|
this.hostSections = sections;
|
|
29053
29159
|
}
|
|
29054
29160
|
/** The timeline-mark groups and their current visibility — the Events tab's rows on next open. */
|
|
29055
|
-
setMarkGroups(groups, visible) {
|
|
29161
|
+
setMarkGroups(groups, visible, ownVisible = visible) {
|
|
29162
|
+
this.markGroupOwnVisible = ownVisible;
|
|
29056
29163
|
this.markGroups = groups;
|
|
29057
29164
|
this.markGroupVisible = visible;
|
|
29058
29165
|
}
|
|
@@ -29118,7 +29225,6 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29118
29225
|
this.onReset = onReset ?? null;
|
|
29119
29226
|
ensureControlStyles();
|
|
29120
29227
|
const mobile = this.mobileLayout;
|
|
29121
|
-
let toggleRail = null;
|
|
29122
29228
|
let burger;
|
|
29123
29229
|
if (mobile) {
|
|
29124
29230
|
burger = document.createElement("button");
|
|
@@ -29126,8 +29232,83 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29126
29232
|
burger.className = "vela-sd-burger";
|
|
29127
29233
|
burger.innerHTML = iconAt("burger", 16);
|
|
29128
29234
|
burger.title = "Sections";
|
|
29129
|
-
burger.addEventListener("click", () => toggleRail?.());
|
|
29235
|
+
burger.addEventListener("click", () => this.toggleRail?.());
|
|
29236
|
+
}
|
|
29237
|
+
const ui = new Dialog({
|
|
29238
|
+
host: this.container,
|
|
29239
|
+
title: "Chart settings",
|
|
29240
|
+
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
29241
|
+
// machine locks pointer events on the whole body, killing the chart, the
|
|
29242
|
+
// legend, and the body-portaled popovers (color picker, select lists).
|
|
29243
|
+
modal: false,
|
|
29244
|
+
contained: true,
|
|
29245
|
+
align: "top",
|
|
29246
|
+
draggable: !mobile,
|
|
29247
|
+
flush: true,
|
|
29248
|
+
className: "vela-dialog--settings",
|
|
29249
|
+
headerStart: burger,
|
|
29250
|
+
closeOnBackdrop: true,
|
|
29251
|
+
footer: (foot) => {
|
|
29252
|
+
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
29253
|
+
const resetBtn = document.createElement("button");
|
|
29254
|
+
resetBtn.type = "button";
|
|
29255
|
+
resetBtn.textContent = "Reset defaults";
|
|
29256
|
+
resetBtn.className = "vela-sd-btn";
|
|
29257
|
+
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
29258
|
+
foot.appendChild(resetBtn);
|
|
29259
|
+
},
|
|
29260
|
+
// A close signal may only close ITS OWN dialog: the machine reports the exit
|
|
29261
|
+
// asynchronously, so a close-then-reopen in one tick would otherwise see the
|
|
29262
|
+
// old instance's signal tear down the freshly opened one.
|
|
29263
|
+
onOpenChange: (open2) => {
|
|
29264
|
+
if (!open2 && this.ui === ui) this.close();
|
|
29265
|
+
}
|
|
29266
|
+
});
|
|
29267
|
+
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
29268
|
+
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
29269
|
+
this.root = ui.positioner;
|
|
29270
|
+
this.ui = ui;
|
|
29271
|
+
const panes = this.buildContent(ui, config, section, mobile);
|
|
29272
|
+
ui.show();
|
|
29273
|
+
this.layoutPanes(panes);
|
|
29274
|
+
}
|
|
29275
|
+
/**
|
|
29276
|
+
* Re-seed every control of an OPEN dialog from `config`, in place: the shell stays
|
|
29277
|
+
* (no close/open transition), only the tab rail and panes rebuild, landing back on
|
|
29278
|
+
* the current tab. The reset path — the restored values must show without the
|
|
29279
|
+
* dialog re-entering. No-op while closed.
|
|
29280
|
+
*/
|
|
29281
|
+
refresh(config) {
|
|
29282
|
+
const ui = this.ui;
|
|
29283
|
+
if (!ui) return;
|
|
29284
|
+
closeOpenPopovers();
|
|
29285
|
+
closeWidthPopover();
|
|
29286
|
+
for (const dispose of this.hintTips) dispose();
|
|
29287
|
+
this.hintTips = [];
|
|
29288
|
+
this.config = config;
|
|
29289
|
+
ui.body.replaceChildren();
|
|
29290
|
+
this.layoutPanes(this.buildContent(ui, config, this.activeSection ?? void 0, this.mobileLayout));
|
|
29291
|
+
}
|
|
29292
|
+
/** Structured chart-type panes (instance strip / group TOC) own their layout and
|
|
29293
|
+
* tag their rows hosts instead; each host gets its own field grid. Runs on a SHOWN
|
|
29294
|
+
* dialog — the grids measure their labels. */
|
|
29295
|
+
layoutPanes(panes) {
|
|
29296
|
+
for (const el of panes) {
|
|
29297
|
+
const hosts = [...el.querySelectorAll("[data-sd-rows-host]")];
|
|
29298
|
+
if (hosts.length === 0) {
|
|
29299
|
+
this.layoutSettingsGrids(el);
|
|
29300
|
+
continue;
|
|
29301
|
+
}
|
|
29302
|
+
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29130
29303
|
}
|
|
29304
|
+
}
|
|
29305
|
+
/**
|
|
29306
|
+
* Build the tab rail + one pane per section into `ui.body` (the linear `body` of
|
|
29307
|
+
* section markers and rows is split afterwards), select `section` (the active
|
|
29308
|
+
* style's own tab when none is asked for), and return the pane elements for
|
|
29309
|
+
* {@link layoutPanes}.
|
|
29310
|
+
*/
|
|
29311
|
+
buildContent(ui, config, section, mobile) {
|
|
29131
29312
|
const body = document.createElement("div");
|
|
29132
29313
|
body.style.cssText = "display:flex;flex-direction:column;gap:0;";
|
|
29133
29314
|
const sid = (el, id) => {
|
|
@@ -29331,9 +29512,28 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29331
29512
|
if (this.markGroups.length > 0) {
|
|
29332
29513
|
body.append(sid(this.section("Events"), MARKS_SETTINGS_ID));
|
|
29333
29514
|
body.append(sid(this.sectionTitle("Visible events"), MARKS_GROUPS_SETTINGS_ID));
|
|
29334
|
-
|
|
29335
|
-
|
|
29515
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
29516
|
+
const rows = markGroupRows(this.markGroups);
|
|
29517
|
+
const refreshDimming = () => {
|
|
29518
|
+
for (const { group } of rows) {
|
|
29519
|
+
const el = rowsById.get(group.id);
|
|
29520
|
+
if (!el || group.parent === void 0) continue;
|
|
29521
|
+
el.classList.toggle("vela-sd-soft", !this.markGroupVisible(group.parent));
|
|
29522
|
+
}
|
|
29523
|
+
};
|
|
29524
|
+
for (const { group: g, depth } of rows) {
|
|
29525
|
+
const row = this.boolRow(g.label, this.markGroupOwnVisible(g.id), (v) => {
|
|
29526
|
+
this.emit({ marks: { groups: { [g.id]: v } } });
|
|
29527
|
+
refreshDimming();
|
|
29528
|
+
});
|
|
29529
|
+
if (depth > 0) {
|
|
29530
|
+
row.classList.add("vela-sd-nested");
|
|
29531
|
+
row.style.setProperty("--vela-sd-depth", String(depth));
|
|
29532
|
+
}
|
|
29533
|
+
rowsById.set(g.id, row);
|
|
29534
|
+
body.append(sid(row, markGroupSettingsId(g.id)));
|
|
29336
29535
|
}
|
|
29536
|
+
refreshDimming();
|
|
29337
29537
|
}
|
|
29338
29538
|
renderChartTypeSections("end");
|
|
29339
29539
|
renderHostSections("end");
|
|
@@ -29361,8 +29561,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29361
29561
|
if (mobile) {
|
|
29362
29562
|
railScrim = document.createElement("div");
|
|
29363
29563
|
railScrim.className = "vela-sd-railscrim";
|
|
29364
|
-
railScrim.addEventListener("click", () => toggleRail?.());
|
|
29365
|
-
toggleRail = (open2) => {
|
|
29564
|
+
railScrim.addEventListener("click", () => this.toggleRail?.());
|
|
29565
|
+
this.toggleRail = (open2) => {
|
|
29366
29566
|
const on = open2 ?? !rail.classList.contains("open");
|
|
29367
29567
|
rail.classList.toggle("open", on);
|
|
29368
29568
|
railScrim?.classList.toggle("open", on);
|
|
@@ -29402,35 +29602,6 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29402
29602
|
});
|
|
29403
29603
|
if (hidActive) activate(0);
|
|
29404
29604
|
};
|
|
29405
|
-
const ui = new Dialog({
|
|
29406
|
-
host: this.container,
|
|
29407
|
-
title: "Chart settings",
|
|
29408
|
-
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
29409
|
-
// machine locks pointer events on the whole body, killing the chart, the
|
|
29410
|
-
// legend, and the body-portaled popovers (color picker, select lists).
|
|
29411
|
-
modal: false,
|
|
29412
|
-
contained: true,
|
|
29413
|
-
align: "top",
|
|
29414
|
-
draggable: !mobile,
|
|
29415
|
-
flush: true,
|
|
29416
|
-
className: "vela-dialog--settings",
|
|
29417
|
-
headerStart: burger,
|
|
29418
|
-
closeOnBackdrop: true,
|
|
29419
|
-
footer: (foot) => {
|
|
29420
|
-
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
29421
|
-
const resetBtn = document.createElement("button");
|
|
29422
|
-
resetBtn.type = "button";
|
|
29423
|
-
resetBtn.textContent = "Reset defaults";
|
|
29424
|
-
resetBtn.className = "vela-sd-btn";
|
|
29425
|
-
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
29426
|
-
foot.appendChild(resetBtn);
|
|
29427
|
-
},
|
|
29428
|
-
onOpenChange: (open2) => {
|
|
29429
|
-
if (!open2) this.close();
|
|
29430
|
-
}
|
|
29431
|
-
});
|
|
29432
|
-
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
29433
|
-
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
29434
29605
|
const activate = (idx) => {
|
|
29435
29606
|
panes.forEach((p, i) => {
|
|
29436
29607
|
p.el.style.display = i === idx ? "block" : "none";
|
|
@@ -29439,7 +29610,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29439
29610
|
this.activeSection = panes[idx]?.title ?? null;
|
|
29440
29611
|
if (mobile) {
|
|
29441
29612
|
ui.titleEl.textContent = panes[idx]?.title ?? "Chart settings";
|
|
29442
|
-
toggleRail?.(false);
|
|
29613
|
+
this.toggleRail?.(false);
|
|
29443
29614
|
}
|
|
29444
29615
|
};
|
|
29445
29616
|
panes.forEach((p, i) => {
|
|
@@ -29454,17 +29625,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29454
29625
|
shell.append(rail, paneHost);
|
|
29455
29626
|
if (railScrim) shell.append(railScrim);
|
|
29456
29627
|
ui.body.appendChild(shell);
|
|
29457
|
-
|
|
29458
|
-
this.ui = ui;
|
|
29459
|
-
ui.show();
|
|
29460
|
-
for (const p of panes) {
|
|
29461
|
-
const hosts = [...p.el.querySelectorAll("[data-sd-rows-host]")];
|
|
29462
|
-
if (hosts.length === 0) {
|
|
29463
|
-
this.layoutSettingsGrids(p.el);
|
|
29464
|
-
continue;
|
|
29465
|
-
}
|
|
29466
|
-
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29467
|
-
}
|
|
29628
|
+
return panes.map((p) => p.el);
|
|
29468
29629
|
}
|
|
29469
29630
|
close() {
|
|
29470
29631
|
closeOpenPopovers();
|
|
@@ -29472,6 +29633,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29472
29633
|
const ui = this.ui;
|
|
29473
29634
|
this.ui = null;
|
|
29474
29635
|
this.root = null;
|
|
29636
|
+
this.toggleRail = null;
|
|
29475
29637
|
this.tabs = [];
|
|
29476
29638
|
for (const dispose of this.hintTips) dispose();
|
|
29477
29639
|
this.hintTips = [];
|
|
@@ -37571,7 +37733,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37571
37733
|
}
|
|
37572
37734
|
this.settingsDialog.setTheme(this.theme);
|
|
37573
37735
|
this.settingsDialog.setHostSections(this.hostSettingsSections);
|
|
37574
|
-
this.settingsDialog.setMarkGroups(
|
|
37736
|
+
this.settingsDialog.setMarkGroups(
|
|
37737
|
+
this.markGroupsForEventsTab(),
|
|
37738
|
+
(id) => markGroupVisible2(this.scene.marks, id, this.scene.markGroups),
|
|
37739
|
+
(id) => markGroupOwnVisible(this.scene.marks.groups, id, this.scene.markGroups)
|
|
37740
|
+
);
|
|
37575
37741
|
this.settingsDialog.setHiddenSettings(this.hiddenSettings);
|
|
37576
37742
|
this.syncThemeControl();
|
|
37577
37743
|
this.settingsDialog.toggle(
|
|
@@ -37579,13 +37745,24 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37579
37745
|
(patch) => this.applyConfig(patch),
|
|
37580
37746
|
(json) => this.applyConfig(json),
|
|
37581
37747
|
() => {
|
|
37582
|
-
if (this.factoryConfig) this.applyConfig(
|
|
37583
|
-
this.settingsDialog?.
|
|
37584
|
-
this.openSettingsDialog();
|
|
37748
|
+
if (this.factoryConfig) this.applyConfig(this.factoryResetDocument(this.factoryConfig));
|
|
37749
|
+
this.settingsDialog?.refresh(this.getConfig());
|
|
37585
37750
|
},
|
|
37586
37751
|
section
|
|
37587
37752
|
);
|
|
37588
37753
|
}
|
|
37754
|
+
/**
|
|
37755
|
+
* The document "Reset defaults" applies: every setting back to its first-run value,
|
|
37756
|
+
* with two things that are NOT settings held or resolved here — the price style
|
|
37757
|
+
* stays the one the user is looking at, and the timeline-mark groups (an additive
|
|
37758
|
+
* merge, like the type bags) are named back to their host-declared visibility.
|
|
37759
|
+
*/
|
|
37760
|
+
factoryResetDocument(factory) {
|
|
37761
|
+
const doc = factoryResetConfig(factory, this.scene.priceStyle);
|
|
37762
|
+
const groups = { ...doc.marks.groups };
|
|
37763
|
+
for (const g of this.markGroupsInUse()) groups[g.id] = g.visible !== false;
|
|
37764
|
+
return { ...doc, marks: { ...doc.marks, groups } };
|
|
37765
|
+
}
|
|
37589
37766
|
/** Close the in-chart dialogs (indicator settings + chart-settings gear). No-op when none are open. */
|
|
37590
37767
|
closeDialogs() {
|
|
37591
37768
|
this.inputsUI?.closeOpenDialog();
|
|
@@ -38648,6 +38825,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38648
38825
|
markGroupsInUse() {
|
|
38649
38826
|
return effectiveMarkGroups(this.scene.timelineMarks, this.scene.markGroups);
|
|
38650
38827
|
}
|
|
38828
|
+
/**
|
|
38829
|
+
* The groups as the Events tab lists them: nested only under a DEFINED parent. The
|
|
38830
|
+
* painter's visibility chain resolves parents against the defined groups alone, so a
|
|
38831
|
+
* parent that marks merely name must not nest (and dim) a child the painter still shows.
|
|
38832
|
+
*/
|
|
38833
|
+
markGroupsForEventsTab() {
|
|
38834
|
+
const defined = new Set(this.scene.markGroups.map((g) => g.id));
|
|
38835
|
+
return this.markGroupsInUse().map((g) => g.parent !== void 0 && !defined.has(g.parent) ? { ...g, parent: void 0 } : g);
|
|
38836
|
+
}
|
|
38651
38837
|
onViewportChange(cb) {
|
|
38652
38838
|
this.viewportCbs.add(cb);
|
|
38653
38839
|
return () => this.viewportCbs.delete(cb);
|
|
@@ -39606,7 +39792,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39606
39792
|
const or2 = overlaySeriesRange(this.scene.indicators.values(), i0, i1, (id) => this.scene.offsetOf(id));
|
|
39607
39793
|
if (or2) dr = dr ? { min: Math.min(dr.min, or2.min), max: Math.max(dr.max, or2.max) } : or2;
|
|
39608
39794
|
}
|
|
39609
|
-
const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels));
|
|
39795
|
+
const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels) || !this.paneHasMeasurableContent(masterModels, dr));
|
|
39610
39796
|
pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
39611
39797
|
pane.percentBaseline = pane.kind === "price" ? this.bars[i0]?.close ?? 0 : this.firstVisibleValue(masterModels, i0);
|
|
39612
39798
|
pane.axisFormat = void 0;
|
|
@@ -39856,6 +40042,13 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39856
40042
|
priceLayersAnchoredToBars(masterModels) {
|
|
39857
40043
|
return masterModels.some((m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type));
|
|
39858
40044
|
}
|
|
40045
|
+
/** True when the pane's master content contributes SOMETHING to its autoscale besides
|
|
40046
|
+
* the candles: a series painted on the pane (force_overlay ones scale elsewhere), a
|
|
40047
|
+
* price line, or a measured drawings range. Mirrors what `computePaneScale` considers. */
|
|
40048
|
+
paneHasMeasurableContent(masterModels, drawings) {
|
|
40049
|
+
if (drawings) return true;
|
|
40050
|
+
return masterModels.some((m) => m.priceLines.length > 0 || m.series.some((s) => s.overlay !== true));
|
|
40051
|
+
}
|
|
39859
40052
|
/** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
|
|
39860
40053
|
* band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
|
|
39861
40054
|
* Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
|