@luxalgo/vela 0.6.3 → 0.6.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-DHX4x6-r.d.ts → DataProvider-BmHkcwvJ.d.ts} +1 -1
- package/dist/{DataProvider-CNmk84SH.d.cts → DataProvider-DrN7ZIou.d.cts} +1 -1
- package/dist/{chunk-TP56QRMK.js → chunk-2R7BANEM.js} +1 -1
- package/dist/{chunk-7Y7CJ7EN.js → chunk-BEJ57HP4.js} +8 -1
- package/dist/{chunk-7D6YIF34.js → chunk-FQBT4NFM.js} +431 -74
- package/dist/{chunk-U5KCQHAC.js → chunk-N6MBJHMV.js} +129 -41
- package/dist/{chunk-QHZ7IXFL.js → chunk-OICPLNU7.js} +59 -9
- package/dist/{contributions-DDpv7hEL.d.cts → contributions-2bEmCF9S.d.cts} +2 -2
- package/dist/{contributions-ciV6Neyd.d.ts → contributions-DatoAzMx.d.ts} +2 -2
- package/dist/{history-CFqZm5re.d.ts → history-BmyjaVir.d.cts} +6 -6
- package/dist/{history-ChoT34nz.d.cts → history-CvOtsjT5.d.ts} +6 -6
- package/dist/index.cjs +500 -85
- package/dist/index.d.cts +63 -9
- package/dist/index.d.ts +63 -9
- package/dist/index.js +4 -4
- package/dist/{options-BqGeFHtp.d.ts → options-YdiaaVjt.d.cts} +20 -1
- package/dist/{options-BqGeFHtp.d.cts → options-YdiaaVjt.d.ts} +20 -1
- package/dist/{plugin-C97gUCVf.d.ts → plugin-Bcone6Vh.d.ts} +9 -5
- package/dist/{plugin-CSb_sTiN.d.cts → plugin-DO_DX0gp.d.cts} +9 -5
- package/dist/plugin.cjs +8 -0
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- 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/ui.cjs +59 -9
- package/dist/ui.d.cts +10 -2
- package/dist/ui.d.ts +10 -2
- package/dist/ui.js +2 -2
- package/dist/vela.global.js +500 -85
- package/dist/vela.global.min.js +84 -47
- package/dist/widget.cjs +801 -227
- package/dist/widget.d.cts +34 -8
- package/dist/widget.d.ts +34 -8
- package/dist/widget.js +145 -73
- package/dist/workspace.cjs +756 -178
- package/dist/workspace.d.cts +29 -5
- package/dist/workspace.d.ts +29 -5
- package/dist/workspace.js +143 -67
- package/package.json +1 -1
- /package/dist/{chunk-Q3XQHLIH.js → chunk-AOGZBKUE.js} +0 -0
package/dist/vela.global.js
CHANGED
|
@@ -9872,7 +9872,7 @@ var Vela = (function (exports) {
|
|
|
9872
9872
|
} else {
|
|
9873
9873
|
const left = Math.min(a.left - h.left, Math.max(0, h.width - tip.offsetWidth - 4));
|
|
9874
9874
|
tip.style.left = `${Math.max(0, left)}px`;
|
|
9875
|
-
tip.style.top = `${a.bottom - h.top + 6}px`;
|
|
9875
|
+
tip.style.top = opts.placement === "above" ? `${Math.max(0, a.top - h.top - tip.offsetHeight - 6)}px` : `${a.bottom - h.top + 6}px`;
|
|
9876
9876
|
}
|
|
9877
9877
|
};
|
|
9878
9878
|
const arm = (e) => {
|
|
@@ -16247,6 +16247,23 @@ var Vela = (function (exports) {
|
|
|
16247
16247
|
background: var(--vela-hover-strong);
|
|
16248
16248
|
color: var(--vela-fg-bright);
|
|
16249
16249
|
}
|
|
16250
|
+
/* Checkmark mode (context/action menus): every row reserves the leading mark column so
|
|
16251
|
+
labels align; a checked row fills it with a \u2713 and brightens its ink \u2014 the row surface
|
|
16252
|
+
stays free for the hover wash. */
|
|
16253
|
+
.vela-menu-item .vela-menu-mark {
|
|
16254
|
+
width: 14px;
|
|
16255
|
+
flex: none;
|
|
16256
|
+
display: inline-flex;
|
|
16257
|
+
align-items: center;
|
|
16258
|
+
justify-content: center;
|
|
16259
|
+
}
|
|
16260
|
+
.vela-menu-item .vela-menu-mark .vela-icon {
|
|
16261
|
+
width: 14px;
|
|
16262
|
+
height: 14px;
|
|
16263
|
+
font-size: 14px;
|
|
16264
|
+
color: var(--vela-fg-bright);
|
|
16265
|
+
}
|
|
16266
|
+
.vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
|
|
16250
16267
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
16251
16268
|
same control language as the settings dialog's toggles. */
|
|
16252
16269
|
.vela-menu-switch {
|
|
@@ -16317,6 +16334,7 @@ var Vela = (function (exports) {
|
|
|
16317
16334
|
this.host = opts.host;
|
|
16318
16335
|
this.onSelect = opts.onSelect;
|
|
16319
16336
|
this.onFavorite = opts.onFavorite;
|
|
16337
|
+
this.checkmarks = opts.checkmarks === true;
|
|
16320
16338
|
this.positioner = doc.createElement("div");
|
|
16321
16339
|
this.positioner.className = "vela-ui-layer";
|
|
16322
16340
|
this.list = doc.createElement("ul");
|
|
@@ -16395,6 +16413,7 @@ var Vela = (function (exports) {
|
|
|
16395
16413
|
build() {
|
|
16396
16414
|
const doc = this.doc;
|
|
16397
16415
|
this.list.replaceChildren();
|
|
16416
|
+
const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
|
|
16398
16417
|
for (const item of this.items) {
|
|
16399
16418
|
if (item.separatorBefore) {
|
|
16400
16419
|
const sep = doc.createElement("li");
|
|
@@ -16408,7 +16427,16 @@ var Vela = (function (exports) {
|
|
|
16408
16427
|
li.dataset.veiId = item.id;
|
|
16409
16428
|
if (item.toggle) {
|
|
16410
16429
|
li.dataset.toggle = "1";
|
|
16411
|
-
}
|
|
16430
|
+
}
|
|
16431
|
+
if (markable) {
|
|
16432
|
+
const mark = doc.createElement("span");
|
|
16433
|
+
mark.className = "vela-menu-mark";
|
|
16434
|
+
if (!branch && !item.toggle && item.checked) {
|
|
16435
|
+
mark.appendChild(iconEl("check", doc));
|
|
16436
|
+
li.dataset.checkmark = "1";
|
|
16437
|
+
}
|
|
16438
|
+
li.appendChild(mark);
|
|
16439
|
+
} else if (!branch && !item.toggle && item.checked) {
|
|
16412
16440
|
li.dataset.checked = "1";
|
|
16413
16441
|
}
|
|
16414
16442
|
if (item.icon) li.appendChild(iconEl(item.icon, doc));
|
|
@@ -16457,6 +16485,7 @@ var Vela = (function (exports) {
|
|
|
16457
16485
|
placement: "right-start",
|
|
16458
16486
|
onSelect: this.onSelect,
|
|
16459
16487
|
onFavorite: this.onFavorite,
|
|
16488
|
+
checkmarks: this.checkmarks,
|
|
16460
16489
|
id: `${this.mid}--${item.id}`
|
|
16461
16490
|
});
|
|
16462
16491
|
sub.setItems(item.submenu ?? []);
|
|
@@ -16481,7 +16510,8 @@ var Vela = (function (exports) {
|
|
|
16481
16510
|
triggerId: opts.triggerId,
|
|
16482
16511
|
id: opts.id,
|
|
16483
16512
|
minWidth: opts.minWidth,
|
|
16484
|
-
onFavorite: opts.onFavorite
|
|
16513
|
+
onFavorite: opts.onFavorite,
|
|
16514
|
+
checkmarks: opts.checkmarks
|
|
16485
16515
|
});
|
|
16486
16516
|
this.root.setItems(opts.items);
|
|
16487
16517
|
}
|
|
@@ -16509,6 +16539,13 @@ var Vela = (function (exports) {
|
|
|
16509
16539
|
}
|
|
16510
16540
|
};
|
|
16511
16541
|
|
|
16542
|
+
// src/core/model/inputs.ts
|
|
16543
|
+
function inputVisible(when, values) {
|
|
16544
|
+
if (!when) return true;
|
|
16545
|
+
const conds = Array.isArray(when) ? when : [when];
|
|
16546
|
+
return conds.every((c) => c.anyOf ? c.anyOf.some((x) => x === values[c.key]) : values[c.key] === c.equals);
|
|
16547
|
+
}
|
|
16548
|
+
|
|
16512
16549
|
// src/ui/tokens.ts
|
|
16513
16550
|
var STATIC_ID = "vela-ui-tokens";
|
|
16514
16551
|
var STATIC_DECLS = Object.entries(STATIC_TOKENS).map(([k, v]) => ` ${k}: ${v};`).join("\n");
|
|
@@ -16517,8 +16554,13 @@ var Vela = (function (exports) {
|
|
|
16517
16554
|
${STATIC_DECLS}
|
|
16518
16555
|
font-family: var(--vela-font, -apple-system, system-ui, sans-serif);
|
|
16519
16556
|
box-sizing: border-box;
|
|
16557
|
+
/* Chrome text (titles, buttons, menus, readouts) is UI, not copy \u2014 never selectable. */
|
|
16558
|
+
user-select: none;
|
|
16559
|
+
-webkit-user-select: none;
|
|
16520
16560
|
}
|
|
16521
16561
|
.vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
|
|
16562
|
+
/* Text ENTRY is the one exception: selection is part of editing. */
|
|
16563
|
+
.vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
|
|
16522
16564
|
.vela-icon { display: inline-flex; align-items: center; flex: none; }
|
|
16523
16565
|
.vela-icon svg { display: block; }
|
|
16524
16566
|
`;
|
|
@@ -18704,6 +18746,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
18704
18746
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
18705
18747
|
-moz-appearance: textfield;
|
|
18706
18748
|
appearance: textfield;
|
|
18749
|
+
/* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
|
|
18750
|
+
user-select: text;
|
|
18751
|
+
-webkit-user-select: text;
|
|
18707
18752
|
}
|
|
18708
18753
|
.vela-num input::-webkit-inner-spin-button, .vela-num input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
|
18709
18754
|
.vela-num input:hover { border-color: var(--vela-fg-muted); }
|
|
@@ -18916,6 +18961,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
18916
18961
|
overflow: hidden;
|
|
18917
18962
|
text-overflow: ellipsis;
|
|
18918
18963
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
18964
|
+
/* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
|
|
18965
|
+
user-select: text;
|
|
18966
|
+
-webkit-user-select: text;
|
|
18919
18967
|
}
|
|
18920
18968
|
.vela-text-field:hover { border-color: var(--vela-fg-muted); }
|
|
18921
18969
|
.vela-text-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
@@ -19009,6 +19057,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19009
19057
|
resize: vertical;
|
|
19010
19058
|
outline: none;
|
|
19011
19059
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
19060
|
+
/* Selectable even under a user-select:none host (chart wrapper, .vela-ui). */
|
|
19061
|
+
user-select: text;
|
|
19062
|
+
-webkit-user-select: text;
|
|
19012
19063
|
}
|
|
19013
19064
|
.vela-textarea-field:hover { border-color: var(--vela-fg-muted); }
|
|
19014
19065
|
.vela-textarea-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
@@ -19537,6 +19588,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19537
19588
|
font-size: 14px;
|
|
19538
19589
|
}
|
|
19539
19590
|
.vela-field-label[data-size='sm'] { font-size: inherit; opacity: 0.85; }
|
|
19591
|
+
/* Titles are fully inert: spans, not label[for] \u2014 native labels propagate :hover and
|
|
19592
|
+
clicks onto their control, and both belong to the input alone. */
|
|
19540
19593
|
.vela-field-cell { display: flex; align-items: center; gap: 8px; justify-self: start; }
|
|
19541
19594
|
.vela-field-bool { display: flex; align-items: center; gap: 8px; min-height: 22px; }
|
|
19542
19595
|
.vela-field-stacked { display: flex; flex-direction: column; gap: 8px; }
|
|
@@ -19595,11 +19648,17 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19595
19648
|
return el;
|
|
19596
19649
|
}
|
|
19597
19650
|
function labelEl(text, opts) {
|
|
19598
|
-
const el =
|
|
19599
|
-
if (opts.id && el instanceof HTMLLabelElement) el.htmlFor = opts.id;
|
|
19651
|
+
const el = document.createElement("span");
|
|
19600
19652
|
el.className = "vela-field-label";
|
|
19601
19653
|
if (opts.size) el.dataset.size = opts.size;
|
|
19602
19654
|
el.textContent = text;
|
|
19655
|
+
const id = opts.id;
|
|
19656
|
+
if (id) {
|
|
19657
|
+
el.id = `${id}--title`;
|
|
19658
|
+
queueMicrotask(() => {
|
|
19659
|
+
el.ownerDocument.getElementById(id)?.setAttribute("aria-labelledby", el.id);
|
|
19660
|
+
});
|
|
19661
|
+
}
|
|
19603
19662
|
return el;
|
|
19604
19663
|
}
|
|
19605
19664
|
function asList(control) {
|
|
@@ -19628,9 +19687,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19628
19687
|
it.className = "vela-field-inline-item";
|
|
19629
19688
|
if (item.toggleFirst) it.style.flexDirection = "row-reverse";
|
|
19630
19689
|
if (item.label) {
|
|
19631
|
-
|
|
19632
|
-
if (item.toggleFirst) lbl.style.cursor = "pointer";
|
|
19633
|
-
it.appendChild(lbl);
|
|
19690
|
+
it.appendChild(labelEl(item.label, { id: item.id, size: size3 }));
|
|
19634
19691
|
}
|
|
19635
19692
|
if (item.fit) it.appendChild(item.control);
|
|
19636
19693
|
else {
|
|
@@ -19655,7 +19712,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19655
19712
|
tone: "bright",
|
|
19656
19713
|
onChange: (v) => opts.toggle?.onChange(v)
|
|
19657
19714
|
});
|
|
19658
|
-
wrap3.append(sw.el, labelEl(opts.label, { size: size3 }));
|
|
19715
|
+
wrap3.append(sw.el, labelEl(opts.label, { id: opts.toggle?.id ?? opts.id, size: size3 }));
|
|
19659
19716
|
if (opts.info) wrap3.appendChild(opts.info);
|
|
19660
19717
|
if (opts.toggle?.get) {
|
|
19661
19718
|
const get = opts.toggle.get;
|
|
@@ -19694,7 +19751,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19694
19751
|
if (controls.length > 0) dimControls(controls, v);
|
|
19695
19752
|
}
|
|
19696
19753
|
});
|
|
19697
|
-
left.append(sw.el, labelEl(opts.label, { size: size3 }));
|
|
19754
|
+
left.append(sw.el, labelEl(opts.label, { id: opts.toggle.id ?? opts.id, size: size3 }));
|
|
19698
19755
|
wrap2.appendChild(left);
|
|
19699
19756
|
if (controls.length > 0) dimControls(controls, opts.toggle.checked);
|
|
19700
19757
|
if (opts.toggle.get) {
|
|
@@ -19848,6 +19905,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19848
19905
|
this.row = null;
|
|
19849
19906
|
this.snapshot = null;
|
|
19850
19907
|
this.dialogTips = [];
|
|
19908
|
+
/** Re-applies every `when` gate against current values; set while the dialog is open. */
|
|
19909
|
+
this.refreshVisibility = null;
|
|
19851
19910
|
this.choicePop = null;
|
|
19852
19911
|
this.calendarPop = null;
|
|
19853
19912
|
this.calendarAnchor = null;
|
|
@@ -19910,14 +19969,17 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19910
19969
|
tabs.style.cssText = `display:flex;gap:12px;padding:0 20px;border-bottom:1px solid ${border};flex:0 0 auto;`;
|
|
19911
19970
|
const tabEls = [];
|
|
19912
19971
|
const bodies = [];
|
|
19972
|
+
const gates = [];
|
|
19973
|
+
let active = 0;
|
|
19913
19974
|
const activate = (idx) => {
|
|
19975
|
+
active = idx;
|
|
19914
19976
|
for (let i = 0; i < tabEls.length; i += 1) {
|
|
19915
|
-
const
|
|
19977
|
+
const on = i === idx;
|
|
19916
19978
|
const el = tabEls[i];
|
|
19917
|
-
el.style.borderBottomColor =
|
|
19918
|
-
el.style.color =
|
|
19919
|
-
el.classList.toggle("vela-ind-tab-active",
|
|
19920
|
-
bodies[i].style.display =
|
|
19979
|
+
el.style.borderBottomColor = on ? fg : "transparent";
|
|
19980
|
+
el.style.color = on ? "inherit" : "var(--vela-fg-muted)";
|
|
19981
|
+
el.classList.toggle("vela-ind-tab-active", on);
|
|
19982
|
+
bodies[i].style.display = on ? "grid" : "none";
|
|
19921
19983
|
}
|
|
19922
19984
|
};
|
|
19923
19985
|
for (const [idx, def] of tabDefs.entries()) {
|
|
@@ -19928,11 +19990,24 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19928
19990
|
tab.addEventListener("click", () => activate(idx));
|
|
19929
19991
|
tabs.appendChild(tab);
|
|
19930
19992
|
tabEls.push(tab);
|
|
19931
|
-
bodies.push(this.buildTabBody(def.inputs, row));
|
|
19993
|
+
bodies.push(this.buildTabBody(def.inputs, row, gates));
|
|
19994
|
+
if (def.inputs.every((d) => d.when)) {
|
|
19995
|
+
gates.push({ el: tab, visible: (bag) => def.inputs.some((d) => inputVisible(d.when, bag)) });
|
|
19996
|
+
}
|
|
19932
19997
|
}
|
|
19933
19998
|
ui.body.appendChild(tabs);
|
|
19934
19999
|
for (const b of bodies) ui.body.appendChild(b);
|
|
19935
20000
|
activate(0);
|
|
20001
|
+
const refresh = () => {
|
|
20002
|
+
const bag = valuesBag(row);
|
|
20003
|
+
for (const g of gates) g.el.style.display = g.visible(bag) ? "" : "none";
|
|
20004
|
+
if (tabEls[active]?.style.display === "none") {
|
|
20005
|
+
const next2 = tabEls.findIndex((el) => el.style.display !== "none");
|
|
20006
|
+
if (next2 >= 0) activate(next2);
|
|
20007
|
+
}
|
|
20008
|
+
};
|
|
20009
|
+
this.refreshVisibility = refresh;
|
|
20010
|
+
refresh();
|
|
19936
20011
|
const onBackdropDown = (e) => {
|
|
19937
20012
|
if (e.target !== ui.backdrop && e.target !== ui.positioner) return;
|
|
19938
20013
|
if (isPopoverOpen() || eventDismissedPopover(e)) {
|
|
@@ -19952,16 +20027,27 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19952
20027
|
}
|
|
19953
20028
|
/** One tab's scrollable body — one shared grid so every group's controls share a
|
|
19954
20029
|
* left edge (a 100px number field lines up with a wider session/time pair). */
|
|
19955
|
-
buildTabBody(inputs, row) {
|
|
20030
|
+
buildTabBody(inputs, row, gates) {
|
|
19956
20031
|
const body = document.createElement("div");
|
|
19957
20032
|
body.style.cssText = inputDialogBodyStyle(this.host.mobile());
|
|
19958
20033
|
for (const group of groupInputs(inputs)) {
|
|
19959
20034
|
const section = document.createElement("div");
|
|
19960
20035
|
section.style.cssText = "display:contents;";
|
|
20036
|
+
let header = null;
|
|
19961
20037
|
if (group.name) {
|
|
19962
|
-
|
|
20038
|
+
header = fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 });
|
|
20039
|
+
section.appendChild(header);
|
|
20040
|
+
}
|
|
20041
|
+
for (const inputRow of group.rows) {
|
|
20042
|
+
const el = this.buildInputRow(section, inputRow, row);
|
|
20043
|
+
if (inputRow.some((d) => d.when)) {
|
|
20044
|
+
gates.push({ el, visible: (bag) => rowVisible(inputRow, bag) });
|
|
20045
|
+
}
|
|
20046
|
+
}
|
|
20047
|
+
if (header && group.rows.every((r) => r.every((d) => d.when))) {
|
|
20048
|
+
const rows = group.rows;
|
|
20049
|
+
gates.push({ el: header, visible: (bag) => rows.some((r) => rowVisible(r, bag)) });
|
|
19963
20050
|
}
|
|
19964
|
-
for (const inputRow of group.rows) this.buildInputRow(section, inputRow, row);
|
|
19965
20051
|
body.appendChild(section);
|
|
19966
20052
|
}
|
|
19967
20053
|
return body;
|
|
@@ -19969,6 +20055,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19969
20055
|
close() {
|
|
19970
20056
|
document.removeEventListener("keydown", this.onDialogKey);
|
|
19971
20057
|
closeOpenPopovers();
|
|
20058
|
+
this.refreshVisibility = null;
|
|
19972
20059
|
this.choicePop = null;
|
|
19973
20060
|
this.calendarPop = null;
|
|
19974
20061
|
this.calendarAnchor = null;
|
|
@@ -19998,11 +20085,21 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
19998
20085
|
}
|
|
19999
20086
|
this.close();
|
|
20000
20087
|
}
|
|
20088
|
+
/** Write one edit through: store it, notify the host, and re-apply the `when` gates. */
|
|
20089
|
+
commit(row, key, value) {
|
|
20090
|
+
row.values[key] = value;
|
|
20091
|
+
this.host.onChange?.({ indicatorId: row.id, key, value });
|
|
20092
|
+
this.refreshVisibility?.();
|
|
20093
|
+
}
|
|
20001
20094
|
/** One settings row (or several `inline=` inputs) placed into a section's grid. */
|
|
20002
20095
|
buildInputRow(grid, decls, row) {
|
|
20003
20096
|
const idOf = (inp) => `vela-inp-${row.id}-${inp.key}`;
|
|
20097
|
+
const append = (el) => {
|
|
20098
|
+
grid.appendChild(el);
|
|
20099
|
+
return el;
|
|
20100
|
+
};
|
|
20004
20101
|
if (decls.length > 1) {
|
|
20005
|
-
|
|
20102
|
+
return append(fieldRow({
|
|
20006
20103
|
label: "",
|
|
20007
20104
|
inline: decls.map((d) => ({
|
|
20008
20105
|
label: nameOf(d) || void 0,
|
|
@@ -20013,14 +20110,13 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20013
20110
|
})),
|
|
20014
20111
|
info: [...decls].reverse().find((d) => d.tooltip)?.tooltip ? this.infoButton([...decls].reverse().find((d) => d.tooltip).tooltip) : void 0
|
|
20015
20112
|
}));
|
|
20016
|
-
return;
|
|
20017
20113
|
}
|
|
20018
20114
|
const lead = decls[0];
|
|
20019
20115
|
const id = idOf(lead);
|
|
20020
20116
|
const info = lead.tooltip ? this.infoButton(lead.tooltip) : void 0;
|
|
20021
20117
|
if (lead.type === "bool") {
|
|
20022
20118
|
const current = Boolean(row.values[lead.key] ?? lead.defval);
|
|
20023
|
-
|
|
20119
|
+
return append(fieldRow({
|
|
20024
20120
|
label: nameOf(lead),
|
|
20025
20121
|
id,
|
|
20026
20122
|
bool: true,
|
|
@@ -20028,26 +20124,21 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20028
20124
|
toggle: {
|
|
20029
20125
|
id,
|
|
20030
20126
|
checked: current,
|
|
20031
|
-
onChange: (v) =>
|
|
20032
|
-
row.values[lead.key] = v;
|
|
20033
|
-
this.host.onChange?.({ indicatorId: row.id, key: lead.key, value: v });
|
|
20034
|
-
}
|
|
20127
|
+
onChange: (v) => this.commit(row, lead.key, v)
|
|
20035
20128
|
}
|
|
20036
20129
|
}));
|
|
20037
|
-
return;
|
|
20038
20130
|
}
|
|
20039
20131
|
if (lead.type === "text_area") {
|
|
20040
|
-
|
|
20132
|
+
return append(fieldRow({
|
|
20041
20133
|
label: nameOf(lead),
|
|
20042
20134
|
id,
|
|
20043
20135
|
stacked: true,
|
|
20044
20136
|
info,
|
|
20045
20137
|
control: this.buildControl(row, lead, id)
|
|
20046
20138
|
}));
|
|
20047
|
-
return;
|
|
20048
20139
|
}
|
|
20049
20140
|
const fit = lead.type === "color" || lead.type === "session" || lead.type === "time";
|
|
20050
|
-
|
|
20141
|
+
return append(fieldRow({
|
|
20051
20142
|
label: nameOf(lead),
|
|
20052
20143
|
id,
|
|
20053
20144
|
fit,
|
|
@@ -20059,10 +20150,7 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20059
20150
|
/** Build the typed control for one input, committing edits live via `onChange`. */
|
|
20060
20151
|
buildControl(row, inp, id) {
|
|
20061
20152
|
const current = row.values[inp.key] ?? inp.defval;
|
|
20062
|
-
const emit = (value) =>
|
|
20063
|
-
row.values[inp.key] = value;
|
|
20064
|
-
this.host.onChange?.({ indicatorId: row.id, key: inp.key, value });
|
|
20065
|
-
};
|
|
20153
|
+
const emit = (value) => this.commit(row, inp.key, value);
|
|
20066
20154
|
if (inp.type === "bool") {
|
|
20067
20155
|
return buildFieldControl({ kind: "switch", id, checked: Boolean(current), onChange: (v) => emit(v) }).el;
|
|
20068
20156
|
}
|
|
@@ -20483,6 +20571,14 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20483
20571
|
}
|
|
20484
20572
|
return names.map((name) => ({ name, inputs: byTab.get(name) }));
|
|
20485
20573
|
}
|
|
20574
|
+
function rowVisible(decls, bag) {
|
|
20575
|
+
return decls.some((d) => inputVisible(d.when, bag));
|
|
20576
|
+
}
|
|
20577
|
+
function valuesBag(row) {
|
|
20578
|
+
const bag = {};
|
|
20579
|
+
for (const inp of row.inputs) bag[inp.key] = row.values[inp.key] ?? inp.defval;
|
|
20580
|
+
return bag;
|
|
20581
|
+
}
|
|
20486
20582
|
function groupInputs(inputs) {
|
|
20487
20583
|
const order = [];
|
|
20488
20584
|
const byGroup = /* @__PURE__ */ new Map();
|
|
@@ -20945,6 +21041,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
20945
21041
|
host: this.container,
|
|
20946
21042
|
items,
|
|
20947
21043
|
placement: "bottom-start",
|
|
21044
|
+
// Right-click action menu — checked state reads as a leading ✓ (see the kit Menu).
|
|
21045
|
+
checkmarks: true,
|
|
20948
21046
|
onSelect: (itemId) => {
|
|
20949
21047
|
if (itemId !== "values" || !this.rowMenuId) return;
|
|
20950
21048
|
const target = this.rows.get(this.rowMenuId);
|
|
@@ -21280,6 +21378,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
21280
21378
|
syncRowActions(row) {
|
|
21281
21379
|
const open2 = row.highlighted;
|
|
21282
21380
|
row.controlsEl.style.display = open2 || row.hidden ? "inline-flex" : "none";
|
|
21381
|
+
if (open2) row.el.appendChild(row.statusEl);
|
|
21382
|
+
else row.el.insertBefore(row.statusEl, row.valuesEl);
|
|
21283
21383
|
for (const child of Array.from(row.controlsEl.children)) {
|
|
21284
21384
|
if (!(child instanceof HTMLElement) || child === row.eyeEl) continue;
|
|
21285
21385
|
if (child === row.extrasEl) {
|
|
@@ -21523,6 +21623,148 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
21523
21623
|
}
|
|
21524
21624
|
};
|
|
21525
21625
|
|
|
21626
|
+
// src/renderers/native/chrome/axisLayout.ts
|
|
21627
|
+
var AXIS_MASTER_W = 64;
|
|
21628
|
+
var AXIS_MERGED_W = 56;
|
|
21629
|
+
var PANE_SEPARATOR_PX = 3;
|
|
21630
|
+
function axisColumnX(dataW, column) {
|
|
21631
|
+
return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
|
|
21632
|
+
}
|
|
21633
|
+
|
|
21634
|
+
// src/renderers/native/chrome/AxisScaleButtons.ts
|
|
21635
|
+
var STYLE_ID2 = "vela-axis-scale-buttons";
|
|
21636
|
+
var BTN_PX = 18;
|
|
21637
|
+
var BTN_GAP = 4;
|
|
21638
|
+
var AXIS_BORDER = 1;
|
|
21639
|
+
var CLUSTER_W = AXIS_MASTER_W - AXIS_BORDER;
|
|
21640
|
+
var PAD_TOP = 8;
|
|
21641
|
+
var PAD_BOTTOM = 6;
|
|
21642
|
+
var CLUSTER_H = BTN_PX + PAD_TOP + PAD_BOTTOM;
|
|
21643
|
+
var MIN_PANE_H = 48;
|
|
21644
|
+
function ensureStyles2() {
|
|
21645
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID2)) return;
|
|
21646
|
+
const st = document.createElement("style");
|
|
21647
|
+
st.id = STYLE_ID2;
|
|
21648
|
+
st.textContent = `
|
|
21649
|
+
.vela-axis-btn{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--vela-radius-sm);background:transparent;cursor:pointer;font:600 10px/1 -apple-system,Segoe UI,sans-serif;color:var(--vela-fg-muted);}
|
|
21650
|
+
.vela-axis-btn:hover{background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
21651
|
+
.vela-axis-on,.vela-axis-on:hover{background:var(--vela-selected-bg);color:var(--vela-selected-fg);}
|
|
21652
|
+
`;
|
|
21653
|
+
document.head.appendChild(st);
|
|
21654
|
+
}
|
|
21655
|
+
var AxisScaleButtons = class {
|
|
21656
|
+
constructor(plot, theme, deps) {
|
|
21657
|
+
this.plot = plot;
|
|
21658
|
+
this.theme = theme;
|
|
21659
|
+
this.deps = deps;
|
|
21660
|
+
this.tipDisposers = [];
|
|
21661
|
+
this.hoverPaneId = null;
|
|
21662
|
+
/** Reveal over the master scale column of the pane under the cursor (mouse only —
|
|
21663
|
+
* touch has no hover, and a drag-rescale on the axis must not sprout buttons). */
|
|
21664
|
+
this.onPlotMove = (e) => {
|
|
21665
|
+
if (e.pointerType !== "mouse") return;
|
|
21666
|
+
const rect = this.plot.getBoundingClientRect();
|
|
21667
|
+
const x = e.clientX - rect.left;
|
|
21668
|
+
const gutterLeft = rect.width - this.deps.rightAxis();
|
|
21669
|
+
let hit = null;
|
|
21670
|
+
if (x >= gutterLeft && x <= gutterLeft + AXIS_MASTER_W) {
|
|
21671
|
+
const y = e.clientY - rect.top;
|
|
21672
|
+
for (const p of this.deps.panes()) {
|
|
21673
|
+
if (p.collapsed || p.height < MIN_PANE_H) continue;
|
|
21674
|
+
if (y >= p.top && y <= p.top + p.height) {
|
|
21675
|
+
hit = p.id;
|
|
21676
|
+
break;
|
|
21677
|
+
}
|
|
21678
|
+
}
|
|
21679
|
+
}
|
|
21680
|
+
this.setHoverPane(hit);
|
|
21681
|
+
if (hit) this.sync();
|
|
21682
|
+
};
|
|
21683
|
+
this.onPlotLeave = () => this.setHoverPane(null);
|
|
21684
|
+
ensureStyles2();
|
|
21685
|
+
this.cluster = document.createElement("div");
|
|
21686
|
+
Object.assign(this.cluster.style, {
|
|
21687
|
+
position: "absolute",
|
|
21688
|
+
display: "none",
|
|
21689
|
+
justifyContent: "center",
|
|
21690
|
+
alignItems: "center",
|
|
21691
|
+
gap: `${BTN_GAP}px`,
|
|
21692
|
+
padding: `${PAD_TOP}px 0 ${PAD_BOTTOM}px`,
|
|
21693
|
+
width: `${CLUSTER_W}px`,
|
|
21694
|
+
boxSizing: "border-box",
|
|
21695
|
+
zIndex: "6",
|
|
21696
|
+
pointerEvents: "auto"
|
|
21697
|
+
});
|
|
21698
|
+
this.applyTheme();
|
|
21699
|
+
this.autoBtn = this.button("A", "Auto (fits data to screen)", () => this.deps.onToggleAuto(this.hoverPaneId ?? ""));
|
|
21700
|
+
this.logBtn = this.button("L", "Logarithmic scale", () => this.deps.onToggleLog(this.hoverPaneId ?? ""));
|
|
21701
|
+
this.cluster.append(this.autoBtn, this.logBtn);
|
|
21702
|
+
this.plot.appendChild(this.cluster);
|
|
21703
|
+
this.plot.addEventListener("pointermove", this.onPlotMove);
|
|
21704
|
+
this.plot.addEventListener("pointerleave", this.onPlotLeave);
|
|
21705
|
+
}
|
|
21706
|
+
button(label, title, onClick) {
|
|
21707
|
+
const b = document.createElement("button");
|
|
21708
|
+
b.type = "button";
|
|
21709
|
+
b.setAttribute("aria-label", title);
|
|
21710
|
+
b.textContent = label;
|
|
21711
|
+
b.className = "vela-axis-btn";
|
|
21712
|
+
Object.assign(b.style, { width: `${BTN_PX}px`, height: `${BTN_PX}px` });
|
|
21713
|
+
this.tipDisposers.push(attachChromeTooltip(b, {
|
|
21714
|
+
host: this.plot,
|
|
21715
|
+
theme: () => this.theme,
|
|
21716
|
+
text: () => title,
|
|
21717
|
+
placement: "above"
|
|
21718
|
+
}));
|
|
21719
|
+
b.addEventListener("click", (e) => {
|
|
21720
|
+
e.stopPropagation();
|
|
21721
|
+
onClick();
|
|
21722
|
+
this.sync();
|
|
21723
|
+
});
|
|
21724
|
+
return b;
|
|
21725
|
+
}
|
|
21726
|
+
/** Re-tint the chip when the plot background/border change (theme swap, config edit). */
|
|
21727
|
+
setTheme(theme) {
|
|
21728
|
+
this.theme = theme;
|
|
21729
|
+
this.applyTheme();
|
|
21730
|
+
}
|
|
21731
|
+
/** The strip is SOLID chart background: the buttons cover axis labels, never blend with them. */
|
|
21732
|
+
applyTheme() {
|
|
21733
|
+
this.cluster.style.background = this.theme.background;
|
|
21734
|
+
}
|
|
21735
|
+
setHoverPane(paneId) {
|
|
21736
|
+
if (this.hoverPaneId === paneId) return;
|
|
21737
|
+
this.hoverPaneId = paneId;
|
|
21738
|
+
this.reposition();
|
|
21739
|
+
}
|
|
21740
|
+
/** Re-place (or hide) the cluster after a hover or pane-layout change. */
|
|
21741
|
+
reposition() {
|
|
21742
|
+
const pane = this.hoverPaneId ? this.deps.panes().find((p) => p.id === this.hoverPaneId) : void 0;
|
|
21743
|
+
if (!pane || pane.collapsed || pane.height < MIN_PANE_H) {
|
|
21744
|
+
this.cluster.style.display = "none";
|
|
21745
|
+
return;
|
|
21746
|
+
}
|
|
21747
|
+
this.cluster.style.right = `${this.deps.rightAxis() - AXIS_MASTER_W}px`;
|
|
21748
|
+
this.cluster.style.top = `${pane.top + pane.height - CLUSTER_H}px`;
|
|
21749
|
+
this.cluster.style.display = "flex";
|
|
21750
|
+
this.sync();
|
|
21751
|
+
}
|
|
21752
|
+
/** Reflect the hovered pane's current auto/log state on the buttons. */
|
|
21753
|
+
sync() {
|
|
21754
|
+
const pane = this.deps.panes().find((p) => p.id === this.hoverPaneId);
|
|
21755
|
+
if (!pane) return;
|
|
21756
|
+
this.autoBtn.classList.toggle("vela-axis-on", pane.auto);
|
|
21757
|
+
this.logBtn.classList.toggle("vela-axis-on", pane.log);
|
|
21758
|
+
}
|
|
21759
|
+
destroy() {
|
|
21760
|
+
this.plot.removeEventListener("pointermove", this.onPlotMove);
|
|
21761
|
+
this.plot.removeEventListener("pointerleave", this.onPlotLeave);
|
|
21762
|
+
for (const dispose of this.tipDisposers) dispose();
|
|
21763
|
+
this.tipDisposers.length = 0;
|
|
21764
|
+
this.cluster.remove();
|
|
21765
|
+
}
|
|
21766
|
+
};
|
|
21767
|
+
|
|
21526
21768
|
// src/renderers/shared/TableOverlay.ts
|
|
21527
21769
|
var SIZE_PX3 = {
|
|
21528
21770
|
auto: 13,
|
|
@@ -21880,6 +22122,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
21880
22122
|
var BASELINE_FILL_ALPHA = 0.25;
|
|
21881
22123
|
var BASELINE_FILL_ALPHA_FAR = 0.05;
|
|
21882
22124
|
var BASELINE_LEVEL_DEFAULT = 50;
|
|
22125
|
+
var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
|
|
22126
|
+
var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
|
|
21883
22127
|
function defaultChartStyle() {
|
|
21884
22128
|
return {
|
|
21885
22129
|
chartTypes: {},
|
|
@@ -21910,7 +22154,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
21910
22154
|
bottomFillColor2: null,
|
|
21911
22155
|
width: 2,
|
|
21912
22156
|
baselineLevel: BASELINE_LEVEL_DEFAULT
|
|
21913
|
-
}
|
|
22157
|
+
},
|
|
22158
|
+
sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE }
|
|
21914
22159
|
};
|
|
21915
22160
|
}
|
|
21916
22161
|
var LINE_STYLES = ["solid", "dashed", "dotted"];
|
|
@@ -22044,6 +22289,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22044
22289
|
const area = asObject(p.area);
|
|
22045
22290
|
const baseline = asObject(p.baseline);
|
|
22046
22291
|
const series = asObject(p.series);
|
|
22292
|
+
const sessions = asObject(p.sessions);
|
|
22047
22293
|
const stacking = asObject(p.stacking);
|
|
22048
22294
|
const stackSeries = asObject(stacking.series);
|
|
22049
22295
|
return {
|
|
@@ -22136,6 +22382,10 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22136
22382
|
baseline: series.baseline === null ? null : isNum(series.baseline) ? series.baseline : base.series.baseline,
|
|
22137
22383
|
spacing: isNum(series.spacing) ? clampSpacing(series.spacing) : base.series.spacing
|
|
22138
22384
|
},
|
|
22385
|
+
sessions: {
|
|
22386
|
+
premarketColor: isColor(sessions.premarketColor) ? sessions.premarketColor : base.sessions.premarketColor,
|
|
22387
|
+
postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor
|
|
22388
|
+
},
|
|
22139
22389
|
stacking: {
|
|
22140
22390
|
candles: isNum(stacking.candles) ? stacking.candles : base.stacking.candles,
|
|
22141
22391
|
// Additive like `chartTypes`: a patch names only the ids it carries; the rest keep
|
|
@@ -22382,6 +22632,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22382
22632
|
this.tradeMarkers = defaultTradeMarkersState();
|
|
22383
22633
|
/** Renderer-owned shaded time bands (session highlighting), behind grid + data. */
|
|
22384
22634
|
this.highlights = [];
|
|
22635
|
+
/** Pre/post-market bands pushed by the host (`sessionZones` feature); null ⇒ no sessions. */
|
|
22636
|
+
this.sessionZones = null;
|
|
22385
22637
|
/** Draw-order key of the price candles, relative to indicator series z (see `seriesZ`).
|
|
22386
22638
|
* Indicators with z below this draw BEHIND the candles; at/above draw in front.
|
|
22387
22639
|
* Default 0 with indicators mounting at z < 0 ⇒ the price reads on top of every overlay,
|
|
@@ -22416,6 +22668,15 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22416
22668
|
if (!this.orderedCache) this.orderedCache = [...this.panes.values()].sort((a, b) => a.order - b.order);
|
|
22417
22669
|
return this.orderedCache;
|
|
22418
22670
|
}
|
|
22671
|
+
/** The session zones resolved into colored bands (pre/post-market washes from the
|
|
22672
|
+
* config's session colors) — consumed by the same painting path as {@link highlights}. */
|
|
22673
|
+
sessionHighlightBands() {
|
|
22674
|
+
if (!this.sessionZones) return [];
|
|
22675
|
+
const out = [];
|
|
22676
|
+
for (const [from, to] of this.sessionZones.pre) out.push({ from, to, color: this.style.sessions.premarketColor });
|
|
22677
|
+
for (const [from, to] of this.sessionZones.post) out.push({ from, to, color: this.style.sessions.postmarketColor });
|
|
22678
|
+
return out;
|
|
22679
|
+
}
|
|
22419
22680
|
indicatorsForPane(paneId) {
|
|
22420
22681
|
const out = [];
|
|
22421
22682
|
for (const model of this.indicators.values()) if (model.paneId === paneId) out.push(model);
|
|
@@ -22499,6 +22760,12 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22499
22760
|
assignIndicatorZ(id) {
|
|
22500
22761
|
if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.bottomZ() - 1);
|
|
22501
22762
|
}
|
|
22763
|
+
/** Mount-time default for a LAYER-BACKED native (it paints on a canvas stacked above the
|
|
22764
|
+
* data canvas by default): top of the stack, so the recorded order tells the truth from
|
|
22765
|
+
* the first frame. Keeps an existing key, so a restored stack survives the remount. */
|
|
22766
|
+
assignIndicatorZTop(id) {
|
|
22767
|
+
if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.topZ() + 1);
|
|
22768
|
+
}
|
|
22502
22769
|
forgetIndicatorZ(id) {
|
|
22503
22770
|
this.seriesZ.delete(id);
|
|
22504
22771
|
}
|
|
@@ -23346,10 +23613,12 @@ void main() {
|
|
|
23346
23613
|
}
|
|
23347
23614
|
}
|
|
23348
23615
|
}
|
|
23349
|
-
/** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
|
|
23616
|
+
/** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
|
|
23617
|
+
* Session-zone washes (pre/post-market) paint first, host highlights on top. */
|
|
23350
23618
|
emitHighlights(b, scene, pane, coords) {
|
|
23351
|
-
|
|
23352
|
-
|
|
23619
|
+
const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
|
|
23620
|
+
if (bands.length === 0) return;
|
|
23621
|
+
for (const band of bands) {
|
|
23353
23622
|
const x1 = coords.timeToX(band.from);
|
|
23354
23623
|
const x2 = coords.timeToX(band.to);
|
|
23355
23624
|
if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
|
|
@@ -25323,10 +25592,12 @@ void main() {
|
|
|
25323
25592
|
ctx.fillStyle = bg.color;
|
|
25324
25593
|
ctx.fillRect(x1, pane.bounds.top, x2 - x1, pane.bounds.height);
|
|
25325
25594
|
}
|
|
25326
|
-
/** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
|
|
25595
|
+
/** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
|
|
25596
|
+
* Session-zone washes (pre/post-market) paint first, host highlights on top. */
|
|
25327
25597
|
drawHighlights(ctx, scene, coords) {
|
|
25328
|
-
|
|
25329
|
-
|
|
25598
|
+
const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
|
|
25599
|
+
if (bands.length === 0) return;
|
|
25600
|
+
for (const band of bands) {
|
|
25330
25601
|
const x1 = coords.timeToX(band.from);
|
|
25331
25602
|
const x2 = coords.timeToX(band.to);
|
|
25332
25603
|
if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
|
|
@@ -26100,14 +26371,6 @@ void main() {
|
|
|
26100
26371
|
}
|
|
26101
26372
|
};
|
|
26102
26373
|
|
|
26103
|
-
// src/renderers/native/chrome/axisLayout.ts
|
|
26104
|
-
var AXIS_MASTER_W = 64;
|
|
26105
|
-
var AXIS_MERGED_W = 56;
|
|
26106
|
-
var PANE_SEPARATOR_PX = 3;
|
|
26107
|
-
function axisColumnX(dataW, column) {
|
|
26108
|
-
return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
|
|
26109
|
-
}
|
|
26110
|
-
|
|
26111
26374
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
26112
26375
|
var ChromeRenderer = class {
|
|
26113
26376
|
constructor() {
|
|
@@ -26990,6 +27253,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
26990
27253
|
for (const hr of hs.rows) {
|
|
26991
27254
|
if (hr.kind === "heading") body.append(this.sectionTitle(hr.label));
|
|
26992
27255
|
else if (hr.kind === "toggle") body.append(this.boolRow(hr.label, hr.get(), (v) => hr.set(v)));
|
|
27256
|
+
else if (hr.kind === "color") body.append(this.colorRow(hr.label, hr.get(), (v) => hr.set(v)));
|
|
26993
27257
|
else body.append(this.selectRowLabeled(hr.label, hr.get(), hr.options.map((o) => [o, o]), (v) => hr.set(v)));
|
|
26994
27258
|
}
|
|
26995
27259
|
}
|
|
@@ -29714,14 +29978,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29714
29978
|
// src/renderers/native/drawings/DrawingSettingsPopup.ts
|
|
29715
29979
|
var BTN = 30;
|
|
29716
29980
|
var ICON = 21;
|
|
29717
|
-
var
|
|
29981
|
+
var STYLE_ID3 = "vela-drawing-popup-styles";
|
|
29718
29982
|
var STYLE_REV = "3";
|
|
29719
|
-
function
|
|
29983
|
+
function ensureStyles3() {
|
|
29720
29984
|
if (typeof document === "undefined") return;
|
|
29721
|
-
const existing = document.getElementById(
|
|
29985
|
+
const existing = document.getElementById(STYLE_ID3);
|
|
29722
29986
|
if (existing?.dataset.rev === STYLE_REV) return;
|
|
29723
29987
|
const s = existing ?? document.createElement("style");
|
|
29724
|
-
s.id =
|
|
29988
|
+
s.id = STYLE_ID3;
|
|
29725
29989
|
s.dataset.rev = STYLE_REV;
|
|
29726
29990
|
s.textContent = `
|
|
29727
29991
|
.vela-dpop-btn{background:transparent;color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
|
|
@@ -29778,7 +30042,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29778
30042
|
* `onClose` fires on an outside-click dismissal (not a programmatic close). */
|
|
29779
30043
|
open(drawing, anchor, actions, onClose) {
|
|
29780
30044
|
this.close();
|
|
29781
|
-
|
|
30045
|
+
ensureStyles3();
|
|
29782
30046
|
this.onClose = onClose ?? null;
|
|
29783
30047
|
const t = this.theme;
|
|
29784
30048
|
const schema = drawing.schema();
|
|
@@ -30506,7 +30770,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
30506
30770
|
this.onCollapse = options.onCollapse ?? (() => {
|
|
30507
30771
|
});
|
|
30508
30772
|
this.onDrawingsSync = options.onDrawingsSync ?? null;
|
|
30509
|
-
|
|
30773
|
+
ensureStyles4();
|
|
30510
30774
|
this.root = document.createElement("div");
|
|
30511
30775
|
this.root.className = "vela-dtb";
|
|
30512
30776
|
this.styleRoot();
|
|
@@ -30989,13 +31253,13 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
30989
31253
|
}
|
|
30990
31254
|
};
|
|
30991
31255
|
var MAGNET_ID = "__magnet__";
|
|
30992
|
-
var
|
|
30993
|
-
function
|
|
31256
|
+
var STYLE_ID4 = "vela-drawing-toolbar-styles";
|
|
31257
|
+
function ensureStyles4() {
|
|
30994
31258
|
if (typeof document === "undefined") return;
|
|
30995
|
-
let s = document.getElementById(
|
|
31259
|
+
let s = document.getElementById(STYLE_ID4);
|
|
30996
31260
|
if (!s) {
|
|
30997
31261
|
s = document.createElement("style");
|
|
30998
|
-
s.id =
|
|
31262
|
+
s.id = STYLE_ID4;
|
|
30999
31263
|
document.head.appendChild(s);
|
|
31000
31264
|
}
|
|
31001
31265
|
s.textContent = `
|
|
@@ -32260,13 +32524,26 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32260
32524
|
return [...registry3.values()];
|
|
32261
32525
|
}
|
|
32262
32526
|
|
|
32527
|
+
// src/renderers/native/core/layerStacking.ts
|
|
32528
|
+
function stackLayers(entries, candleZ) {
|
|
32529
|
+
const keyed = entries.map((e) => ({
|
|
32530
|
+
id: e.id,
|
|
32531
|
+
key: e.ownerZ ?? (e.placement === "below-data" ? -Infinity : candleZ)
|
|
32532
|
+
}));
|
|
32533
|
+
keyed.sort((a, b) => a.key - b.key);
|
|
32534
|
+
const below = [];
|
|
32535
|
+
const above = [];
|
|
32536
|
+
for (const k of keyed) (k.key < candleZ ? below : above).push(k.id);
|
|
32537
|
+
return { below, above };
|
|
32538
|
+
}
|
|
32539
|
+
|
|
32263
32540
|
// src/renderers/native/chrome/luxalgo-logos.ts
|
|
32264
32541
|
var LUXALGO_SYMBOL_SVG = '<svg viewBox="0 0 45 40" aria-hidden="true"><g fill="currentColor"><path d="m40.25 38 4.58-7.998L28.802 2l-16.03 28 9.16-.001 6.87-12z"/><path d="M34.525 32.002 9.33 31.997 27.655 0h-9.158L.18 31.993 4.759 40h34.347z"/></g></svg>';
|
|
32265
32542
|
var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M 614 94.500 L 614 189 633.500 189 L 653 189 653 94.500 L 653 0 633.500 0 L 614 0 614 94.500 M 0 97.500 L 0 189 63.023 189 L 126.045 189 125.773 170.750 L 125.500 152.500 84.250 152.239 L 43 151.978 43 78.989 L 43 6 21.500 6 L 0 6 0 97.500 M 450.387 96.451 C 421.871 146.253, 398.306 186.994, 398.020 186.987 C 397.423 186.972, 362 124.724, 362 123.691 C 362 123.316, 370.100 108.818, 380 91.472 C 389.900 74.127, 398 59.500, 398 58.968 C 398 58.380, 391.007 58, 380.210 58 L 362.420 58 348.060 83.138 C 334.400 107.052, 333.637 108.160, 332.399 105.888 C 331.682 104.575, 325.287 93.375, 318.187 81 L 305.277 58.500 287.139 58.227 C 277.162 58.078, 269 58.186, 269 58.469 C 269 58.751, 277.154 73.274, 287.120 90.741 C 297.086 108.209, 305.261 122.983, 305.287 123.574 C 305.312 124.164, 297.318 138.789, 287.522 156.074 C 277.725 173.358, 269.524 187.838, 269.296 188.250 C 269.068 188.662, 276.977 189, 286.870 189 L 304.859 189 319.163 163.959 C 331.321 142.674, 333.647 139.165, 334.670 140.563 C 335.331 141.468, 341.864 152.737, 349.186 165.606 L 362.500 189.003 402.442 188.752 L 442.384 188.500 472.080 136.695 C 488.413 108.203, 501.939 84.729, 502.138 84.531 C 502.337 84.333, 516 107.757, 532.500 136.583 L 562.500 188.994 584.750 188.997 C 596.987 188.999, 607 188.854, 607 188.675 C 607 188.319, 503.014 6.686, 502.532 6.201 C 502.369 6.037, 478.904 46.649, 450.387 96.451 M 717.500 55.017 C 697.175 57.389, 680.641 68.752, 672.036 86.264 C 665.866 98.818, 663.840 108.024, 663.840 123.500 C 663.840 133.409, 664.328 138.355, 665.892 144.300 C 672.076 167.810, 689.191 184.757, 711.529 189.489 C 730.549 193.518, 747.264 189.540, 761.282 177.648 C 764.944 174.542, 768.178 172, 768.470 172 C 769.345 172, 769.030 185.018, 767.979 192.285 C 765.618 208.613, 755.148 218.672, 738.473 220.632 C 732.586 221.324, 723.136 219.599, 717.709 216.843 C 712.693 214.295, 706.636 207.946, 703.972 202.443 C 702.789 199.999, 701.749 198, 701.661 198.001 C 700.201 198.007, 665.596 205.071, 665.230 205.437 C 664.326 206.341, 669.156 218.313, 672.607 223.721 C 682.146 238.672, 700.008 248.257, 723.500 251.033 C 758.132 255.124, 788.930 241.875, 800.287 218 C 806.923 204.047, 807 203.001, 807 126.542 L 807 58 788.500 58 L 770 58 770 66.500 C 770 71.175, 769.641 75, 769.203 75 C 768.765 75, 766.627 73.142, 764.453 70.872 C 752.805 58.708, 735.614 52.903, 717.500 55.017 M 878.500 55.031 C 850.107 59.412, 830.755 75.064, 822.285 100.500 C 819.905 107.644, 819.614 110.051, 819.565 123 C 819.501 139.581, 820.786 145.825, 826.763 158 C 832.104 168.880, 842.842 179.424, 854.077 184.822 C 867.651 191.343, 873.132 192.462, 891.500 192.462 C 909.868 192.462, 915.349 191.343, 928.923 184.822 C 944.653 177.265, 956.667 162.306, 961.710 144 C 963.074 139.048, 963.481 134.047, 963.418 123 C 963.346 110.140, 963.038 107.595, 960.694 100.500 C 953.325 78.193, 936.695 62.854, 913.500 56.969 C 904.262 54.625, 887.240 53.682, 878.500 55.031 M 134.015 102.750 C 134.027 139.250, 134.310 148.689, 135.549 153.947 C 141.139 177.663, 159.815 193, 183.105 193 C 196.453 193, 210.163 186.171, 217.936 175.650 C 219.826 173.093, 221.738 171, 222.186 171 C 222.634 171, 223 175.050, 223 180 L 223 189 242.507 189 L 262.014 189 261.757 123.750 L 261.500 58.500 242.500 58.500 L 223.500 58.500 223 99.500 C 222.513 139.406, 222.441 140.610, 220.289 144.634 C 209.729 164.377, 183.014 164.880, 174.299 145.500 C 172.677 141.895, 172.469 137.382, 172.190 99.750 L 171.880 58 152.940 58 L 134 58 134.015 102.750 M 729.932 87.998 C 719.311 89.821, 710.155 97.164, 705.141 107.880 C 702.851 112.772, 702.510 114.798, 702.505 123.521 C 702.500 132.834, 702.732 134.014, 705.787 140.218 C 709.761 148.290, 715.989 154.097, 723.853 157.063 C 731.409 159.912, 744.359 159.466, 750.795 156.136 C 767.289 147.601, 774.103 127.713, 767.019 108.780 C 761.433 93.851, 746.051 85.232, 729.932 87.998 M 883.972 88.348 C 865.749 92.310, 854.873 109.966, 858.119 130.316 C 860.933 147.963, 872.346 158.830, 889.084 159.799 C 900.714 160.472, 906.867 158.234, 914.650 150.497 C 919.856 145.323, 921.123 143.340, 922.900 137.587 C 925.412 129.456, 925.655 118.570, 923.489 111.275 C 918.479 94.405, 901.478 84.541, 883.972 88.348"/></svg>';
|
|
32266
32543
|
|
|
32267
32544
|
// src/renderers/native/chrome/AttributionMark.ts
|
|
32268
32545
|
var ATTRIBUTION_URL = "https://luxalgo.com/vela";
|
|
32269
|
-
var
|
|
32546
|
+
var STYLE_ID5 = "vela-attribution-styles";
|
|
32270
32547
|
var CSS2 = `
|
|
32271
32548
|
.vela-attribution {
|
|
32272
32549
|
text-decoration: none;
|
|
@@ -32329,11 +32606,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32329
32606
|
[data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
|
|
32330
32607
|
[data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
|
|
32331
32608
|
`;
|
|
32332
|
-
function
|
|
32333
|
-
let s = doc.getElementById(
|
|
32609
|
+
function ensureStyles5(doc) {
|
|
32610
|
+
let s = doc.getElementById(STYLE_ID5);
|
|
32334
32611
|
if (!s) {
|
|
32335
32612
|
s = doc.createElement("style");
|
|
32336
|
-
s.id =
|
|
32613
|
+
s.id = STYLE_ID5;
|
|
32337
32614
|
doc.head.appendChild(s);
|
|
32338
32615
|
}
|
|
32339
32616
|
s.textContent = CSS2;
|
|
@@ -32362,7 +32639,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32362
32639
|
return el;
|
|
32363
32640
|
}
|
|
32364
32641
|
function createAttributionMark(doc, background) {
|
|
32365
|
-
|
|
32642
|
+
ensureStyles5(doc);
|
|
32366
32643
|
const a = doc.createElement("a");
|
|
32367
32644
|
a.className = "vela-attribution";
|
|
32368
32645
|
a.href = ATTRIBUTION_URL;
|
|
@@ -32760,6 +33037,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32760
33037
|
this.volumeRenderer = new VolumeRenderer();
|
|
32761
33038
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
32762
33039
|
this.extLayers = [];
|
|
33040
|
+
/** Last applied layer-canvas order (ids below + above the data canvas) — re-slotted only on change. */
|
|
33041
|
+
this.layerOrderSig = "";
|
|
32763
33042
|
// The attribution mark (see chrome/AttributionMark + the NOTICE file): default-on;
|
|
32764
33043
|
// disabling requires an equivalent visible attribution elsewhere in the host UI.
|
|
32765
33044
|
this.attributionEl = null;
|
|
@@ -32874,6 +33153,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32874
33153
|
// total right-gutter width (master column + merged-scale columns)
|
|
32875
33154
|
this.paneControls = null;
|
|
32876
33155
|
// per-pane hover button cluster (top-right)
|
|
33156
|
+
this.axisScaleButtons = null;
|
|
33157
|
+
// A/L hover buttons at the bottom of each pane's price scale
|
|
32877
33158
|
this.paneActionCbs = /* @__PURE__ */ new Set();
|
|
32878
33159
|
// ── data window (crosshair OHLC + per-series readout, pulled by host panels) ──
|
|
32879
33160
|
this.hoverLogical = null;
|
|
@@ -32917,7 +33198,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32917
33198
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
32918
33199
|
this.tableOverlays = /* @__PURE__ */ new Map();
|
|
32919
33200
|
this.name = "native";
|
|
32920
|
-
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
|
|
33201
|
+
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
|
|
32921
33202
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
32922
33203
|
* so moving onto the button doesn't count as leaving). */
|
|
32923
33204
|
this.onScrollProximityMove = (e) => {
|
|
@@ -33033,6 +33314,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33033
33314
|
case "highlights":
|
|
33034
33315
|
this.scene.highlights = sanitizeHighlights(value);
|
|
33035
33316
|
break;
|
|
33317
|
+
case "sessionZones":
|
|
33318
|
+
this.scene.sessionZones = sanitizeSessionZones(value);
|
|
33319
|
+
break;
|
|
33036
33320
|
case "gridlines":
|
|
33037
33321
|
this.scene.showGrid = Boolean(value);
|
|
33038
33322
|
break;
|
|
@@ -33149,6 +33433,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33149
33433
|
return this.scene.indicatorZOrder();
|
|
33150
33434
|
case "highlights":
|
|
33151
33435
|
return this.scene.highlights;
|
|
33436
|
+
case "sessionZones":
|
|
33437
|
+
return this.scene.sessionZones;
|
|
33152
33438
|
case "gridlines":
|
|
33153
33439
|
return this.scene.showGrid;
|
|
33154
33440
|
case "axisLabels":
|
|
@@ -33358,7 +33644,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33358
33644
|
baselineLevel: s.baseline.baselineLevel
|
|
33359
33645
|
};
|
|
33360
33646
|
})(),
|
|
33361
|
-
series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale }
|
|
33647
|
+
series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale },
|
|
33648
|
+
sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor }
|
|
33362
33649
|
};
|
|
33363
33650
|
}
|
|
33364
33651
|
/**
|
|
@@ -33440,6 +33727,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33440
33727
|
width: next2.baseline.width,
|
|
33441
33728
|
baselineLevel: next2.baseline.baselineLevel
|
|
33442
33729
|
};
|
|
33730
|
+
s.sessions = { premarketColor: next2.sessions.premarketColor, postmarketColor: next2.sessions.postmarketColor };
|
|
33443
33731
|
this.setPriceStyle(next2.series.style);
|
|
33444
33732
|
this.scene.candleOverride = candleOverrideFor(this.scene.priceStyle, s.chartTypes);
|
|
33445
33733
|
this.scene.baselineValue = next2.series.baseline;
|
|
@@ -33453,6 +33741,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33453
33741
|
}
|
|
33454
33742
|
this.inputsUI?.setTheme(this.theme);
|
|
33455
33743
|
this.paneControls?.setTheme(this.theme);
|
|
33744
|
+
this.axisScaleButtons?.setTheme(this.theme);
|
|
33456
33745
|
this.userDrawings?.setTheme(this.chromeTheme());
|
|
33457
33746
|
this.refreshScrollButtonTheme();
|
|
33458
33747
|
this.refreshAttributionColor();
|
|
@@ -33680,9 +33969,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33680
33969
|
if (el.getAttribute("data-vela-screenshot") === "under") rasterizeOverlay(ctx, el, frame);
|
|
33681
33970
|
}
|
|
33682
33971
|
}
|
|
33683
|
-
const
|
|
33684
|
-
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
33685
|
-
for (const canvas of [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas]) {
|
|
33972
|
+
for (const canvas of [...this.canvasPile(), this.chromeCanvas, this.drawingsCanvas]) {
|
|
33686
33973
|
if (canvas && canvas.width > 0 && canvas.height > 0) ctx.drawImage(canvas, 0, 0);
|
|
33687
33974
|
}
|
|
33688
33975
|
if (frame) {
|
|
@@ -33791,7 +34078,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33791
34078
|
this.surfaceBackground = theme.background;
|
|
33792
34079
|
this.surfaceTextColor = theme.textColor;
|
|
33793
34080
|
this.wrapper = document.createElement("div");
|
|
33794
|
-
Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair" });
|
|
34081
|
+
Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair", userSelect: "none", webkitUserSelect: "none" });
|
|
33795
34082
|
applyChromeTokens(this.wrapper, this.chromeTheme());
|
|
33796
34083
|
this.volumeCanvas = document.createElement("canvas");
|
|
33797
34084
|
Object.assign(this.volumeCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
@@ -33816,6 +34103,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33816
34103
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
33817
34104
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
33818
34105
|
this.plot.append(...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
34106
|
+
this.layerOrderSig = "";
|
|
33819
34107
|
this.wrapper.appendChild(this.plot);
|
|
33820
34108
|
this.factoryConfig = this.getConfig();
|
|
33821
34109
|
this.attributionEl = this.buildAttributionEl();
|
|
@@ -33960,6 +34248,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33960
34248
|
this.emitPaneAction({ type: "maximize", paneId, maximized });
|
|
33961
34249
|
}
|
|
33962
34250
|
});
|
|
34251
|
+
this.axisScaleButtons = new AxisScaleButtons(this.plot, theme, {
|
|
34252
|
+
panes: () => this.axisScaleViews(),
|
|
34253
|
+
rightAxis: () => this.rightAxisW,
|
|
34254
|
+
onToggleAuto: (paneId) => this.togglePaneAuto(paneId),
|
|
34255
|
+
onToggleLog: (paneId) => {
|
|
34256
|
+
const pane = this.scene.panes.get(paneId);
|
|
34257
|
+
if (pane) this.setPaneLog(paneId, !paneLogScale(this.scene, pane));
|
|
34258
|
+
}
|
|
34259
|
+
});
|
|
33963
34260
|
this.resizeObserver = new ResizeObserver(() => this.resize());
|
|
33964
34261
|
this.resizeObserver.observe(this.wrapper);
|
|
33965
34262
|
this.watchDpr();
|
|
@@ -34036,6 +34333,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34036
34333
|
this.applyBackground();
|
|
34037
34334
|
this.inputsUI.setTheme(theme);
|
|
34038
34335
|
this.paneControls?.setTheme(this.theme);
|
|
34336
|
+
this.axisScaleButtons?.setTheme(this.theme);
|
|
34039
34337
|
this.settingsDialog?.refreshConfig(this.getConfig());
|
|
34040
34338
|
this.syncThemeControl();
|
|
34041
34339
|
this.settingsDialog?.setTheme(this.theme);
|
|
@@ -34084,6 +34382,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34084
34382
|
this.liveRegion = null;
|
|
34085
34383
|
this.inputsUI?.destroy();
|
|
34086
34384
|
this.paneControls?.destroy();
|
|
34385
|
+
this.axisScaleButtons?.destroy();
|
|
34087
34386
|
for (const overlay of this.tableOverlays.values()) overlay.destroy();
|
|
34088
34387
|
this.tableOverlays.clear();
|
|
34089
34388
|
this.resizeObserver?.disconnect();
|
|
@@ -34333,7 +34632,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34333
34632
|
mountIndicator(model) {
|
|
34334
34633
|
this.scene.indicators.set(model.id, model);
|
|
34335
34634
|
this.refreshAnchorOffset(model);
|
|
34336
|
-
this.scene.
|
|
34635
|
+
if (model.native && this.extLayers.some((l) => l.def.id === model.native.type)) this.scene.assignIndicatorZTop(model.id);
|
|
34636
|
+
else this.scene.assignIndicatorZ(model.id);
|
|
34337
34637
|
this.inputsUI.upsert(model.id, model.shorttitle ?? model.title, model.inputs, model.inputValues, model.paneId, {
|
|
34338
34638
|
native: !!model.native,
|
|
34339
34639
|
...model.shorttitle ? { settingsTitle: model.title } : {}
|
|
@@ -35152,10 +35452,17 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35152
35452
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
35153
35453
|
for (const l of this.extLayers) {
|
|
35154
35454
|
if (!l.def.repaintOnCursor) continue;
|
|
35155
|
-
|
|
35455
|
+
const lp = this.layerPane(l.def.id) ?? pane;
|
|
35456
|
+
if (lp.collapsed) continue;
|
|
35457
|
+
l.instance.render(this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs));
|
|
35156
35458
|
if (this.animZoom && l.instance.animating?.()) this.animator.start();
|
|
35157
35459
|
}
|
|
35158
35460
|
}
|
|
35461
|
+
/** Blank one SDK layer canvas (a collapsed host pane suppresses the layer's painting). */
|
|
35462
|
+
clearLayerCanvas(canvas) {
|
|
35463
|
+
if (canvas.width === 0 || canvas.height === 0) return;
|
|
35464
|
+
canvas.getContext("2d")?.clearRect(0, 0, canvas.width, canvas.height);
|
|
35465
|
+
}
|
|
35159
35466
|
/** One frame's args for an SDK renderer layer (shared by the data + cursor paint paths). */
|
|
35160
35467
|
extLayerArgs(id, scale, bounds, nowMs) {
|
|
35161
35468
|
return {
|
|
@@ -35174,6 +35481,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35174
35481
|
}
|
|
35175
35482
|
/** Paint the below-data (L-1) + geometry (L0) + chrome (L1) layers from the current scene/coords. */
|
|
35176
35483
|
paintData() {
|
|
35484
|
+
this.syncLayerCanvasOrder();
|
|
35177
35485
|
this.stampScaleInvert();
|
|
35178
35486
|
this.backend.modelAlpha = this.modelAlpha;
|
|
35179
35487
|
this.backend.candleBodyAlpha = this.candleBodyAlpha;
|
|
@@ -35204,9 +35512,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35204
35512
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
35205
35513
|
let folded = null;
|
|
35206
35514
|
for (const l of this.extLayers) {
|
|
35207
|
-
const
|
|
35515
|
+
const lp = this.layerPane(l.def.id) ?? pane;
|
|
35516
|
+
if (lp.collapsed) {
|
|
35517
|
+
this.clearLayerCanvas(l.canvas);
|
|
35518
|
+
continue;
|
|
35519
|
+
}
|
|
35520
|
+
const args = this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs);
|
|
35208
35521
|
l.instance.render(args);
|
|
35209
|
-
folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
|
|
35522
|
+
if (lp === pane) folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
|
|
35210
35523
|
if (this.animZoom && l.instance.animating?.()) this.animator.start();
|
|
35211
35524
|
}
|
|
35212
35525
|
if (folded) {
|
|
@@ -35368,6 +35681,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35368
35681
|
pane.scaleTarget = { min: 0, max: maxVol / VOLUME_PANE_FILL_FRAC };
|
|
35369
35682
|
pane.axisFormat = "volume";
|
|
35370
35683
|
}
|
|
35684
|
+
} else if (this.layerNativesOwnPane(pane, masterModels)) {
|
|
35685
|
+
pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
35686
|
+
pane.percentBaseline = this.bars[i0]?.close ?? 0;
|
|
35371
35687
|
}
|
|
35372
35688
|
if (pane === pricePane && this.scene.tradeMarkers.visible && pane.bounds.height > 0) {
|
|
35373
35689
|
const th = this.tradesScaleHints(vr);
|
|
@@ -35454,6 +35770,59 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35454
35770
|
}
|
|
35455
35771
|
return null;
|
|
35456
35772
|
}
|
|
35773
|
+
/** The mounted native indicator that OWNS an SDK layer — the one whose type equals the
|
|
35774
|
+
* layer id (the id doubles as the data channel, so the pairing is the SDK's own
|
|
35775
|
+
* contract). Null for chart-type channels and while the owner is hidden (a hidden
|
|
35776
|
+
* indicator leaves the scene; its cleared data channel paints nothing anyway). */
|
|
35777
|
+
layerOwner(layerId) {
|
|
35778
|
+
for (const m of this.scene.indicators.values()) {
|
|
35779
|
+
if (m.native?.type === layerId) return m;
|
|
35780
|
+
}
|
|
35781
|
+
return null;
|
|
35782
|
+
}
|
|
35783
|
+
/** The pane an SDK layer paints on: its owner's pane, else the price pane. */
|
|
35784
|
+
layerPane(layerId) {
|
|
35785
|
+
const owner = this.layerOwner(layerId);
|
|
35786
|
+
const paneId = owner ? owner.paneId ?? PRICE_PANE_ID : PRICE_PANE_ID;
|
|
35787
|
+
return this.scene.panes.get(paneId) ?? null;
|
|
35788
|
+
}
|
|
35789
|
+
/** The SDK layer canvases split around the data canvas, each side back-to-front:
|
|
35790
|
+
* owned layers by their owner's z key against the candles' (an indicator restacked
|
|
35791
|
+
* below the candles takes its layer canvas along), unowned by declared placement. */
|
|
35792
|
+
orderedLayerCanvases() {
|
|
35793
|
+
const byId = new Map(this.extLayers.map((l) => [l.def.id, l.canvas]));
|
|
35794
|
+
const { below, above } = stackLayers(
|
|
35795
|
+
this.extLayers.map((l) => {
|
|
35796
|
+
const owner = this.layerOwner(l.def.id);
|
|
35797
|
+
return {
|
|
35798
|
+
id: l.def.id,
|
|
35799
|
+
placement: l.def.placement === "below-data" ? "below-data" : "above-data",
|
|
35800
|
+
ownerZ: owner ? this.scene.zOf(owner.id) : null
|
|
35801
|
+
};
|
|
35802
|
+
}),
|
|
35803
|
+
this.scene.candleZ
|
|
35804
|
+
);
|
|
35805
|
+
return { below: below.map((id) => byId.get(id)), above: above.map((id) => byId.get(id)) };
|
|
35806
|
+
}
|
|
35807
|
+
/** The full canvas pile in paint order (layers + data/volume/vpvr) — what the DOM
|
|
35808
|
+
* stacking and the screenshot compositor must both follow. */
|
|
35809
|
+
canvasPile() {
|
|
35810
|
+
const { below, above } = this.orderedLayerCanvases();
|
|
35811
|
+
return [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above];
|
|
35812
|
+
}
|
|
35813
|
+
/** Re-slot the SDK layer canvases in the plot when the computed order changed (a z
|
|
35814
|
+
* write, a restored config, an indicator mount/remove/restack). Runs at the top of
|
|
35815
|
+
* every data frame; a no-op when the signature is unchanged. Re-inserting an
|
|
35816
|
+
* absolutely-positioned, pointer-transparent canvas repaints nothing by itself. */
|
|
35817
|
+
syncLayerCanvasOrder() {
|
|
35818
|
+
if (this.extLayers.length === 0 || !this.plot) return;
|
|
35819
|
+
const { below, above } = this.orderedLayerCanvases();
|
|
35820
|
+
const sig = [...below.map((c) => this.extLayers.find((l) => l.canvas === c).def.id), "|", ...above.map((c) => this.extLayers.find((l) => l.canvas === c).def.id)].join(",");
|
|
35821
|
+
if (sig === this.layerOrderSig) return;
|
|
35822
|
+
this.layerOrderSig = sig;
|
|
35823
|
+
for (const c of below) this.plot.insertBefore(c, this.dataCanvas);
|
|
35824
|
+
for (const c of above) this.plot.insertBefore(c, this.chromeCanvas);
|
|
35825
|
+
}
|
|
35457
35826
|
/** True when an active volume layer is this study pane's ONLY content — so its scale should
|
|
35458
35827
|
* come from volume, not the empty {0,1} placeholder. (In the price pane, or alongside a real
|
|
35459
35828
|
* series, volume stays a bottom overlay and the master scale wins.) */
|
|
@@ -35463,6 +35832,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35463
35832
|
if (this.nativeLayerPane("volume") !== pane) return false;
|
|
35464
35833
|
return masterModels.every((m) => m.native?.type === "volume");
|
|
35465
35834
|
}
|
|
35835
|
+
/** True when this study pane's master content is only SDK-layer natives (series-less
|
|
35836
|
+
* models whose type names a mounted layer) — its scale then follows the visible bars
|
|
35837
|
+
* (see the call site). Any real master series takes over the scale as usual. */
|
|
35838
|
+
layerNativesOwnPane(pane, masterModels) {
|
|
35839
|
+
if (pane.kind === "price" || masterModels.length === 0) return false;
|
|
35840
|
+
return masterModels.every(
|
|
35841
|
+
(m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type)
|
|
35842
|
+
);
|
|
35843
|
+
}
|
|
35466
35844
|
/** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
|
|
35467
35845
|
* band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
|
|
35468
35846
|
* Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
|
|
@@ -35478,6 +35856,25 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35478
35856
|
invert: paneInvert(this.scene, p)
|
|
35479
35857
|
}));
|
|
35480
35858
|
}
|
|
35859
|
+
/** Thin projection of the panes for the axis A/L hover buttons (top-to-bottom order). */
|
|
35860
|
+
axisScaleViews() {
|
|
35861
|
+
return this.scene.orderedPanes().map((p) => ({
|
|
35862
|
+
id: p.id,
|
|
35863
|
+
top: p.bounds.top,
|
|
35864
|
+
height: p.bounds.height,
|
|
35865
|
+
collapsed: p.collapsed,
|
|
35866
|
+
auto: p.manualScale == null,
|
|
35867
|
+
log: paneLogScale(this.scene, p)
|
|
35868
|
+
}));
|
|
35869
|
+
}
|
|
35870
|
+
/** Toggle one pane's autoscale (the A axis button): off freezes the current window into
|
|
35871
|
+
* manual mode, on drops it — the per-pane twin of the chart-level `autoScale` feature. */
|
|
35872
|
+
togglePaneAuto(paneId) {
|
|
35873
|
+
const pane = this.scene.panes.get(paneId);
|
|
35874
|
+
if (!pane) return;
|
|
35875
|
+
pane.manualScale = pane.manualScale == null ? { ...pane.scale } : null;
|
|
35876
|
+
this.scheduler?.invalidate(4 /* Full */);
|
|
35877
|
+
}
|
|
35481
35878
|
/** Set one pane's axis mode. The price pane routes to the scene-level setting (persisted +
|
|
35482
35879
|
* keyboard shortcuts); a study pane keeps its own, so panes never affect each other. */
|
|
35483
35880
|
setPaneScaleMode(paneId, mode) {
|
|
@@ -35489,19 +35886,18 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35489
35886
|
}
|
|
35490
35887
|
this.scheduler?.invalidate(4 /* Full */);
|
|
35491
35888
|
}
|
|
35492
|
-
/** Set one pane's logarithmic flag
|
|
35493
|
-
*
|
|
35889
|
+
/** Set one pane's logarithmic flag; the price pane routes to the scene-level flag.
|
|
35890
|
+
* Log and auto are INDEPENDENT: a frozen (manual) window is kept — its price bounds
|
|
35891
|
+
* stay put and only its `log` tag flips, so the same range re-renders in the new
|
|
35892
|
+
* space instead of snapping back to autoscale. */
|
|
35494
35893
|
setPaneLog(paneId, log) {
|
|
35495
|
-
|
|
35496
|
-
|
|
35497
|
-
|
|
35498
|
-
if (
|
|
35499
|
-
|
|
35500
|
-
const p = this.scene.panes.get(paneId);
|
|
35501
|
-
if (!p) return;
|
|
35502
|
-
p.logScale = log;
|
|
35503
|
-
p.manualScale = null;
|
|
35894
|
+
const pane = paneId === PRICE_PANE_ID ? this.scene.orderedPanes().find((p) => p.kind === "price") : this.scene.panes.get(paneId);
|
|
35895
|
+
if (paneId === PRICE_PANE_ID) this.scene.logScale = log;
|
|
35896
|
+
else {
|
|
35897
|
+
if (!pane) return;
|
|
35898
|
+
pane.logScale = log;
|
|
35504
35899
|
}
|
|
35900
|
+
if (pane?.manualScale) pane.manualScale.log = log;
|
|
35505
35901
|
this.scheduler?.invalidate(4 /* Full */);
|
|
35506
35902
|
}
|
|
35507
35903
|
/** Flip one pane's axis (high at the bottom). The price pane routes to the scene-level flag
|
|
@@ -35600,6 +35996,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35600
35996
|
}
|
|
35601
35997
|
this.inputsUI?.reposition();
|
|
35602
35998
|
this.paneControls?.reposition();
|
|
35999
|
+
this.axisScaleButtons?.reposition();
|
|
35603
36000
|
this.repositionScrollButton();
|
|
35604
36001
|
this.positionAttribution();
|
|
35605
36002
|
this.publishPricePaneBounds();
|
|
@@ -35618,6 +36015,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35618
36015
|
}
|
|
35619
36016
|
this.inputsUI?.reposition();
|
|
35620
36017
|
this.paneControls?.reposition();
|
|
36018
|
+
this.axisScaleButtons?.reposition();
|
|
35621
36019
|
this.repositionScrollButton();
|
|
35622
36020
|
this.positionAttribution();
|
|
35623
36021
|
this.publishPricePaneBounds();
|
|
@@ -35825,6 +36223,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35825
36223
|
}
|
|
35826
36224
|
return out.sort((a, b) => a.from - b.from);
|
|
35827
36225
|
}
|
|
36226
|
+
function sanitizeSessionZones(value) {
|
|
36227
|
+
if (value == null || typeof value !== "object") return null;
|
|
36228
|
+
const v = value;
|
|
36229
|
+
const windows = (raw) => {
|
|
36230
|
+
if (!Array.isArray(raw)) return [];
|
|
36231
|
+
const out = [];
|
|
36232
|
+
for (const w of raw) {
|
|
36233
|
+
if (!Array.isArray(w)) continue;
|
|
36234
|
+
const from = Number(w[0]);
|
|
36235
|
+
const to = Number(w[1]);
|
|
36236
|
+
if (Number.isFinite(from) && Number.isFinite(to) && to > from) out.push([from, to]);
|
|
36237
|
+
}
|
|
36238
|
+
return out.sort((a, b) => a[0] - b[0]);
|
|
36239
|
+
};
|
|
36240
|
+
return { pre: windows(v.pre), post: windows(v.post) };
|
|
36241
|
+
}
|
|
35828
36242
|
function clamp012(v) {
|
|
35829
36243
|
return Math.max(0, Math.min(1, v));
|
|
35830
36244
|
}
|
|
@@ -37703,6 +38117,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37703
38117
|
exports.getDrawingType = getDrawingType;
|
|
37704
38118
|
exports.getNativeIndicator = getNativeIndicator;
|
|
37705
38119
|
exports.iconMarkup = iconMarkup;
|
|
38120
|
+
exports.inputVisible = inputVisible;
|
|
37706
38121
|
exports.legendActions = legendActions;
|
|
37707
38122
|
exports.nativeIndicatorDescriptors = nativeIndicatorDescriptors;
|
|
37708
38123
|
exports.nativeIndicatorTypes = nativeIndicatorTypes;
|