@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/vela.global.js
CHANGED
|
@@ -7864,6 +7864,7 @@ var Vela = (function (exports) {
|
|
|
7864
7864
|
this.activeEngineStyle = null;
|
|
7865
7865
|
/** The chart's current price style (tracked from the renderer's change events + initial read). */
|
|
7866
7866
|
this.priceStyle = "candles";
|
|
7867
|
+
this.priceStyleWillChangeUnsub = null;
|
|
7867
7868
|
this.unresolvedUnsub = null;
|
|
7868
7869
|
/** Latest chart visible range (left/right bar times), fed to viewport-dependent scripts. */
|
|
7869
7870
|
this.visibleRange = null;
|
|
@@ -7962,6 +7963,7 @@ var Vela = (function (exports) {
|
|
|
7962
7963
|
this.renderer.onThemeSelect?.((name) => this.setTheme(resolveTheme(name)));
|
|
7963
7964
|
this.viewportUnsub = this.renderer.onViewportChange((range) => this.onViewportChange(range));
|
|
7964
7965
|
this.renderer.onPriceStyleChange?.((style) => this.syncPriceStyle(style));
|
|
7966
|
+
this.priceStyleWillChangeUnsub = this.renderer.onPriceStyleWillChange?.((from, to) => this.events.emit("priceStyle:change", { from, to })) ?? null;
|
|
7965
7967
|
const initialStyle = this.renderer.readFeature("priceStyle");
|
|
7966
7968
|
if (typeof initialStyle === "string") this.priceStyle = initialStyle;
|
|
7967
7969
|
this.barTransform = barTransformFor(initialStyle);
|
|
@@ -9492,6 +9494,8 @@ var Vela = (function (exports) {
|
|
|
9492
9494
|
if (this.viewportTimer != null) clearTimeout(this.viewportTimer);
|
|
9493
9495
|
this.viewportUnsub?.();
|
|
9494
9496
|
this.paneActionUnsub?.();
|
|
9497
|
+
this.priceStyleWillChangeUnsub?.();
|
|
9498
|
+
this.priceStyleWillChangeUnsub = null;
|
|
9495
9499
|
for (const record of this.registry.all()) {
|
|
9496
9500
|
this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
|
|
9497
9501
|
record.session?.stop();
|
|
@@ -13705,6 +13709,55 @@ ${STATIC_DECLS}
|
|
|
13705
13709
|
);
|
|
13706
13710
|
}
|
|
13707
13711
|
|
|
13712
|
+
// src/ui/surface-exit.ts
|
|
13713
|
+
var CLOSING_ATTR = "data-closing";
|
|
13714
|
+
var SURFACE_EXIT_CAP_MS = 1e3;
|
|
13715
|
+
function animationsOf(els) {
|
|
13716
|
+
return els.flatMap((el) => el.getAnimations({ subtree: true }));
|
|
13717
|
+
}
|
|
13718
|
+
function ends(a) {
|
|
13719
|
+
return Number.isFinite(a.effect?.getComputedTiming().endTime ?? Number.POSITIVE_INFINITY);
|
|
13720
|
+
}
|
|
13721
|
+
function holdForExit(els, done, opts = {}) {
|
|
13722
|
+
const list = Array.isArray(els) ? els : [els];
|
|
13723
|
+
if (list.length === 0 || !list.every((el) => typeof el.getAnimations === "function")) {
|
|
13724
|
+
done();
|
|
13725
|
+
return null;
|
|
13726
|
+
}
|
|
13727
|
+
const before = new Set(animationsOf(list));
|
|
13728
|
+
for (const el of list) el.setAttribute(CLOSING_ATTR, "");
|
|
13729
|
+
const exit = animationsOf(list).filter((a) => !before.has(a) && ends(a));
|
|
13730
|
+
if (exit.length === 0) {
|
|
13731
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
13732
|
+
done();
|
|
13733
|
+
return null;
|
|
13734
|
+
}
|
|
13735
|
+
const frozen = [...list, ...opts.inert ?? []];
|
|
13736
|
+
const wasInert = frozen.map((el) => el.inert);
|
|
13737
|
+
for (const el of frozen) el.inert = true;
|
|
13738
|
+
let settled = false;
|
|
13739
|
+
const settle = (complete) => {
|
|
13740
|
+
if (settled) return;
|
|
13741
|
+
settled = true;
|
|
13742
|
+
clearTimeout(cap);
|
|
13743
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
13744
|
+
frozen.forEach((el, i) => {
|
|
13745
|
+
el.inert = wasInert[i];
|
|
13746
|
+
});
|
|
13747
|
+
if (complete) done();
|
|
13748
|
+
};
|
|
13749
|
+
const cap = setTimeout(() => settle(true), SURFACE_EXIT_CAP_MS);
|
|
13750
|
+
let pending = exit.length;
|
|
13751
|
+
const one = () => {
|
|
13752
|
+
if (--pending === 0) settle(true);
|
|
13753
|
+
};
|
|
13754
|
+
for (const a of exit) a.finished.then(one, one);
|
|
13755
|
+
return {
|
|
13756
|
+
cancel: () => settle(false),
|
|
13757
|
+
finish: () => settle(true)
|
|
13758
|
+
};
|
|
13759
|
+
}
|
|
13760
|
+
|
|
13708
13761
|
// node_modules/@zag-js/anatomy/dist/create-anatomy.mjs
|
|
13709
13762
|
var createAnatomy = (name, parts3 = []) => ({
|
|
13710
13763
|
parts: (...values) => {
|
|
@@ -18117,6 +18170,35 @@ ${STATIC_DECLS}
|
|
|
18117
18170
|
color: var(--vela-fg-bright);
|
|
18118
18171
|
}
|
|
18119
18172
|
.vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
|
|
18173
|
+
/* Badge mode (iconBadges): the row icon sits in a rounded tile and the row grows to fit
|
|
18174
|
+
it. An icon-less row of the same level keeps an invisible slot, so labels align. */
|
|
18175
|
+
.vela-menu[data-badges] > .vela-menu-item { padding: 5px 10px 5px 6px; }
|
|
18176
|
+
.vela-menu-item .vela-menu-badge {
|
|
18177
|
+
width: 24px;
|
|
18178
|
+
height: 24px;
|
|
18179
|
+
padding: 3px;
|
|
18180
|
+
flex: none;
|
|
18181
|
+
box-sizing: border-box;
|
|
18182
|
+
display: inline-flex;
|
|
18183
|
+
align-items: center;
|
|
18184
|
+
justify-content: center;
|
|
18185
|
+
border-radius: var(--vela-radius-md);
|
|
18186
|
+
background: var(--vela-hover);
|
|
18187
|
+
border: 1px solid var(--vela-border);
|
|
18188
|
+
color: var(--vela-fg-muted);
|
|
18189
|
+
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);
|
|
18190
|
+
}
|
|
18191
|
+
.vela-menu-item .vela-menu-badge:empty { background: transparent; border-color: transparent; }
|
|
18192
|
+
.vela-menu-item .vela-menu-badge .vela-icon { color: inherit; }
|
|
18193
|
+
.vela-menu-item[data-highlighted] .vela-menu-badge:not(:empty) { color: var(--vela-fg-bright); }
|
|
18194
|
+
/* The selected tile inverts: a bright fill under the surface-colored glyph. After the
|
|
18195
|
+
hover rule, so a selected row keeps its tile while highlighted. */
|
|
18196
|
+
.vela-menu-item[data-checked] .vela-menu-badge:not(:empty),
|
|
18197
|
+
.vela-menu-item[data-checkmark] .vela-menu-badge:not(:empty) {
|
|
18198
|
+
background: color-mix(in srgb, var(--vela-fg-bright) 83%, var(--vela-surface));
|
|
18199
|
+
border-color: color-mix(in srgb, var(--vela-fg-bright) 40%, transparent);
|
|
18200
|
+
color: var(--vela-surface);
|
|
18201
|
+
}
|
|
18120
18202
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
18121
18203
|
same control language as the settings dialog's toggles. */
|
|
18122
18204
|
.vela-menu-switch {
|
|
@@ -18185,11 +18267,14 @@ ${STATIC_DECLS}
|
|
|
18185
18267
|
this.pinnedAnchor = null;
|
|
18186
18268
|
/** The open state last announced — see {@link announce}. */
|
|
18187
18269
|
this.shown = false;
|
|
18270
|
+
/** The list's exit animation after a close; a reopen cancels it. */
|
|
18271
|
+
this.exit = null;
|
|
18188
18272
|
this.doc = doc;
|
|
18189
18273
|
this.host = opts.host;
|
|
18190
18274
|
this.onSelect = opts.onSelect;
|
|
18191
18275
|
this.onFavorite = opts.onFavorite;
|
|
18192
18276
|
this.checkmarks = opts.checkmarks === true;
|
|
18277
|
+
this.iconBadges = opts.iconBadges === true;
|
|
18193
18278
|
this.positioner = doc.createElement("div");
|
|
18194
18279
|
this.positioner.className = "vela-ui-layer";
|
|
18195
18280
|
this.list = doc.createElement("ul");
|
|
@@ -18220,14 +18305,30 @@ ${STATIC_DECLS}
|
|
|
18220
18305
|
if (opts.triggerId && trigger) trigger.id = opts.triggerId;
|
|
18221
18306
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
18222
18307
|
const api = this.ctrl.connect(service);
|
|
18223
|
-
|
|
18308
|
+
const closing = this.shown && !api.open;
|
|
18309
|
+
if (closing) this.announce(false);
|
|
18310
|
+
if (api.open && this.exit) {
|
|
18311
|
+
this.exit.cancel();
|
|
18312
|
+
this.exit = null;
|
|
18313
|
+
}
|
|
18224
18314
|
if (trigger) spreadProps(trigger, api.getTriggerProps(), this.mid);
|
|
18225
18315
|
spreadProps(this.positioner, api.getPositionerProps(), this.mid);
|
|
18226
18316
|
spreadProps(this.list, api.getContentProps(), this.mid);
|
|
18227
18317
|
this.project(api);
|
|
18318
|
+
if (closing) this.holdExit();
|
|
18228
18319
|
if (api.open && !this.shown) this.announce(true);
|
|
18229
18320
|
});
|
|
18230
18321
|
}
|
|
18322
|
+
/** Keep the just-closed list up through its exit animation. `spreadProps` re-applies
|
|
18323
|
+
* `hidden` only when the machine's value for it changes, so the list stays shown until
|
|
18324
|
+
* the exit ends — or until a reopen, which projects `hidden: false` itself. */
|
|
18325
|
+
holdExit() {
|
|
18326
|
+
this.list.hidden = false;
|
|
18327
|
+
this.exit = holdForExit(this.list, () => {
|
|
18328
|
+
this.exit = null;
|
|
18329
|
+
if (!this.shown) this.list.hidden = true;
|
|
18330
|
+
});
|
|
18331
|
+
}
|
|
18231
18332
|
get api() {
|
|
18232
18333
|
return this.ctrl.connect(this.handle.service);
|
|
18233
18334
|
}
|
|
@@ -18251,6 +18352,7 @@ ${STATIC_DECLS}
|
|
|
18251
18352
|
destroy() {
|
|
18252
18353
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
18253
18354
|
this.subs.clear();
|
|
18355
|
+
this.exit?.finish();
|
|
18254
18356
|
this.handle.stop();
|
|
18255
18357
|
if (this.shown) this.announce(false);
|
|
18256
18358
|
this.positioner.remove();
|
|
@@ -18277,6 +18379,10 @@ ${STATIC_DECLS}
|
|
|
18277
18379
|
const doc = this.doc;
|
|
18278
18380
|
this.list.replaceChildren();
|
|
18279
18381
|
const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
|
|
18382
|
+
const iconic = this.items.some((i) => !!i.icon);
|
|
18383
|
+
const badged = this.iconBadges && iconic;
|
|
18384
|
+
if (badged) this.list.dataset.badges = "1";
|
|
18385
|
+
else delete this.list.dataset.badges;
|
|
18280
18386
|
for (const item of this.items) {
|
|
18281
18387
|
if (item.separatorBefore) {
|
|
18282
18388
|
const sep = doc.createElement("li");
|
|
@@ -18302,7 +18408,16 @@ ${STATIC_DECLS}
|
|
|
18302
18408
|
} else if (!branch && !item.toggle && item.checked) {
|
|
18303
18409
|
li.dataset.checked = "1";
|
|
18304
18410
|
}
|
|
18305
|
-
if (
|
|
18411
|
+
if (badged) {
|
|
18412
|
+
const badge = doc.createElement("span");
|
|
18413
|
+
badge.className = "vela-menu-badge";
|
|
18414
|
+
if (item.icon) badge.appendChild(iconEl(item.icon, doc));
|
|
18415
|
+
li.appendChild(badge);
|
|
18416
|
+
} else if (item.icon) {
|
|
18417
|
+
li.appendChild(iconEl(item.icon, doc));
|
|
18418
|
+
} else if (iconic) {
|
|
18419
|
+
li.appendChild(iconEl("", doc));
|
|
18420
|
+
}
|
|
18306
18421
|
const label = doc.createElement("span");
|
|
18307
18422
|
label.className = "vela-menu-label";
|
|
18308
18423
|
label.textContent = item.label;
|
|
@@ -18350,6 +18465,7 @@ ${STATIC_DECLS}
|
|
|
18350
18465
|
onSelect: this.onSelect,
|
|
18351
18466
|
onFavorite: this.onFavorite,
|
|
18352
18467
|
checkmarks: this.checkmarks,
|
|
18468
|
+
iconBadges: this.iconBadges,
|
|
18353
18469
|
id: `${this.mid}--${item.id}`
|
|
18354
18470
|
});
|
|
18355
18471
|
sub2.setItems(item.submenu ?? []);
|
|
@@ -18375,7 +18491,8 @@ ${STATIC_DECLS}
|
|
|
18375
18491
|
id: opts.id,
|
|
18376
18492
|
minWidth: opts.minWidth,
|
|
18377
18493
|
onFavorite: opts.onFavorite,
|
|
18378
|
-
checkmarks: opts.checkmarks
|
|
18494
|
+
checkmarks: opts.checkmarks,
|
|
18495
|
+
iconBadges: opts.iconBadges
|
|
18379
18496
|
});
|
|
18380
18497
|
this.root.setItems(opts.items);
|
|
18381
18498
|
}
|
|
@@ -18479,6 +18596,7 @@ ${STATIC_DECLS}
|
|
|
18479
18596
|
|
|
18480
18597
|
// src/ui/components/popover/view.ts
|
|
18481
18598
|
var open = null;
|
|
18599
|
+
var leaving = /* @__PURE__ */ new Set();
|
|
18482
18600
|
function closeOpenPopovers() {
|
|
18483
18601
|
open?.hide();
|
|
18484
18602
|
}
|
|
@@ -18506,6 +18624,8 @@ ${STATIC_DECLS}
|
|
|
18506
18624
|
this.shown = false;
|
|
18507
18625
|
/** The pending removal of a fading-out shell; a show() that reuses the shell cancels it. */
|
|
18508
18626
|
this.leaveTimer = null;
|
|
18627
|
+
/** The shell's exit animation before it leaves the DOM; a show() that reuses the shell cancels it. */
|
|
18628
|
+
this.exit = null;
|
|
18509
18629
|
const doc = opts.trigger.ownerDocument;
|
|
18510
18630
|
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
18511
18631
|
this.trigger = opts.trigger;
|
|
@@ -18542,6 +18662,10 @@ ${STATIC_DECLS}
|
|
|
18542
18662
|
clearTimeout(this.leaveTimer);
|
|
18543
18663
|
this.leaveTimer = null;
|
|
18544
18664
|
}
|
|
18665
|
+
this.exit?.cancel();
|
|
18666
|
+
this.exit = null;
|
|
18667
|
+
leaving.delete(this);
|
|
18668
|
+
for (const p of [...leaving]) if (p.trigger === this.trigger) p.exit?.finish();
|
|
18545
18669
|
ensureUIHost(this.el, this.inheritsTokens ? void 0 : this.theme);
|
|
18546
18670
|
if (this.fadeMs > 0) {
|
|
18547
18671
|
this.el.style.transition = `opacity ${this.fadeMs}ms ease`;
|
|
@@ -18598,7 +18722,12 @@ ${STATIC_DECLS}
|
|
|
18598
18722
|
this.el.remove();
|
|
18599
18723
|
}, this.fadeMs);
|
|
18600
18724
|
} else {
|
|
18601
|
-
this.el
|
|
18725
|
+
this.exit = holdForExit(this.el, () => {
|
|
18726
|
+
this.exit = null;
|
|
18727
|
+
leaving.delete(this);
|
|
18728
|
+
this.el.remove();
|
|
18729
|
+
});
|
|
18730
|
+
if (this.exit) leaving.add(this);
|
|
18602
18731
|
}
|
|
18603
18732
|
if (open === this) open = null;
|
|
18604
18733
|
this.ctrl.onClose?.();
|
|
@@ -18610,6 +18739,7 @@ ${STATIC_DECLS}
|
|
|
18610
18739
|
}
|
|
18611
18740
|
destroy() {
|
|
18612
18741
|
this.hide();
|
|
18742
|
+
this.exit?.finish();
|
|
18613
18743
|
}
|
|
18614
18744
|
reposition() {
|
|
18615
18745
|
if (this.shown) this.place();
|
|
@@ -20491,6 +20621,9 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20491
20621
|
constructor(opts = {}) {
|
|
20492
20622
|
this.opener = null;
|
|
20493
20623
|
this.wasOpen = false;
|
|
20624
|
+
/** The panel and scrim's exit animation after a close; a reopen cancels it. */
|
|
20625
|
+
this.exit = null;
|
|
20626
|
+
this.destroyed = false;
|
|
20494
20627
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
20495
20628
|
injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
|
|
20496
20629
|
const host = opts.host ?? doc.body;
|
|
@@ -20587,19 +20720,57 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20587
20720
|
const mid = String(this.ctrl.props.id);
|
|
20588
20721
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
20589
20722
|
const api = this.ctrl.connect(service);
|
|
20590
|
-
|
|
20723
|
+
const closing = this.wasOpen && !api.open;
|
|
20724
|
+
if (closing) this.announce(false);
|
|
20591
20725
|
spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
20592
20726
|
spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
20593
20727
|
spreadProps(this.panel, api.getContentProps(), mid);
|
|
20594
20728
|
spreadProps(title, api.getTitleProps(), mid);
|
|
20595
20729
|
spreadProps(close, api.getCloseTriggerProps(), mid);
|
|
20596
|
-
|
|
20597
|
-
|
|
20730
|
+
if (api.open) {
|
|
20731
|
+
this.exit?.cancel();
|
|
20732
|
+
this.exit = null;
|
|
20733
|
+
this.setShown(true);
|
|
20734
|
+
} else if (closing) {
|
|
20735
|
+
this.holdExit();
|
|
20736
|
+
} else if (!this.exit) {
|
|
20737
|
+
this.setShown(false);
|
|
20738
|
+
}
|
|
20598
20739
|
if (api.open && !this.wasOpen) this.announce(true);
|
|
20599
20740
|
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
20600
20741
|
this.wasOpen = api.open;
|
|
20601
20742
|
});
|
|
20602
20743
|
}
|
|
20744
|
+
setShown(shown) {
|
|
20745
|
+
this.backdrop.style.display = shown ? "" : "none";
|
|
20746
|
+
this.positioner.style.display = shown ? "" : "none";
|
|
20747
|
+
}
|
|
20748
|
+
/** Keep the just-closed dialog up through its exit animation, then hide it — or
|
|
20749
|
+
* remove it, when it was destroyed meanwhile. `spreadProps` re-applies the close
|
|
20750
|
+
* props' `hidden` only when its value changes, so lifting it keeps the panel and
|
|
20751
|
+
* scrim up until the exit ends. The positioner spans the viewport: inert, it lets
|
|
20752
|
+
* clicks through to the page while the panel animates out. */
|
|
20753
|
+
holdExit() {
|
|
20754
|
+
const held = [this.panel, this.backdrop];
|
|
20755
|
+
for (const el of held) el.hidden = false;
|
|
20756
|
+
this.exit = holdForExit(
|
|
20757
|
+
held,
|
|
20758
|
+
() => {
|
|
20759
|
+
this.exit = null;
|
|
20760
|
+
if (this.destroyed) {
|
|
20761
|
+
this.detach();
|
|
20762
|
+
} else if (!this.open) {
|
|
20763
|
+
for (const el of held) el.hidden = true;
|
|
20764
|
+
this.setShown(false);
|
|
20765
|
+
}
|
|
20766
|
+
},
|
|
20767
|
+
{ inert: [this.positioner] }
|
|
20768
|
+
);
|
|
20769
|
+
}
|
|
20770
|
+
detach() {
|
|
20771
|
+
this.backdrop.remove();
|
|
20772
|
+
this.positioner.remove();
|
|
20773
|
+
}
|
|
20603
20774
|
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
20604
20775
|
* teardown that outruns the machine's close notification). */
|
|
20605
20776
|
restoreOpener() {
|
|
@@ -20628,15 +20799,16 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20628
20799
|
return el?.closest(".vela-popover, .vela-menu") != null;
|
|
20629
20800
|
}
|
|
20630
20801
|
destroy() {
|
|
20802
|
+
if (this.wasOpen && !this.open) this.handle.flush();
|
|
20631
20803
|
if (this.open) this.hide();
|
|
20632
20804
|
this.handle.stop();
|
|
20805
|
+
this.destroyed = true;
|
|
20633
20806
|
if (this.wasOpen) {
|
|
20634
20807
|
this.wasOpen = false;
|
|
20635
20808
|
this.announce(false);
|
|
20636
20809
|
}
|
|
20637
20810
|
this.restoreOpener();
|
|
20638
|
-
this.
|
|
20639
|
-
this.positioner.remove();
|
|
20811
|
+
if (!this.exit) this.detach();
|
|
20640
20812
|
}
|
|
20641
20813
|
};
|
|
20642
20814
|
|
|
@@ -23088,7 +23260,10 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23088
23260
|
var LEGEND_CTL_PX = 18;
|
|
23089
23261
|
var LEGEND_ROW_PAD_Y = 2;
|
|
23090
23262
|
var LEGEND_ROW_PAD_X = 6;
|
|
23263
|
+
var LEGEND_LEFT_PX = 10;
|
|
23264
|
+
var LEGEND_SCALE_GAP_PX = 6;
|
|
23091
23265
|
var LEGEND_TITLE_VALUES_GAP_PX = 6;
|
|
23266
|
+
var LEGEND_VALUES_GAP_PX = 5;
|
|
23092
23267
|
var LEGEND_TITLE_STATUS_GAP_PX = 8;
|
|
23093
23268
|
var LEGEND_ACTION_GAP_PX = 6;
|
|
23094
23269
|
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;`;
|
|
@@ -23374,14 +23549,15 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23374
23549
|
row.valuesEl.replaceChildren();
|
|
23375
23550
|
return;
|
|
23376
23551
|
}
|
|
23377
|
-
row.valuesEl.style.display = "
|
|
23552
|
+
row.valuesEl.style.display = "block";
|
|
23378
23553
|
row.valuesEl.replaceChildren();
|
|
23379
|
-
|
|
23554
|
+
row.plotValues.forEach((v, i) => {
|
|
23380
23555
|
const span = document.createElement("span");
|
|
23381
23556
|
span.textContent = v.value;
|
|
23382
23557
|
span.style.color = v.color;
|
|
23558
|
+
if (i > 0) span.style.marginLeft = `${LEGEND_VALUES_GAP_PX}px`;
|
|
23383
23559
|
row.valuesEl.appendChild(span);
|
|
23384
|
-
}
|
|
23560
|
+
});
|
|
23385
23561
|
}
|
|
23386
23562
|
// ── legend move/merge (menu + drag) ─────────────────────────────────────
|
|
23387
23563
|
/** Open the "Move to" menu for a row, anchored under its move button. */
|
|
@@ -23482,7 +23658,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23482
23658
|
if (!lg) {
|
|
23483
23659
|
lg = document.createElement("div");
|
|
23484
23660
|
lg.dataset.velaPane = paneId;
|
|
23485
|
-
lg.style.cssText =
|
|
23661
|
+
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;`;
|
|
23486
23662
|
applyChromeTokens(lg, this.theme);
|
|
23487
23663
|
this.positionLegend(lg, paneId);
|
|
23488
23664
|
this.container.appendChild(lg);
|
|
@@ -23494,6 +23670,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23494
23670
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
23495
23671
|
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
23496
23672
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
23673
|
+
lg.style.maxWidth = `calc(100% - ${(bounds.rightAxis ?? 0) + LEGEND_LEFT_PX + LEGEND_SCALE_GAP_PX}px)`;
|
|
23497
23674
|
const collapsed = this.paneCollapse.has(paneId);
|
|
23498
23675
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
23499
23676
|
for (const row of this.rows.values()) {
|
|
@@ -23585,7 +23762,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23585
23762
|
}
|
|
23586
23763
|
ensureDialogStyles();
|
|
23587
23764
|
const el = document.createElement("div");
|
|
23588
|
-
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;`;
|
|
23765
|
+
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;`;
|
|
23589
23766
|
el.addEventListener("mouseleave", () => {
|
|
23590
23767
|
if (this.selectedId !== id) this.setRowHighlighted(id, false);
|
|
23591
23768
|
});
|
|
@@ -23613,7 +23790,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23613
23790
|
const statusEl = document.createElement("span");
|
|
23614
23791
|
statusEl.style.cssText = "display:none;box-sizing:border-box;flex:none;";
|
|
23615
23792
|
const titleWrap = document.createElement("span");
|
|
23616
|
-
titleWrap.style.cssText = "white-space:nowrap;";
|
|
23793
|
+
titleWrap.style.cssText = "white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;";
|
|
23617
23794
|
titleWrap.addEventListener("mouseenter", () => this.setRowHighlighted(id, true));
|
|
23618
23795
|
const titleEl = document.createElement("span");
|
|
23619
23796
|
titleEl.textContent = title;
|
|
@@ -23631,7 +23808,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23631
23808
|
el.appendChild(calloutsEl);
|
|
23632
23809
|
el.appendChild(statusEl);
|
|
23633
23810
|
const valuesEl = document.createElement("span");
|
|
23634
|
-
valuesEl.style.cssText = `display:none;
|
|
23811
|
+
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;`;
|
|
23635
23812
|
el.appendChild(valuesEl);
|
|
23636
23813
|
const controlsEl = document.createElement("span");
|
|
23637
23814
|
controlsEl.style.cssText = `display:none;align-items:center;gap:${LEGEND_ACTION_GAP_PX}px;flex:none;margin-left:${LEGEND_ACTION_GAP_PX}px;`;
|
|
@@ -26226,6 +26403,11 @@ void main() {
|
|
|
26226
26403
|
/** When true (the default), the separators between stacked panes are draggable to
|
|
26227
26404
|
* resize the panes above/below. When false, a press there is a normal data-area pan. */
|
|
26228
26405
|
this.paneResize = true;
|
|
26406
|
+
/** When true (the default), the user drawings take part in pointer input: they claim a
|
|
26407
|
+
* press over a drawing or with a tool armed, and see hover moves. When false, every press
|
|
26408
|
+
* is a plain pan/click and hover leaves them alone; a drawing gesture already under way
|
|
26409
|
+
* still finishes. */
|
|
26410
|
+
this.drawings = true;
|
|
26229
26411
|
this.el = null;
|
|
26230
26412
|
this.dragging = false;
|
|
26231
26413
|
this.moved = false;
|
|
@@ -26289,18 +26471,18 @@ void main() {
|
|
|
26289
26471
|
this.onModifier = (e) => {
|
|
26290
26472
|
if (e.repeat || e.key !== "Shift" && e.key !== "Control" && e.key !== "Meta") return;
|
|
26291
26473
|
if (Number.isNaN(this.cursorX)) return;
|
|
26292
|
-
if (this.dragging
|
|
26474
|
+
if (this.dragging ? this.region !== "drawing" : !this.drawings) return;
|
|
26293
26475
|
this.deps.drawingsPointerMove?.(this.cursorX, this.cursorY, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26294
26476
|
};
|
|
26295
26477
|
this.onDown = (e) => {
|
|
26296
26478
|
if (e.button === 1) {
|
|
26297
26479
|
const { x: x2, y: y2 } = this.local(e);
|
|
26298
|
-
this.middleDeleted = this.deps.drawingsDeleteAt?.(x2, y2) ?? false;
|
|
26480
|
+
this.middleDeleted = this.drawings && (this.deps.drawingsDeleteAt?.(x2, y2) ?? false);
|
|
26299
26481
|
if (this.middleDeleted) e.preventDefault();
|
|
26300
26482
|
return;
|
|
26301
26483
|
}
|
|
26302
26484
|
if (e.button === 2) {
|
|
26303
|
-
this.rightCancelled = this.deps.drawingsCancelPlacement?.() ?? false;
|
|
26485
|
+
this.rightCancelled = this.drawings && (this.deps.drawingsCancelPlacement?.() ?? false);
|
|
26304
26486
|
if (this.rightCancelled) e.preventDefault();
|
|
26305
26487
|
return;
|
|
26306
26488
|
}
|
|
@@ -26322,20 +26504,20 @@ void main() {
|
|
|
26322
26504
|
this.startY = y;
|
|
26323
26505
|
this.prevPressDrawing = this.lastPressDrawing;
|
|
26324
26506
|
this.lastPressDrawing = false;
|
|
26325
|
-
if (this.deps.drawingsClaim?.(x, y)) {
|
|
26507
|
+
if (this.drawings && this.deps.drawingsClaim?.(x, y)) {
|
|
26326
26508
|
this.region = "drawing";
|
|
26327
26509
|
this.lastPressDrawing = true;
|
|
26328
26510
|
this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26329
26511
|
this.capture(e.pointerId);
|
|
26330
26512
|
return;
|
|
26331
26513
|
}
|
|
26332
|
-
if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
26514
|
+
if (this.drawings && e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
26333
26515
|
this.region = "drawing";
|
|
26334
26516
|
this.lastPressDrawing = true;
|
|
26335
26517
|
this.capture(e.pointerId);
|
|
26336
26518
|
return;
|
|
26337
26519
|
}
|
|
26338
|
-
if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
26520
|
+
if (this.drawings && (e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
26339
26521
|
this.region = "drawing";
|
|
26340
26522
|
this.lastPressDrawing = true;
|
|
26341
26523
|
this.capture(e.pointerId);
|
|
@@ -26412,7 +26594,7 @@ void main() {
|
|
|
26412
26594
|
const drawCursor = this.deps.drawingsCursor?.(x, y);
|
|
26413
26595
|
const r = this.regionAt(x, y);
|
|
26414
26596
|
this.el.style.cursor = drawCursor ?? (r === "price" ? "ns-resize" : r === "time" ? "ew-resize" : r === "separator" ? "row-resize" : "");
|
|
26415
|
-
this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26597
|
+
if (this.drawings) this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26416
26598
|
}
|
|
26417
26599
|
if (e.pointerType !== "touch") this.deps.onPointerMove(x, y);
|
|
26418
26600
|
};
|
|
@@ -26654,7 +26836,7 @@ void main() {
|
|
|
26654
26836
|
}
|
|
26655
26837
|
/** Shared double-click / double-tap routing, by the region under the point. */
|
|
26656
26838
|
doubleActivate(x, y) {
|
|
26657
|
-
if (this.deps.drawingsDblClick?.(x, y)) return;
|
|
26839
|
+
if (this.drawings && this.deps.drawingsDblClick?.(x, y)) return;
|
|
26658
26840
|
const region = this.regionAt(x, y);
|
|
26659
26841
|
if (region === "price") this.deps.resetPriceScale(x, y);
|
|
26660
26842
|
else if (region === "separator") this.deps.resetPaneSize(y);
|
|
@@ -28668,10 +28850,13 @@ void main() {
|
|
|
28668
28850
|
const norm = raw / mag;
|
|
28669
28851
|
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
28670
28852
|
const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);
|
|
28671
|
-
const out = [];
|
|
28672
28853
|
const start = Math.ceil(min2 / step) * step;
|
|
28673
|
-
|
|
28674
|
-
|
|
28854
|
+
if (!(step > 0) || start + step === start) return [];
|
|
28855
|
+
const count = Math.floor((max2 - start) / step + 1e-6) + 1;
|
|
28856
|
+
if (!(count > 0) || count > 1e3) return [];
|
|
28857
|
+
const out = [];
|
|
28858
|
+
for (let i = 0; i < count; i += 1) {
|
|
28859
|
+
out.push(Number((start + i * step).toFixed(decimals)));
|
|
28675
28860
|
}
|
|
28676
28861
|
return out;
|
|
28677
28862
|
}
|
|
@@ -28869,6 +29054,100 @@ void main() {
|
|
|
28869
29054
|
return out;
|
|
28870
29055
|
}
|
|
28871
29056
|
|
|
29057
|
+
// src/renderers/native/chrome/priceAxisTicks.ts
|
|
29058
|
+
var PriceAxisTickSource = class {
|
|
29059
|
+
constructor() {
|
|
29060
|
+
this.fn = null;
|
|
29061
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
29062
|
+
this.warned = false;
|
|
29063
|
+
}
|
|
29064
|
+
/** The host function, or null for the built-in ticks. */
|
|
29065
|
+
get hook() {
|
|
29066
|
+
return this.fn;
|
|
29067
|
+
}
|
|
29068
|
+
/** Set (or clear) the host function. Drops every memoized result, so setting the same
|
|
29069
|
+
* function again recomputes — how a host refreshes ticks that depend on its own state. */
|
|
29070
|
+
setHook(fn) {
|
|
29071
|
+
this.fn = fn;
|
|
29072
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
29073
|
+
}
|
|
29074
|
+
/** The pane's master-scale ticks for this frame. An unscaled pane (`axisFormat: 'none'`)
|
|
29075
|
+
* gets none, and the host is never asked for it. */
|
|
29076
|
+
ticksFor(scene, pane, coords) {
|
|
29077
|
+
const format = pane.axisFormat;
|
|
29078
|
+
if (format === "none") return [];
|
|
29079
|
+
const pct = percentScaleFor(scene, pane);
|
|
29080
|
+
const height = pane.bounds.height;
|
|
29081
|
+
const fn = this.fn;
|
|
29082
|
+
if (!fn || !(height > 0)) return paneAxisTicks(pane.scale, height, pct, scene.priceMintick, format);
|
|
29083
|
+
const s = pane.scale;
|
|
29084
|
+
const m = this.memo.get(pane);
|
|
29085
|
+
const fontSize = scene.style.fontSize;
|
|
29086
|
+
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) {
|
|
29087
|
+
return m.ticks;
|
|
29088
|
+
}
|
|
29089
|
+
const defaults2 = paneAxisTicks(s, height, pct, scene.priceMintick, format);
|
|
29090
|
+
const ticks = this.callHook(fn, scene, pane, coords, defaults2, pct?.baseline, pct ? pct.indexed ? "indexed" : "percent" : "price");
|
|
29091
|
+
this.memo.set(pane, {
|
|
29092
|
+
kind: pane.kind,
|
|
29093
|
+
min: s.min,
|
|
29094
|
+
max: s.max,
|
|
29095
|
+
log: !!s.log,
|
|
29096
|
+
invert: !!s.invert,
|
|
29097
|
+
height,
|
|
29098
|
+
baseline: pct?.baseline,
|
|
29099
|
+
indexed: !!pct?.indexed,
|
|
29100
|
+
mintick: scene.priceMintick,
|
|
29101
|
+
format,
|
|
29102
|
+
fontSize,
|
|
29103
|
+
ticks
|
|
29104
|
+
});
|
|
29105
|
+
return ticks;
|
|
29106
|
+
}
|
|
29107
|
+
callHook(fn, scene, pane, coords, defaults2, baseline, mode) {
|
|
29108
|
+
const scale = { min: pane.scale.min, max: pane.scale.max, log: !!pane.scale.log, invert: !!pane.scale.invert };
|
|
29109
|
+
const bounds = { top: 0, height: pane.bounds.height };
|
|
29110
|
+
const ctx = {
|
|
29111
|
+
pane: { id: pane.id, kind: pane.kind },
|
|
29112
|
+
min: Math.min(scale.min, scale.max),
|
|
29113
|
+
max: Math.max(scale.min, scale.max),
|
|
29114
|
+
log: !!scale.log,
|
|
29115
|
+
height: bounds.height,
|
|
29116
|
+
mode,
|
|
29117
|
+
baseline,
|
|
29118
|
+
mintick: scene.priceMintick,
|
|
29119
|
+
fontSize: scene.style.fontSize,
|
|
29120
|
+
priceToY: (price) => coords.priceToY(price, scale, bounds),
|
|
29121
|
+
defaults: defaults2
|
|
29122
|
+
};
|
|
29123
|
+
let out;
|
|
29124
|
+
try {
|
|
29125
|
+
out = fn(ctx);
|
|
29126
|
+
} catch (err) {
|
|
29127
|
+
this.warnOnce("threw", err);
|
|
29128
|
+
return defaults2;
|
|
29129
|
+
}
|
|
29130
|
+
if (out == null) return defaults2;
|
|
29131
|
+
if (!Array.isArray(out)) {
|
|
29132
|
+
this.warnOnce("returned a non-array", out);
|
|
29133
|
+
return defaults2;
|
|
29134
|
+
}
|
|
29135
|
+
const ticks = [];
|
|
29136
|
+
for (const t of out) {
|
|
29137
|
+
if (!t || typeof t !== "object") continue;
|
|
29138
|
+
const { price, label, major } = t;
|
|
29139
|
+
if (typeof price !== "number" || !Number.isFinite(price) || typeof label !== "string") continue;
|
|
29140
|
+
ticks.push(typeof major === "boolean" ? { price, label, major } : { price, label });
|
|
29141
|
+
}
|
|
29142
|
+
return ticks;
|
|
29143
|
+
}
|
|
29144
|
+
warnOnce(what, detail) {
|
|
29145
|
+
if (this.warned) return;
|
|
29146
|
+
this.warned = true;
|
|
29147
|
+
console.warn(`[vela] the priceAxisTicks function ${what} \u2014 the default ticks are used instead.`, detail);
|
|
29148
|
+
}
|
|
29149
|
+
};
|
|
29150
|
+
|
|
28872
29151
|
// src/renderers/native/chrome/countdown.ts
|
|
28873
29152
|
function countdownText(barOpen, barMs, now2) {
|
|
28874
29153
|
if (!(barMs > 0)) return null;
|
|
@@ -29221,8 +29500,11 @@ void main() {
|
|
|
29221
29500
|
}
|
|
29222
29501
|
|
|
29223
29502
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
29503
|
+
var MINOR_LABEL_ALPHA = 0.5;
|
|
29224
29504
|
var ChromeRenderer = class {
|
|
29225
|
-
|
|
29505
|
+
/** `axisTicks` is shared with the backdrop layer, so price labels and gridlines agree. */
|
|
29506
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
29507
|
+
this.axisTicks = axisTicks;
|
|
29226
29508
|
this.canvas = null;
|
|
29227
29509
|
this.ctx = null;
|
|
29228
29510
|
// The color for axis tick labels — the host-passed surface text, set each frame in render().
|
|
@@ -29327,7 +29609,8 @@ void main() {
|
|
|
29327
29609
|
this.drawTimeAxis(ctx, scene, coords, theme, dataW, dataH, fullH);
|
|
29328
29610
|
this.drawMarkLane(ctx, scene, coords, theme, dataW, dataH);
|
|
29329
29611
|
}
|
|
29330
|
-
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge
|
|
29612
|
+
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge,
|
|
29613
|
+
* but clipped to the data area so a glyph on the edge bar never paints over the price scale. */
|
|
29331
29614
|
drawMarkLane(ctx, scene, coords, theme, dataW, dataH) {
|
|
29332
29615
|
if (!scene.marks.visible || scene.timelineMarks.length === 0) {
|
|
29333
29616
|
this.markLayout = { glyphs: [], stacks: /* @__PURE__ */ new Map() };
|
|
@@ -29345,6 +29628,10 @@ void main() {
|
|
|
29345
29628
|
expanded: scene.marksExpandedStack
|
|
29346
29629
|
});
|
|
29347
29630
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
29631
|
+
ctx.save();
|
|
29632
|
+
ctx.beginPath();
|
|
29633
|
+
ctx.rect(0, 0, dataW, dataH);
|
|
29634
|
+
ctx.clip();
|
|
29348
29635
|
paintMarkLane(ctx, this.markLayout, {
|
|
29349
29636
|
axisY: dataH,
|
|
29350
29637
|
background: theme.background,
|
|
@@ -29358,6 +29645,7 @@ void main() {
|
|
|
29358
29645
|
flashKey: scene.marksFlash && scene.marksFlash.until > nowMs ? scene.marksFlash.key : null,
|
|
29359
29646
|
nowMs
|
|
29360
29647
|
});
|
|
29648
|
+
ctx.restore();
|
|
29361
29649
|
}
|
|
29362
29650
|
barTimes(scene) {
|
|
29363
29651
|
if (this.barTimesSrc !== scene.bars || this.barTimesCache.length !== scene.bars.length) {
|
|
@@ -29414,13 +29702,19 @@ void main() {
|
|
|
29414
29702
|
if (!scene.showAxisLabels) return;
|
|
29415
29703
|
ctx.fillStyle = this.axisTextColor;
|
|
29416
29704
|
ctx.textAlign = "left";
|
|
29705
|
+
const font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
29706
|
+
const majorFont = `600 ${font}`;
|
|
29707
|
+
const alpha = ctx.globalAlpha;
|
|
29417
29708
|
for (const pane of panes) {
|
|
29418
29709
|
if (pane.collapsed) continue;
|
|
29419
|
-
const
|
|
29420
|
-
for (const t of paneAxisTicks(pane.scale, pane.bounds.height, pct, scene.priceMintick, pane.axisFormat)) {
|
|
29710
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
29421
29711
|
const y = coords.priceToY(t.price, pane.scale, pane.bounds);
|
|
29422
29712
|
if (y < pane.bounds.top + 6 || y > pane.bounds.top + pane.bounds.height - 4) continue;
|
|
29713
|
+
if (t.major === true) ctx.font = majorFont;
|
|
29714
|
+
else if (t.major === false) ctx.globalAlpha = alpha * MINOR_LABEL_ALPHA;
|
|
29423
29715
|
ctx.fillText(t.label, dataW + 6, y);
|
|
29716
|
+
if (t.major === true) ctx.font = font;
|
|
29717
|
+
else if (t.major === false) ctx.globalAlpha = alpha;
|
|
29424
29718
|
}
|
|
29425
29719
|
if (pane.axisBands) {
|
|
29426
29720
|
for (const b of pane.axisBands) {
|
|
@@ -29662,15 +29956,23 @@ void main() {
|
|
|
29662
29956
|
constructor() {
|
|
29663
29957
|
this.canvas = null;
|
|
29664
29958
|
this.ctx = null;
|
|
29959
|
+
this.shade = null;
|
|
29665
29960
|
}
|
|
29666
29961
|
mount(canvas) {
|
|
29667
29962
|
this.canvas = canvas;
|
|
29668
29963
|
this.ctx = canvas.getContext("2d");
|
|
29669
29964
|
}
|
|
29965
|
+
/** The plot area the last frame veiled for `crosshairOverride.shadeRight` — from the
|
|
29966
|
+
* picked bar's right edge to the price scale, full plot height (the union when both the
|
|
29967
|
+
* local and a synced crosshair veil) — or null when no veil was painted. */
|
|
29968
|
+
get shadeRightArea() {
|
|
29969
|
+
return this.shade;
|
|
29970
|
+
}
|
|
29670
29971
|
/** Clear the cursor canvas and (re)draw the crosshair lines + axis chips. The optional
|
|
29671
29972
|
* `separatorHoverY` highlights the draggable pane separator under the cursor;
|
|
29672
29973
|
* `external` is a SYNCED ghost crosshair (another chart's pointer, pixel-resolved). */
|
|
29673
29974
|
render(scene, coords, theme, separatorHoverY = null, external = null) {
|
|
29975
|
+
this.shade = null;
|
|
29674
29976
|
const ctx = this.ctx;
|
|
29675
29977
|
const canvas = this.canvas;
|
|
29676
29978
|
if (!ctx || !canvas) return;
|
|
@@ -29693,12 +29995,7 @@ void main() {
|
|
|
29693
29995
|
const x = crisp(coords.logicalToX(logical), ov?.width ?? cs.width);
|
|
29694
29996
|
if (vertical && ov?.shadeRight) {
|
|
29695
29997
|
const from = Math.max(0, Math.round(coords.logicalToX(logical + 0.5)));
|
|
29696
|
-
if (from < dataW)
|
|
29697
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
29698
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
29699
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
29700
|
-
ctx.globalAlpha = 1;
|
|
29701
|
-
}
|
|
29998
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
29702
29999
|
}
|
|
29703
30000
|
ctx.strokeStyle = ov?.color ?? cs.color ?? theme.textColor;
|
|
29704
30001
|
ctx.lineWidth = ov?.width ?? cs.width;
|
|
@@ -29734,6 +30031,7 @@ void main() {
|
|
|
29734
30031
|
destroy() {
|
|
29735
30032
|
this.canvas = null;
|
|
29736
30033
|
this.ctx = null;
|
|
30034
|
+
this.shade = null;
|
|
29737
30035
|
}
|
|
29738
30036
|
/** The synced ghost: a dimmed vertical line at the bar the renderer resolved as
|
|
29739
30037
|
* CONTAINING the foreign time (+ horizontal line when a comparable price came
|
|
@@ -29753,12 +30051,7 @@ void main() {
|
|
|
29753
30051
|
if (ov?.vertical === false) return;
|
|
29754
30052
|
if (ov?.shadeRight) {
|
|
29755
30053
|
const from = Math.max(0, Math.round(coords.logicalToX(Math.round(coords.xToLogical(ext.x)) + 0.5)));
|
|
29756
|
-
if (from < dataW)
|
|
29757
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
29758
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
29759
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
29760
|
-
ctx.globalAlpha = 1;
|
|
29761
|
-
}
|
|
30054
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
29762
30055
|
}
|
|
29763
30056
|
if (ext.line === false || x < 0 || x > dataW) return;
|
|
29764
30057
|
ctx.font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
@@ -29794,6 +30087,15 @@ void main() {
|
|
|
29794
30087
|
this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
|
|
29795
30088
|
ctx.globalAlpha = 1;
|
|
29796
30089
|
}
|
|
30090
|
+
/** Paint the `shadeRight` veil from `from` to the price scale and record the veiled area. */
|
|
30091
|
+
paintShade(ctx, shade, from, dataW, dataH) {
|
|
30092
|
+
ctx.fillStyle = shade.color;
|
|
30093
|
+
ctx.globalAlpha = shade.opacity ?? 1;
|
|
30094
|
+
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
30095
|
+
ctx.globalAlpha = 1;
|
|
30096
|
+
const x = this.shade ? Math.min(this.shade.x, from) : from;
|
|
30097
|
+
this.shade = { x, width: dataW - x, height: dataH };
|
|
30098
|
+
}
|
|
29797
30099
|
/** A soft band + a brighter crisp center line over the hovered separator, so it reads as
|
|
29798
30100
|
* a draggable handle (the cursor is already `row-resize`). Spans the full width (data +
|
|
29799
30101
|
* scale gutter) to match the separator itself. Theme-derived (textColor). */
|
|
@@ -33289,15 +33591,16 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33289
33591
|
}
|
|
33290
33592
|
open(drawing, actions, kind) {
|
|
33291
33593
|
this.close();
|
|
33292
|
-
const
|
|
33594
|
+
const live = actions.resolve() ?? drawing;
|
|
33595
|
+
const snapshot2 = clonePlain(live.serialize());
|
|
33293
33596
|
const grid = fieldGrid({ variant: "inputs" });
|
|
33294
33597
|
grid.style.padding = "16px 20px";
|
|
33295
33598
|
grid.style.overflowY = "auto";
|
|
33296
33599
|
grid.style.overflowX = "hidden";
|
|
33297
33600
|
grid.style.flex = "1 1 auto";
|
|
33298
|
-
if (kind === "position" &&
|
|
33299
|
-
else if (kind === "frvp" &&
|
|
33300
|
-
else if (kind === "levels") this.buildLevels(grid,
|
|
33601
|
+
if (kind === "position" && live instanceof PositionTool) this.buildPosition(grid, live, actions);
|
|
33602
|
+
else if (kind === "frvp" && live instanceof FixedRangeVolumeProfile) this.buildFrvp(grid, live, actions);
|
|
33603
|
+
else if (kind === "levels") this.buildLevels(grid, live, actions);
|
|
33301
33604
|
else return;
|
|
33302
33605
|
const ui = new Dialog({
|
|
33303
33606
|
host: this.host,
|
|
@@ -33944,7 +34247,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33944
34247
|
this.reposition();
|
|
33945
34248
|
return;
|
|
33946
34249
|
}
|
|
33947
|
-
const text = drawing.text;
|
|
34250
|
+
const text = (actions.resolve() ?? drawing).text;
|
|
33948
34251
|
const panel = document.createElement("div");
|
|
33949
34252
|
panel.style.cssText = `padding:6px;border-top:1px solid var(--vela-border);display:flex;flex-direction:column;gap:4px;`;
|
|
33950
34253
|
const ta = new TextArea({
|
|
@@ -34540,6 +34843,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34540
34843
|
// src/renderers/native/drawings/DrawingToolbar.ts
|
|
34541
34844
|
var TOOLBAR_WIDTH = 44;
|
|
34542
34845
|
var TOOLBAR_COLLAPSED_WIDTH = 16;
|
|
34846
|
+
var FLYOUT_GAP = 3;
|
|
34543
34847
|
var DrawingToolbar = class {
|
|
34544
34848
|
constructor(host, theme, onArm, onMagnet = () => {
|
|
34545
34849
|
}, onMeasure = () => {
|
|
@@ -34569,6 +34873,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34569
34873
|
// group id (or MAGNET_ID) whose flyout is open
|
|
34570
34874
|
this.flyoutCell = null;
|
|
34571
34875
|
// the cell the open flyout is anchored to
|
|
34876
|
+
/** Closed flyouts still playing their exit animation, by owner id — reopening the same
|
|
34877
|
+
* owner takes its element back instead of stacking a second one on it. */
|
|
34878
|
+
this.exitingFlyouts = /* @__PURE__ */ new Map();
|
|
34572
34879
|
this.groupCells = /* @__PURE__ */ new Map();
|
|
34573
34880
|
// the composite cell (hover/active bg + flyout anchor)
|
|
34574
34881
|
this.groupIcons = /* @__PURE__ */ new Map();
|
|
@@ -34677,6 +34984,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34677
34984
|
}
|
|
34678
34985
|
destroy() {
|
|
34679
34986
|
this.closeFlyout();
|
|
34987
|
+
for (const { exit } of [...this.exitingFlyouts.values()]) exit.finish();
|
|
34680
34988
|
for (const dispose of this.tipDisposers.splice(0)) dispose();
|
|
34681
34989
|
this.root.remove();
|
|
34682
34990
|
}
|
|
@@ -34904,16 +35212,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34904
35212
|
* outside-dismiss. Callers fill it with items. */
|
|
34905
35213
|
beginFlyout(ownerId, cell) {
|
|
34906
35214
|
const t = this.theme;
|
|
34907
|
-
const
|
|
35215
|
+
const back = this.exitingFlyouts.get(ownerId);
|
|
35216
|
+
if (back) {
|
|
35217
|
+
this.exitingFlyouts.delete(ownerId);
|
|
35218
|
+
back.exit.cancel();
|
|
35219
|
+
back.fly.replaceChildren();
|
|
35220
|
+
}
|
|
35221
|
+
const fly = back?.fly ?? document.createElement("div");
|
|
34908
35222
|
fly.className = "vela-dtb-flyout";
|
|
34909
|
-
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:
|
|
35223
|
+
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;`;
|
|
34910
35224
|
applyChromeTokens(fly, t);
|
|
34911
35225
|
this.host.appendChild(fly);
|
|
34912
35226
|
const r = cell.getBoundingClientRect();
|
|
34913
35227
|
const rootR = this.root.getBoundingClientRect();
|
|
34914
35228
|
const hostR = this.host.getBoundingClientRect();
|
|
34915
35229
|
const top = r.top - hostR.top;
|
|
34916
|
-
fly.style.left = `${rootR.right - hostR.left
|
|
35230
|
+
fly.style.left = `${rootR.right - hostR.left + FLYOUT_GAP}px`;
|
|
34917
35231
|
fly.style.top = `${top}px`;
|
|
34918
35232
|
fly.style.maxHeight = `${Math.max(120, hostR.height - top - 8)}px`;
|
|
34919
35233
|
this.flyout = fly;
|
|
@@ -34929,6 +35243,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34929
35243
|
if (!cell) return;
|
|
34930
35244
|
const fly = this.beginFlyout(group.id, cell);
|
|
34931
35245
|
const sections = group.sections ?? [{ label: "", tools: group.tools }];
|
|
35246
|
+
const badge = sections.some((s) => s.tools.some((tool) => !!tool.icon));
|
|
34932
35247
|
for (let si = 0; si < sections.length; si++) {
|
|
34933
35248
|
const section = sections[si];
|
|
34934
35249
|
if (section.label) {
|
|
@@ -34939,6 +35254,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34939
35254
|
fly.appendChild(
|
|
34940
35255
|
this.makeFlyoutItem({
|
|
34941
35256
|
icon: tool.icon,
|
|
35257
|
+
badge,
|
|
34942
35258
|
label: tool.label,
|
|
34943
35259
|
selected: tool.type === this.active,
|
|
34944
35260
|
shortcut: this.shortcuts.get(tool.type),
|
|
@@ -34989,21 +35305,24 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34989
35305
|
div.style.cssText = `height:1px;margin:4px 8px;background:${this.borderColor};`;
|
|
34990
35306
|
return div;
|
|
34991
35307
|
}
|
|
34992
|
-
/** A flyout row, left to right: optional leading icon, label, a check when it's the
|
|
34993
|
-
* entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
34994
|
-
* Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
35308
|
+
/** A flyout row, left to right: optional leading icon badge, label, a check when it's the
|
|
35309
|
+
* selected entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
35310
|
+
* rows). Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
34995
35311
|
makeFlyoutItem(opts) {
|
|
34996
35312
|
const t = this.theme;
|
|
34997
35313
|
const item = document.createElement("button");
|
|
34998
35314
|
item.type = "button";
|
|
34999
35315
|
item.className = "vela-dtb-item";
|
|
35000
35316
|
item.setAttribute("aria-label", opts.label);
|
|
35001
|
-
|
|
35002
|
-
|
|
35003
|
-
|
|
35004
|
-
|
|
35005
|
-
|
|
35006
|
-
|
|
35317
|
+
const badged = opts.badge === true || !!opts.icon;
|
|
35318
|
+
const box = badged ? "gap:10px;padding:4px 10px 4px 6px;" : "gap:8px;padding:5px 10px 5px 8px;";
|
|
35319
|
+
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;`;
|
|
35320
|
+
if (opts.selected) item.dataset.selected = "1";
|
|
35321
|
+
if (badged) {
|
|
35322
|
+
const badge = document.createElement("span");
|
|
35323
|
+
badge.className = "vela-dtb-badge";
|
|
35324
|
+
if (opts.icon) badge.innerHTML = opts.icon;
|
|
35325
|
+
item.appendChild(badge);
|
|
35007
35326
|
}
|
|
35008
35327
|
const label = document.createElement("span");
|
|
35009
35328
|
label.textContent = opts.label;
|
|
@@ -35048,7 +35367,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35048
35367
|
if (fly) {
|
|
35049
35368
|
this.flyout = null;
|
|
35050
35369
|
announceSurface(fly, false, "menu", this.flyoutCell);
|
|
35051
|
-
|
|
35370
|
+
const owner = this.flyoutOwnerId ?? "";
|
|
35371
|
+
const exit = holdForExit(fly, () => {
|
|
35372
|
+
if (this.exitingFlyouts.get(owner)?.fly === fly) this.exitingFlyouts.delete(owner);
|
|
35373
|
+
fly.remove();
|
|
35374
|
+
});
|
|
35375
|
+
if (exit) this.exitingFlyouts.set(owner, { fly, exit });
|
|
35052
35376
|
this.flyoutCell?.classList.remove("vela-open");
|
|
35053
35377
|
this.flyoutCell = null;
|
|
35054
35378
|
this.flyoutOwnerId = null;
|
|
@@ -35132,6 +35456,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35132
35456
|
.vela-dtb[data-collapsed='1'] .vela-dtb-collapse .vela-dtb-hit{width:14px;}
|
|
35133
35457
|
.vela-dtb-item{background:transparent;border:none;transition:background var(--vela-dur-fast) ease;}
|
|
35134
35458
|
.vela-dtb-item:hover{background:var(--vela-hover-strong);}
|
|
35459
|
+
.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;}
|
|
35460
|
+
.vela-dtb-badge:empty{background:transparent;border-color:transparent;}
|
|
35461
|
+
.vela-dtb-badge svg{width:100%;height:100%;}
|
|
35462
|
+
.vela-dtb-item:hover .vela-dtb-badge:not(:empty){background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
35463
|
+
.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);}
|
|
35135
35464
|
.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;}
|
|
35136
35465
|
.vela-dtb-star svg{width:16px;height:16px;}
|
|
35137
35466
|
.vela-dtb-item:hover .vela-dtb-star{opacity:.55;}
|
|
@@ -35751,6 +36080,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35751
36080
|
this.render();
|
|
35752
36081
|
}
|
|
35753
36082
|
}
|
|
36083
|
+
/** Drop the hover (its handles) — the pointer no longer reaches the drawings. */
|
|
36084
|
+
clearHover() {
|
|
36085
|
+
if (this.hoveredId == null) return;
|
|
36086
|
+
this.hoveredId = null;
|
|
36087
|
+
this.render();
|
|
36088
|
+
}
|
|
35754
36089
|
pointerUp(x, y, snap = "off") {
|
|
35755
36090
|
if (this.eraserMode) {
|
|
35756
36091
|
this.erasing = false;
|
|
@@ -36741,9 +37076,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
36741
37076
|
consider(drawings.max);
|
|
36742
37077
|
}
|
|
36743
37078
|
if (min2 === Infinity || max2 === -Infinity) return { min: 0, max: 1 };
|
|
36744
|
-
if (min2
|
|
36745
|
-
const
|
|
36746
|
-
|
|
37079
|
+
if (max2 - min2 <= Math.max(Math.abs(min2), Math.abs(max2)) * 1e-12) {
|
|
37080
|
+
const mid = (min2 + max2) / 2;
|
|
37081
|
+
const pad = Math.abs(mid) * 0.1 || 1;
|
|
37082
|
+
return { min: mid - pad, max: mid + pad, log: log && mid - pad > 0 };
|
|
36747
37083
|
}
|
|
36748
37084
|
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
36749
37085
|
const above = margins.top / 100 / content;
|
|
@@ -37247,7 +37583,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37247
37583
|
return Math.ceil(logical) - 0.5;
|
|
37248
37584
|
}
|
|
37249
37585
|
var BackdropRenderer = class {
|
|
37250
|
-
|
|
37586
|
+
/** `axisTicks` is shared with the chrome layer, so gridlines and price labels agree. */
|
|
37587
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
37588
|
+
this.axisTicks = axisTicks;
|
|
37251
37589
|
this.canvas = null;
|
|
37252
37590
|
this.ctx = null;
|
|
37253
37591
|
}
|
|
@@ -37324,9 +37662,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37324
37662
|
if (scene.showGrid && gridHorz.visible && !pane.collapsed) {
|
|
37325
37663
|
ctx.globalAlpha = gridAlpha;
|
|
37326
37664
|
ctx.strokeStyle = horzColor;
|
|
37327
|
-
const pct = percentScaleFor(scene, pane);
|
|
37328
37665
|
ctx.beginPath();
|
|
37329
|
-
for (const t of
|
|
37666
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
37330
37667
|
const y = Math.round(coords.priceToY(t.price, pane.scale, pane.bounds)) + 0.5;
|
|
37331
37668
|
if (y < pane.bounds.top || y > pane.bounds.top + pane.bounds.height) continue;
|
|
37332
37669
|
ctx.moveTo(0, y);
|
|
@@ -37942,8 +38279,16 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37942
38279
|
this.toolbarGutter = 0;
|
|
37943
38280
|
// px reserved on the left for the docked drawings toolbar (0 when hidden)
|
|
37944
38281
|
this.mountContainer = null;
|
|
38282
|
+
/** The host-styleable twin of the `shadeRight` veil (`.vela-shade-right`), directly under
|
|
38283
|
+
* the cursor canvas; hidden whenever the crosshair layer paints no veil. */
|
|
38284
|
+
this.shadeRightEl = null;
|
|
38285
|
+
/** The veil geometry last written to {@link shadeRightEl} ('' = hidden) — the DOM is
|
|
38286
|
+
* touched only when it changes, never on a plain pointer move. */
|
|
38287
|
+
this.shadeRightSig = "";
|
|
37945
38288
|
this.userDrawings = null;
|
|
37946
|
-
|
|
38289
|
+
/** One price-axis tick source for the gridlines (backdrop) and the labels (chrome). */
|
|
38290
|
+
this.priceAxisTicks = new PriceAxisTickSource();
|
|
38291
|
+
this.backdropRenderer = new BackdropRenderer(this.priceAxisTicks);
|
|
37947
38292
|
this.volumeRenderer = new VolumeRenderer();
|
|
37948
38293
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
37949
38294
|
/**
|
|
@@ -37976,7 +38321,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37976
38321
|
this.backendMode = "auto";
|
|
37977
38322
|
this.glowAmount = 0;
|
|
37978
38323
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
37979
|
-
this.chrome = new ChromeRenderer();
|
|
38324
|
+
this.chrome = new ChromeRenderer(this.priceAxisTicks);
|
|
37980
38325
|
/** Prepaints each indicator's Pine drawings into interleave slices at the model's z. */
|
|
37981
38326
|
this.indicatorSlices = new IndicatorDrawingSlices();
|
|
37982
38327
|
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
@@ -38093,6 +38438,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38093
38438
|
// pane just below it
|
|
38094
38439
|
this.resizeSplitStart = null;
|
|
38095
38440
|
// the two panes' shared span when the drag began
|
|
38441
|
+
/** The user drawings take part in pointer input (the runtime `drawingsInteractive` feature). */
|
|
38442
|
+
this.drawingsInteractive = true;
|
|
38096
38443
|
this.hoverSeparatorY = null;
|
|
38097
38444
|
// pixel y of the separator under the cursor (drives its hover highlight)
|
|
38098
38445
|
// ── pane management (merge / reorder / collapse / maximize) ──
|
|
@@ -38148,8 +38495,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38148
38495
|
this.toggleVisibleCbs = /* @__PURE__ */ new Set();
|
|
38149
38496
|
this.moveIndicatorCbs = /* @__PURE__ */ new Set();
|
|
38150
38497
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
38498
|
+
this.priceStyleWillChangeCbs = /* @__PURE__ */ new Set();
|
|
38151
38499
|
this.name = "native";
|
|
38152
|
-
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"];
|
|
38500
|
+
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"];
|
|
38153
38501
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
38154
38502
|
* so moving onto the button doesn't count as leaving). */
|
|
38155
38503
|
this.onScrollProximityMove = (e) => {
|
|
@@ -38193,6 +38541,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38193
38541
|
this.scene.priceStyle = opts.priceStyle;
|
|
38194
38542
|
this.scene.basePainting = basePaintingOf(opts.priceStyle);
|
|
38195
38543
|
this.scene.candleOverride = candleOverrideFor(opts.priceStyle, this.scene.style.chartTypes);
|
|
38544
|
+
if (typeof opts.priceAxisTicks === "function") this.priceAxisTicks.setHook(opts.priceAxisTicks);
|
|
38196
38545
|
}
|
|
38197
38546
|
this.theme = this.deriveTheme(DARK_THEME);
|
|
38198
38547
|
}
|
|
@@ -38331,6 +38680,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38331
38680
|
case "crosshairOverride":
|
|
38332
38681
|
this.scene.crosshairOverride = sanitizeCrosshairOverride(value);
|
|
38333
38682
|
break;
|
|
38683
|
+
case "drawingsInteractive":
|
|
38684
|
+
this.drawingsInteractive = value !== false;
|
|
38685
|
+
if (this.input) this.input.drawings = this.drawingsInteractive;
|
|
38686
|
+
if (!this.drawingsInteractive) this.userDrawings?.clearHover();
|
|
38687
|
+
return;
|
|
38688
|
+
// affects the next gesture only — clearHover repaints what it drops
|
|
38689
|
+
case "priceAxisTicks":
|
|
38690
|
+
this.priceAxisTicks.setHook(typeof value === "function" ? value : null);
|
|
38691
|
+
break;
|
|
38334
38692
|
case "keyboard":
|
|
38335
38693
|
this.setKeyboardEnabled(Boolean(value));
|
|
38336
38694
|
return;
|
|
@@ -38446,6 +38804,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38446
38804
|
return { visible: this.scene.marks.visible, groups: { ...this.scene.marks.groups } };
|
|
38447
38805
|
case "crosshairOverride":
|
|
38448
38806
|
return this.scene.crosshairOverride ? { ...this.scene.crosshairOverride } : null;
|
|
38807
|
+
case "drawingsInteractive":
|
|
38808
|
+
return this.drawingsInteractive;
|
|
38809
|
+
case "priceAxisTicks":
|
|
38810
|
+
return this.priceAxisTicks.hook;
|
|
38449
38811
|
case "keyboard":
|
|
38450
38812
|
return this.keyboardEnabled;
|
|
38451
38813
|
case "historyChords":
|
|
@@ -39119,6 +39481,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39119
39481
|
Object.assign(this.drawingsCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
39120
39482
|
this.cursorCanvas = document.createElement("canvas");
|
|
39121
39483
|
Object.assign(this.cursorCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
39484
|
+
this.shadeRightEl = document.createElement("div");
|
|
39485
|
+
this.shadeRightEl.className = "vela-shade-right";
|
|
39486
|
+
this.shadeRightEl.setAttribute("aria-hidden", "true");
|
|
39487
|
+
this.shadeRightEl.hidden = true;
|
|
39488
|
+
Object.assign(this.shadeRightEl.style, { position: "absolute", top: "0", left: "0", width: "0", height: "0", pointerEvents: "none", display: "none" });
|
|
39489
|
+
this.shadeRightSig = "";
|
|
39122
39490
|
this.overlayRoot = document.createElement("div");
|
|
39123
39491
|
Object.assign(this.overlayRoot.style, { position: "absolute", inset: "0", pointerEvents: "none" });
|
|
39124
39492
|
this.plot = document.createElement("div");
|
|
@@ -39126,7 +39494,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39126
39494
|
this.extLayers = rendererLayers().map((def) => ({ def, instance: def.create(), canvas: this.createLayerCanvas(), channel: def.id, owner: null }));
|
|
39127
39495
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
39128
39496
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
39129
|
-
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
39497
|
+
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.shadeRightEl, this.cursorCanvas, this.overlayRoot);
|
|
39130
39498
|
this.layerOrderSig = "";
|
|
39131
39499
|
this.wrapper.appendChild(this.plot);
|
|
39132
39500
|
this.factoryConfig = this.getConfig();
|
|
@@ -39184,13 +39552,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39184
39552
|
drawingsPointerDown: (x, y, snap, shift4, mod) => this.userDrawings?.pointerDown(x, y, snap, shift4, mod),
|
|
39185
39553
|
drawingsPointerMove: (x, y, snap, shift4, mod) => this.userDrawings?.pointerMove(x, y, snap, shift4, mod),
|
|
39186
39554
|
drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
|
|
39187
|
-
drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
39555
|
+
drawingsCursor: (x, y) => (this.drawingsInteractive ? this.userDrawings?.cursorAt(x, y) : null) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
39188
39556
|
drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
|
|
39189
39557
|
drawingsClearTransient: () => this.userDrawings?.clearTransient()
|
|
39190
39558
|
});
|
|
39191
39559
|
this.input.rightEdgeZoom = this.zoomAnchorMode === "right";
|
|
39192
39560
|
this.input.axisDrag = this.axisDragEnabled;
|
|
39193
39561
|
this.input.paneResize = this.paneResizeEnabled;
|
|
39562
|
+
this.input.drawings = this.drawingsInteractive;
|
|
39194
39563
|
this.input.attach(this.dataCanvas);
|
|
39195
39564
|
if (this.settingsEnabled) this.setSettingsEnabled(true);
|
|
39196
39565
|
this.syncCountdownTimer();
|
|
@@ -39499,6 +39868,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39499
39868
|
this.backend.destroy();
|
|
39500
39869
|
this.chrome.destroy();
|
|
39501
39870
|
this.crosshairLayer.destroy();
|
|
39871
|
+
this.shadeRightEl?.remove();
|
|
39872
|
+
this.shadeRightEl = null;
|
|
39502
39873
|
this.attributionEl?.remove();
|
|
39503
39874
|
this.attributionEl = null;
|
|
39504
39875
|
this.mountContainer?.style.removeProperty("--vela-toolbar-gutter");
|
|
@@ -39845,14 +40216,21 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39845
40216
|
this.priceStyleCbs.add(cb);
|
|
39846
40217
|
return () => this.priceStyleCbs.delete(cb);
|
|
39847
40218
|
}
|
|
40219
|
+
onPriceStyleWillChange(cb) {
|
|
40220
|
+
this.priceStyleWillChangeCbs.add(cb);
|
|
40221
|
+
return () => this.priceStyleWillChangeCbs.delete(cb);
|
|
40222
|
+
}
|
|
39848
40223
|
/**
|
|
39849
40224
|
* THE single write path for the base price style at runtime (feature set / settings dialog /
|
|
39850
|
-
* config template — the constructor seeds the field directly, pre-listeners).
|
|
39851
|
-
*
|
|
39852
|
-
*
|
|
40225
|
+
* config template — the constructor seeds the field directly, pre-listeners). Announces the
|
|
40226
|
+
* switch while the scene still holds the old style (a listener may paint/capture that frame),
|
|
40227
|
+
* then updates the scene, eases any reveal layer toward the new style's target, and notifies
|
|
40228
|
+
* the core — which owns the DATA side of styles that need one (a chart type's SeriesDataEngine).
|
|
39853
40229
|
*/
|
|
39854
40230
|
setPriceStyle(style) {
|
|
39855
40231
|
if (style === this.scene.priceStyle) return;
|
|
40232
|
+
const from = this.scene.priceStyle;
|
|
40233
|
+
for (const cb of this.priceStyleWillChangeCbs) cb(from, style);
|
|
39856
40234
|
this.scene.priceStyle = style;
|
|
39857
40235
|
this.scene.basePainting = basePaintingOf(style);
|
|
39858
40236
|
this.scene.candleOverride = candleOverrideFor(style, this.scene.style.chartTypes);
|
|
@@ -40082,7 +40460,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
40082
40460
|
if (this.easeLiveBar(dtMs)) active = true;
|
|
40083
40461
|
this.skeletonClockMs += dtMs;
|
|
40084
40462
|
this.paintData();
|
|
40085
|
-
this.
|
|
40463
|
+
this.renderCrosshairLayer();
|
|
40086
40464
|
this.updateLegendValues();
|
|
40087
40465
|
this.emitViewportChange();
|
|
40088
40466
|
return active;
|
|
@@ -40657,10 +41035,31 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
40657
41035
|
this.chrome.render(this.scene, this.coords, this.theme, this.axisSurface());
|
|
40658
41036
|
this.trackMarkPopover();
|
|
40659
41037
|
}
|
|
40660
|
-
this.
|
|
41038
|
+
this.renderCrosshairLayer();
|
|
40661
41039
|
if (!repaintsData(level) && this.paintedData) this.repaintCursorLayers();
|
|
40662
41040
|
this.updateLegendValues();
|
|
40663
41041
|
}
|
|
41042
|
+
/** Repaint the L2 crosshair canvas and keep the `.vela-shade-right` element on the veil it painted. */
|
|
41043
|
+
renderCrosshairLayer() {
|
|
41044
|
+
this.crosshairLayer.render(this.scene, this.coords, this.theme, this.hoverSeparatorY, this.externalCrossPx());
|
|
41045
|
+
this.syncShadeRight();
|
|
41046
|
+
}
|
|
41047
|
+
/** Position (or hide) the `.vela-shade-right` element over the veil of the last crosshair frame. */
|
|
41048
|
+
syncShadeRight() {
|
|
41049
|
+
const el = this.shadeRightEl;
|
|
41050
|
+
if (!el) return;
|
|
41051
|
+
const area = this.crosshairLayer.shadeRightArea;
|
|
41052
|
+
const sig = area ? `${area.x},${area.width},${area.height}` : "";
|
|
41053
|
+
if (sig === this.shadeRightSig) return;
|
|
41054
|
+
this.shadeRightSig = sig;
|
|
41055
|
+
if (!area) {
|
|
41056
|
+
el.hidden = true;
|
|
41057
|
+
el.style.display = "none";
|
|
41058
|
+
return;
|
|
41059
|
+
}
|
|
41060
|
+
Object.assign(el.style, { left: `${area.x}px`, width: `${area.width}px`, height: `${area.height}px`, display: "" });
|
|
41061
|
+
el.hidden = false;
|
|
41062
|
+
}
|
|
40664
41063
|
/** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
|
|
40665
41064
|
repaintCursorLayers() {
|
|
40666
41065
|
const pane = this.scene.panes.get(PRICE_PANE_ID);
|
|
@@ -41454,7 +41853,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
41454
41853
|
if (this.animator.active) {
|
|
41455
41854
|
this.computeScales();
|
|
41456
41855
|
this.paintData();
|
|
41457
|
-
this.
|
|
41856
|
+
this.renderCrosshairLayer();
|
|
41458
41857
|
} else {
|
|
41459
41858
|
this.scheduler.flushNow(4 /* Full */);
|
|
41460
41859
|
}
|
|
@@ -45498,7 +45897,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
45498
45897
|
glow: options.glow ?? 0,
|
|
45499
45898
|
upColor: options.upColor ?? BULLISH,
|
|
45500
45899
|
downColor: options.downColor ?? BEARISH,
|
|
45501
|
-
priceStyle: options.priceStyle ?? "candles"
|
|
45900
|
+
priceStyle: options.priceStyle ?? "candles",
|
|
45901
|
+
priceAxisTicks: options.priceAxis?.ticks ?? null
|
|
45502
45902
|
};
|
|
45503
45903
|
const RendererClass = options.renderer ?? NativeRenderer;
|
|
45504
45904
|
if (typeof RendererClass !== "function") {
|
|
@@ -45984,6 +46384,23 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
45984
46384
|
return { ...Object.fromEntries(defaultEngines), ...overrides };
|
|
45985
46385
|
}
|
|
45986
46386
|
|
|
46387
|
+
// src/widget/context-menu-model.ts
|
|
46388
|
+
var CONTEXT_MENU_BUILTIN_ORDER = {
|
|
46389
|
+
body: { "reset-view": -30, "remove-drawings": -20, "remove-indicators": -10, settings: 1e3 },
|
|
46390
|
+
"price-axis": {
|
|
46391
|
+
auto: -80,
|
|
46392
|
+
invert: -70,
|
|
46393
|
+
"scale:regular": -60,
|
|
46394
|
+
"scale:percent": -50,
|
|
46395
|
+
"scale:indexed": -40,
|
|
46396
|
+
"scale:log": -30,
|
|
46397
|
+
labels: -20,
|
|
46398
|
+
levels: -10,
|
|
46399
|
+
settings: 1e3
|
|
46400
|
+
},
|
|
46401
|
+
"time-axis": { timezone: -10, settings: 1e3 }
|
|
46402
|
+
};
|
|
46403
|
+
|
|
45987
46404
|
// src/widget/side-panel.ts
|
|
45988
46405
|
var DEFAULT_PANEL_WIDTH = 280;
|
|
45989
46406
|
var DEFAULT_PANEL_MIN_WIDTH = 200;
|
|
@@ -47233,6 +47650,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
47233
47650
|
exports.BinanceProvider = BinanceProvider;
|
|
47234
47651
|
exports.CATEGORICAL = CATEGORICAL;
|
|
47235
47652
|
exports.CHIP_PLATE = CHIP_PLATE;
|
|
47653
|
+
exports.CONTEXT_MENU_BUILTIN_ORDER = CONTEXT_MENU_BUILTIN_ORDER;
|
|
47236
47654
|
exports.CROSSHAIR = CROSSHAIR;
|
|
47237
47655
|
exports.CachingDataFeed = CachingDataFeed;
|
|
47238
47656
|
exports.CoinbaseProvider = CoinbaseProvider;
|