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