@luxalgo/vela 0.8.2 → 0.8.3
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-DpOWZD8F.d.ts → DataProvider-oH78tYXL.d.ts} +1 -1
- package/dist/{DataProvider-COGDs9Xg.d.cts → DataProvider-wbtJ3-Sa.d.cts} +1 -1
- package/dist/{chunk-R2JMNIHY.js → chunk-2IPXYM4B.js} +258 -169
- package/dist/{chunk-NLDTVFOI.js → chunk-G7B7ZCBF.js} +129 -10
- package/dist/{chunk-T3DZIR5Y.js → chunk-HMHA733L.js} +306 -168
- package/dist/{chunk-DPAA3SRT.js → chunk-KWI3YDBW.js} +254 -9
- package/dist/{chunk-27UTYE7T.js → chunk-TO3GIK7Z.js} +49 -7
- package/dist/{chunk-HKNO24GD.js → chunk-VHGACEHO.js} +50 -1
- package/dist/{contributions-Cgm9RJYP.d.cts → contributions-BhOBRenr.d.cts} +44 -4
- package/dist/{contributions-bWgwyiVi.d.ts → contributions-HGqnNb_P.d.ts} +44 -4
- package/dist/index.cjs +497 -79
- package/dist/index.d.cts +34 -14
- package/dist/index.d.ts +34 -14
- package/dist/index.js +5 -5
- package/dist/{options-NAUHiQEC.d.ts → options-lMUwmRwh.d.cts} +66 -1
- package/dist/{options-NAUHiQEC.d.cts → options-lMUwmRwh.d.ts} +66 -1
- package/dist/{plugin-CqxWoDKS.d.ts → plugin-Cr2Pj8q8.d.ts} +4 -4
- package/dist/{plugin-CjjzZX2F.d.cts → plugin-Do5J5xuF.d.cts} +4 -4
- package/dist/plugin.cjs +18 -0
- package/dist/plugin.d.cts +5 -5
- package/dist/plugin.d.ts +5 -5
- package/dist/plugin.js +3 -3
- 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/{side-panel-wMNfgyZb.d.cts → side-panel-_2UK1gSm.d.cts} +45 -3
- package/dist/{side-panel-wMNfgyZb.d.ts → side-panel-_2UK1gSm.d.ts} +45 -3
- package/dist/{statusline-model-Dpfpq54A.d.cts → statusline-model-B-2xGlB_.d.cts} +3 -3
- package/dist/{statusline-model-Dih6FGWP.d.ts → statusline-model-CY1wzxHg.d.ts} +3 -3
- package/dist/ui.cjs +224 -14
- package/dist/ui.d.cts +119 -87
- package/dist/ui.d.ts +119 -87
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +497 -79
- package/dist/vela.global.min.js +86 -52
- package/dist/widget.cjs +850 -167
- package/dist/widget.d.cts +24 -10
- package/dist/widget.d.ts +24 -10
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +849 -167
- package/dist/workspace.d.cts +47 -20
- package/dist/workspace.d.ts +47 -20
- package/dist/workspace.js +6 -6
- package/package.json +1 -1
- /package/dist/{chunk-JU4CWEBF.js → chunk-W5YPYZ2P.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -7888,6 +7888,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
7888
7888
|
this.activeEngineStyle = null;
|
|
7889
7889
|
/** The chart's current price style (tracked from the renderer's change events + initial read). */
|
|
7890
7890
|
this.priceStyle = "candles";
|
|
7891
|
+
this.priceStyleWillChangeUnsub = null;
|
|
7891
7892
|
this.unresolvedUnsub = null;
|
|
7892
7893
|
/** Latest chart visible range (left/right bar times), fed to viewport-dependent scripts. */
|
|
7893
7894
|
this.visibleRange = null;
|
|
@@ -7986,6 +7987,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
7986
7987
|
this.renderer.onThemeSelect?.((name) => this.setTheme(resolveTheme(name)));
|
|
7987
7988
|
this.viewportUnsub = this.renderer.onViewportChange((range) => this.onViewportChange(range));
|
|
7988
7989
|
this.renderer.onPriceStyleChange?.((style) => this.syncPriceStyle(style));
|
|
7990
|
+
this.priceStyleWillChangeUnsub = this.renderer.onPriceStyleWillChange?.((from, to) => this.events.emit("priceStyle:change", { from, to })) ?? null;
|
|
7989
7991
|
const initialStyle = this.renderer.readFeature("priceStyle");
|
|
7990
7992
|
if (typeof initialStyle === "string") this.priceStyle = initialStyle;
|
|
7991
7993
|
this.barTransform = barTransformFor(initialStyle);
|
|
@@ -9516,6 +9518,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9516
9518
|
if (this.viewportTimer != null) clearTimeout(this.viewportTimer);
|
|
9517
9519
|
this.viewportUnsub?.();
|
|
9518
9520
|
this.paneActionUnsub?.();
|
|
9521
|
+
this.priceStyleWillChangeUnsub?.();
|
|
9522
|
+
this.priceStyleWillChangeUnsub = null;
|
|
9519
9523
|
for (const record of this.registry.all()) {
|
|
9520
9524
|
this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
|
|
9521
9525
|
record.session?.stop();
|
|
@@ -11811,6 +11815,55 @@ function announceSurface(el, open2, kind, trigger = null) {
|
|
|
11811
11815
|
new View.CustomEvent(open2 ? SURFACE_OPEN_EVENT : SURFACE_CLOSE_EVENT, { bubbles: true, composed: true, detail: { kind, trigger } })
|
|
11812
11816
|
);
|
|
11813
11817
|
}
|
|
11818
|
+
|
|
11819
|
+
// src/ui/surface-exit.ts
|
|
11820
|
+
var CLOSING_ATTR = "data-closing";
|
|
11821
|
+
var SURFACE_EXIT_CAP_MS = 1e3;
|
|
11822
|
+
function animationsOf(els) {
|
|
11823
|
+
return els.flatMap((el) => el.getAnimations({ subtree: true }));
|
|
11824
|
+
}
|
|
11825
|
+
function ends(a) {
|
|
11826
|
+
return Number.isFinite(a.effect?.getComputedTiming().endTime ?? Number.POSITIVE_INFINITY);
|
|
11827
|
+
}
|
|
11828
|
+
function holdForExit(els, done, opts = {}) {
|
|
11829
|
+
const list = Array.isArray(els) ? els : [els];
|
|
11830
|
+
if (list.length === 0 || !list.every((el) => typeof el.getAnimations === "function")) {
|
|
11831
|
+
done();
|
|
11832
|
+
return null;
|
|
11833
|
+
}
|
|
11834
|
+
const before = new Set(animationsOf(list));
|
|
11835
|
+
for (const el of list) el.setAttribute(CLOSING_ATTR, "");
|
|
11836
|
+
const exit = animationsOf(list).filter((a) => !before.has(a) && ends(a));
|
|
11837
|
+
if (exit.length === 0) {
|
|
11838
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
11839
|
+
done();
|
|
11840
|
+
return null;
|
|
11841
|
+
}
|
|
11842
|
+
const frozen = [...list, ...opts.inert ?? []];
|
|
11843
|
+
const wasInert = frozen.map((el) => el.inert);
|
|
11844
|
+
for (const el of frozen) el.inert = true;
|
|
11845
|
+
let settled = false;
|
|
11846
|
+
const settle = (complete) => {
|
|
11847
|
+
if (settled) return;
|
|
11848
|
+
settled = true;
|
|
11849
|
+
clearTimeout(cap);
|
|
11850
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
11851
|
+
frozen.forEach((el, i) => {
|
|
11852
|
+
el.inert = wasInert[i];
|
|
11853
|
+
});
|
|
11854
|
+
if (complete) done();
|
|
11855
|
+
};
|
|
11856
|
+
const cap = setTimeout(() => settle(true), SURFACE_EXIT_CAP_MS);
|
|
11857
|
+
let pending = exit.length;
|
|
11858
|
+
const one = () => {
|
|
11859
|
+
if (--pending === 0) settle(true);
|
|
11860
|
+
};
|
|
11861
|
+
for (const a of exit) a.finished.then(one, one);
|
|
11862
|
+
return {
|
|
11863
|
+
cancel: () => settle(false),
|
|
11864
|
+
finish: () => settle(true)
|
|
11865
|
+
};
|
|
11866
|
+
}
|
|
11814
11867
|
function menuController(opts) {
|
|
11815
11868
|
return {
|
|
11816
11869
|
machine: menu__namespace.machine,
|
|
@@ -11892,6 +11945,35 @@ var MENU_CSS = `
|
|
|
11892
11945
|
color: var(--vela-fg-bright);
|
|
11893
11946
|
}
|
|
11894
11947
|
.vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
|
|
11948
|
+
/* Badge mode (iconBadges): the row icon sits in a rounded tile and the row grows to fit
|
|
11949
|
+
it. An icon-less row of the same level keeps an invisible slot, so labels align. */
|
|
11950
|
+
.vela-menu[data-badges] > .vela-menu-item { padding: 5px 10px 5px 6px; }
|
|
11951
|
+
.vela-menu-item .vela-menu-badge {
|
|
11952
|
+
width: 24px;
|
|
11953
|
+
height: 24px;
|
|
11954
|
+
padding: 3px;
|
|
11955
|
+
flex: none;
|
|
11956
|
+
box-sizing: border-box;
|
|
11957
|
+
display: inline-flex;
|
|
11958
|
+
align-items: center;
|
|
11959
|
+
justify-content: center;
|
|
11960
|
+
border-radius: var(--vela-radius-md);
|
|
11961
|
+
background: var(--vela-hover);
|
|
11962
|
+
border: 1px solid var(--vela-border);
|
|
11963
|
+
color: var(--vela-fg-muted);
|
|
11964
|
+
transition: background var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease), color var(--vela-dur-fast) var(--vela-ease);
|
|
11965
|
+
}
|
|
11966
|
+
.vela-menu-item .vela-menu-badge:empty { background: transparent; border-color: transparent; }
|
|
11967
|
+
.vela-menu-item .vela-menu-badge .vela-icon { color: inherit; }
|
|
11968
|
+
.vela-menu-item[data-highlighted] .vela-menu-badge:not(:empty) { color: var(--vela-fg-bright); }
|
|
11969
|
+
/* The selected tile inverts: a bright fill under the surface-colored glyph. After the
|
|
11970
|
+
hover rule, so a selected row keeps its tile while highlighted. */
|
|
11971
|
+
.vela-menu-item[data-checked] .vela-menu-badge:not(:empty),
|
|
11972
|
+
.vela-menu-item[data-checkmark] .vela-menu-badge:not(:empty) {
|
|
11973
|
+
background: color-mix(in srgb, var(--vela-fg-bright) 83%, var(--vela-surface));
|
|
11974
|
+
border-color: color-mix(in srgb, var(--vela-fg-bright) 40%, transparent);
|
|
11975
|
+
color: var(--vela-surface);
|
|
11976
|
+
}
|
|
11895
11977
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
11896
11978
|
same control language as the settings dialog's toggles. */
|
|
11897
11979
|
.vela-menu-switch {
|
|
@@ -11958,11 +12040,14 @@ var Surface = class _Surface {
|
|
|
11958
12040
|
this.pinnedAnchor = null;
|
|
11959
12041
|
/** The open state last announced — see {@link announce}. */
|
|
11960
12042
|
this.shown = false;
|
|
12043
|
+
/** The list's exit animation after a close; a reopen cancels it. */
|
|
12044
|
+
this.exit = null;
|
|
11961
12045
|
this.doc = doc;
|
|
11962
12046
|
this.host = opts.host;
|
|
11963
12047
|
this.onSelect = opts.onSelect;
|
|
11964
12048
|
this.onFavorite = opts.onFavorite;
|
|
11965
12049
|
this.checkmarks = opts.checkmarks === true;
|
|
12050
|
+
this.iconBadges = opts.iconBadges === true;
|
|
11966
12051
|
this.positioner = doc.createElement("div");
|
|
11967
12052
|
this.positioner.className = "vela-ui-layer";
|
|
11968
12053
|
this.list = doc.createElement("ul");
|
|
@@ -11993,14 +12078,30 @@ var Surface = class _Surface {
|
|
|
11993
12078
|
if (opts.triggerId && trigger) trigger.id = opts.triggerId;
|
|
11994
12079
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
11995
12080
|
const api = this.ctrl.connect(service);
|
|
11996
|
-
|
|
12081
|
+
const closing = this.shown && !api.open;
|
|
12082
|
+
if (closing) this.announce(false);
|
|
12083
|
+
if (api.open && this.exit) {
|
|
12084
|
+
this.exit.cancel();
|
|
12085
|
+
this.exit = null;
|
|
12086
|
+
}
|
|
11997
12087
|
if (trigger) vanilla.spreadProps(trigger, api.getTriggerProps(), this.mid);
|
|
11998
12088
|
vanilla.spreadProps(this.positioner, api.getPositionerProps(), this.mid);
|
|
11999
12089
|
vanilla.spreadProps(this.list, api.getContentProps(), this.mid);
|
|
12000
12090
|
this.project(api);
|
|
12091
|
+
if (closing) this.holdExit();
|
|
12001
12092
|
if (api.open && !this.shown) this.announce(true);
|
|
12002
12093
|
});
|
|
12003
12094
|
}
|
|
12095
|
+
/** Keep the just-closed list up through its exit animation. `spreadProps` re-applies
|
|
12096
|
+
* `hidden` only when the machine's value for it changes, so the list stays shown until
|
|
12097
|
+
* the exit ends — or until a reopen, which projects `hidden: false` itself. */
|
|
12098
|
+
holdExit() {
|
|
12099
|
+
this.list.hidden = false;
|
|
12100
|
+
this.exit = holdForExit(this.list, () => {
|
|
12101
|
+
this.exit = null;
|
|
12102
|
+
if (!this.shown) this.list.hidden = true;
|
|
12103
|
+
});
|
|
12104
|
+
}
|
|
12004
12105
|
get api() {
|
|
12005
12106
|
return this.ctrl.connect(this.handle.service);
|
|
12006
12107
|
}
|
|
@@ -12024,6 +12125,7 @@ var Surface = class _Surface {
|
|
|
12024
12125
|
destroy() {
|
|
12025
12126
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
12026
12127
|
this.subs.clear();
|
|
12128
|
+
this.exit?.finish();
|
|
12027
12129
|
this.handle.stop();
|
|
12028
12130
|
if (this.shown) this.announce(false);
|
|
12029
12131
|
this.positioner.remove();
|
|
@@ -12050,6 +12152,10 @@ var Surface = class _Surface {
|
|
|
12050
12152
|
const doc = this.doc;
|
|
12051
12153
|
this.list.replaceChildren();
|
|
12052
12154
|
const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
|
|
12155
|
+
const iconic = this.items.some((i) => !!i.icon);
|
|
12156
|
+
const badged = this.iconBadges && iconic;
|
|
12157
|
+
if (badged) this.list.dataset.badges = "1";
|
|
12158
|
+
else delete this.list.dataset.badges;
|
|
12053
12159
|
for (const item of this.items) {
|
|
12054
12160
|
if (item.separatorBefore) {
|
|
12055
12161
|
const sep = doc.createElement("li");
|
|
@@ -12075,7 +12181,16 @@ var Surface = class _Surface {
|
|
|
12075
12181
|
} else if (!branch && !item.toggle && item.checked) {
|
|
12076
12182
|
li.dataset.checked = "1";
|
|
12077
12183
|
}
|
|
12078
|
-
if (
|
|
12184
|
+
if (badged) {
|
|
12185
|
+
const badge = doc.createElement("span");
|
|
12186
|
+
badge.className = "vela-menu-badge";
|
|
12187
|
+
if (item.icon) badge.appendChild(iconEl(item.icon, doc));
|
|
12188
|
+
li.appendChild(badge);
|
|
12189
|
+
} else if (item.icon) {
|
|
12190
|
+
li.appendChild(iconEl(item.icon, doc));
|
|
12191
|
+
} else if (iconic) {
|
|
12192
|
+
li.appendChild(iconEl("", doc));
|
|
12193
|
+
}
|
|
12079
12194
|
const label = doc.createElement("span");
|
|
12080
12195
|
label.className = "vela-menu-label";
|
|
12081
12196
|
label.textContent = item.label;
|
|
@@ -12123,6 +12238,7 @@ var Surface = class _Surface {
|
|
|
12123
12238
|
onSelect: this.onSelect,
|
|
12124
12239
|
onFavorite: this.onFavorite,
|
|
12125
12240
|
checkmarks: this.checkmarks,
|
|
12241
|
+
iconBadges: this.iconBadges,
|
|
12126
12242
|
id: `${this.mid}--${item.id}`
|
|
12127
12243
|
});
|
|
12128
12244
|
sub2.setItems(item.submenu ?? []);
|
|
@@ -12148,7 +12264,8 @@ var Menu = class {
|
|
|
12148
12264
|
id: opts.id,
|
|
12149
12265
|
minWidth: opts.minWidth,
|
|
12150
12266
|
onFavorite: opts.onFavorite,
|
|
12151
|
-
checkmarks: opts.checkmarks
|
|
12267
|
+
checkmarks: opts.checkmarks,
|
|
12268
|
+
iconBadges: opts.iconBadges
|
|
12152
12269
|
});
|
|
12153
12270
|
this.root.setItems(opts.items);
|
|
12154
12271
|
}
|
|
@@ -12252,6 +12369,7 @@ var POPOVER_CSS = `
|
|
|
12252
12369
|
|
|
12253
12370
|
// src/ui/components/popover/view.ts
|
|
12254
12371
|
var open = null;
|
|
12372
|
+
var leaving = /* @__PURE__ */ new Set();
|
|
12255
12373
|
function closeOpenPopovers() {
|
|
12256
12374
|
open?.hide();
|
|
12257
12375
|
}
|
|
@@ -12279,6 +12397,8 @@ var Popover = class {
|
|
|
12279
12397
|
this.shown = false;
|
|
12280
12398
|
/** The pending removal of a fading-out shell; a show() that reuses the shell cancels it. */
|
|
12281
12399
|
this.leaveTimer = null;
|
|
12400
|
+
/** The shell's exit animation before it leaves the DOM; a show() that reuses the shell cancels it. */
|
|
12401
|
+
this.exit = null;
|
|
12282
12402
|
const doc = opts.trigger.ownerDocument;
|
|
12283
12403
|
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
12284
12404
|
this.trigger = opts.trigger;
|
|
@@ -12315,6 +12435,10 @@ var Popover = class {
|
|
|
12315
12435
|
clearTimeout(this.leaveTimer);
|
|
12316
12436
|
this.leaveTimer = null;
|
|
12317
12437
|
}
|
|
12438
|
+
this.exit?.cancel();
|
|
12439
|
+
this.exit = null;
|
|
12440
|
+
leaving.delete(this);
|
|
12441
|
+
for (const p of [...leaving]) if (p.trigger === this.trigger) p.exit?.finish();
|
|
12318
12442
|
ensureUIHost(this.el, this.inheritsTokens ? void 0 : this.theme);
|
|
12319
12443
|
if (this.fadeMs > 0) {
|
|
12320
12444
|
this.el.style.transition = `opacity ${this.fadeMs}ms ease`;
|
|
@@ -12371,7 +12495,12 @@ var Popover = class {
|
|
|
12371
12495
|
this.el.remove();
|
|
12372
12496
|
}, this.fadeMs);
|
|
12373
12497
|
} else {
|
|
12374
|
-
this.el
|
|
12498
|
+
this.exit = holdForExit(this.el, () => {
|
|
12499
|
+
this.exit = null;
|
|
12500
|
+
leaving.delete(this);
|
|
12501
|
+
this.el.remove();
|
|
12502
|
+
});
|
|
12503
|
+
if (this.exit) leaving.add(this);
|
|
12375
12504
|
}
|
|
12376
12505
|
if (open === this) open = null;
|
|
12377
12506
|
this.ctrl.onClose?.();
|
|
@@ -12383,6 +12512,7 @@ var Popover = class {
|
|
|
12383
12512
|
}
|
|
12384
12513
|
destroy() {
|
|
12385
12514
|
this.hide();
|
|
12515
|
+
this.exit?.finish();
|
|
12386
12516
|
}
|
|
12387
12517
|
reposition() {
|
|
12388
12518
|
if (this.shown) this.place();
|
|
@@ -13075,6 +13205,9 @@ var Dialog = class {
|
|
|
13075
13205
|
constructor(opts = {}) {
|
|
13076
13206
|
this.opener = null;
|
|
13077
13207
|
this.wasOpen = false;
|
|
13208
|
+
/** The panel and scrim's exit animation after a close; a reopen cancels it. */
|
|
13209
|
+
this.exit = null;
|
|
13210
|
+
this.destroyed = false;
|
|
13078
13211
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
13079
13212
|
injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
|
|
13080
13213
|
const host = opts.host ?? doc.body;
|
|
@@ -13171,19 +13304,57 @@ var Dialog = class {
|
|
|
13171
13304
|
const mid = String(this.ctrl.props.id);
|
|
13172
13305
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
13173
13306
|
const api = this.ctrl.connect(service);
|
|
13174
|
-
|
|
13307
|
+
const closing = this.wasOpen && !api.open;
|
|
13308
|
+
if (closing) this.announce(false);
|
|
13175
13309
|
vanilla.spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
13176
13310
|
vanilla.spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
13177
13311
|
vanilla.spreadProps(this.panel, api.getContentProps(), mid);
|
|
13178
13312
|
vanilla.spreadProps(title, api.getTitleProps(), mid);
|
|
13179
13313
|
vanilla.spreadProps(close, api.getCloseTriggerProps(), mid);
|
|
13180
|
-
|
|
13181
|
-
|
|
13314
|
+
if (api.open) {
|
|
13315
|
+
this.exit?.cancel();
|
|
13316
|
+
this.exit = null;
|
|
13317
|
+
this.setShown(true);
|
|
13318
|
+
} else if (closing) {
|
|
13319
|
+
this.holdExit();
|
|
13320
|
+
} else if (!this.exit) {
|
|
13321
|
+
this.setShown(false);
|
|
13322
|
+
}
|
|
13182
13323
|
if (api.open && !this.wasOpen) this.announce(true);
|
|
13183
13324
|
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
13184
13325
|
this.wasOpen = api.open;
|
|
13185
13326
|
});
|
|
13186
13327
|
}
|
|
13328
|
+
setShown(shown) {
|
|
13329
|
+
this.backdrop.style.display = shown ? "" : "none";
|
|
13330
|
+
this.positioner.style.display = shown ? "" : "none";
|
|
13331
|
+
}
|
|
13332
|
+
/** Keep the just-closed dialog up through its exit animation, then hide it — or
|
|
13333
|
+
* remove it, when it was destroyed meanwhile. `spreadProps` re-applies the close
|
|
13334
|
+
* props' `hidden` only when its value changes, so lifting it keeps the panel and
|
|
13335
|
+
* scrim up until the exit ends. The positioner spans the viewport: inert, it lets
|
|
13336
|
+
* clicks through to the page while the panel animates out. */
|
|
13337
|
+
holdExit() {
|
|
13338
|
+
const held = [this.panel, this.backdrop];
|
|
13339
|
+
for (const el of held) el.hidden = false;
|
|
13340
|
+
this.exit = holdForExit(
|
|
13341
|
+
held,
|
|
13342
|
+
() => {
|
|
13343
|
+
this.exit = null;
|
|
13344
|
+
if (this.destroyed) {
|
|
13345
|
+
this.detach();
|
|
13346
|
+
} else if (!this.open) {
|
|
13347
|
+
for (const el of held) el.hidden = true;
|
|
13348
|
+
this.setShown(false);
|
|
13349
|
+
}
|
|
13350
|
+
},
|
|
13351
|
+
{ inert: [this.positioner] }
|
|
13352
|
+
);
|
|
13353
|
+
}
|
|
13354
|
+
detach() {
|
|
13355
|
+
this.backdrop.remove();
|
|
13356
|
+
this.positioner.remove();
|
|
13357
|
+
}
|
|
13187
13358
|
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
13188
13359
|
* teardown that outruns the machine's close notification). */
|
|
13189
13360
|
restoreOpener() {
|
|
@@ -13212,15 +13383,16 @@ var Dialog = class {
|
|
|
13212
13383
|
return el?.closest(".vela-popover, .vela-menu") != null;
|
|
13213
13384
|
}
|
|
13214
13385
|
destroy() {
|
|
13386
|
+
if (this.wasOpen && !this.open) this.handle.flush();
|
|
13215
13387
|
if (this.open) this.hide();
|
|
13216
13388
|
this.handle.stop();
|
|
13389
|
+
this.destroyed = true;
|
|
13217
13390
|
if (this.wasOpen) {
|
|
13218
13391
|
this.wasOpen = false;
|
|
13219
13392
|
this.announce(false);
|
|
13220
13393
|
}
|
|
13221
13394
|
this.restoreOpener();
|
|
13222
|
-
this.
|
|
13223
|
-
this.positioner.remove();
|
|
13395
|
+
if (!this.exit) this.detach();
|
|
13224
13396
|
}
|
|
13225
13397
|
};
|
|
13226
13398
|
|
|
@@ -15672,7 +15844,10 @@ var LEGEND_ICON_PX2 = 16;
|
|
|
15672
15844
|
var LEGEND_CTL_PX = 18;
|
|
15673
15845
|
var LEGEND_ROW_PAD_Y = 2;
|
|
15674
15846
|
var LEGEND_ROW_PAD_X = 6;
|
|
15847
|
+
var LEGEND_LEFT_PX = 10;
|
|
15848
|
+
var LEGEND_SCALE_GAP_PX = 6;
|
|
15675
15849
|
var LEGEND_TITLE_VALUES_GAP_PX = 6;
|
|
15850
|
+
var LEGEND_VALUES_GAP_PX = 5;
|
|
15676
15851
|
var LEGEND_TITLE_STATUS_GAP_PX = 8;
|
|
15677
15852
|
var LEGEND_ACTION_GAP_PX = 6;
|
|
15678
15853
|
var LEGEND_CTL_CSS = `cursor:pointer;display:inline-flex;align-items:center;justify-content:center;border:none;line-height:0;padding:0;width:${LEGEND_CTL_PX}px;height:${LEGEND_CTL_PX}px;border-radius:3px;box-sizing:border-box;flex:none;`;
|
|
@@ -15958,14 +16133,15 @@ var InputsUI = class {
|
|
|
15958
16133
|
row.valuesEl.replaceChildren();
|
|
15959
16134
|
return;
|
|
15960
16135
|
}
|
|
15961
|
-
row.valuesEl.style.display = "
|
|
16136
|
+
row.valuesEl.style.display = "block";
|
|
15962
16137
|
row.valuesEl.replaceChildren();
|
|
15963
|
-
|
|
16138
|
+
row.plotValues.forEach((v, i) => {
|
|
15964
16139
|
const span = document.createElement("span");
|
|
15965
16140
|
span.textContent = v.value;
|
|
15966
16141
|
span.style.color = v.color;
|
|
16142
|
+
if (i > 0) span.style.marginLeft = `${LEGEND_VALUES_GAP_PX}px`;
|
|
15967
16143
|
row.valuesEl.appendChild(span);
|
|
15968
|
-
}
|
|
16144
|
+
});
|
|
15969
16145
|
}
|
|
15970
16146
|
// ── legend move/merge (menu + drag) ─────────────────────────────────────
|
|
15971
16147
|
/** Open the "Move to" menu for a row, anchored under its move button. */
|
|
@@ -16066,7 +16242,7 @@ var InputsUI = class {
|
|
|
16066
16242
|
if (!lg) {
|
|
16067
16243
|
lg = document.createElement("div");
|
|
16068
16244
|
lg.dataset.velaPane = paneId;
|
|
16069
|
-
lg.style.cssText =
|
|
16245
|
+
lg.style.cssText = `position:absolute;left:${LEGEND_LEFT_PX}px;z-index:5;display:flex;flex-direction:column;align-items:flex-start;gap:0;pointer-events:none;font:12px -apple-system,Segoe UI,sans-serif;`;
|
|
16070
16246
|
applyChromeTokens(lg, this.theme);
|
|
16071
16247
|
this.positionLegend(lg, paneId);
|
|
16072
16248
|
this.container.appendChild(lg);
|
|
@@ -16078,6 +16254,7 @@ var InputsUI = class {
|
|
|
16078
16254
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
16079
16255
|
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
16080
16256
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
16257
|
+
lg.style.maxWidth = `calc(100% - ${(bounds.rightAxis ?? 0) + LEGEND_LEFT_PX + LEGEND_SCALE_GAP_PX}px)`;
|
|
16081
16258
|
const collapsed = this.paneCollapse.has(paneId);
|
|
16082
16259
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
16083
16260
|
for (const row of this.rows.values()) {
|
|
@@ -16169,7 +16346,7 @@ var InputsUI = class {
|
|
|
16169
16346
|
}
|
|
16170
16347
|
ensureDialogStyles();
|
|
16171
16348
|
const el = document.createElement("div");
|
|
16172
|
-
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
16349
|
+
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;max-width:calc(100% + ${LEGEND_ROW_PAD_X}px);min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
16173
16350
|
el.addEventListener("mouseleave", () => {
|
|
16174
16351
|
if (this.selectedId !== id) this.setRowHighlighted(id, false);
|
|
16175
16352
|
});
|
|
@@ -16197,7 +16374,7 @@ var InputsUI = class {
|
|
|
16197
16374
|
const statusEl = document.createElement("span");
|
|
16198
16375
|
statusEl.style.cssText = "display:none;box-sizing:border-box;flex:none;";
|
|
16199
16376
|
const titleWrap = document.createElement("span");
|
|
16200
|
-
titleWrap.style.cssText = "white-space:nowrap;";
|
|
16377
|
+
titleWrap.style.cssText = "white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;";
|
|
16201
16378
|
titleWrap.addEventListener("mouseenter", () => this.setRowHighlighted(id, true));
|
|
16202
16379
|
const titleEl = document.createElement("span");
|
|
16203
16380
|
titleEl.textContent = title;
|
|
@@ -16215,7 +16392,7 @@ var InputsUI = class {
|
|
|
16215
16392
|
el.appendChild(calloutsEl);
|
|
16216
16393
|
el.appendChild(statusEl);
|
|
16217
16394
|
const valuesEl = document.createElement("span");
|
|
16218
|
-
valuesEl.style.cssText = `display:none;
|
|
16395
|
+
valuesEl.style.cssText = `display:none;flex:1 1 0%;min-width:0;overflow:hidden;text-overflow:ellipsis;margin-left:${LEGEND_TITLE_VALUES_GAP_PX}px;white-space:nowrap;font-variant-numeric:tabular-nums;`;
|
|
16219
16396
|
el.appendChild(valuesEl);
|
|
16220
16397
|
const controlsEl = document.createElement("span");
|
|
16221
16398
|
controlsEl.style.cssText = `display:none;align-items:center;gap:${LEGEND_ACTION_GAP_PX}px;flex:none;margin-left:${LEGEND_ACTION_GAP_PX}px;`;
|
|
@@ -18810,6 +18987,11 @@ var InputController = class {
|
|
|
18810
18987
|
/** When true (the default), the separators between stacked panes are draggable to
|
|
18811
18988
|
* resize the panes above/below. When false, a press there is a normal data-area pan. */
|
|
18812
18989
|
this.paneResize = true;
|
|
18990
|
+
/** When true (the default), the user drawings take part in pointer input: they claim a
|
|
18991
|
+
* press over a drawing or with a tool armed, and see hover moves. When false, every press
|
|
18992
|
+
* is a plain pan/click and hover leaves them alone; a drawing gesture already under way
|
|
18993
|
+
* still finishes. */
|
|
18994
|
+
this.drawings = true;
|
|
18813
18995
|
this.el = null;
|
|
18814
18996
|
this.dragging = false;
|
|
18815
18997
|
this.moved = false;
|
|
@@ -18873,18 +19055,18 @@ var InputController = class {
|
|
|
18873
19055
|
this.onModifier = (e) => {
|
|
18874
19056
|
if (e.repeat || e.key !== "Shift" && e.key !== "Control" && e.key !== "Meta") return;
|
|
18875
19057
|
if (Number.isNaN(this.cursorX)) return;
|
|
18876
|
-
if (this.dragging
|
|
19058
|
+
if (this.dragging ? this.region !== "drawing" : !this.drawings) return;
|
|
18877
19059
|
this.deps.drawingsPointerMove?.(this.cursorX, this.cursorY, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
18878
19060
|
};
|
|
18879
19061
|
this.onDown = (e) => {
|
|
18880
19062
|
if (e.button === 1) {
|
|
18881
19063
|
const { x: x2, y: y2 } = this.local(e);
|
|
18882
|
-
this.middleDeleted = this.deps.drawingsDeleteAt?.(x2, y2) ?? false;
|
|
19064
|
+
this.middleDeleted = this.drawings && (this.deps.drawingsDeleteAt?.(x2, y2) ?? false);
|
|
18883
19065
|
if (this.middleDeleted) e.preventDefault();
|
|
18884
19066
|
return;
|
|
18885
19067
|
}
|
|
18886
19068
|
if (e.button === 2) {
|
|
18887
|
-
this.rightCancelled = this.deps.drawingsCancelPlacement?.() ?? false;
|
|
19069
|
+
this.rightCancelled = this.drawings && (this.deps.drawingsCancelPlacement?.() ?? false);
|
|
18888
19070
|
if (this.rightCancelled) e.preventDefault();
|
|
18889
19071
|
return;
|
|
18890
19072
|
}
|
|
@@ -18906,20 +19088,20 @@ var InputController = class {
|
|
|
18906
19088
|
this.startY = y;
|
|
18907
19089
|
this.prevPressDrawing = this.lastPressDrawing;
|
|
18908
19090
|
this.lastPressDrawing = false;
|
|
18909
|
-
if (this.deps.drawingsClaim?.(x, y)) {
|
|
19091
|
+
if (this.drawings && this.deps.drawingsClaim?.(x, y)) {
|
|
18910
19092
|
this.region = "drawing";
|
|
18911
19093
|
this.lastPressDrawing = true;
|
|
18912
19094
|
this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
18913
19095
|
this.capture(e.pointerId);
|
|
18914
19096
|
return;
|
|
18915
19097
|
}
|
|
18916
|
-
if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
19098
|
+
if (this.drawings && e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
18917
19099
|
this.region = "drawing";
|
|
18918
19100
|
this.lastPressDrawing = true;
|
|
18919
19101
|
this.capture(e.pointerId);
|
|
18920
19102
|
return;
|
|
18921
19103
|
}
|
|
18922
|
-
if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
19104
|
+
if (this.drawings && (e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
18923
19105
|
this.region = "drawing";
|
|
18924
19106
|
this.lastPressDrawing = true;
|
|
18925
19107
|
this.capture(e.pointerId);
|
|
@@ -18996,7 +19178,7 @@ var InputController = class {
|
|
|
18996
19178
|
const drawCursor = this.deps.drawingsCursor?.(x, y);
|
|
18997
19179
|
const r = this.regionAt(x, y);
|
|
18998
19180
|
this.el.style.cursor = drawCursor ?? (r === "price" ? "ns-resize" : r === "time" ? "ew-resize" : r === "separator" ? "row-resize" : "");
|
|
18999
|
-
this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
19181
|
+
if (this.drawings) this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
19000
19182
|
}
|
|
19001
19183
|
if (e.pointerType !== "touch") this.deps.onPointerMove(x, y);
|
|
19002
19184
|
};
|
|
@@ -19238,7 +19420,7 @@ var InputController = class {
|
|
|
19238
19420
|
}
|
|
19239
19421
|
/** Shared double-click / double-tap routing, by the region under the point. */
|
|
19240
19422
|
doubleActivate(x, y) {
|
|
19241
|
-
if (this.deps.drawingsDblClick?.(x, y)) return;
|
|
19423
|
+
if (this.drawings && this.deps.drawingsDblClick?.(x, y)) return;
|
|
19242
19424
|
const region = this.regionAt(x, y);
|
|
19243
19425
|
if (region === "price") this.deps.resetPriceScale(x, y);
|
|
19244
19426
|
else if (region === "separator") this.deps.resetPaneSize(y);
|
|
@@ -21252,10 +21434,13 @@ function priceTicks(min, max, target = 6) {
|
|
|
21252
21434
|
const norm = raw / mag;
|
|
21253
21435
|
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
21254
21436
|
const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);
|
|
21255
|
-
const out = [];
|
|
21256
21437
|
const start = Math.ceil(min / step) * step;
|
|
21257
|
-
|
|
21258
|
-
|
|
21438
|
+
if (!(step > 0) || start + step === start) return [];
|
|
21439
|
+
const count = Math.floor((max - start) / step + 1e-6) + 1;
|
|
21440
|
+
if (!(count > 0) || count > 1e3) return [];
|
|
21441
|
+
const out = [];
|
|
21442
|
+
for (let i = 0; i < count; i += 1) {
|
|
21443
|
+
out.push(Number((start + i * step).toFixed(decimals)));
|
|
21259
21444
|
}
|
|
21260
21445
|
return out;
|
|
21261
21446
|
}
|
|
@@ -21453,6 +21638,100 @@ function calendarTicks(zFrom, zTo, months, offsetMs) {
|
|
|
21453
21638
|
return out;
|
|
21454
21639
|
}
|
|
21455
21640
|
|
|
21641
|
+
// src/renderers/native/chrome/priceAxisTicks.ts
|
|
21642
|
+
var PriceAxisTickSource = class {
|
|
21643
|
+
constructor() {
|
|
21644
|
+
this.fn = null;
|
|
21645
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
21646
|
+
this.warned = false;
|
|
21647
|
+
}
|
|
21648
|
+
/** The host function, or null for the built-in ticks. */
|
|
21649
|
+
get hook() {
|
|
21650
|
+
return this.fn;
|
|
21651
|
+
}
|
|
21652
|
+
/** Set (or clear) the host function. Drops every memoized result, so setting the same
|
|
21653
|
+
* function again recomputes — how a host refreshes ticks that depend on its own state. */
|
|
21654
|
+
setHook(fn) {
|
|
21655
|
+
this.fn = fn;
|
|
21656
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
21657
|
+
}
|
|
21658
|
+
/** The pane's master-scale ticks for this frame. An unscaled pane (`axisFormat: 'none'`)
|
|
21659
|
+
* gets none, and the host is never asked for it. */
|
|
21660
|
+
ticksFor(scene, pane, coords) {
|
|
21661
|
+
const format = pane.axisFormat;
|
|
21662
|
+
if (format === "none") return [];
|
|
21663
|
+
const pct = percentScaleFor(scene, pane);
|
|
21664
|
+
const height = pane.bounds.height;
|
|
21665
|
+
const fn = this.fn;
|
|
21666
|
+
if (!fn || !(height > 0)) return paneAxisTicks(pane.scale, height, pct, scene.priceMintick, format);
|
|
21667
|
+
const s = pane.scale;
|
|
21668
|
+
const m = this.memo.get(pane);
|
|
21669
|
+
const fontSize = scene.style.fontSize;
|
|
21670
|
+
if (m && m.kind === pane.kind && m.min === s.min && m.max === s.max && m.log === !!s.log && m.invert === !!s.invert && m.height === height && m.baseline === pct?.baseline && m.indexed === !!pct?.indexed && m.mintick === scene.priceMintick && m.format === format && m.fontSize === fontSize) {
|
|
21671
|
+
return m.ticks;
|
|
21672
|
+
}
|
|
21673
|
+
const defaults2 = paneAxisTicks(s, height, pct, scene.priceMintick, format);
|
|
21674
|
+
const ticks = this.callHook(fn, scene, pane, coords, defaults2, pct?.baseline, pct ? pct.indexed ? "indexed" : "percent" : "price");
|
|
21675
|
+
this.memo.set(pane, {
|
|
21676
|
+
kind: pane.kind,
|
|
21677
|
+
min: s.min,
|
|
21678
|
+
max: s.max,
|
|
21679
|
+
log: !!s.log,
|
|
21680
|
+
invert: !!s.invert,
|
|
21681
|
+
height,
|
|
21682
|
+
baseline: pct?.baseline,
|
|
21683
|
+
indexed: !!pct?.indexed,
|
|
21684
|
+
mintick: scene.priceMintick,
|
|
21685
|
+
format,
|
|
21686
|
+
fontSize,
|
|
21687
|
+
ticks
|
|
21688
|
+
});
|
|
21689
|
+
return ticks;
|
|
21690
|
+
}
|
|
21691
|
+
callHook(fn, scene, pane, coords, defaults2, baseline, mode) {
|
|
21692
|
+
const scale = { min: pane.scale.min, max: pane.scale.max, log: !!pane.scale.log, invert: !!pane.scale.invert };
|
|
21693
|
+
const bounds = { top: 0, height: pane.bounds.height };
|
|
21694
|
+
const ctx = {
|
|
21695
|
+
pane: { id: pane.id, kind: pane.kind },
|
|
21696
|
+
min: Math.min(scale.min, scale.max),
|
|
21697
|
+
max: Math.max(scale.min, scale.max),
|
|
21698
|
+
log: !!scale.log,
|
|
21699
|
+
height: bounds.height,
|
|
21700
|
+
mode,
|
|
21701
|
+
baseline,
|
|
21702
|
+
mintick: scene.priceMintick,
|
|
21703
|
+
fontSize: scene.style.fontSize,
|
|
21704
|
+
priceToY: (price) => coords.priceToY(price, scale, bounds),
|
|
21705
|
+
defaults: defaults2
|
|
21706
|
+
};
|
|
21707
|
+
let out;
|
|
21708
|
+
try {
|
|
21709
|
+
out = fn(ctx);
|
|
21710
|
+
} catch (err) {
|
|
21711
|
+
this.warnOnce("threw", err);
|
|
21712
|
+
return defaults2;
|
|
21713
|
+
}
|
|
21714
|
+
if (out == null) return defaults2;
|
|
21715
|
+
if (!Array.isArray(out)) {
|
|
21716
|
+
this.warnOnce("returned a non-array", out);
|
|
21717
|
+
return defaults2;
|
|
21718
|
+
}
|
|
21719
|
+
const ticks = [];
|
|
21720
|
+
for (const t of out) {
|
|
21721
|
+
if (!t || typeof t !== "object") continue;
|
|
21722
|
+
const { price, label, major } = t;
|
|
21723
|
+
if (typeof price !== "number" || !Number.isFinite(price) || typeof label !== "string") continue;
|
|
21724
|
+
ticks.push(typeof major === "boolean" ? { price, label, major } : { price, label });
|
|
21725
|
+
}
|
|
21726
|
+
return ticks;
|
|
21727
|
+
}
|
|
21728
|
+
warnOnce(what, detail) {
|
|
21729
|
+
if (this.warned) return;
|
|
21730
|
+
this.warned = true;
|
|
21731
|
+
console.warn(`[vela] the priceAxisTicks function ${what} \u2014 the default ticks are used instead.`, detail);
|
|
21732
|
+
}
|
|
21733
|
+
};
|
|
21734
|
+
|
|
21456
21735
|
// src/renderers/native/chrome/countdown.ts
|
|
21457
21736
|
function countdownText(barOpen, barMs, now2) {
|
|
21458
21737
|
if (!(barMs > 0)) return null;
|
|
@@ -21805,8 +22084,11 @@ function standaloneSvg(markup, ink, px) {
|
|
|
21805
22084
|
}
|
|
21806
22085
|
|
|
21807
22086
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
22087
|
+
var MINOR_LABEL_ALPHA = 0.5;
|
|
21808
22088
|
var ChromeRenderer = class {
|
|
21809
|
-
|
|
22089
|
+
/** `axisTicks` is shared with the backdrop layer, so price labels and gridlines agree. */
|
|
22090
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
22091
|
+
this.axisTicks = axisTicks;
|
|
21810
22092
|
this.canvas = null;
|
|
21811
22093
|
this.ctx = null;
|
|
21812
22094
|
// The color for axis tick labels — the host-passed surface text, set each frame in render().
|
|
@@ -21911,7 +22193,8 @@ var ChromeRenderer = class {
|
|
|
21911
22193
|
this.drawTimeAxis(ctx, scene, coords, theme, dataW, dataH, fullH);
|
|
21912
22194
|
this.drawMarkLane(ctx, scene, coords, theme, dataW, dataH);
|
|
21913
22195
|
}
|
|
21914
|
-
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge
|
|
22196
|
+
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge,
|
|
22197
|
+
* but clipped to the data area so a glyph on the edge bar never paints over the price scale. */
|
|
21915
22198
|
drawMarkLane(ctx, scene, coords, theme, dataW, dataH) {
|
|
21916
22199
|
if (!scene.marks.visible || scene.timelineMarks.length === 0) {
|
|
21917
22200
|
this.markLayout = { glyphs: [], stacks: /* @__PURE__ */ new Map() };
|
|
@@ -21929,6 +22212,10 @@ var ChromeRenderer = class {
|
|
|
21929
22212
|
expanded: scene.marksExpandedStack
|
|
21930
22213
|
});
|
|
21931
22214
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
22215
|
+
ctx.save();
|
|
22216
|
+
ctx.beginPath();
|
|
22217
|
+
ctx.rect(0, 0, dataW, dataH);
|
|
22218
|
+
ctx.clip();
|
|
21932
22219
|
paintMarkLane(ctx, this.markLayout, {
|
|
21933
22220
|
axisY: dataH,
|
|
21934
22221
|
background: theme.background,
|
|
@@ -21942,6 +22229,7 @@ var ChromeRenderer = class {
|
|
|
21942
22229
|
flashKey: scene.marksFlash && scene.marksFlash.until > nowMs ? scene.marksFlash.key : null,
|
|
21943
22230
|
nowMs
|
|
21944
22231
|
});
|
|
22232
|
+
ctx.restore();
|
|
21945
22233
|
}
|
|
21946
22234
|
barTimes(scene) {
|
|
21947
22235
|
if (this.barTimesSrc !== scene.bars || this.barTimesCache.length !== scene.bars.length) {
|
|
@@ -21998,13 +22286,19 @@ var ChromeRenderer = class {
|
|
|
21998
22286
|
if (!scene.showAxisLabels) return;
|
|
21999
22287
|
ctx.fillStyle = this.axisTextColor;
|
|
22000
22288
|
ctx.textAlign = "left";
|
|
22289
|
+
const font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
22290
|
+
const majorFont = `600 ${font}`;
|
|
22291
|
+
const alpha = ctx.globalAlpha;
|
|
22001
22292
|
for (const pane of panes) {
|
|
22002
22293
|
if (pane.collapsed) continue;
|
|
22003
|
-
const
|
|
22004
|
-
for (const t of paneAxisTicks(pane.scale, pane.bounds.height, pct, scene.priceMintick, pane.axisFormat)) {
|
|
22294
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
22005
22295
|
const y = coords.priceToY(t.price, pane.scale, pane.bounds);
|
|
22006
22296
|
if (y < pane.bounds.top + 6 || y > pane.bounds.top + pane.bounds.height - 4) continue;
|
|
22297
|
+
if (t.major === true) ctx.font = majorFont;
|
|
22298
|
+
else if (t.major === false) ctx.globalAlpha = alpha * MINOR_LABEL_ALPHA;
|
|
22007
22299
|
ctx.fillText(t.label, dataW + 6, y);
|
|
22300
|
+
if (t.major === true) ctx.font = font;
|
|
22301
|
+
else if (t.major === false) ctx.globalAlpha = alpha;
|
|
22008
22302
|
}
|
|
22009
22303
|
if (pane.axisBands) {
|
|
22010
22304
|
for (const b of pane.axisBands) {
|
|
@@ -22246,15 +22540,23 @@ var CrosshairRenderer = class {
|
|
|
22246
22540
|
constructor() {
|
|
22247
22541
|
this.canvas = null;
|
|
22248
22542
|
this.ctx = null;
|
|
22543
|
+
this.shade = null;
|
|
22249
22544
|
}
|
|
22250
22545
|
mount(canvas) {
|
|
22251
22546
|
this.canvas = canvas;
|
|
22252
22547
|
this.ctx = canvas.getContext("2d");
|
|
22253
22548
|
}
|
|
22549
|
+
/** The plot area the last frame veiled for `crosshairOverride.shadeRight` — from the
|
|
22550
|
+
* picked bar's right edge to the price scale, full plot height (the union when both the
|
|
22551
|
+
* local and a synced crosshair veil) — or null when no veil was painted. */
|
|
22552
|
+
get shadeRightArea() {
|
|
22553
|
+
return this.shade;
|
|
22554
|
+
}
|
|
22254
22555
|
/** Clear the cursor canvas and (re)draw the crosshair lines + axis chips. The optional
|
|
22255
22556
|
* `separatorHoverY` highlights the draggable pane separator under the cursor;
|
|
22256
22557
|
* `external` is a SYNCED ghost crosshair (another chart's pointer, pixel-resolved). */
|
|
22257
22558
|
render(scene, coords, theme, separatorHoverY = null, external = null) {
|
|
22559
|
+
this.shade = null;
|
|
22258
22560
|
const ctx = this.ctx;
|
|
22259
22561
|
const canvas = this.canvas;
|
|
22260
22562
|
if (!ctx || !canvas) return;
|
|
@@ -22277,12 +22579,7 @@ var CrosshairRenderer = class {
|
|
|
22277
22579
|
const x = crisp(coords.logicalToX(logical), ov?.width ?? cs.width);
|
|
22278
22580
|
if (vertical && ov?.shadeRight) {
|
|
22279
22581
|
const from = Math.max(0, Math.round(coords.logicalToX(logical + 0.5)));
|
|
22280
|
-
if (from < dataW)
|
|
22281
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
22282
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
22283
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22284
|
-
ctx.globalAlpha = 1;
|
|
22285
|
-
}
|
|
22582
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
22286
22583
|
}
|
|
22287
22584
|
ctx.strokeStyle = ov?.color ?? cs.color ?? theme.textColor;
|
|
22288
22585
|
ctx.lineWidth = ov?.width ?? cs.width;
|
|
@@ -22318,6 +22615,7 @@ var CrosshairRenderer = class {
|
|
|
22318
22615
|
destroy() {
|
|
22319
22616
|
this.canvas = null;
|
|
22320
22617
|
this.ctx = null;
|
|
22618
|
+
this.shade = null;
|
|
22321
22619
|
}
|
|
22322
22620
|
/** The synced ghost: a dimmed vertical line at the bar the renderer resolved as
|
|
22323
22621
|
* CONTAINING the foreign time (+ horizontal line when a comparable price came
|
|
@@ -22337,12 +22635,7 @@ var CrosshairRenderer = class {
|
|
|
22337
22635
|
if (ov?.vertical === false) return;
|
|
22338
22636
|
if (ov?.shadeRight) {
|
|
22339
22637
|
const from = Math.max(0, Math.round(coords.logicalToX(Math.round(coords.xToLogical(ext.x)) + 0.5)));
|
|
22340
|
-
if (from < dataW)
|
|
22341
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
22342
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
22343
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22344
|
-
ctx.globalAlpha = 1;
|
|
22345
|
-
}
|
|
22638
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
22346
22639
|
}
|
|
22347
22640
|
if (ext.line === false || x < 0 || x > dataW) return;
|
|
22348
22641
|
ctx.font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
@@ -22378,6 +22671,15 @@ var CrosshairRenderer = class {
|
|
|
22378
22671
|
this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
|
|
22379
22672
|
ctx.globalAlpha = 1;
|
|
22380
22673
|
}
|
|
22674
|
+
/** Paint the `shadeRight` veil from `from` to the price scale and record the veiled area. */
|
|
22675
|
+
paintShade(ctx, shade, from, dataW, dataH) {
|
|
22676
|
+
ctx.fillStyle = shade.color;
|
|
22677
|
+
ctx.globalAlpha = shade.opacity ?? 1;
|
|
22678
|
+
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22679
|
+
ctx.globalAlpha = 1;
|
|
22680
|
+
const x = this.shade ? Math.min(this.shade.x, from) : from;
|
|
22681
|
+
this.shade = { x, width: dataW - x, height: dataH };
|
|
22682
|
+
}
|
|
22381
22683
|
/** A soft band + a brighter crisp center line over the hovered separator, so it reads as
|
|
22382
22684
|
* a draggable handle (the cursor is already `row-resize`). Spans the full width (data +
|
|
22383
22685
|
* scale gutter) to match the separator itself. Theme-derived (textColor). */
|
|
@@ -25873,15 +26175,16 @@ var DrawingSettingsDialog = class {
|
|
|
25873
26175
|
}
|
|
25874
26176
|
open(drawing, actions, kind) {
|
|
25875
26177
|
this.close();
|
|
25876
|
-
const
|
|
26178
|
+
const live = actions.resolve() ?? drawing;
|
|
26179
|
+
const snapshot = clonePlain(live.serialize());
|
|
25877
26180
|
const grid = fieldGrid({ variant: "inputs" });
|
|
25878
26181
|
grid.style.padding = "16px 20px";
|
|
25879
26182
|
grid.style.overflowY = "auto";
|
|
25880
26183
|
grid.style.overflowX = "hidden";
|
|
25881
26184
|
grid.style.flex = "1 1 auto";
|
|
25882
|
-
if (kind === "position" &&
|
|
25883
|
-
else if (kind === "frvp" &&
|
|
25884
|
-
else if (kind === "levels") this.buildLevels(grid,
|
|
26185
|
+
if (kind === "position" && live instanceof PositionTool) this.buildPosition(grid, live, actions);
|
|
26186
|
+
else if (kind === "frvp" && live instanceof FixedRangeVolumeProfile) this.buildFrvp(grid, live, actions);
|
|
26187
|
+
else if (kind === "levels") this.buildLevels(grid, live, actions);
|
|
25885
26188
|
else return;
|
|
25886
26189
|
const ui = new Dialog({
|
|
25887
26190
|
host: this.host,
|
|
@@ -26528,7 +26831,7 @@ var DrawingSettingsPopup = class {
|
|
|
26528
26831
|
this.reposition();
|
|
26529
26832
|
return;
|
|
26530
26833
|
}
|
|
26531
|
-
const text = drawing.text;
|
|
26834
|
+
const text = (actions.resolve() ?? drawing).text;
|
|
26532
26835
|
const panel = document.createElement("div");
|
|
26533
26836
|
panel.style.cssText = `padding:6px;border-top:1px solid var(--vela-border);display:flex;flex-direction:column;gap:4px;`;
|
|
26534
26837
|
const ta = new TextArea({
|
|
@@ -27124,6 +27427,7 @@ function sized(svg, size = ICON) {
|
|
|
27124
27427
|
// src/renderers/native/drawings/DrawingToolbar.ts
|
|
27125
27428
|
var TOOLBAR_WIDTH = 44;
|
|
27126
27429
|
var TOOLBAR_COLLAPSED_WIDTH = 16;
|
|
27430
|
+
var FLYOUT_GAP = 3;
|
|
27127
27431
|
var DrawingToolbar = class {
|
|
27128
27432
|
constructor(host, theme, onArm, onMagnet = () => {
|
|
27129
27433
|
}, onMeasure = () => {
|
|
@@ -27153,6 +27457,9 @@ var DrawingToolbar = class {
|
|
|
27153
27457
|
// group id (or MAGNET_ID) whose flyout is open
|
|
27154
27458
|
this.flyoutCell = null;
|
|
27155
27459
|
// the cell the open flyout is anchored to
|
|
27460
|
+
/** Closed flyouts still playing their exit animation, by owner id — reopening the same
|
|
27461
|
+
* owner takes its element back instead of stacking a second one on it. */
|
|
27462
|
+
this.exitingFlyouts = /* @__PURE__ */ new Map();
|
|
27156
27463
|
this.groupCells = /* @__PURE__ */ new Map();
|
|
27157
27464
|
// the composite cell (hover/active bg + flyout anchor)
|
|
27158
27465
|
this.groupIcons = /* @__PURE__ */ new Map();
|
|
@@ -27261,6 +27568,7 @@ var DrawingToolbar = class {
|
|
|
27261
27568
|
}
|
|
27262
27569
|
destroy() {
|
|
27263
27570
|
this.closeFlyout();
|
|
27571
|
+
for (const { exit } of [...this.exitingFlyouts.values()]) exit.finish();
|
|
27264
27572
|
for (const dispose of this.tipDisposers.splice(0)) dispose();
|
|
27265
27573
|
this.root.remove();
|
|
27266
27574
|
}
|
|
@@ -27488,16 +27796,22 @@ var DrawingToolbar = class {
|
|
|
27488
27796
|
* outside-dismiss. Callers fill it with items. */
|
|
27489
27797
|
beginFlyout(ownerId, cell) {
|
|
27490
27798
|
const t = this.theme;
|
|
27491
|
-
const
|
|
27799
|
+
const back = this.exitingFlyouts.get(ownerId);
|
|
27800
|
+
if (back) {
|
|
27801
|
+
this.exitingFlyouts.delete(ownerId);
|
|
27802
|
+
back.exit.cancel();
|
|
27803
|
+
back.fly.replaceChildren();
|
|
27804
|
+
}
|
|
27805
|
+
const fly = back?.fly ?? document.createElement("div");
|
|
27492
27806
|
fly.className = "vela-dtb-flyout";
|
|
27493
|
-
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:
|
|
27807
|
+
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:var(--vela-radius-md);background:var(--vela-surface);border:1px solid ${this.borderOverride ?? "var(--vela-border-strong)"};box-shadow:var(--vela-shadow);pointer-events:auto;overflow-y:auto;overscroll-behavior:contain;`;
|
|
27494
27808
|
applyChromeTokens(fly, t);
|
|
27495
27809
|
this.host.appendChild(fly);
|
|
27496
27810
|
const r = cell.getBoundingClientRect();
|
|
27497
27811
|
const rootR = this.root.getBoundingClientRect();
|
|
27498
27812
|
const hostR = this.host.getBoundingClientRect();
|
|
27499
27813
|
const top = r.top - hostR.top;
|
|
27500
|
-
fly.style.left = `${rootR.right - hostR.left
|
|
27814
|
+
fly.style.left = `${rootR.right - hostR.left + FLYOUT_GAP}px`;
|
|
27501
27815
|
fly.style.top = `${top}px`;
|
|
27502
27816
|
fly.style.maxHeight = `${Math.max(120, hostR.height - top - 8)}px`;
|
|
27503
27817
|
this.flyout = fly;
|
|
@@ -27513,6 +27827,7 @@ var DrawingToolbar = class {
|
|
|
27513
27827
|
if (!cell) return;
|
|
27514
27828
|
const fly = this.beginFlyout(group.id, cell);
|
|
27515
27829
|
const sections = group.sections ?? [{ label: "", tools: group.tools }];
|
|
27830
|
+
const badge = sections.some((s) => s.tools.some((tool) => !!tool.icon));
|
|
27516
27831
|
for (let si = 0; si < sections.length; si++) {
|
|
27517
27832
|
const section = sections[si];
|
|
27518
27833
|
if (section.label) {
|
|
@@ -27523,6 +27838,7 @@ var DrawingToolbar = class {
|
|
|
27523
27838
|
fly.appendChild(
|
|
27524
27839
|
this.makeFlyoutItem({
|
|
27525
27840
|
icon: tool.icon,
|
|
27841
|
+
badge,
|
|
27526
27842
|
label: tool.label,
|
|
27527
27843
|
selected: tool.type === this.active,
|
|
27528
27844
|
shortcut: this.shortcuts.get(tool.type),
|
|
@@ -27573,21 +27889,24 @@ var DrawingToolbar = class {
|
|
|
27573
27889
|
div.style.cssText = `height:1px;margin:4px 8px;background:${this.borderColor};`;
|
|
27574
27890
|
return div;
|
|
27575
27891
|
}
|
|
27576
|
-
/** A flyout row, left to right: optional leading icon, label, a check when it's the
|
|
27577
|
-
* entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
27578
|
-
* Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
27892
|
+
/** A flyout row, left to right: optional leading icon badge, label, a check when it's the
|
|
27893
|
+
* selected entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
27894
|
+
* rows). Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
27579
27895
|
makeFlyoutItem(opts) {
|
|
27580
27896
|
const t = this.theme;
|
|
27581
27897
|
const item = document.createElement("button");
|
|
27582
27898
|
item.type = "button";
|
|
27583
27899
|
item.className = "vela-dtb-item";
|
|
27584
27900
|
item.setAttribute("aria-label", opts.label);
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
27588
|
-
|
|
27589
|
-
|
|
27590
|
-
|
|
27901
|
+
const badged = opts.badge === true || !!opts.icon;
|
|
27902
|
+
const box = badged ? "gap:10px;padding:4px 10px 4px 6px;" : "gap:8px;padding:5px 10px 5px 8px;";
|
|
27903
|
+
item.style.cssText = `display:flex;align-items:center;${box}cursor:pointer;color:${t.textColor};border-radius:var(--vela-radius-sm);font:13px ${t.fontFamily};white-space:nowrap;min-width:148px;`;
|
|
27904
|
+
if (opts.selected) item.dataset.selected = "1";
|
|
27905
|
+
if (badged) {
|
|
27906
|
+
const badge = document.createElement("span");
|
|
27907
|
+
badge.className = "vela-dtb-badge";
|
|
27908
|
+
if (opts.icon) badge.innerHTML = opts.icon;
|
|
27909
|
+
item.appendChild(badge);
|
|
27591
27910
|
}
|
|
27592
27911
|
const label = document.createElement("span");
|
|
27593
27912
|
label.textContent = opts.label;
|
|
@@ -27632,7 +27951,12 @@ var DrawingToolbar = class {
|
|
|
27632
27951
|
if (fly) {
|
|
27633
27952
|
this.flyout = null;
|
|
27634
27953
|
announceSurface(fly, false, "menu", this.flyoutCell);
|
|
27635
|
-
|
|
27954
|
+
const owner = this.flyoutOwnerId ?? "";
|
|
27955
|
+
const exit = holdForExit(fly, () => {
|
|
27956
|
+
if (this.exitingFlyouts.get(owner)?.fly === fly) this.exitingFlyouts.delete(owner);
|
|
27957
|
+
fly.remove();
|
|
27958
|
+
});
|
|
27959
|
+
if (exit) this.exitingFlyouts.set(owner, { fly, exit });
|
|
27636
27960
|
this.flyoutCell?.classList.remove("vela-open");
|
|
27637
27961
|
this.flyoutCell = null;
|
|
27638
27962
|
this.flyoutOwnerId = null;
|
|
@@ -27716,6 +28040,11 @@ function ensureStyles4() {
|
|
|
27716
28040
|
.vela-dtb[data-collapsed='1'] .vela-dtb-collapse .vela-dtb-hit{width:14px;}
|
|
27717
28041
|
.vela-dtb-item{background:transparent;border:none;transition:background var(--vela-dur-fast) ease;}
|
|
27718
28042
|
.vela-dtb-item:hover{background:var(--vela-hover-strong);}
|
|
28043
|
+
.vela-dtb-badge{width:24px;height:24px;padding:3px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;border-radius:var(--vela-radius-md);background:var(--vela-hover);border:1px solid var(--vela-border);color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,border-color var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
|
|
28044
|
+
.vela-dtb-badge:empty{background:transparent;border-color:transparent;}
|
|
28045
|
+
.vela-dtb-badge svg{width:100%;height:100%;}
|
|
28046
|
+
.vela-dtb-item:hover .vela-dtb-badge:not(:empty){background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
28047
|
+
.vela-dtb-item[data-selected='1'] .vela-dtb-badge:not(:empty){background:color-mix(in srgb,var(--vela-fg-bright) 83%,var(--vela-surface));border-color:color-mix(in srgb,var(--vela-fg-bright) 40%,transparent);color:var(--vela-surface);}
|
|
27719
28048
|
.vela-dtb-star{width:26px;height:22px;margin:-3px -5px -3px 0;padding:3px 5px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;opacity:0;color:inherit;border-radius:var(--vela-radius-sm);transition:opacity .1s ease,color .1s ease,background .1s ease;}
|
|
27720
28049
|
.vela-dtb-star svg{width:16px;height:16px;}
|
|
27721
28050
|
.vela-dtb-item:hover .vela-dtb-star{opacity:.55;}
|
|
@@ -28335,6 +28664,12 @@ var UserDrawingController = class {
|
|
|
28335
28664
|
this.render();
|
|
28336
28665
|
}
|
|
28337
28666
|
}
|
|
28667
|
+
/** Drop the hover (its handles) — the pointer no longer reaches the drawings. */
|
|
28668
|
+
clearHover() {
|
|
28669
|
+
if (this.hoveredId == null) return;
|
|
28670
|
+
this.hoveredId = null;
|
|
28671
|
+
this.render();
|
|
28672
|
+
}
|
|
28338
28673
|
pointerUp(x, y, snap = "off") {
|
|
28339
28674
|
if (this.eraserMode) {
|
|
28340
28675
|
this.erasing = false;
|
|
@@ -29325,9 +29660,10 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
29325
29660
|
consider(drawings.max);
|
|
29326
29661
|
}
|
|
29327
29662
|
if (min === Infinity || max === -Infinity) return { min: 0, max: 1 };
|
|
29328
|
-
if (min
|
|
29329
|
-
const
|
|
29330
|
-
|
|
29663
|
+
if (max - min <= Math.max(Math.abs(min), Math.abs(max)) * 1e-12) {
|
|
29664
|
+
const mid = (min + max) / 2;
|
|
29665
|
+
const pad = Math.abs(mid) * 0.1 || 1;
|
|
29666
|
+
return { min: mid - pad, max: mid + pad, log: log && mid - pad > 0 };
|
|
29331
29667
|
}
|
|
29332
29668
|
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
29333
29669
|
const above = margins.top / 100 / content;
|
|
@@ -29831,7 +30167,9 @@ function bandEdgeSlot(logical) {
|
|
|
29831
30167
|
return Math.ceil(logical) - 0.5;
|
|
29832
30168
|
}
|
|
29833
30169
|
var BackdropRenderer = class {
|
|
29834
|
-
|
|
30170
|
+
/** `axisTicks` is shared with the chrome layer, so gridlines and price labels agree. */
|
|
30171
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
30172
|
+
this.axisTicks = axisTicks;
|
|
29835
30173
|
this.canvas = null;
|
|
29836
30174
|
this.ctx = null;
|
|
29837
30175
|
}
|
|
@@ -29908,9 +30246,8 @@ var BackdropRenderer = class {
|
|
|
29908
30246
|
if (scene.showGrid && gridHorz.visible && !pane.collapsed) {
|
|
29909
30247
|
ctx.globalAlpha = gridAlpha;
|
|
29910
30248
|
ctx.strokeStyle = horzColor;
|
|
29911
|
-
const pct = percentScaleFor(scene, pane);
|
|
29912
30249
|
ctx.beginPath();
|
|
29913
|
-
for (const t of
|
|
30250
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
29914
30251
|
const y = Math.round(coords.priceToY(t.price, pane.scale, pane.bounds)) + 0.5;
|
|
29915
30252
|
if (y < pane.bounds.top || y > pane.bounds.top + pane.bounds.height) continue;
|
|
29916
30253
|
ctx.moveTo(0, y);
|
|
@@ -30526,8 +30863,16 @@ var NativeRenderer = class {
|
|
|
30526
30863
|
this.toolbarGutter = 0;
|
|
30527
30864
|
// px reserved on the left for the docked drawings toolbar (0 when hidden)
|
|
30528
30865
|
this.mountContainer = null;
|
|
30866
|
+
/** The host-styleable twin of the `shadeRight` veil (`.vela-shade-right`), directly under
|
|
30867
|
+
* the cursor canvas; hidden whenever the crosshair layer paints no veil. */
|
|
30868
|
+
this.shadeRightEl = null;
|
|
30869
|
+
/** The veil geometry last written to {@link shadeRightEl} ('' = hidden) — the DOM is
|
|
30870
|
+
* touched only when it changes, never on a plain pointer move. */
|
|
30871
|
+
this.shadeRightSig = "";
|
|
30529
30872
|
this.userDrawings = null;
|
|
30530
|
-
|
|
30873
|
+
/** One price-axis tick source for the gridlines (backdrop) and the labels (chrome). */
|
|
30874
|
+
this.priceAxisTicks = new PriceAxisTickSource();
|
|
30875
|
+
this.backdropRenderer = new BackdropRenderer(this.priceAxisTicks);
|
|
30531
30876
|
this.volumeRenderer = new VolumeRenderer();
|
|
30532
30877
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
30533
30878
|
/**
|
|
@@ -30560,7 +30905,7 @@ var NativeRenderer = class {
|
|
|
30560
30905
|
this.backendMode = "auto";
|
|
30561
30906
|
this.glowAmount = 0;
|
|
30562
30907
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
30563
|
-
this.chrome = new ChromeRenderer();
|
|
30908
|
+
this.chrome = new ChromeRenderer(this.priceAxisTicks);
|
|
30564
30909
|
/** Prepaints each indicator's Pine drawings into interleave slices at the model's z. */
|
|
30565
30910
|
this.indicatorSlices = new IndicatorDrawingSlices();
|
|
30566
30911
|
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
@@ -30677,6 +31022,8 @@ var NativeRenderer = class {
|
|
|
30677
31022
|
// pane just below it
|
|
30678
31023
|
this.resizeSplitStart = null;
|
|
30679
31024
|
// the two panes' shared span when the drag began
|
|
31025
|
+
/** The user drawings take part in pointer input (the runtime `drawingsInteractive` feature). */
|
|
31026
|
+
this.drawingsInteractive = true;
|
|
30680
31027
|
this.hoverSeparatorY = null;
|
|
30681
31028
|
// pixel y of the separator under the cursor (drives its hover highlight)
|
|
30682
31029
|
// ── pane management (merge / reorder / collapse / maximize) ──
|
|
@@ -30732,8 +31079,9 @@ var NativeRenderer = class {
|
|
|
30732
31079
|
this.toggleVisibleCbs = /* @__PURE__ */ new Set();
|
|
30733
31080
|
this.moveIndicatorCbs = /* @__PURE__ */ new Set();
|
|
30734
31081
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
31082
|
+
this.priceStyleWillChangeCbs = /* @__PURE__ */ new Set();
|
|
30735
31083
|
this.name = "native";
|
|
30736
|
-
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "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", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride"];
|
|
31084
|
+
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "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", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride", "drawingsInteractive", "priceAxisTicks"];
|
|
30737
31085
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
30738
31086
|
* so moving onto the button doesn't count as leaving). */
|
|
30739
31087
|
this.onScrollProximityMove = (e) => {
|
|
@@ -30777,6 +31125,7 @@ var NativeRenderer = class {
|
|
|
30777
31125
|
this.scene.priceStyle = opts.priceStyle;
|
|
30778
31126
|
this.scene.basePainting = basePaintingOf(opts.priceStyle);
|
|
30779
31127
|
this.scene.candleOverride = candleOverrideFor(opts.priceStyle, this.scene.style.chartTypes);
|
|
31128
|
+
if (typeof opts.priceAxisTicks === "function") this.priceAxisTicks.setHook(opts.priceAxisTicks);
|
|
30780
31129
|
}
|
|
30781
31130
|
this.theme = this.deriveTheme(DARK_THEME);
|
|
30782
31131
|
}
|
|
@@ -30915,6 +31264,15 @@ var NativeRenderer = class {
|
|
|
30915
31264
|
case "crosshairOverride":
|
|
30916
31265
|
this.scene.crosshairOverride = sanitizeCrosshairOverride(value);
|
|
30917
31266
|
break;
|
|
31267
|
+
case "drawingsInteractive":
|
|
31268
|
+
this.drawingsInteractive = value !== false;
|
|
31269
|
+
if (this.input) this.input.drawings = this.drawingsInteractive;
|
|
31270
|
+
if (!this.drawingsInteractive) this.userDrawings?.clearHover();
|
|
31271
|
+
return;
|
|
31272
|
+
// affects the next gesture only — clearHover repaints what it drops
|
|
31273
|
+
case "priceAxisTicks":
|
|
31274
|
+
this.priceAxisTicks.setHook(typeof value === "function" ? value : null);
|
|
31275
|
+
break;
|
|
30918
31276
|
case "keyboard":
|
|
30919
31277
|
this.setKeyboardEnabled(Boolean(value));
|
|
30920
31278
|
return;
|
|
@@ -31030,6 +31388,10 @@ var NativeRenderer = class {
|
|
|
31030
31388
|
return { visible: this.scene.marks.visible, groups: { ...this.scene.marks.groups } };
|
|
31031
31389
|
case "crosshairOverride":
|
|
31032
31390
|
return this.scene.crosshairOverride ? { ...this.scene.crosshairOverride } : null;
|
|
31391
|
+
case "drawingsInteractive":
|
|
31392
|
+
return this.drawingsInteractive;
|
|
31393
|
+
case "priceAxisTicks":
|
|
31394
|
+
return this.priceAxisTicks.hook;
|
|
31033
31395
|
case "keyboard":
|
|
31034
31396
|
return this.keyboardEnabled;
|
|
31035
31397
|
case "historyChords":
|
|
@@ -31703,6 +32065,12 @@ var NativeRenderer = class {
|
|
|
31703
32065
|
Object.assign(this.drawingsCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
31704
32066
|
this.cursorCanvas = document.createElement("canvas");
|
|
31705
32067
|
Object.assign(this.cursorCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
32068
|
+
this.shadeRightEl = document.createElement("div");
|
|
32069
|
+
this.shadeRightEl.className = "vela-shade-right";
|
|
32070
|
+
this.shadeRightEl.setAttribute("aria-hidden", "true");
|
|
32071
|
+
this.shadeRightEl.hidden = true;
|
|
32072
|
+
Object.assign(this.shadeRightEl.style, { position: "absolute", top: "0", left: "0", width: "0", height: "0", pointerEvents: "none", display: "none" });
|
|
32073
|
+
this.shadeRightSig = "";
|
|
31706
32074
|
this.overlayRoot = document.createElement("div");
|
|
31707
32075
|
Object.assign(this.overlayRoot.style, { position: "absolute", inset: "0", pointerEvents: "none" });
|
|
31708
32076
|
this.plot = document.createElement("div");
|
|
@@ -31710,7 +32078,7 @@ var NativeRenderer = class {
|
|
|
31710
32078
|
this.extLayers = rendererLayers().map((def) => ({ def, instance: def.create(), canvas: this.createLayerCanvas(), channel: def.id, owner: null }));
|
|
31711
32079
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
31712
32080
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
31713
|
-
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
32081
|
+
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.shadeRightEl, this.cursorCanvas, this.overlayRoot);
|
|
31714
32082
|
this.layerOrderSig = "";
|
|
31715
32083
|
this.wrapper.appendChild(this.plot);
|
|
31716
32084
|
this.factoryConfig = this.getConfig();
|
|
@@ -31768,13 +32136,14 @@ var NativeRenderer = class {
|
|
|
31768
32136
|
drawingsPointerDown: (x, y, snap, shift2, mod) => this.userDrawings?.pointerDown(x, y, snap, shift2, mod),
|
|
31769
32137
|
drawingsPointerMove: (x, y, snap, shift2, mod) => this.userDrawings?.pointerMove(x, y, snap, shift2, mod),
|
|
31770
32138
|
drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
|
|
31771
|
-
drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
32139
|
+
drawingsCursor: (x, y) => (this.drawingsInteractive ? this.userDrawings?.cursorAt(x, y) : null) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
31772
32140
|
drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
|
|
31773
32141
|
drawingsClearTransient: () => this.userDrawings?.clearTransient()
|
|
31774
32142
|
});
|
|
31775
32143
|
this.input.rightEdgeZoom = this.zoomAnchorMode === "right";
|
|
31776
32144
|
this.input.axisDrag = this.axisDragEnabled;
|
|
31777
32145
|
this.input.paneResize = this.paneResizeEnabled;
|
|
32146
|
+
this.input.drawings = this.drawingsInteractive;
|
|
31778
32147
|
this.input.attach(this.dataCanvas);
|
|
31779
32148
|
if (this.settingsEnabled) this.setSettingsEnabled(true);
|
|
31780
32149
|
this.syncCountdownTimer();
|
|
@@ -32083,6 +32452,8 @@ var NativeRenderer = class {
|
|
|
32083
32452
|
this.backend.destroy();
|
|
32084
32453
|
this.chrome.destroy();
|
|
32085
32454
|
this.crosshairLayer.destroy();
|
|
32455
|
+
this.shadeRightEl?.remove();
|
|
32456
|
+
this.shadeRightEl = null;
|
|
32086
32457
|
this.attributionEl?.remove();
|
|
32087
32458
|
this.attributionEl = null;
|
|
32088
32459
|
this.mountContainer?.style.removeProperty("--vela-toolbar-gutter");
|
|
@@ -32429,14 +32800,21 @@ var NativeRenderer = class {
|
|
|
32429
32800
|
this.priceStyleCbs.add(cb);
|
|
32430
32801
|
return () => this.priceStyleCbs.delete(cb);
|
|
32431
32802
|
}
|
|
32803
|
+
onPriceStyleWillChange(cb) {
|
|
32804
|
+
this.priceStyleWillChangeCbs.add(cb);
|
|
32805
|
+
return () => this.priceStyleWillChangeCbs.delete(cb);
|
|
32806
|
+
}
|
|
32432
32807
|
/**
|
|
32433
32808
|
* THE single write path for the base price style at runtime (feature set / settings dialog /
|
|
32434
|
-
* config template — the constructor seeds the field directly, pre-listeners).
|
|
32435
|
-
*
|
|
32436
|
-
*
|
|
32809
|
+
* config template — the constructor seeds the field directly, pre-listeners). Announces the
|
|
32810
|
+
* switch while the scene still holds the old style (a listener may paint/capture that frame),
|
|
32811
|
+
* then updates the scene, eases any reveal layer toward the new style's target, and notifies
|
|
32812
|
+
* the core — which owns the DATA side of styles that need one (a chart type's SeriesDataEngine).
|
|
32437
32813
|
*/
|
|
32438
32814
|
setPriceStyle(style) {
|
|
32439
32815
|
if (style === this.scene.priceStyle) return;
|
|
32816
|
+
const from = this.scene.priceStyle;
|
|
32817
|
+
for (const cb of this.priceStyleWillChangeCbs) cb(from, style);
|
|
32440
32818
|
this.scene.priceStyle = style;
|
|
32441
32819
|
this.scene.basePainting = basePaintingOf(style);
|
|
32442
32820
|
this.scene.candleOverride = candleOverrideFor(style, this.scene.style.chartTypes);
|
|
@@ -32666,7 +33044,7 @@ var NativeRenderer = class {
|
|
|
32666
33044
|
if (this.easeLiveBar(dtMs)) active = true;
|
|
32667
33045
|
this.skeletonClockMs += dtMs;
|
|
32668
33046
|
this.paintData();
|
|
32669
|
-
this.
|
|
33047
|
+
this.renderCrosshairLayer();
|
|
32670
33048
|
this.updateLegendValues();
|
|
32671
33049
|
this.emitViewportChange();
|
|
32672
33050
|
return active;
|
|
@@ -33241,10 +33619,31 @@ var NativeRenderer = class {
|
|
|
33241
33619
|
this.chrome.render(this.scene, this.coords, this.theme, this.axisSurface());
|
|
33242
33620
|
this.trackMarkPopover();
|
|
33243
33621
|
}
|
|
33244
|
-
this.
|
|
33622
|
+
this.renderCrosshairLayer();
|
|
33245
33623
|
if (!repaintsData(level) && this.paintedData) this.repaintCursorLayers();
|
|
33246
33624
|
this.updateLegendValues();
|
|
33247
33625
|
}
|
|
33626
|
+
/** Repaint the L2 crosshair canvas and keep the `.vela-shade-right` element on the veil it painted. */
|
|
33627
|
+
renderCrosshairLayer() {
|
|
33628
|
+
this.crosshairLayer.render(this.scene, this.coords, this.theme, this.hoverSeparatorY, this.externalCrossPx());
|
|
33629
|
+
this.syncShadeRight();
|
|
33630
|
+
}
|
|
33631
|
+
/** Position (or hide) the `.vela-shade-right` element over the veil of the last crosshair frame. */
|
|
33632
|
+
syncShadeRight() {
|
|
33633
|
+
const el = this.shadeRightEl;
|
|
33634
|
+
if (!el) return;
|
|
33635
|
+
const area = this.crosshairLayer.shadeRightArea;
|
|
33636
|
+
const sig = area ? `${area.x},${area.width},${area.height}` : "";
|
|
33637
|
+
if (sig === this.shadeRightSig) return;
|
|
33638
|
+
this.shadeRightSig = sig;
|
|
33639
|
+
if (!area) {
|
|
33640
|
+
el.hidden = true;
|
|
33641
|
+
el.style.display = "none";
|
|
33642
|
+
return;
|
|
33643
|
+
}
|
|
33644
|
+
Object.assign(el.style, { left: `${area.x}px`, width: `${area.width}px`, height: `${area.height}px`, display: "" });
|
|
33645
|
+
el.hidden = false;
|
|
33646
|
+
}
|
|
33248
33647
|
/** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
|
|
33249
33648
|
repaintCursorLayers() {
|
|
33250
33649
|
const pane = this.scene.panes.get(PRICE_PANE_ID);
|
|
@@ -34038,7 +34437,7 @@ var NativeRenderer = class {
|
|
|
34038
34437
|
if (this.animator.active) {
|
|
34039
34438
|
this.computeScales();
|
|
34040
34439
|
this.paintData();
|
|
34041
|
-
this.
|
|
34440
|
+
this.renderCrosshairLayer();
|
|
34042
34441
|
} else {
|
|
34043
34442
|
this.scheduler.flushNow(4 /* Full */);
|
|
34044
34443
|
}
|
|
@@ -38082,7 +38481,8 @@ var Vela = class {
|
|
|
38082
38481
|
glow: options.glow ?? 0,
|
|
38083
38482
|
upColor: options.upColor ?? BULLISH,
|
|
38084
38483
|
downColor: options.downColor ?? BEARISH,
|
|
38085
|
-
priceStyle: options.priceStyle ?? "candles"
|
|
38484
|
+
priceStyle: options.priceStyle ?? "candles",
|
|
38485
|
+
priceAxisTicks: options.priceAxis?.ticks ?? null
|
|
38086
38486
|
};
|
|
38087
38487
|
const RendererClass = options.renderer ?? NativeRenderer;
|
|
38088
38488
|
if (typeof RendererClass !== "function") {
|
|
@@ -38568,6 +38968,23 @@ function resolveEngines(overrides) {
|
|
|
38568
38968
|
return { ...Object.fromEntries(defaultEngines), ...overrides };
|
|
38569
38969
|
}
|
|
38570
38970
|
|
|
38971
|
+
// src/widget/context-menu-model.ts
|
|
38972
|
+
var CONTEXT_MENU_BUILTIN_ORDER = {
|
|
38973
|
+
body: { "reset-view": -30, "remove-drawings": -20, "remove-indicators": -10, settings: 1e3 },
|
|
38974
|
+
"price-axis": {
|
|
38975
|
+
auto: -80,
|
|
38976
|
+
invert: -70,
|
|
38977
|
+
"scale:regular": -60,
|
|
38978
|
+
"scale:percent": -50,
|
|
38979
|
+
"scale:indexed": -40,
|
|
38980
|
+
"scale:log": -30,
|
|
38981
|
+
labels: -20,
|
|
38982
|
+
levels: -10,
|
|
38983
|
+
settings: 1e3
|
|
38984
|
+
},
|
|
38985
|
+
"time-axis": { timezone: -10, settings: 1e3 }
|
|
38986
|
+
};
|
|
38987
|
+
|
|
38571
38988
|
// src/widget/side-panel.ts
|
|
38572
38989
|
var DEFAULT_PANEL_WIDTH = 280;
|
|
38573
38990
|
var DEFAULT_PANEL_MIN_WIDTH = 200;
|
|
@@ -38586,6 +39003,7 @@ exports.BULLISH = BULLISH;
|
|
|
38586
39003
|
exports.BarStore = BarStore;
|
|
38587
39004
|
exports.CATEGORICAL = CATEGORICAL;
|
|
38588
39005
|
exports.CHIP_PLATE = CHIP_PLATE;
|
|
39006
|
+
exports.CONTEXT_MENU_BUILTIN_ORDER = CONTEXT_MENU_BUILTIN_ORDER;
|
|
38589
39007
|
exports.CROSSHAIR = CROSSHAIR;
|
|
38590
39008
|
exports.CachingDataFeed = CachingDataFeed;
|
|
38591
39009
|
exports.DARK_THEME = DARK_THEME;
|