@luxalgo/vela 0.5.3 → 0.6.0
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/README.md +3 -2
- package/dist/{DataProvider-gnzv-dgc.d.cts → DataProvider-Ce6PuCzQ.d.cts} +10 -1
- package/dist/{DataProvider-DTOEXKTI.d.ts → DataProvider-DNx9ntBD.d.ts} +10 -1
- package/dist/{chunk-EY63XBFZ.js → chunk-7ROONW6H.js} +132 -23
- package/dist/{chunk-L5NTMWPP.js → chunk-CA3N3PYT.js} +785 -857
- package/dist/{chunk-LJLZR44Y.js → chunk-EMS6PO2T.js} +5 -7
- package/dist/chunk-OGRQW3M6.js +1328 -0
- package/dist/{chunk-P556H6EA.js → chunk-QWIEKZRE.js} +59 -34
- package/dist/{chunk-WJKLBYRM.js → chunk-ZNL2X6U2.js} +1 -1
- package/dist/{contributions-Ci_i3IN2.d.cts → contributions-DToMhiw3.d.ts} +9 -2
- package/dist/{contributions-b6AVrqqG.d.ts → contributions-Dlc7ZWZz.d.cts} +9 -2
- package/dist/{history-Ci3M0xLV.d.ts → history-BZADxpKx.d.cts} +5 -3
- package/dist/{history-Cf1lVsap.d.cts → history-BuHXJZJ2.d.ts} +5 -3
- package/dist/index.cjs +2761 -1597
- package/dist/index.d.cts +14 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.js +4 -4
- package/dist/{options-XTBpbx0s.d.ts → options-BlQ00Fju.d.cts} +3 -1
- package/dist/{options-XTBpbx0s.d.cts → options-BlQ00Fju.d.ts} +3 -1
- package/dist/{plugin-DlidwMj6.d.cts → plugin-BNPLOQO1.d.cts} +3 -3
- package/dist/{plugin-DP6A4t4o.d.ts → plugin-BqjEa5r3.d.ts} +3 -3
- package/dist/plugin.cjs +4 -0
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/ui.cjs +1318 -4
- package/dist/ui.d.cts +383 -2
- package/dist/ui.d.ts +383 -2
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +3729 -2565
- package/dist/vela.global.min.js +315 -66
- package/dist/widget.cjs +2359 -1054
- package/dist/widget.d.cts +30 -16
- package/dist/widget.d.ts +30 -16
- package/dist/widget.js +37 -14
- package/dist/workspace.cjs +2259 -944
- package/dist/workspace.d.cts +10 -5
- package/dist/workspace.d.ts +10 -5
- package/dist/workspace.js +29 -8
- package/package.json +1 -1
- package/dist/chunk-ATPU5CI6.js +0 -81
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ renderer layers.
|
|
|
7
7
|
- **`vela`** — the headless chart: data model, engines, drawings, providers, native renderer.
|
|
8
8
|
- **`vela/widget`** — the full chart app: topbar (symbol / timeframe / style / indicators),
|
|
9
9
|
status line, watermark, bottom bar (ranges, clock, timezone), object tree, keyboard-first UX.
|
|
10
|
-
- **`vela/ui`** — the component kit the widget is built on
|
|
11
|
-
[Zag.js](https://zagjs.com)
|
|
10
|
+
- **`vela/ui`** — the component kit the widget is built on: design tokens, overlay chrome
|
|
11
|
+
([Zag.js](https://zagjs.com) menu/dialog/drawer/tooltip), form primitives (switch, select,
|
|
12
|
+
number, text, color, popover), and the `KeymapManager`.
|
|
12
13
|
- **`vela/plugin`** — the extension SDK: chart types, renderer layers, native indicators.
|
|
13
14
|
- **`vela/workspace`** — the multi-chart shell: a grid of full charts under one shared
|
|
14
15
|
topbar, with named cells, sync groups and one persisted state document.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-
|
|
1
|
+
import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-BlQ00Fju.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
|
|
@@ -64,6 +64,15 @@ interface SymbolDescriptor {
|
|
|
64
64
|
description?: string;
|
|
65
65
|
/** Instrument class, free-form (e.g. `crypto`, `futures`, `stock`). */
|
|
66
66
|
type?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The instrument's LISTING-venue prefix (`NASDAQ`, `NYSE`, `AMEX`) — a property of the
|
|
69
|
+
* SYMBOL, not of the provider: AAPL is Nasdaq-listed and IBM NYSE-listed even when one
|
|
70
|
+
* provider supplies both tapes. When declared, it is what pickers/legends display, what
|
|
71
|
+
* `PREFIX:TICKER` strings resolve against (TradingView parity: `NYSE:AAPL` is NOT
|
|
72
|
+
* found), and what the canonical committed/persisted form carries. Absent on venues
|
|
73
|
+
* where the provider IS the identity (crypto, fx) — the provider name prefixes those.
|
|
74
|
+
*/
|
|
75
|
+
prefix?: string;
|
|
67
76
|
/** Owning provider name — annotated by the registry aggregation (badges in pickers). */
|
|
68
77
|
provider?: string;
|
|
69
78
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-
|
|
1
|
+
import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-BlQ00Fju.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
|
|
@@ -64,6 +64,15 @@ interface SymbolDescriptor {
|
|
|
64
64
|
description?: string;
|
|
65
65
|
/** Instrument class, free-form (e.g. `crypto`, `futures`, `stock`). */
|
|
66
66
|
type?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The instrument's LISTING-venue prefix (`NASDAQ`, `NYSE`, `AMEX`) — a property of the
|
|
69
|
+
* SYMBOL, not of the provider: AAPL is Nasdaq-listed and IBM NYSE-listed even when one
|
|
70
|
+
* provider supplies both tapes. When declared, it is what pickers/legends display, what
|
|
71
|
+
* `PREFIX:TICKER` strings resolve against (TradingView parity: `NYSE:AAPL` is NOT
|
|
72
|
+
* found), and what the canonical committed/persisted form carries. Absent on venues
|
|
73
|
+
* where the provider IS the identity (crypto, fx) — the provider name prefixes those.
|
|
74
|
+
*/
|
|
75
|
+
prefix?: string;
|
|
67
76
|
/** Owning provider name — annotated by the registry aggregation (badges in pickers). */
|
|
68
77
|
provider?: string;
|
|
69
78
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-
|
|
2
|
-
import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-
|
|
3
|
-
import { Tooltip, Menu, Dialog, Drawer } from './chunk-
|
|
4
|
-
import { iconMarkup, registerIcon, injectStyles, iconEl, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, categoricalColor } from './chunk-
|
|
1
|
+
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-CA3N3PYT.js';
|
|
2
|
+
import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-ZNL2X6U2.js';
|
|
3
|
+
import { Tooltip, Menu, Dialog, Drawer } from './chunk-QWIEKZRE.js';
|
|
4
|
+
import { iconMarkup, registerIcon, injectStyles, iconEl, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, categoricalColor } from './chunk-EMS6PO2T.js';
|
|
5
5
|
|
|
6
6
|
// src/widget/timeframe.ts
|
|
7
7
|
var UNIT_MS = {
|
|
@@ -74,6 +74,24 @@ function timeframeMs(value) {
|
|
|
74
74
|
if (!Number.isFinite(count) || count < 1) return NaN;
|
|
75
75
|
return count * UNIT_MS[key];
|
|
76
76
|
}
|
|
77
|
+
function favoriteTimeframeChips(favorites) {
|
|
78
|
+
const seen = /* @__PURE__ */ new Set();
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const tf of favorites) {
|
|
81
|
+
if (seen.has(tf)) continue;
|
|
82
|
+
seen.add(tf);
|
|
83
|
+
out.push(tf);
|
|
84
|
+
}
|
|
85
|
+
return out.sort((a, b) => {
|
|
86
|
+
const ma = timeframeMs(a);
|
|
87
|
+
const mb = timeframeMs(b);
|
|
88
|
+
const aOk = Number.isFinite(ma);
|
|
89
|
+
const bOk = Number.isFinite(mb);
|
|
90
|
+
if (aOk && bOk && ma !== mb) return ma - mb;
|
|
91
|
+
if (aOk !== bOk) return aOk ? -1 : 1;
|
|
92
|
+
return 0;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
77
95
|
function timeframeLabel(value) {
|
|
78
96
|
const parsed = parseTimeframe(value);
|
|
79
97
|
if (!parsed.valid) return value;
|
|
@@ -454,6 +472,36 @@ var CSS2 = `
|
|
|
454
472
|
color: var(--vela-fg-bright);
|
|
455
473
|
}
|
|
456
474
|
.vela-widget-symbol:hover, .vela-widget-tf:hover, .vela-widget-style:hover, .vela-widget-indicators:hover, .vela-widget-action-left:hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
475
|
+
/* Timeframe cluster: duration-sorted favorite chips, highlight in place, caret
|
|
476
|
+
opening the full dropdown. With no favorites the caret is the merged trigger
|
|
477
|
+
(label + chevron). An unstarred current value sits as an extra chip by the caret. */
|
|
478
|
+
.vela-widget-tf-group { display: inline-flex; align-items: center; gap: 2px; }
|
|
479
|
+
.vela-widget-tf-chips { display: inline-flex; align-items: center; gap: 2px; }
|
|
480
|
+
.vela-widget-tf-chips:empty { display: none; }
|
|
481
|
+
.vela-widget-tf[data-current='1'] { background: var(--vela-hover-strong); color: var(--vela-fg-bright); }
|
|
482
|
+
.vela-widget-tf-caret {
|
|
483
|
+
all: unset;
|
|
484
|
+
display: inline-flex;
|
|
485
|
+
align-items: center;
|
|
486
|
+
justify-content: center;
|
|
487
|
+
width: 18px;
|
|
488
|
+
height: 30px;
|
|
489
|
+
border-radius: 4px;
|
|
490
|
+
cursor: pointer;
|
|
491
|
+
color: var(--vela-fg-muted);
|
|
492
|
+
}
|
|
493
|
+
.vela-widget-tf-caret:hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
494
|
+
.vela-widget-tf-caret[data-solo='1'] {
|
|
495
|
+
width: auto;
|
|
496
|
+
padding: 0 6px 0 9px;
|
|
497
|
+
gap: 4px;
|
|
498
|
+
color: var(--vela-fg-bright);
|
|
499
|
+
background: var(--vela-hover-strong);
|
|
500
|
+
font-size: 13px;
|
|
501
|
+
font-weight: 550;
|
|
502
|
+
white-space: nowrap;
|
|
503
|
+
}
|
|
504
|
+
.vela-widget-topbar .vela-widget-tf-caret .vela-icon { font-size: 14px; width: 14px; height: 14px; }
|
|
457
505
|
.vela-widget-topbar .vela-icon { color: inherit; font-size: 16px; width: 16px; height: 16px; }
|
|
458
506
|
/* Width is set in syncHairlines() to exactly one device pixel \u2014 a CSS 1px at
|
|
459
507
|
fractional DPR (1.25, 1.5\u2026) straddles two physical pixels and siblings end
|
|
@@ -558,9 +606,16 @@ var Topbar = class {
|
|
|
558
606
|
this.symbolEl.className = "vela-widget-symbol";
|
|
559
607
|
this.symbolEl.textContent = parseSymbol(opts.symbol).ticker;
|
|
560
608
|
if (opts.onSymbolClick) this.symbolEl.addEventListener("click", opts.onSymbolClick);
|
|
561
|
-
this.
|
|
562
|
-
this.
|
|
563
|
-
this.
|
|
609
|
+
this.tfFavs = [...opts.timeframeFavorites ?? []];
|
|
610
|
+
this.tfChipsHost = doc.createElement("span");
|
|
611
|
+
this.tfChipsHost.className = "vela-widget-tf-chips";
|
|
612
|
+
this.tfCaret = doc.createElement("button");
|
|
613
|
+
this.tfCaret.className = "vela-widget-tf-caret";
|
|
614
|
+
this.tfCaret.appendChild(iconEl("chevron-down", doc));
|
|
615
|
+
this.tfCaret.setAttribute("aria-label", "Timeframes");
|
|
616
|
+
const tfGroup = doc.createElement("span");
|
|
617
|
+
tfGroup.className = "vela-widget-tf-group";
|
|
618
|
+
tfGroup.append(this.tfChipsHost, this.tfCaret);
|
|
564
619
|
this.styleButton = doc.createElement("button");
|
|
565
620
|
this.styleButton.className = "vela-widget-style";
|
|
566
621
|
this.renderStyleButton(doc);
|
|
@@ -600,19 +655,20 @@ var Topbar = class {
|
|
|
600
655
|
d.className = "vela-sep";
|
|
601
656
|
return d;
|
|
602
657
|
};
|
|
603
|
-
const leading = [this.symbolEl, sep(),
|
|
658
|
+
const leading = [this.symbolEl, sep(), tfGroup, sep(), this.styleButton, sep()];
|
|
604
659
|
if (this.layoutButton) leading.push(this.layoutButton, sep());
|
|
605
660
|
if (indicatorsBtn) leading.push(indicatorsBtn, sep());
|
|
606
661
|
this.leftActionsSep = sep();
|
|
607
662
|
this.leftActionsSep.hidden = true;
|
|
608
663
|
this.el.append(...leading, this.leftActionsHost, this.leftActionsSep, this.undoBtn, this.redoBtn, this.actionsHost, this.alertsBtn, this.panelsHost, screenshotBtn);
|
|
609
664
|
host.appendChild(this.el);
|
|
665
|
+
this.renderTfChips();
|
|
610
666
|
this.onHairlineSync();
|
|
611
667
|
this.hairlineRo = new ResizeObserver(this.onHairlineSync);
|
|
612
668
|
this.hairlineRo.observe(this.el);
|
|
613
669
|
doc.defaultView?.addEventListener("resize", this.onHairlineSync);
|
|
614
670
|
this.renderActions();
|
|
615
|
-
this.tooltips.push(new Tooltip(this.
|
|
671
|
+
this.tooltips.push(new Tooltip(this.tfCaret, { content: "Timeframe", triggerId: "vela-topbar-tf", host }));
|
|
616
672
|
this.tooltips.push(new Tooltip(this.styleButton, { content: "Chart style", triggerId: "vela-topbar-style", host }));
|
|
617
673
|
if (this.layoutButton && opts.layout) {
|
|
618
674
|
this.tooltips.push(new Tooltip(this.layoutButton, { content: "Layout", triggerId: "vela-topbar-layout", host }));
|
|
@@ -629,11 +685,12 @@ var Topbar = class {
|
|
|
629
685
|
});
|
|
630
686
|
}
|
|
631
687
|
this.tfMenu = new Menu({
|
|
632
|
-
trigger: this.
|
|
688
|
+
trigger: this.tfCaret,
|
|
633
689
|
triggerId: "vela-topbar-tf",
|
|
634
690
|
host,
|
|
635
691
|
items: this.tfItems(),
|
|
636
692
|
onSelect: (id) => opts.onTimeframe(id),
|
|
693
|
+
onFavorite: (id, on) => opts.onTimeframeFavorite?.(id, on),
|
|
637
694
|
// Timeframe labels are two-or-three characters ("1m", "4h", "1D") — the
|
|
638
695
|
// stylesheet's default min-width would leave the list mostly empty.
|
|
639
696
|
minWidth: "84px"
|
|
@@ -651,8 +708,48 @@ var Topbar = class {
|
|
|
651
708
|
}
|
|
652
709
|
setTimeframe(tf) {
|
|
653
710
|
this.timeframe = tf;
|
|
654
|
-
this.tfButton.textContent = timeframeLabel(tf);
|
|
655
711
|
this.tfMenu.setItems(this.tfItems());
|
|
712
|
+
this.renderTfChips();
|
|
713
|
+
}
|
|
714
|
+
/** Reflect the favorite-timeframe set — the quick-switch chips and the dropdown stars. */
|
|
715
|
+
setTimeframeFavorites(favs) {
|
|
716
|
+
this.tfFavs = [...favs];
|
|
717
|
+
this.renderTfChips();
|
|
718
|
+
this.tfMenu.setItems(this.tfItems());
|
|
719
|
+
}
|
|
720
|
+
/** Rebuild the quick-switch chips (current value changed, or the favorite set did). */
|
|
721
|
+
renderTfChips() {
|
|
722
|
+
const doc = this.el.ownerDocument;
|
|
723
|
+
this.tfChipsHost.replaceChildren();
|
|
724
|
+
const stars = this.opts.onTimeframeFavorite !== void 0;
|
|
725
|
+
const chips = stars ? favoriteTimeframeChips(this.tfFavs) : [];
|
|
726
|
+
const currentIsFav = chips.includes(this.timeframe);
|
|
727
|
+
const shown = currentIsFav || chips.length === 0 ? chips : [...chips, this.timeframe];
|
|
728
|
+
for (const tf of shown) {
|
|
729
|
+
const b = doc.createElement("button");
|
|
730
|
+
b.className = "vela-widget-tf";
|
|
731
|
+
const label = timeframeLabel(tf);
|
|
732
|
+
b.textContent = label;
|
|
733
|
+
if (tf === this.timeframe) {
|
|
734
|
+
b.dataset.current = "1";
|
|
735
|
+
b.setAttribute("aria-current", "true");
|
|
736
|
+
} else {
|
|
737
|
+
b.setAttribute("aria-label", `Switch timeframe to ${label}`);
|
|
738
|
+
b.addEventListener("click", () => this.opts.onTimeframe(tf));
|
|
739
|
+
}
|
|
740
|
+
this.tfChipsHost.appendChild(b);
|
|
741
|
+
}
|
|
742
|
+
this.tfCaret.replaceChildren();
|
|
743
|
+
if (chips.length === 0) {
|
|
744
|
+
this.tfCaret.dataset.solo = "1";
|
|
745
|
+
this.tfCaret.append(doc.createTextNode(timeframeLabel(this.timeframe)), iconEl("chevron-down", doc));
|
|
746
|
+
this.tfCaret.setAttribute("aria-label", `Timeframe \u2014 ${timeframeLabel(this.timeframe)}`);
|
|
747
|
+
} else {
|
|
748
|
+
delete this.tfCaret.dataset.solo;
|
|
749
|
+
this.tfCaret.appendChild(iconEl("chevron-down", doc));
|
|
750
|
+
this.tfCaret.setAttribute("aria-label", "Timeframes");
|
|
751
|
+
}
|
|
752
|
+
this.onHairlineSync();
|
|
656
753
|
}
|
|
657
754
|
setPriceStyle(style) {
|
|
658
755
|
this.priceStyle = style;
|
|
@@ -770,7 +867,13 @@ var Topbar = class {
|
|
|
770
867
|
}
|
|
771
868
|
}
|
|
772
869
|
tfItems() {
|
|
773
|
-
|
|
870
|
+
const stars = this.opts.onTimeframeFavorite !== void 0;
|
|
871
|
+
return this.opts.timeframes.map((tf) => ({
|
|
872
|
+
id: tf,
|
|
873
|
+
label: timeframeLabel(tf),
|
|
874
|
+
checked: tf === this.timeframe,
|
|
875
|
+
...stars ? { favorite: this.tfFavs.includes(tf) } : {}
|
|
876
|
+
}));
|
|
774
877
|
}
|
|
775
878
|
styleItems() {
|
|
776
879
|
return priceStyleIds().map((id, i) => ({
|
|
@@ -1502,10 +1605,11 @@ function parseQuery(raw, venues) {
|
|
|
1502
1605
|
function onlyOne(matches) {
|
|
1503
1606
|
return matches.length === 1 ? matches[0] : null;
|
|
1504
1607
|
}
|
|
1608
|
+
var venueOf = (s) => s.prefix ?? s.provider;
|
|
1505
1609
|
function filterSymbols(list, query, limit = 100) {
|
|
1506
|
-
const venues = [...new Set(list.
|
|
1610
|
+
const venues = [...new Set(list.flatMap((s) => [venueOf(s)?.toLowerCase(), s.provider?.toLowerCase()]).filter((p) => !!p))];
|
|
1507
1611
|
const { scope, term } = parseQuery(query, venues);
|
|
1508
|
-
const pool = scope ? list.filter((s) => s.provider?.toLowerCase() === scope) : list;
|
|
1612
|
+
const pool = scope ? list.filter((s) => venueOf(s)?.toLowerCase() === scope || s.provider?.toLowerCase() === scope) : list;
|
|
1509
1613
|
const q = term.toUpperCase();
|
|
1510
1614
|
if (!q) {
|
|
1511
1615
|
if (scope) return [...pool].sort((a, b) => a.ticker.localeCompare(b.ticker)).slice(0, limit);
|
|
@@ -1524,7 +1628,7 @@ function filterSymbols(list, query, limit = 100) {
|
|
|
1524
1628
|
if (t.startsWith(q)) prefix.push(s);
|
|
1525
1629
|
else if (t.includes(q)) substr.push(s);
|
|
1526
1630
|
else if ((s.description ?? "").toUpperCase().includes(q)) desc.push(s);
|
|
1527
|
-
else if (!scope && s.provider?.toLowerCase().includes(qLower)) venue.push(s);
|
|
1631
|
+
else if (!scope && (venueOf(s)?.toLowerCase().includes(qLower) || s.provider?.toLowerCase().includes(qLower))) venue.push(s);
|
|
1528
1632
|
if (prefix.length >= limit) break;
|
|
1529
1633
|
}
|
|
1530
1634
|
return [...prefix, ...substr, ...desc, ...venue].slice(0, limit);
|
|
@@ -1676,14 +1780,14 @@ var SymbolPicker = class {
|
|
|
1676
1780
|
else if (e.key === "ArrowUp") this.moveHighlight(-1);
|
|
1677
1781
|
else if (e.key === "Enter") {
|
|
1678
1782
|
const pick = this.rows[this.highlighted];
|
|
1679
|
-
if (pick) this.select(pick.ticker, pick.provider, opts.onSelect);
|
|
1783
|
+
if (pick) this.select(pick.ticker, pick.prefix ?? pick.provider, opts.onSelect);
|
|
1680
1784
|
return;
|
|
1681
1785
|
} else return;
|
|
1682
1786
|
e.preventDefault();
|
|
1683
1787
|
});
|
|
1684
1788
|
this.list.addEventListener("click", (e) => {
|
|
1685
1789
|
const row = e.target.closest(".vela-sp-row");
|
|
1686
|
-
if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.
|
|
1790
|
+
if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.venue, opts.onSelect);
|
|
1687
1791
|
});
|
|
1688
1792
|
}
|
|
1689
1793
|
/** Wire where symbols come from (re-called on every widget rebuild). */
|
|
@@ -1700,9 +1804,9 @@ var SymbolPicker = class {
|
|
|
1700
1804
|
destroy() {
|
|
1701
1805
|
this.dialog.destroy();
|
|
1702
1806
|
}
|
|
1703
|
-
select(ticker,
|
|
1807
|
+
select(ticker, venue, onSelect) {
|
|
1704
1808
|
this.close();
|
|
1705
|
-
onSelect(
|
|
1809
|
+
onSelect(venue ? `${venue}:${ticker}` : ticker);
|
|
1706
1810
|
}
|
|
1707
1811
|
moveHighlight(delta) {
|
|
1708
1812
|
if (!this.rows.length) return;
|
|
@@ -1735,7 +1839,8 @@ var SymbolPicker = class {
|
|
|
1735
1839
|
const row = doc.createElement("div");
|
|
1736
1840
|
row.className = "vela-sp-row";
|
|
1737
1841
|
row.dataset.ticker = s.ticker;
|
|
1738
|
-
|
|
1842
|
+
const venue = s.prefix ?? s.provider;
|
|
1843
|
+
if (venue) row.dataset.venue = venue;
|
|
1739
1844
|
const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
|
|
1740
1845
|
const main = doc.createElement("span");
|
|
1741
1846
|
main.className = "vela-sp-main";
|
|
@@ -1747,11 +1852,11 @@ var SymbolPicker = class {
|
|
|
1747
1852
|
d.textContent = s.description ?? (s.type ?? "");
|
|
1748
1853
|
main.append(t, d);
|
|
1749
1854
|
row.append(av, main);
|
|
1750
|
-
if (
|
|
1855
|
+
if (venue) {
|
|
1751
1856
|
const badge = doc.createElement("span");
|
|
1752
1857
|
badge.className = "vela-sp-badge";
|
|
1753
|
-
badge.dataset.p = s.provider;
|
|
1754
|
-
badge.textContent =
|
|
1858
|
+
badge.dataset.p = s.provider ?? venue;
|
|
1859
|
+
badge.textContent = venue;
|
|
1755
1860
|
row.appendChild(badge);
|
|
1756
1861
|
}
|
|
1757
1862
|
this.list.appendChild(row);
|
|
@@ -4140,6 +4245,10 @@ function sanitizeState(doc) {
|
|
|
4140
4245
|
const favs = d.favorites.filter((f) => typeof f === "string");
|
|
4141
4246
|
if (favs.length > 0) out.favorites = favs;
|
|
4142
4247
|
}
|
|
4248
|
+
if (Array.isArray(d.timeframeFavorites)) {
|
|
4249
|
+
const favs = d.timeframeFavorites.filter((f) => typeof f === "string");
|
|
4250
|
+
if (favs.length > 0) out.timeframeFavorites = favs;
|
|
4251
|
+
}
|
|
4143
4252
|
const sync = sanitizeSync(d.sync);
|
|
4144
4253
|
if (sync) out.sync = sync;
|
|
4145
4254
|
const tracks = sanitizeTrackSizes(d.trackSizes);
|