@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/workspace.cjs
CHANGED
|
@@ -5061,6 +5061,17 @@ function hasOwnCandlePaint(style) {
|
|
|
5061
5061
|
for (const t of chartTypes()) if (t.id === style) return (t.basePainting ?? "candles") === "candles";
|
|
5062
5062
|
return false;
|
|
5063
5063
|
}
|
|
5064
|
+
var CANDLE_OVERRIDE_KEYS = [
|
|
5065
|
+
"candleUpColor",
|
|
5066
|
+
"candleDownColor",
|
|
5067
|
+
"candleBodyVisible",
|
|
5068
|
+
"candleBorderVisible",
|
|
5069
|
+
"candleBorderUpColor",
|
|
5070
|
+
"candleBorderDownColor",
|
|
5071
|
+
"candleWickVisible",
|
|
5072
|
+
"candleWickUpColor",
|
|
5073
|
+
"candleWickDownColor"
|
|
5074
|
+
];
|
|
5064
5075
|
function candleOverrideFor(style, bags) {
|
|
5065
5076
|
if (!hasOwnCandlePaint(style)) return null;
|
|
5066
5077
|
const bag = bags[style] ?? {};
|
|
@@ -5132,7 +5143,7 @@ function clampLevel(v) {
|
|
|
5132
5143
|
function clampSpacing(v) {
|
|
5133
5144
|
return v < 0.1 ? 0.1 : v > 10 ? 10 : v;
|
|
5134
5145
|
}
|
|
5135
|
-
function factoryResetConfig(factory) {
|
|
5146
|
+
function factoryResetConfig(factory, priceStyle = factory.series.style) {
|
|
5136
5147
|
const bag = {};
|
|
5137
5148
|
for (const t of chartTypes()) {
|
|
5138
5149
|
const section = t.settings;
|
|
@@ -5154,10 +5165,16 @@ function factoryResetConfig(factory) {
|
|
|
5154
5165
|
if (!section.instances) addRows(section.rows);
|
|
5155
5166
|
bag[t.id] = defaults2;
|
|
5156
5167
|
}
|
|
5168
|
+
for (const t of chartTypes()) {
|
|
5169
|
+
if (!hasOwnCandlePaint(t.id)) continue;
|
|
5170
|
+
const defaults2 = bag[t.id] ?? {};
|
|
5171
|
+
for (const key of CANDLE_OVERRIDE_KEYS) defaults2[key] = null;
|
|
5172
|
+
bag[t.id] = defaults2;
|
|
5173
|
+
}
|
|
5157
5174
|
for (const [typeId, vals] of Object.entries(factory.chartTypes)) {
|
|
5158
5175
|
bag[typeId] = { ...bag[typeId] ?? {}, ...vals };
|
|
5159
5176
|
}
|
|
5160
|
-
return { ...factory, chartTypes: bag };
|
|
5177
|
+
return { ...factory, chartTypes: bag, series: { ...factory.series, style: priceStyle } };
|
|
5161
5178
|
}
|
|
5162
5179
|
function mergeConfig(base, patch) {
|
|
5163
5180
|
const p = asObject(patch);
|
|
@@ -18585,6 +18602,42 @@ function presetToRange(preset, bars) {
|
|
|
18585
18602
|
return { from: Math.max(first, from), to: last };
|
|
18586
18603
|
}
|
|
18587
18604
|
|
|
18605
|
+
// src/core/marks/visibility.ts
|
|
18606
|
+
function markGroupOwnVisible(choices, groupId, groups) {
|
|
18607
|
+
const chosen = choices[groupId];
|
|
18608
|
+
if (typeof chosen === "boolean") return chosen;
|
|
18609
|
+
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
18610
|
+
}
|
|
18611
|
+
function markGroupVisible(choices, groupId, groups) {
|
|
18612
|
+
if (groupId === void 0) return true;
|
|
18613
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18614
|
+
let id = groupId;
|
|
18615
|
+
while (id !== void 0 && !seen.has(id)) {
|
|
18616
|
+
seen.add(id);
|
|
18617
|
+
if (!markGroupOwnVisible(choices, id, groups)) return false;
|
|
18618
|
+
const parent = groups.find((g) => g.id === id)?.parent;
|
|
18619
|
+
id = parent !== void 0 && groups.some((g) => g.id === parent) ? parent : void 0;
|
|
18620
|
+
}
|
|
18621
|
+
return true;
|
|
18622
|
+
}
|
|
18623
|
+
function markGroupRows(groups) {
|
|
18624
|
+
const ids = new Set(groups.map((g) => g.id));
|
|
18625
|
+
const out = [];
|
|
18626
|
+
const placed = /* @__PURE__ */ new Set();
|
|
18627
|
+
const place = (group, depth) => {
|
|
18628
|
+
if (placed.has(group.id)) return;
|
|
18629
|
+
placed.add(group.id);
|
|
18630
|
+
out.push({ group, depth });
|
|
18631
|
+
for (const child of groups) if (child.parent === group.id && child.id !== group.id) place(child, depth + 1);
|
|
18632
|
+
};
|
|
18633
|
+
for (const g of groups) {
|
|
18634
|
+
const parentKnown = g.parent !== void 0 && ids.has(g.parent) && g.parent !== g.id;
|
|
18635
|
+
if (!parentKnown) place(g, 0);
|
|
18636
|
+
}
|
|
18637
|
+
for (const g of groups) place(g, 0);
|
|
18638
|
+
return out;
|
|
18639
|
+
}
|
|
18640
|
+
|
|
18588
18641
|
// src/core/marks/MarksController.ts
|
|
18589
18642
|
var MarksController = class {
|
|
18590
18643
|
constructor(renderer, events) {
|
|
@@ -18629,6 +18682,7 @@ var MarksController = class {
|
|
|
18629
18682
|
defineGroup(group) {
|
|
18630
18683
|
if (!group || typeof group.id !== "string" || group.id.length === 0) throw new Error("[vela] marks.defineGroup: `id` must be a non-empty string");
|
|
18631
18684
|
if (typeof group.label !== "string") throw new Error(`[vela] marks.defineGroup: group "${group.id}" needs a string \`label\``);
|
|
18685
|
+
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`);
|
|
18632
18686
|
this.groups.set(group.id, { ...group });
|
|
18633
18687
|
this.sync();
|
|
18634
18688
|
}
|
|
@@ -18648,12 +18702,16 @@ var MarksController = class {
|
|
|
18648
18702
|
}
|
|
18649
18703
|
this.renderer.applyFeature("marks", { groups: { [id]: visible } });
|
|
18650
18704
|
}
|
|
18651
|
-
/**
|
|
18705
|
+
/**
|
|
18706
|
+
* A group's effective visibility: its own switch — the user's (persisted) choice, else
|
|
18707
|
+
* the declared default, else visible — AND every ancestor's, so a child under a
|
|
18708
|
+
* switched-off parent reads hidden whatever its own choice says.
|
|
18709
|
+
*/
|
|
18652
18710
|
isGroupVisible(id) {
|
|
18653
18711
|
const state = this.renderer.readFeature("marks");
|
|
18654
|
-
const
|
|
18655
|
-
if (typeof
|
|
18656
|
-
return this.groups.
|
|
18712
|
+
const groups = {};
|
|
18713
|
+
for (const [gid, v] of Object.entries(state?.groups ?? {})) if (typeof v === "boolean") groups[gid] = v;
|
|
18714
|
+
return markGroupVisible(groups, id, [...this.groups.values()]);
|
|
18657
18715
|
}
|
|
18658
18716
|
destroy() {
|
|
18659
18717
|
for (const unsub of this.subs) unsub();
|
|
@@ -25642,11 +25700,8 @@ function mergeMarksState(base, patch) {
|
|
|
25642
25700
|
for (const [id, v] of Object.entries(g)) if (typeof v === "boolean") groups[id] = v;
|
|
25643
25701
|
return { visible: typeof p.visible === "boolean" ? p.visible : base.visible, groups };
|
|
25644
25702
|
}
|
|
25645
|
-
function
|
|
25646
|
-
|
|
25647
|
-
const chosen = state.groups[groupId];
|
|
25648
|
-
if (typeof chosen === "boolean") return chosen;
|
|
25649
|
-
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
25703
|
+
function markGroupVisible2(state, groupId, groups) {
|
|
25704
|
+
return markGroupVisible(state.groups, groupId, groups);
|
|
25650
25705
|
}
|
|
25651
25706
|
|
|
25652
25707
|
// src/renderers/native/core/SceneGraph.ts
|
|
@@ -27658,6 +27713,32 @@ function clusterMarks(marks, barTimes, intervalMs2, hidden) {
|
|
|
27658
27713
|
}
|
|
27659
27714
|
return out;
|
|
27660
27715
|
}
|
|
27716
|
+
function foldOverlappingClusters(clusters, xOf, bucketPx = MARK_CLUSTER_PX) {
|
|
27717
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
27718
|
+
for (const c of clusters) {
|
|
27719
|
+
const list = byGroup.get(c.group);
|
|
27720
|
+
if (list) list.push(c);
|
|
27721
|
+
else byGroup.set(c.group, [c]);
|
|
27722
|
+
}
|
|
27723
|
+
const out = [];
|
|
27724
|
+
for (const list of byGroup.values()) {
|
|
27725
|
+
list.sort((a, b) => a.bar - b.bar);
|
|
27726
|
+
const runs = [];
|
|
27727
|
+
let anchorX = Number.NaN;
|
|
27728
|
+
for (const c of list) {
|
|
27729
|
+
const x = xOf(c.bar);
|
|
27730
|
+
const run = runs[runs.length - 1];
|
|
27731
|
+
if (run && Number.isFinite(x) && Number.isFinite(anchorX) && x - anchorX < bucketPx) {
|
|
27732
|
+
run.marks.push(...c.marks);
|
|
27733
|
+
} else {
|
|
27734
|
+
runs.push({ key: c.key, bar: c.bar, group: c.group, marks: [...c.marks] });
|
|
27735
|
+
anchorX = x;
|
|
27736
|
+
}
|
|
27737
|
+
}
|
|
27738
|
+
out.push(...runs);
|
|
27739
|
+
}
|
|
27740
|
+
return out;
|
|
27741
|
+
}
|
|
27661
27742
|
function groupRank(groups, clusters) {
|
|
27662
27743
|
const rank = /* @__PURE__ */ new Map();
|
|
27663
27744
|
groups.forEach((g, i) => rank.set(g.id, i));
|
|
@@ -27667,7 +27748,7 @@ function groupRank(groups, clusters) {
|
|
|
27667
27748
|
return (group) => group === void 0 ? Number.MAX_SAFE_INTEGER : rank.get(group) ?? Number.MAX_SAFE_INTEGER - 1;
|
|
27668
27749
|
}
|
|
27669
27750
|
function layoutMarkLane(input) {
|
|
27670
|
-
const clusters = clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden);
|
|
27751
|
+
const clusters = foldOverlappingClusters(clusterMarks(input.marks, input.barTimes, input.intervalMs, input.hidden), input.xOf);
|
|
27671
27752
|
const rankOf = groupRank(input.groups, clusters);
|
|
27672
27753
|
const byBar = /* @__PURE__ */ new Map();
|
|
27673
27754
|
for (const c of clusters) {
|
|
@@ -27796,13 +27877,29 @@ function paintMarkLane(ctx, layout, deps) {
|
|
|
27796
27877
|
const img = deps.icons.get(mark.glyph.icon, ink, symbolPx, deps.dpr);
|
|
27797
27878
|
if (img) ctx.drawImage(img, center.x - symbolPx / 2, center.y - symbolPx / 2, symbolPx, symbolPx);
|
|
27798
27879
|
} else if (mark.glyph.letter) {
|
|
27880
|
+
const letter = mark.glyph.letter.slice(0, 2);
|
|
27799
27881
|
ctx.fillStyle = ink;
|
|
27800
|
-
|
|
27801
|
-
ctx.
|
|
27882
|
+
let px = letterFontPx(size, letter);
|
|
27883
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
27884
|
+
if (letter.length > 1) {
|
|
27885
|
+
px = fitLetterPx(px, ctx.measureText(letter).width, symbolInnerWidth(shape, size));
|
|
27886
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
27887
|
+
}
|
|
27888
|
+
ctx.fillText(letter, center.x, center.y + 0.5);
|
|
27802
27889
|
}
|
|
27803
27890
|
}
|
|
27804
27891
|
ctx.restore();
|
|
27805
27892
|
}
|
|
27893
|
+
function letterFontPx(size, letter) {
|
|
27894
|
+
return Math.round(size * (letter.length > 1 ? 0.38 : 0.58));
|
|
27895
|
+
}
|
|
27896
|
+
function symbolInnerWidth(shape, size) {
|
|
27897
|
+
return (shape === "pin" ? size * 0.82 : size) - 3;
|
|
27898
|
+
}
|
|
27899
|
+
function fitLetterPx(px, width, inner) {
|
|
27900
|
+
if (!(width > inner) || !(width > 0)) return px;
|
|
27901
|
+
return Math.max(4, Math.floor(px * inner / width));
|
|
27902
|
+
}
|
|
27806
27903
|
function traceShape(ctx, shape, x, y, size) {
|
|
27807
27904
|
const r = size / 2;
|
|
27808
27905
|
ctx.beginPath();
|
|
@@ -28004,7 +28101,7 @@ var ChromeRenderer = class {
|
|
|
28004
28101
|
this.markLayout = layoutMarkLane({
|
|
28005
28102
|
marks: scene.timelineMarks,
|
|
28006
28103
|
groups: scene.markGroups,
|
|
28007
|
-
hidden: (groupId) => !
|
|
28104
|
+
hidden: (groupId) => !markGroupVisible2(scene.marks, groupId, scene.markGroups),
|
|
28008
28105
|
barTimes: this.barTimes(scene),
|
|
28009
28106
|
intervalMs: coords.barInterval,
|
|
28010
28107
|
xOf: (bar) => coords.logicalToX(bar),
|
|
@@ -28694,6 +28791,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
28694
28791
|
muted and non-interactive. Applied to each row's children so it survives display:contents;
|
|
28695
28792
|
!important beats the inline opacity on labels. */
|
|
28696
28793
|
.vela-sd-soft>*{opacity:0.4 !important;pointer-events:none !important;}
|
|
28794
|
+
/* A mark group nested under a parent group on the Events tab: indented one step per level
|
|
28795
|
+
(--vela-sd-depth). The indent rides the first child (the switch) as a MARGIN \u2014 padding
|
|
28796
|
+
would push the switch's own tick out of its box. */
|
|
28797
|
+
.vela-sd-nested>*:first-child{margin-left:calc(var(--vela-sd-depth,1)*24px);}
|
|
28697
28798
|
/* \u2500\u2500 mobile presentation (.vela-sd-mobile on the scrim; structural sizes are inline in open()) \u2500\u2500
|
|
28698
28799
|
The tab rail becomes a burger-opened overlay sidebar; the group TOC becomes a sticky
|
|
28699
28800
|
row of horizontally scrollable tabs; the instance strip scrolls instead of wrapping;
|
|
@@ -28742,6 +28843,8 @@ var SettingsDialog = class {
|
|
|
28742
28843
|
this.hostSections = [];
|
|
28743
28844
|
/** The timeline-mark groups (defined + named by marks) — one checkbox each on the Events tab. */
|
|
28744
28845
|
this.markGroups = [];
|
|
28846
|
+
/** A group's OWN switch (what its checkbox shows) — a child under an off parent keeps its own state. */
|
|
28847
|
+
this.markGroupOwnVisible = () => true;
|
|
28745
28848
|
this.markGroupVisible = () => true;
|
|
28746
28849
|
/** The Canvas → Theme row: current app theme + where a pick is raised. The row is a
|
|
28747
28850
|
* host callback, NOT a config patch — the app theme stays out of the persisted
|
|
@@ -28759,6 +28862,9 @@ var SettingsDialog = class {
|
|
|
28759
28862
|
this.activeSection = null;
|
|
28760
28863
|
/** Mobile chrome: fullscreen card, burger-opened section sidebar, TOC as top tabs. */
|
|
28761
28864
|
this.mobileLayout = false;
|
|
28865
|
+
/** Opens/closes the mobile section sidebar of the CURRENT build (the burger in the
|
|
28866
|
+
* shell header outlives pane rebuilds, so it reaches the rail through here). */
|
|
28867
|
+
this.toggleRail = null;
|
|
28762
28868
|
/** The visibility policy: setting ids hidden by the host (subtree semantics). */
|
|
28763
28869
|
this.hiddenSettings = /* @__PURE__ */ new Set();
|
|
28764
28870
|
if (getComputedStyle(container).position === "static") container.style.position = "relative";
|
|
@@ -28768,7 +28874,8 @@ var SettingsDialog = class {
|
|
|
28768
28874
|
this.hostSections = sections;
|
|
28769
28875
|
}
|
|
28770
28876
|
/** The timeline-mark groups and their current visibility — the Events tab's rows on next open. */
|
|
28771
|
-
setMarkGroups(groups, visible) {
|
|
28877
|
+
setMarkGroups(groups, visible, ownVisible = visible) {
|
|
28878
|
+
this.markGroupOwnVisible = ownVisible;
|
|
28772
28879
|
this.markGroups = groups;
|
|
28773
28880
|
this.markGroupVisible = visible;
|
|
28774
28881
|
}
|
|
@@ -28834,7 +28941,6 @@ var SettingsDialog = class {
|
|
|
28834
28941
|
this.onReset = onReset ?? null;
|
|
28835
28942
|
ensureControlStyles();
|
|
28836
28943
|
const mobile = this.mobileLayout;
|
|
28837
|
-
let toggleRail = null;
|
|
28838
28944
|
let burger;
|
|
28839
28945
|
if (mobile) {
|
|
28840
28946
|
burger = document.createElement("button");
|
|
@@ -28842,8 +28948,83 @@ var SettingsDialog = class {
|
|
|
28842
28948
|
burger.className = "vela-sd-burger";
|
|
28843
28949
|
burger.innerHTML = iconAt("burger", 16);
|
|
28844
28950
|
burger.title = "Sections";
|
|
28845
|
-
burger.addEventListener("click", () => toggleRail?.());
|
|
28951
|
+
burger.addEventListener("click", () => this.toggleRail?.());
|
|
28846
28952
|
}
|
|
28953
|
+
const ui = new Dialog({
|
|
28954
|
+
host: this.container,
|
|
28955
|
+
title: "Chart settings",
|
|
28956
|
+
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
28957
|
+
// machine locks pointer events on the whole body, killing the chart, the
|
|
28958
|
+
// legend, and the body-portaled popovers (color picker, select lists).
|
|
28959
|
+
modal: false,
|
|
28960
|
+
contained: true,
|
|
28961
|
+
align: "top",
|
|
28962
|
+
draggable: !mobile,
|
|
28963
|
+
flush: true,
|
|
28964
|
+
className: "vela-dialog--settings",
|
|
28965
|
+
headerStart: burger,
|
|
28966
|
+
closeOnBackdrop: true,
|
|
28967
|
+
footer: (foot) => {
|
|
28968
|
+
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
28969
|
+
const resetBtn = document.createElement("button");
|
|
28970
|
+
resetBtn.type = "button";
|
|
28971
|
+
resetBtn.textContent = "Reset defaults";
|
|
28972
|
+
resetBtn.className = "vela-sd-btn";
|
|
28973
|
+
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
28974
|
+
foot.appendChild(resetBtn);
|
|
28975
|
+
},
|
|
28976
|
+
// A close signal may only close ITS OWN dialog: the machine reports the exit
|
|
28977
|
+
// asynchronously, so a close-then-reopen in one tick would otherwise see the
|
|
28978
|
+
// old instance's signal tear down the freshly opened one.
|
|
28979
|
+
onOpenChange: (open2) => {
|
|
28980
|
+
if (!open2 && this.ui === ui) this.close();
|
|
28981
|
+
}
|
|
28982
|
+
});
|
|
28983
|
+
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
28984
|
+
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
28985
|
+
this.root = ui.positioner;
|
|
28986
|
+
this.ui = ui;
|
|
28987
|
+
const panes = this.buildContent(ui, config, section, mobile);
|
|
28988
|
+
ui.show();
|
|
28989
|
+
this.layoutPanes(panes);
|
|
28990
|
+
}
|
|
28991
|
+
/**
|
|
28992
|
+
* Re-seed every control of an OPEN dialog from `config`, in place: the shell stays
|
|
28993
|
+
* (no close/open transition), only the tab rail and panes rebuild, landing back on
|
|
28994
|
+
* the current tab. The reset path — the restored values must show without the
|
|
28995
|
+
* dialog re-entering. No-op while closed.
|
|
28996
|
+
*/
|
|
28997
|
+
refresh(config) {
|
|
28998
|
+
const ui = this.ui;
|
|
28999
|
+
if (!ui) return;
|
|
29000
|
+
closeOpenPopovers();
|
|
29001
|
+
closeWidthPopover();
|
|
29002
|
+
for (const dispose of this.hintTips) dispose();
|
|
29003
|
+
this.hintTips = [];
|
|
29004
|
+
this.config = config;
|
|
29005
|
+
ui.body.replaceChildren();
|
|
29006
|
+
this.layoutPanes(this.buildContent(ui, config, this.activeSection ?? void 0, this.mobileLayout));
|
|
29007
|
+
}
|
|
29008
|
+
/** Structured chart-type panes (instance strip / group TOC) own their layout and
|
|
29009
|
+
* tag their rows hosts instead; each host gets its own field grid. Runs on a SHOWN
|
|
29010
|
+
* dialog — the grids measure their labels. */
|
|
29011
|
+
layoutPanes(panes) {
|
|
29012
|
+
for (const el of panes) {
|
|
29013
|
+
const hosts = [...el.querySelectorAll("[data-sd-rows-host]")];
|
|
29014
|
+
if (hosts.length === 0) {
|
|
29015
|
+
this.layoutSettingsGrids(el);
|
|
29016
|
+
continue;
|
|
29017
|
+
}
|
|
29018
|
+
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29019
|
+
}
|
|
29020
|
+
}
|
|
29021
|
+
/**
|
|
29022
|
+
* Build the tab rail + one pane per section into `ui.body` (the linear `body` of
|
|
29023
|
+
* section markers and rows is split afterwards), select `section` (the active
|
|
29024
|
+
* style's own tab when none is asked for), and return the pane elements for
|
|
29025
|
+
* {@link layoutPanes}.
|
|
29026
|
+
*/
|
|
29027
|
+
buildContent(ui, config, section, mobile) {
|
|
28847
29028
|
const body = document.createElement("div");
|
|
28848
29029
|
body.style.cssText = "display:flex;flex-direction:column;gap:0;";
|
|
28849
29030
|
const sid = (el, id) => {
|
|
@@ -29047,9 +29228,28 @@ var SettingsDialog = class {
|
|
|
29047
29228
|
if (this.markGroups.length > 0) {
|
|
29048
29229
|
body.append(sid(this.section("Events"), MARKS_SETTINGS_ID));
|
|
29049
29230
|
body.append(sid(this.sectionTitle("Visible events"), MARKS_GROUPS_SETTINGS_ID));
|
|
29050
|
-
|
|
29051
|
-
|
|
29231
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
29232
|
+
const rows = markGroupRows(this.markGroups);
|
|
29233
|
+
const refreshDimming = () => {
|
|
29234
|
+
for (const { group } of rows) {
|
|
29235
|
+
const el = rowsById.get(group.id);
|
|
29236
|
+
if (!el || group.parent === void 0) continue;
|
|
29237
|
+
el.classList.toggle("vela-sd-soft", !this.markGroupVisible(group.parent));
|
|
29238
|
+
}
|
|
29239
|
+
};
|
|
29240
|
+
for (const { group: g, depth } of rows) {
|
|
29241
|
+
const row = this.boolRow(g.label, this.markGroupOwnVisible(g.id), (v) => {
|
|
29242
|
+
this.emit({ marks: { groups: { [g.id]: v } } });
|
|
29243
|
+
refreshDimming();
|
|
29244
|
+
});
|
|
29245
|
+
if (depth > 0) {
|
|
29246
|
+
row.classList.add("vela-sd-nested");
|
|
29247
|
+
row.style.setProperty("--vela-sd-depth", String(depth));
|
|
29248
|
+
}
|
|
29249
|
+
rowsById.set(g.id, row);
|
|
29250
|
+
body.append(sid(row, markGroupSettingsId(g.id)));
|
|
29052
29251
|
}
|
|
29252
|
+
refreshDimming();
|
|
29053
29253
|
}
|
|
29054
29254
|
renderChartTypeSections("end");
|
|
29055
29255
|
renderHostSections("end");
|
|
@@ -29077,8 +29277,8 @@ var SettingsDialog = class {
|
|
|
29077
29277
|
if (mobile) {
|
|
29078
29278
|
railScrim = document.createElement("div");
|
|
29079
29279
|
railScrim.className = "vela-sd-railscrim";
|
|
29080
|
-
railScrim.addEventListener("click", () => toggleRail?.());
|
|
29081
|
-
toggleRail = (open2) => {
|
|
29280
|
+
railScrim.addEventListener("click", () => this.toggleRail?.());
|
|
29281
|
+
this.toggleRail = (open2) => {
|
|
29082
29282
|
const on = open2 ?? !rail.classList.contains("open");
|
|
29083
29283
|
rail.classList.toggle("open", on);
|
|
29084
29284
|
railScrim?.classList.toggle("open", on);
|
|
@@ -29118,35 +29318,6 @@ var SettingsDialog = class {
|
|
|
29118
29318
|
});
|
|
29119
29319
|
if (hidActive) activate(0);
|
|
29120
29320
|
};
|
|
29121
|
-
const ui = new Dialog({
|
|
29122
|
-
host: this.container,
|
|
29123
|
-
title: "Chart settings",
|
|
29124
|
-
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
29125
|
-
// machine locks pointer events on the whole body, killing the chart, the
|
|
29126
|
-
// legend, and the body-portaled popovers (color picker, select lists).
|
|
29127
|
-
modal: false,
|
|
29128
|
-
contained: true,
|
|
29129
|
-
align: "top",
|
|
29130
|
-
draggable: !mobile,
|
|
29131
|
-
flush: true,
|
|
29132
|
-
className: "vela-dialog--settings",
|
|
29133
|
-
headerStart: burger,
|
|
29134
|
-
closeOnBackdrop: true,
|
|
29135
|
-
footer: (foot) => {
|
|
29136
|
-
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
29137
|
-
const resetBtn = document.createElement("button");
|
|
29138
|
-
resetBtn.type = "button";
|
|
29139
|
-
resetBtn.textContent = "Reset defaults";
|
|
29140
|
-
resetBtn.className = "vela-sd-btn";
|
|
29141
|
-
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
29142
|
-
foot.appendChild(resetBtn);
|
|
29143
|
-
},
|
|
29144
|
-
onOpenChange: (open2) => {
|
|
29145
|
-
if (!open2) this.close();
|
|
29146
|
-
}
|
|
29147
|
-
});
|
|
29148
|
-
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
29149
|
-
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
29150
29321
|
const activate = (idx) => {
|
|
29151
29322
|
panes.forEach((p, i) => {
|
|
29152
29323
|
p.el.style.display = i === idx ? "block" : "none";
|
|
@@ -29155,7 +29326,7 @@ var SettingsDialog = class {
|
|
|
29155
29326
|
this.activeSection = panes[idx]?.title ?? null;
|
|
29156
29327
|
if (mobile) {
|
|
29157
29328
|
ui.titleEl.textContent = panes[idx]?.title ?? "Chart settings";
|
|
29158
|
-
toggleRail?.(false);
|
|
29329
|
+
this.toggleRail?.(false);
|
|
29159
29330
|
}
|
|
29160
29331
|
};
|
|
29161
29332
|
panes.forEach((p, i) => {
|
|
@@ -29170,17 +29341,7 @@ var SettingsDialog = class {
|
|
|
29170
29341
|
shell.append(rail, paneHost);
|
|
29171
29342
|
if (railScrim) shell.append(railScrim);
|
|
29172
29343
|
ui.body.appendChild(shell);
|
|
29173
|
-
|
|
29174
|
-
this.ui = ui;
|
|
29175
|
-
ui.show();
|
|
29176
|
-
for (const p of panes) {
|
|
29177
|
-
const hosts = [...p.el.querySelectorAll("[data-sd-rows-host]")];
|
|
29178
|
-
if (hosts.length === 0) {
|
|
29179
|
-
this.layoutSettingsGrids(p.el);
|
|
29180
|
-
continue;
|
|
29181
|
-
}
|
|
29182
|
-
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29183
|
-
}
|
|
29344
|
+
return panes.map((p) => p.el);
|
|
29184
29345
|
}
|
|
29185
29346
|
close() {
|
|
29186
29347
|
closeOpenPopovers();
|
|
@@ -29188,6 +29349,7 @@ var SettingsDialog = class {
|
|
|
29188
29349
|
const ui = this.ui;
|
|
29189
29350
|
this.ui = null;
|
|
29190
29351
|
this.root = null;
|
|
29352
|
+
this.toggleRail = null;
|
|
29191
29353
|
this.tabs = [];
|
|
29192
29354
|
for (const dispose of this.hintTips) dispose();
|
|
29193
29355
|
this.hintTips = [];
|
|
@@ -36506,7 +36668,11 @@ var NativeRenderer = class {
|
|
|
36506
36668
|
}
|
|
36507
36669
|
this.settingsDialog.setTheme(this.theme);
|
|
36508
36670
|
this.settingsDialog.setHostSections(this.hostSettingsSections);
|
|
36509
|
-
this.settingsDialog.setMarkGroups(
|
|
36671
|
+
this.settingsDialog.setMarkGroups(
|
|
36672
|
+
this.markGroupsForEventsTab(),
|
|
36673
|
+
(id) => markGroupVisible2(this.scene.marks, id, this.scene.markGroups),
|
|
36674
|
+
(id) => markGroupOwnVisible(this.scene.marks.groups, id, this.scene.markGroups)
|
|
36675
|
+
);
|
|
36510
36676
|
this.settingsDialog.setHiddenSettings(this.hiddenSettings);
|
|
36511
36677
|
this.syncThemeControl();
|
|
36512
36678
|
this.settingsDialog.toggle(
|
|
@@ -36514,13 +36680,24 @@ var NativeRenderer = class {
|
|
|
36514
36680
|
(patch) => this.applyConfig(patch),
|
|
36515
36681
|
(json) => this.applyConfig(json),
|
|
36516
36682
|
() => {
|
|
36517
|
-
if (this.factoryConfig) this.applyConfig(
|
|
36518
|
-
this.settingsDialog?.
|
|
36519
|
-
this.openSettingsDialog();
|
|
36683
|
+
if (this.factoryConfig) this.applyConfig(this.factoryResetDocument(this.factoryConfig));
|
|
36684
|
+
this.settingsDialog?.refresh(this.getConfig());
|
|
36520
36685
|
},
|
|
36521
36686
|
section
|
|
36522
36687
|
);
|
|
36523
36688
|
}
|
|
36689
|
+
/**
|
|
36690
|
+
* The document "Reset defaults" applies: every setting back to its first-run value,
|
|
36691
|
+
* with two things that are NOT settings held or resolved here — the price style
|
|
36692
|
+
* stays the one the user is looking at, and the timeline-mark groups (an additive
|
|
36693
|
+
* merge, like the type bags) are named back to their host-declared visibility.
|
|
36694
|
+
*/
|
|
36695
|
+
factoryResetDocument(factory) {
|
|
36696
|
+
const doc = factoryResetConfig(factory, this.scene.priceStyle);
|
|
36697
|
+
const groups = { ...doc.marks.groups };
|
|
36698
|
+
for (const g of this.markGroupsInUse()) groups[g.id] = g.visible !== false;
|
|
36699
|
+
return { ...doc, marks: { ...doc.marks, groups } };
|
|
36700
|
+
}
|
|
36524
36701
|
/** Close the in-chart dialogs (indicator settings + chart-settings gear). No-op when none are open. */
|
|
36525
36702
|
closeDialogs() {
|
|
36526
36703
|
this.inputsUI?.closeOpenDialog();
|
|
@@ -37583,6 +37760,15 @@ var NativeRenderer = class {
|
|
|
37583
37760
|
markGroupsInUse() {
|
|
37584
37761
|
return effectiveMarkGroups(this.scene.timelineMarks, this.scene.markGroups);
|
|
37585
37762
|
}
|
|
37763
|
+
/**
|
|
37764
|
+
* The groups as the Events tab lists them: nested only under a DEFINED parent. The
|
|
37765
|
+
* painter's visibility chain resolves parents against the defined groups alone, so a
|
|
37766
|
+
* parent that marks merely name must not nest (and dim) a child the painter still shows.
|
|
37767
|
+
*/
|
|
37768
|
+
markGroupsForEventsTab() {
|
|
37769
|
+
const defined = new Set(this.scene.markGroups.map((g) => g.id));
|
|
37770
|
+
return this.markGroupsInUse().map((g) => g.parent !== void 0 && !defined.has(g.parent) ? { ...g, parent: void 0 } : g);
|
|
37771
|
+
}
|
|
37586
37772
|
onViewportChange(cb) {
|
|
37587
37773
|
this.viewportCbs.add(cb);
|
|
37588
37774
|
return () => this.viewportCbs.delete(cb);
|
|
@@ -38541,7 +38727,7 @@ var NativeRenderer = class {
|
|
|
38541
38727
|
const or = overlaySeriesRange(this.scene.indicators.values(), i0, i1, (id) => this.scene.offsetOf(id));
|
|
38542
38728
|
if (or) dr = dr ? { min: Math.min(dr.min, or.min), max: Math.max(dr.max, or.max) } : or;
|
|
38543
38729
|
}
|
|
38544
|
-
const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels));
|
|
38730
|
+
const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels) || !this.paneHasMeasurableContent(masterModels, dr));
|
|
38545
38731
|
pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
38546
38732
|
pane.percentBaseline = pane.kind === "price" ? this.bars[i0]?.close ?? 0 : this.firstVisibleValue(masterModels, i0);
|
|
38547
38733
|
pane.axisFormat = void 0;
|
|
@@ -38791,6 +38977,13 @@ var NativeRenderer = class {
|
|
|
38791
38977
|
priceLayersAnchoredToBars(masterModels) {
|
|
38792
38978
|
return masterModels.some((m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type));
|
|
38793
38979
|
}
|
|
38980
|
+
/** True when the pane's master content contributes SOMETHING to its autoscale besides
|
|
38981
|
+
* the candles: a series painted on the pane (force_overlay ones scale elsewhere), a
|
|
38982
|
+
* price line, or a measured drawings range. Mirrors what `computePaneScale` considers. */
|
|
38983
|
+
paneHasMeasurableContent(masterModels, drawings) {
|
|
38984
|
+
if (drawings) return true;
|
|
38985
|
+
return masterModels.some((m) => m.priceLines.length > 0 || m.series.some((s) => s.overlay !== true));
|
|
38986
|
+
}
|
|
38794
38987
|
/** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
|
|
38795
38988
|
* band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
|
|
38796
38989
|
* Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
|
|
@@ -43468,6 +43661,11 @@ var ChartCell = class {
|
|
|
43468
43661
|
if (typeof zone === "string" && normalizeTimezone(zone) !== normalizeTimezone(this.deps.timezone())) {
|
|
43469
43662
|
this.deps.setTimezone(normalizeTimezone(zone));
|
|
43470
43663
|
}
|
|
43664
|
+
const style = this.priceStyle;
|
|
43665
|
+
if (style !== (this.state.priceStyle ?? "candles")) {
|
|
43666
|
+
this.state.priceStyle = style;
|
|
43667
|
+
this.deps.onPriceStyleChanged(this.id);
|
|
43668
|
+
}
|
|
43471
43669
|
this.syncStatuslineColors();
|
|
43472
43670
|
this.syncPlotOverlayTokens();
|
|
43473
43671
|
});
|
package/dist/workspace.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { O as OHLCV, V as VisibleRangePreset, a as VisibleRange, b as VelaOptions, I as InputValue, c as VelaTheme, N as NativeBackend, d as MarketSession } from './options-
|
|
2
|
-
import { S as SyncSetting, V as VelaStorage, C as CellState, W as WidgetHistory, R as ResolvedIndicator, a as StatuslinePart, b as RangePreset, T as TrackSizes, c as VelaShellOptions, d as SyncOptions, e as SyncKind, f as WorkspaceState } from './statusline-model-
|
|
3
|
-
export { g as ChartState, P as PanelsState, h as decodeState, i as encodeState, s as sanitizeState } from './statusline-model-
|
|
1
|
+
import { O as OHLCV, V as VisibleRangePreset, a as VisibleRange, b as VelaOptions, I as InputValue, c as VelaTheme, N as NativeBackend, d as MarketSession } from './options-DNBoMKkX.cjs';
|
|
2
|
+
import { S as SyncSetting, V as VelaStorage, C as CellState, W as WidgetHistory, R as ResolvedIndicator, a as StatuslinePart, b as RangePreset, T as TrackSizes, c as VelaShellOptions, d as SyncOptions, e as SyncKind, f as WorkspaceState } from './statusline-model-gA__yaog.cjs';
|
|
3
|
+
export { g as ChartState, P as PanelsState, h as decodeState, i as encodeState, s as sanitizeState } from './statusline-model-gA__yaog.cjs';
|
|
4
4
|
import { K as KeymapManager } from './keymap-CGOz5F5f.cjs';
|
|
5
|
-
import { I as IndicatorHandle, S as ScriptingEngine, W as WidgetContext, V as Vela, E as ExternalIndicatorEntry, a as ScriptRun } from './contributions-
|
|
6
|
-
import { M as MarketDataFeed } from './DataProvider-
|
|
5
|
+
import { I as IndicatorHandle, S as ScriptingEngine, W as WidgetContext, V as Vela, E as ExternalIndicatorEntry, a as ScriptRun } from './contributions-DYtiRtES.cjs';
|
|
6
|
+
import { M as MarketDataFeed } from './DataProvider-Dut6lXGM.cjs';
|
|
7
7
|
|
|
8
8
|
/** The cells that follow `originId` under `setting` — PURE (never includes the origin). */
|
|
9
9
|
declare function syncTargets(originId: string, setting: SyncSetting | undefined, cellIds: readonly string[]): string[];
|
package/dist/workspace.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { O as OHLCV, V as VisibleRangePreset, a as VisibleRange, b as VelaOptions, I as InputValue, c as VelaTheme, N as NativeBackend, d as MarketSession } from './options-
|
|
2
|
-
import { S as SyncSetting, V as VelaStorage, C as CellState, W as WidgetHistory, R as ResolvedIndicator, a as StatuslinePart, b as RangePreset, T as TrackSizes, c as VelaShellOptions, d as SyncOptions, e as SyncKind, f as WorkspaceState } from './statusline-model-
|
|
3
|
-
export { g as ChartState, P as PanelsState, h as decodeState, i as encodeState, s as sanitizeState } from './statusline-model-
|
|
1
|
+
import { O as OHLCV, V as VisibleRangePreset, a as VisibleRange, b as VelaOptions, I as InputValue, c as VelaTheme, N as NativeBackend, d as MarketSession } from './options-DNBoMKkX.js';
|
|
2
|
+
import { S as SyncSetting, V as VelaStorage, C as CellState, W as WidgetHistory, R as ResolvedIndicator, a as StatuslinePart, b as RangePreset, T as TrackSizes, c as VelaShellOptions, d as SyncOptions, e as SyncKind, f as WorkspaceState } from './statusline-model-DypGXjtr.js';
|
|
3
|
+
export { g as ChartState, P as PanelsState, h as decodeState, i as encodeState, s as sanitizeState } from './statusline-model-DypGXjtr.js';
|
|
4
4
|
import { K as KeymapManager } from './keymap-CGOz5F5f.js';
|
|
5
|
-
import { I as IndicatorHandle, S as ScriptingEngine, W as WidgetContext, V as Vela, E as ExternalIndicatorEntry, a as ScriptRun } from './contributions-
|
|
6
|
-
import { M as MarketDataFeed } from './DataProvider-
|
|
5
|
+
import { I as IndicatorHandle, S as ScriptingEngine, W as WidgetContext, V as Vela, E as ExternalIndicatorEntry, a as ScriptRun } from './contributions-CEkgJKTU.js';
|
|
6
|
+
import { M as MarketDataFeed } from './DataProvider-DLeFs1gE.js';
|
|
7
7
|
|
|
8
8
|
/** The cells that follow `originId` under `setting` — PURE (never includes the origin). */
|
|
9
9
|
declare function syncTargets(originId: string, setting: SyncSetting | undefined, cellIds: readonly string[]): string[];
|
package/dist/workspace.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { ChartCell, GRID_PICKER_MAX, VelaWorkspace, activeAfterLayout, decodeState, encodeState, ensureLayout, evenTracks, gridStyles, layoutDefinition, layoutForGrid, layoutShape, layouts, memoryStorageAdapter, rangesWithin, registerBuiltinLayouts, registerLayout, resizeTracks, sanitizeState, syncTargets, trackOffsets, unregisterLayout } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { ChartCell, GRID_PICKER_MAX, VelaWorkspace, activeAfterLayout, decodeState, encodeState, ensureLayout, evenTracks, gridStyles, layoutDefinition, layoutForGrid, layoutShape, layouts, memoryStorageAdapter, rangesWithin, registerBuiltinLayouts, registerLayout, resizeTracks, sanitizeState, syncTargets, trackOffsets, unregisterLayout } from './chunk-MNG5XPLV.js';
|
|
2
|
+
import './chunk-MCLI6B3S.js';
|
|
3
3
|
import './chunk-BFA32GOU.js';
|
|
4
4
|
import './chunk-ZADTVRUO.js';
|
|
5
5
|
import './chunk-BKHSQ4YM.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luxalgo/vela",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Open-source charting library with a native high-performance renderer, drawing tools, pluggable chart types and pluggable scripting engines.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://luxalgo.com/vela",
|