@luxalgo/vela 0.6.6 → 0.6.7
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-JJq7M2it.d.ts → DataProvider-BSLlBpB9.d.ts} +10 -0
- package/dist/{DataProvider-DDUYw2qP.d.cts → DataProvider-BsQM2WNH.d.cts} +10 -0
- package/dist/{bottombar-COnL2Sk4.d.ts → bottombar-Br4rzx_R.d.ts} +52 -6
- package/dist/{bottombar-DeDXbF_K.d.cts → bottombar-Div5zibZ.d.cts} +52 -6
- package/dist/{chunk-JQFO2WMU.js → chunk-6WFKTAT4.js} +37 -1
- package/dist/{chunk-OEUGUXL7.js → chunk-L3I2CCYO.js} +49 -1
- package/dist/chunk-W4EJWLEO.js +12 -0
- package/dist/{chunk-TMXK2SJR.js → chunk-WVP4XV5A.js} +221 -65
- package/dist/{contributions-DNMqrAuw.d.cts → contributions-Hv4_cOJo.d.cts} +46 -3
- package/dist/{contributions-BqCIsbaP.d.ts → contributions-Z12BrWCy.d.ts} +46 -3
- package/dist/index.cjs +64 -0
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +2 -2
- package/dist/{plugin-WnvNNJOp.d.ts → plugin-9koBjc5H.d.ts} +2 -2
- package/dist/{plugin-CN8U2__Q.d.cts → plugin-BXhDHTYn.d.cts} +2 -2
- package/dist/plugin.cjs +28 -0
- package/dist/plugin.d.cts +3 -3
- package/dist/plugin.d.ts +3 -3
- package/dist/plugin.js +1 -1
- package/dist/providers/binance.cjs +16 -0
- package/dist/providers/binance.d.cts +4 -1
- package/dist/providers/binance.d.ts +4 -1
- package/dist/providers/binance.js +7 -0
- package/dist/providers/coinbase.cjs +16 -0
- package/dist/providers/coinbase.d.cts +4 -1
- package/dist/providers/coinbase.d.ts +4 -1
- package/dist/providers/coinbase.js +7 -0
- package/dist/providers/hyperliquid.cjs +16 -0
- package/dist/providers/hyperliquid.d.cts +4 -1
- package/dist/providers/hyperliquid.d.ts +4 -1
- package/dist/providers/hyperliquid.js +7 -0
- package/dist/vela.global.js +90 -0
- package/dist/vela.global.min.js +42 -42
- package/dist/widget.cjs +319 -63
- package/dist/widget.d.cts +52 -10
- package/dist/widget.d.ts +52 -10
- package/dist/widget.js +5 -4
- package/dist/workspace.cjs +265 -65
- package/dist/workspace.d.cts +13 -4
- package/dist/workspace.d.ts +13 -4
- package/dist/workspace.js +4 -3
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-
|
|
2
|
-
import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, resolveEngines, legendActionsProviderFor, statePersistenceHandlers, rendererDefaults, widgetAttachments, getDrawingType } from './chunk-
|
|
1
|
+
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-6WFKTAT4.js';
|
|
2
|
+
import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType } from './chunk-L3I2CCYO.js';
|
|
3
3
|
import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-2R7BANEM.js';
|
|
4
4
|
import { Menu, Dialog, applyPlotOverlayTokens, ensureUIHost } from './chunk-OICPLNU7.js';
|
|
5
5
|
import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, categoricalColor } from './chunk-PN5KWFZ4.js';
|
|
6
|
+
import { baseOf } from './chunk-W4EJWLEO.js';
|
|
6
7
|
|
|
7
8
|
// src/widget/timeframe.ts
|
|
8
9
|
var UNIT_MS = {
|
|
@@ -526,9 +527,14 @@ var CSS2 = `
|
|
|
526
527
|
align-items: center;
|
|
527
528
|
justify-content: center;
|
|
528
529
|
}
|
|
529
|
-
|
|
530
|
+
/* The right side of the bar \u2014 whatever the composition puts there rides this one
|
|
531
|
+
auto-margin push (the flow-actions host used to carry it; composition can omit it). */
|
|
532
|
+
.vela-topbar-right { margin-left: auto; display: inline-flex; align-items: center; gap: var(--vela-space-1); }
|
|
533
|
+
.vela-widget-actions { display: inline-flex; gap: var(--vela-space-1); }
|
|
530
534
|
/* Left-aligned contributed actions \u2014 the primary-chrome cluster after the dropdowns. */
|
|
531
535
|
.vela-widget-actions-left { display: inline-flex; align-items: center; gap: var(--vela-space-1); }
|
|
536
|
+
/* One PINNED contributed action's slot (a composition entry naming the action's id). */
|
|
537
|
+
.vela-widget-action-pin { display: inline-flex; align-items: center; }
|
|
532
538
|
/* The side-panel toggles, one per docked panel \u2014 a group so the dock can rebuild them
|
|
533
539
|
without disturbing the tools around it. */
|
|
534
540
|
.vela-widget-panels { display: inline-flex; align-items: center; gap: var(--vela-space-1); }
|
|
@@ -587,6 +593,12 @@ var Topbar = class {
|
|
|
587
593
|
this.tooltips = [];
|
|
588
594
|
this.panelBtns = /* @__PURE__ */ new Map();
|
|
589
595
|
this.panelTooltips = [];
|
|
596
|
+
/** Pinned contributed-action slots, by action id (composition entries that name one,
|
|
597
|
+
* plus built-in slots taken over by an override). */
|
|
598
|
+
this.pinned = /* @__PURE__ */ new Map();
|
|
599
|
+
/** Overrides that LEFT their native slot (default side + a declared `order`) — they
|
|
600
|
+
* render through the flow cluster like ordinary actions. */
|
|
601
|
+
this.flowingOverrides = /* @__PURE__ */ new Set();
|
|
590
602
|
this.onHairlineSync = () => {
|
|
591
603
|
if (this.hairlineRaf) return;
|
|
592
604
|
const win = this.el.ownerDocument.defaultView;
|
|
@@ -601,6 +613,8 @@ var Topbar = class {
|
|
|
601
613
|
this.host = host;
|
|
602
614
|
this.timeframe = opts.timeframe;
|
|
603
615
|
this.priceStyle = opts.priceStyle;
|
|
616
|
+
this.comp = resolveTopbarComposition(opts.composition);
|
|
617
|
+
const vis = (id) => topbarHas(this.comp, id);
|
|
604
618
|
const doc = host.ownerDocument;
|
|
605
619
|
injectStyles(STYLE_ID2, CSS2, doc);
|
|
606
620
|
this.el = doc.createElement("div");
|
|
@@ -623,7 +637,7 @@ var Topbar = class {
|
|
|
623
637
|
this.styleButton.className = "vela-widget-style";
|
|
624
638
|
this.renderStyleButton(doc);
|
|
625
639
|
let indicatorsBtn = null;
|
|
626
|
-
if (opts.onIndicatorsClick) {
|
|
640
|
+
if (opts.onIndicatorsClick && !topbarActionOverride("indicators")) {
|
|
627
641
|
indicatorsBtn = doc.createElement("button");
|
|
628
642
|
indicatorsBtn.className = "vela-widget-indicators";
|
|
629
643
|
indicatorsBtn.append(iconEl("indicators", doc), doc.createTextNode("Indicators"));
|
|
@@ -636,18 +650,23 @@ var Topbar = class {
|
|
|
636
650
|
this.panelsHost = doc.createElement("span");
|
|
637
651
|
this.panelsHost.className = "vela-widget-panels";
|
|
638
652
|
const tool = (cls, icon2, tip, onClick) => this.toolButton(cls, icon2, tip, onClick, this.tooltips);
|
|
639
|
-
|
|
640
|
-
|
|
653
|
+
if (vis("undo-redo")) {
|
|
654
|
+
this.undoBtn = tool("vela-widget-undo", "undo", "Undo", opts.onUndoClick);
|
|
655
|
+
this.redoBtn = tool("vela-widget-redo", "redo", "Redo", opts.onRedoClick);
|
|
656
|
+
} else {
|
|
657
|
+
this.undoBtn = doc.createElement("button");
|
|
658
|
+
this.redoBtn = doc.createElement("button");
|
|
659
|
+
}
|
|
641
660
|
this.setHistoryState(false, false);
|
|
642
|
-
const screenshotBtn = tool("vela-widget-screenshot", "camera", "Download screenshot", opts.onScreenshotClick);
|
|
643
|
-
this.alertsBtn = tool("vela-widget-alerts", "bell", "Alerts");
|
|
661
|
+
const screenshotBtn = vis("screenshot") && !topbarActionOverride("screenshot") ? tool("vela-widget-screenshot", "camera", "Download screenshot", opts.onScreenshotClick) : null;
|
|
662
|
+
this.alertsBtn = vis("alerts") ? tool("vela-widget-alerts", "bell", "Alerts") : doc.createElement("button");
|
|
644
663
|
this.alertsBtn.style.position = "relative";
|
|
645
664
|
if (opts.onAlertsClick) this.alertsBtn.addEventListener("click", () => opts.onAlertsClick(this.alertsBtn));
|
|
646
665
|
this.alertsBadge = doc.createElement("span");
|
|
647
666
|
this.alertsBadge.className = "vela-alerts-badge";
|
|
648
667
|
this.alertsBadge.style.display = "none";
|
|
649
668
|
this.alertsBtn.appendChild(this.alertsBadge);
|
|
650
|
-
if (opts.layout) {
|
|
669
|
+
if (opts.layout && vis("layout")) {
|
|
651
670
|
this.layoutId = opts.layout.current;
|
|
652
671
|
this.layoutButton = doc.createElement("button");
|
|
653
672
|
this.layoutButton.className = "vela-widget-style";
|
|
@@ -658,12 +677,65 @@ var Topbar = class {
|
|
|
658
677
|
d.className = "vela-sep";
|
|
659
678
|
return d;
|
|
660
679
|
};
|
|
661
|
-
const leading = [this.symbolEl, sep(), tfGroup, sep(), this.styleButton, sep()];
|
|
662
|
-
if (this.layoutButton) leading.push(this.layoutButton, sep());
|
|
663
|
-
if (indicatorsBtn) leading.push(indicatorsBtn, sep());
|
|
664
680
|
this.leftActionsSep = sep();
|
|
665
681
|
this.leftActionsSep.hidden = true;
|
|
666
|
-
|
|
682
|
+
const primaries = /* @__PURE__ */ new Set(["symbol", "timeframes", "style", "layout", "indicators"]);
|
|
683
|
+
const pinSlot = (id, left) => {
|
|
684
|
+
const slot = doc.createElement("span");
|
|
685
|
+
slot.className = "vela-widget-action-pin";
|
|
686
|
+
this.pinned.set(id, { host: slot, left });
|
|
687
|
+
return [slot];
|
|
688
|
+
};
|
|
689
|
+
const overridden = (id, left) => {
|
|
690
|
+
const ov = topbarActionOverride(id);
|
|
691
|
+
if (!ov) return null;
|
|
692
|
+
const sideDeclared = left ? opts.composition?.left != null : opts.composition?.right != null;
|
|
693
|
+
if (!sideDeclared && ov.order !== void 0) {
|
|
694
|
+
this.flowingOverrides.add(id);
|
|
695
|
+
return [];
|
|
696
|
+
}
|
|
697
|
+
return pinSlot(id, left);
|
|
698
|
+
};
|
|
699
|
+
const elementsFor = (id, left) => {
|
|
700
|
+
switch (id) {
|
|
701
|
+
case "symbol":
|
|
702
|
+
return [this.symbolEl];
|
|
703
|
+
case "timeframes":
|
|
704
|
+
return [tfGroup];
|
|
705
|
+
case "style":
|
|
706
|
+
return [this.styleButton];
|
|
707
|
+
case "layout":
|
|
708
|
+
return this.layoutButton ? [this.layoutButton] : [];
|
|
709
|
+
case "indicators":
|
|
710
|
+
return overridden(id, left) ?? (indicatorsBtn ? [indicatorsBtn] : []);
|
|
711
|
+
case "actions":
|
|
712
|
+
return left ? [this.leftActionsHost, this.leftActionsSep] : [this.actionsHost];
|
|
713
|
+
case "undo-redo":
|
|
714
|
+
return [this.undoBtn, this.redoBtn];
|
|
715
|
+
case "alerts":
|
|
716
|
+
return [this.alertsBtn];
|
|
717
|
+
case "panels":
|
|
718
|
+
return [this.panelsHost];
|
|
719
|
+
case "screenshot":
|
|
720
|
+
return overridden(id, left) ?? (screenshotBtn ? [screenshotBtn] : []);
|
|
721
|
+
default:
|
|
722
|
+
return pinSlot(id, left);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
const sideEls = (list, left) => {
|
|
726
|
+
const out = [];
|
|
727
|
+
for (const [i, id] of list.entries()) {
|
|
728
|
+
const els = elementsFor(id, left);
|
|
729
|
+
if (els.length === 0) continue;
|
|
730
|
+
out.push(...els);
|
|
731
|
+
if (primaries.has(id) && i < list.length - 1) out.push(sep());
|
|
732
|
+
}
|
|
733
|
+
return out;
|
|
734
|
+
};
|
|
735
|
+
const right = doc.createElement("span");
|
|
736
|
+
right.className = "vela-topbar-right";
|
|
737
|
+
right.append(...sideEls(this.comp.right, false));
|
|
738
|
+
this.el.append(...sideEls(this.comp.left, true), right);
|
|
667
739
|
host.appendChild(this.el);
|
|
668
740
|
this.renderTfChips();
|
|
669
741
|
this.onHairlineSync();
|
|
@@ -780,14 +852,24 @@ var Topbar = class {
|
|
|
780
852
|
else this.styleButton.appendChild(doc.createTextNode(priceStyleLabel(this.priceStyle)));
|
|
781
853
|
this.styleButton.setAttribute("aria-label", `Chart style \u2014 ${priceStyleLabel(this.priceStyle)}`);
|
|
782
854
|
}
|
|
783
|
-
/** Re-project the contributed topbar actions (call after registrations change).
|
|
855
|
+
/** Re-project the contributed topbar actions (call after registrations change).
|
|
856
|
+
* An action PINNED by the composition renders into its named slot (list position
|
|
857
|
+
* wins over `align`/`order`); the rest flow into the side's `actions` slot — or
|
|
858
|
+
* not at all when an explicit list omits it (the list is the side's contract). */
|
|
784
859
|
renderActions() {
|
|
785
860
|
const ctx = this.opts.getContext?.();
|
|
786
861
|
this.actionsHost.replaceChildren();
|
|
787
862
|
this.leftActionsHost.replaceChildren();
|
|
863
|
+
for (const pin of this.pinned.values()) pin.host.replaceChildren();
|
|
788
864
|
const doc = this.actionsHost.ownerDocument;
|
|
865
|
+
const flowLeft = this.comp.left.includes("actions");
|
|
866
|
+
const flowRight = this.comp.right.includes("actions");
|
|
867
|
+
const builtin = new Set(TOPBAR_BUILTIN_IDS);
|
|
789
868
|
for (const action of widgetActions("topbar", ctx)) {
|
|
790
|
-
const
|
|
869
|
+
const pin = this.pinned.get(action.id);
|
|
870
|
+
if (!pin && builtin.has(action.id) && !this.flowingOverrides.has(action.id)) continue;
|
|
871
|
+
const left = pin ? pin.left : action.align === "left";
|
|
872
|
+
if (!pin && !(left ? flowLeft : flowRight)) continue;
|
|
791
873
|
const b = doc.createElement("button");
|
|
792
874
|
b.className = left ? "vela-widget-action-left" : "vela-widget-action";
|
|
793
875
|
if (action.icon) b.appendChild(iconEl(action.icon, doc));
|
|
@@ -796,7 +878,7 @@ var Topbar = class {
|
|
|
796
878
|
const c = this.opts.getContext?.();
|
|
797
879
|
if (c) action.run(c);
|
|
798
880
|
});
|
|
799
|
-
(left ? this.leftActionsHost : this.actionsHost).appendChild(b);
|
|
881
|
+
(pin ? pin.host : left ? this.leftActionsHost : this.actionsHost).appendChild(b);
|
|
800
882
|
}
|
|
801
883
|
this.leftActionsSep.hidden = this.leftActionsHost.childElementCount === 0;
|
|
802
884
|
this.onHairlineSync();
|
|
@@ -1113,36 +1195,27 @@ var iconFailed = /* @__PURE__ */ new Set();
|
|
|
1113
1195
|
function initialsOf(name) {
|
|
1114
1196
|
return (name || "?").replace(/[^A-Za-z0-9]/g, "").slice(0, 2).toUpperCase() || "?";
|
|
1115
1197
|
}
|
|
1116
|
-
function
|
|
1117
|
-
return `https://crypto-icons.ledger.com/${encodeURIComponent(base.toUpperCase())}.png`;
|
|
1118
|
-
}
|
|
1119
|
-
function baseOf(d) {
|
|
1120
|
-
const fromDesc = d.description?.split("/")[0]?.trim();
|
|
1121
|
-
if (fromDesc) return fromDesc.replace(/\s+Perpetual$/i, "");
|
|
1122
|
-
return d.ticker.replace(/[-_/]?(USDT|USDC|USD1|USDS|BUSD|USD|EUR|PERP)$/i, "") || d.ticker;
|
|
1123
|
-
}
|
|
1124
|
-
function tickerIconEl(doc, base, name, className) {
|
|
1198
|
+
function tickerIconEl(doc, base, name, className, iconUrl) {
|
|
1125
1199
|
const wrap = doc.createElement("span");
|
|
1126
1200
|
wrap.className = className;
|
|
1127
|
-
const key = base.toUpperCase();
|
|
1128
1201
|
const fallback = () => {
|
|
1129
1202
|
wrap.replaceChildren();
|
|
1130
1203
|
wrap.style.background = categoricalColor(name);
|
|
1131
1204
|
wrap.textContent = initialsOf(base || name);
|
|
1132
1205
|
};
|
|
1133
|
-
if (!
|
|
1206
|
+
if (!iconUrl || iconFailed.has(iconUrl)) {
|
|
1134
1207
|
fallback();
|
|
1135
1208
|
return wrap;
|
|
1136
1209
|
}
|
|
1137
1210
|
const img = doc.createElement("img");
|
|
1138
1211
|
img.alt = "";
|
|
1139
1212
|
img.crossOrigin = "anonymous";
|
|
1140
|
-
img.src =
|
|
1213
|
+
img.src = iconUrl;
|
|
1141
1214
|
img.style.cssText = "width:100%;height:100%;border-radius:50%;display:block;object-fit:cover;";
|
|
1142
1215
|
img.addEventListener(
|
|
1143
1216
|
"error",
|
|
1144
1217
|
() => {
|
|
1145
|
-
iconFailed.add(
|
|
1218
|
+
iconFailed.add(iconUrl);
|
|
1146
1219
|
fallback();
|
|
1147
1220
|
},
|
|
1148
1221
|
{ once: true }
|
|
@@ -1553,11 +1626,14 @@ var MenuBuilder = class {
|
|
|
1553
1626
|
}
|
|
1554
1627
|
};
|
|
1555
1628
|
var ObjectTree = class extends SidePanel {
|
|
1556
|
-
constructor(host) {
|
|
1629
|
+
constructor(host, iconFor) {
|
|
1557
1630
|
super(host, "Object tree", "vela-ot");
|
|
1631
|
+
this.iconFor = iconFor;
|
|
1558
1632
|
this.chart = null;
|
|
1559
1633
|
this.selectedDrawing = null;
|
|
1560
1634
|
this.symbolName = "";
|
|
1635
|
+
/** The raw (possibly venue-prefixed) symbol — what icon resolution routes on. */
|
|
1636
|
+
this.symbolRaw = "";
|
|
1561
1637
|
/** Drawing bundles — a view-side grouping, held for the panel's lifetime and never persisted.
|
|
1562
1638
|
* Kept per chart because a workspace points this one panel at whichever chart is active, and
|
|
1563
1639
|
* each chart's bundles have to survive the switch. */
|
|
@@ -1594,6 +1670,7 @@ var ObjectTree = class extends SidePanel {
|
|
|
1594
1670
|
if (open) this.refresh();
|
|
1595
1671
|
}
|
|
1596
1672
|
setSymbol(symbol) {
|
|
1673
|
+
this.symbolRaw = symbol;
|
|
1597
1674
|
this.symbolName = parseSymbol(symbol).ticker;
|
|
1598
1675
|
}
|
|
1599
1676
|
/** (Re)bind to a chart instance — called after every widget rebuild. */
|
|
@@ -1794,7 +1871,7 @@ var ObjectTree = class extends SidePanel {
|
|
|
1794
1871
|
const { chart } = pass;
|
|
1795
1872
|
if (row.kind === "price") {
|
|
1796
1873
|
const base = this.symbolName.replace(/[-_/]?(USDT|USDC|USD1|USDS|BUSD|USD|EUR|PERP)$/i, "") || this.symbolName;
|
|
1797
|
-
const icon2 = tickerIconEl(doc, base || "P", this.symbolName || "Price", "vela-ot-avatar");
|
|
1874
|
+
const icon2 = tickerIconEl(doc, base || "P", this.symbolName || "Price", "vela-ot-avatar", this.symbolRaw ? this.iconFor?.(this.symbolRaw) : void 0);
|
|
1798
1875
|
const el2 = this.row(icon2, row.label, row.visible, [
|
|
1799
1876
|
{
|
|
1800
1877
|
icon: row.visible ? "eye" : "eye-off",
|
|
@@ -2826,6 +2903,17 @@ var PanelDock = class {
|
|
|
2826
2903
|
};
|
|
2827
2904
|
|
|
2828
2905
|
// src/widget/symbol-picker.ts
|
|
2906
|
+
function dedupeSymbols(list) {
|
|
2907
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2908
|
+
const out = [];
|
|
2909
|
+
for (const s of list) {
|
|
2910
|
+
const key = `${(s.prefix ?? s.provider ?? "").toLowerCase()}:${s.ticker.toUpperCase()}`;
|
|
2911
|
+
if (seen.has(key)) continue;
|
|
2912
|
+
seen.add(key);
|
|
2913
|
+
out.push(s);
|
|
2914
|
+
}
|
|
2915
|
+
return out;
|
|
2916
|
+
}
|
|
2829
2917
|
var TOP_TICKERS = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT", "DOGEUSDT", "ADAUSDT", "LINKUSDT"];
|
|
2830
2918
|
function parseQuery(raw, venues) {
|
|
2831
2919
|
const m = raw.match(/^\s*([^\s:]+)\s*[:\s]\s*(.*)$/);
|
|
@@ -2840,17 +2928,18 @@ function onlyOne(matches) {
|
|
|
2840
2928
|
return matches.length === 1 ? matches[0] : null;
|
|
2841
2929
|
}
|
|
2842
2930
|
var venueOf = (s) => s.prefix ?? s.provider;
|
|
2843
|
-
function filterSymbols(list, query, limit = 100) {
|
|
2931
|
+
function filterSymbols(list, query, limit = 100, top = TOP_TICKERS) {
|
|
2844
2932
|
const venues = [...new Set(list.flatMap((s) => [venueOf(s)?.toLowerCase(), s.provider?.toLowerCase()]).filter((p) => !!p))];
|
|
2845
2933
|
const { scope, term } = parseQuery(query, venues);
|
|
2846
2934
|
const pool = scope ? list.filter((s) => venueOf(s)?.toLowerCase() === scope || s.provider?.toLowerCase() === scope) : list;
|
|
2847
2935
|
const q = term.toUpperCase();
|
|
2848
2936
|
if (!q) {
|
|
2849
2937
|
if (scope) return [...pool].sort((a, b) => a.ticker.localeCompare(b.ticker)).slice(0, limit);
|
|
2938
|
+
if (top === false) return pool.slice(0, limit);
|
|
2850
2939
|
const byTicker = new Map(pool.map((s) => [s.ticker.toUpperCase(), s]));
|
|
2851
|
-
const
|
|
2852
|
-
const rest = pool.filter((s) => !
|
|
2853
|
-
return [...
|
|
2940
|
+
const pinned = top.map((t) => byTicker.get(t)).filter((s) => s !== void 0);
|
|
2941
|
+
const rest = pool.filter((s) => !top.includes(s.ticker.toUpperCase()));
|
|
2942
|
+
return [...pinned, ...rest].slice(0, limit);
|
|
2854
2943
|
}
|
|
2855
2944
|
const qLower = term.toLowerCase();
|
|
2856
2945
|
const prefix = [];
|
|
@@ -2960,12 +3049,16 @@ var CSS6 = `
|
|
|
2960
3049
|
var PAGE = 100;
|
|
2961
3050
|
var SymbolPicker = class {
|
|
2962
3051
|
constructor(opts) {
|
|
3052
|
+
this.opts = opts;
|
|
2963
3053
|
this.source = () => [];
|
|
2964
3054
|
this.rows = [];
|
|
2965
3055
|
this.highlighted = 0;
|
|
2966
3056
|
this.seed = "";
|
|
2967
3057
|
this.activeTab = "All";
|
|
2968
3058
|
this.visible = PAGE;
|
|
3059
|
+
/** The ranked pool cache — `key` fingerprints the raw pool the ranking ran on. */
|
|
3060
|
+
this.ranked = null;
|
|
3061
|
+
this.ranking = false;
|
|
2969
3062
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
2970
3063
|
injectStyles(STYLE_ID6, CSS6, doc);
|
|
2971
3064
|
this.input = doc.createElement("input");
|
|
@@ -3063,10 +3156,34 @@ var SymbolPicker = class {
|
|
|
3063
3156
|
} else delete el.dataset.highlighted;
|
|
3064
3157
|
});
|
|
3065
3158
|
}
|
|
3159
|
+
/** The picker's pool: the source, shaped by the registered symbol ranking. Cached —
|
|
3160
|
+
* the hook runs when the pool CHANGES (an index lands or refreshes), never per
|
|
3161
|
+
* keystroke; an async hook resolves onto the next repaint (stale-while-revalidate
|
|
3162
|
+
* in between, the raw pool before the first resolve). */
|
|
3163
|
+
pool() {
|
|
3164
|
+
const raw = this.source();
|
|
3165
|
+
const hook = symbolRanking();
|
|
3166
|
+
if (!hook) return raw;
|
|
3167
|
+
const key = `${raw.length}:${raw[0]?.ticker ?? ""}:${raw[raw.length - 1]?.ticker ?? ""}`;
|
|
3168
|
+
if (this.ranked?.key !== key && !this.ranking) {
|
|
3169
|
+
this.ranking = true;
|
|
3170
|
+
Promise.resolve([...raw]).then((copy) => hook(copy)).then((out) => {
|
|
3171
|
+
this.ranked = { key, result: dedupeSymbols(out) };
|
|
3172
|
+
}).catch((err) => {
|
|
3173
|
+
console.warn("[vela] symbol ranking failed \u2014 pool order kept:", err);
|
|
3174
|
+
this.ranked = { key, result: [...raw] };
|
|
3175
|
+
}).finally(() => {
|
|
3176
|
+
this.ranking = false;
|
|
3177
|
+
this.refresh();
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
return this.ranked?.result ?? raw;
|
|
3181
|
+
}
|
|
3066
3182
|
computeRows() {
|
|
3067
3183
|
const TAB_TYPES = { Crypto: ["crypto"], Stocks: ["stock"], ETFs: ["etf"], Forex: ["forex"], Commodities: ["commodity"] };
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3184
|
+
const all = this.pool();
|
|
3185
|
+
const pool = this.activeTab === "All" ? all : all.filter((s) => TAB_TYPES[this.activeTab]?.includes((s.type ?? "").toLowerCase()) || this.activeTab === "Crypto" && (s.type ?? "").toLowerCase() === "futures");
|
|
3186
|
+
return filterSymbols(pool, this.input.value, this.visible, symbolRanking() ? false : TOP_TICKERS);
|
|
3070
3187
|
}
|
|
3071
3188
|
refresh() {
|
|
3072
3189
|
const doc = this.list.ownerDocument;
|
|
@@ -3097,7 +3214,7 @@ var SymbolPicker = class {
|
|
|
3097
3214
|
row.dataset.ticker = s.ticker;
|
|
3098
3215
|
const venue = s.prefix ?? s.provider;
|
|
3099
3216
|
if (venue) row.dataset.venue = venue;
|
|
3100
|
-
const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
|
|
3217
|
+
const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar", this.opts.iconFor?.(s));
|
|
3101
3218
|
const main = doc.createElement("span");
|
|
3102
3219
|
main.className = "vela-sp-main";
|
|
3103
3220
|
const t = doc.createElement("span");
|
|
@@ -3924,8 +4041,9 @@ function statuslineInkOf(renderer, priceStyle) {
|
|
|
3924
4041
|
}
|
|
3925
4042
|
}
|
|
3926
4043
|
var Statusline = class {
|
|
3927
|
-
constructor(host, symbol) {
|
|
4044
|
+
constructor(host, symbol, iconFor) {
|
|
3928
4045
|
this.host = host;
|
|
4046
|
+
this.iconFor = iconFor;
|
|
3929
4047
|
this.parts = { name: true, market: true, ohlc: true, change: true };
|
|
3930
4048
|
this.lastBar = null;
|
|
3931
4049
|
this.hoverBar = null;
|
|
@@ -3949,7 +4067,7 @@ var Statusline = class {
|
|
|
3949
4067
|
this.el.className = "vela-statusline";
|
|
3950
4068
|
this.el.dataset.velaScreenshot = "1";
|
|
3951
4069
|
const ticker = parseSymbol(symbol).ticker;
|
|
3952
|
-
this.avatarEl = tickerIconEl(doc, baseOfTicker(ticker), ticker, "vela-sl-avatar");
|
|
4070
|
+
this.avatarEl = tickerIconEl(doc, baseOfTicker(ticker), ticker, "vela-sl-avatar", this.iconFor?.(symbol));
|
|
3953
4071
|
this.symbolEl = doc.createElement("span");
|
|
3954
4072
|
this.symbolEl.className = "vela-sl-symbol";
|
|
3955
4073
|
this.symbolEl.textContent = ticker;
|
|
@@ -3970,7 +4088,7 @@ var Statusline = class {
|
|
|
3970
4088
|
setSymbol(symbol) {
|
|
3971
4089
|
const ticker = parseSymbol(symbol).ticker;
|
|
3972
4090
|
this.symbolEl.textContent = ticker;
|
|
3973
|
-
const fresh = tickerIconEl(this.el.ownerDocument, baseOfTicker(ticker), ticker, "vela-sl-avatar");
|
|
4091
|
+
const fresh = tickerIconEl(this.el.ownerDocument, baseOfTicker(ticker), ticker, "vela-sl-avatar", this.iconFor?.(symbol));
|
|
3974
4092
|
this.avatarEl.replaceWith(fresh);
|
|
3975
4093
|
this.avatarEl = fresh;
|
|
3976
4094
|
this.fit();
|
|
@@ -4816,12 +4934,15 @@ var ChartCell = class {
|
|
|
4816
4934
|
this.watermarkOn = seed.watermark ?? deps.watermark;
|
|
4817
4935
|
this.watermark = deps.watermark ? new Watermark(this.host, symbol ?? "", seed.timeframe ?? "60") : null;
|
|
4818
4936
|
if (!this.watermarkOn) this.watermark?.setVisible(false);
|
|
4819
|
-
this.statusline = deps.statusline ? new Statusline(this.host, symbol ?? "") : null;
|
|
4937
|
+
this.statusline = deps.statusline ? new Statusline(this.host, symbol ?? "", (sym) => this.inner?.data.symbolIcon(sym)) : null;
|
|
4820
4938
|
this.statusline?.setMeta(seed.timeframe ?? "60", this.state.provider ?? "");
|
|
4821
4939
|
this.statusline?.onChart(this.inner);
|
|
4822
4940
|
this.marketStatus = this.statusline ? new MarketStatusTracker((s) => this.statusline?.setMarketStatus(s)) : null;
|
|
4823
4941
|
void this.inner.data.ready().then(() => {
|
|
4824
|
-
if (this.inner && this.state.symbol)
|
|
4942
|
+
if (this.inner && this.state.symbol) {
|
|
4943
|
+
this.statusline?.setSymbol(this.state.symbol);
|
|
4944
|
+
this.statusline?.setMeta(this.state.timeframe ?? "60", this.inner.data.displayPrefix(this.state.symbol) ?? this.state.provider ?? "");
|
|
4945
|
+
}
|
|
4825
4946
|
this.refreshSessionAvailable();
|
|
4826
4947
|
if (this.inner && this.state.symbol) this.marketStatus?.track(this.inner.data, this.state.symbol);
|
|
4827
4948
|
});
|
|
@@ -6003,7 +6124,8 @@ var MobileBar = class {
|
|
|
6003
6124
|
if (!ctx) return;
|
|
6004
6125
|
const doc = this.el.ownerDocument;
|
|
6005
6126
|
this.actionsHost.replaceChildren();
|
|
6006
|
-
|
|
6127
|
+
const builtin = new Set(TOPBAR_BUILTIN_IDS);
|
|
6128
|
+
for (const action of widgetActions("topbar", ctx).filter((a) => a.align === "left" && !builtin.has(a.id))) {
|
|
6007
6129
|
const b = doc.createElement("button");
|
|
6008
6130
|
b.className = "vela-mb-item";
|
|
6009
6131
|
b.setAttribute("aria-label", action.label);
|
|
@@ -6532,10 +6654,10 @@ var MoreDrawer = class {
|
|
|
6532
6654
|
});
|
|
6533
6655
|
actions.appendChild(b);
|
|
6534
6656
|
};
|
|
6535
|
-
action("undo", "Undo", this.opts.canUndo(), this.opts.onUndo);
|
|
6536
|
-
action("redo", "Redo", this.opts.canRedo(), this.opts.onRedo);
|
|
6537
|
-
action("camera", "Screenshot", true, this.opts.onScreenshot);
|
|
6538
|
-
this.drawer.body.appendChild(actions);
|
|
6657
|
+
if (this.opts.onUndo) action("undo", "Undo", this.opts.canUndo(), this.opts.onUndo);
|
|
6658
|
+
if (this.opts.onRedo) action("redo", "Redo", this.opts.canRedo(), this.opts.onRedo);
|
|
6659
|
+
if (this.opts.onScreenshot) action("camera", "Screenshot", true, this.opts.onScreenshot);
|
|
6660
|
+
if (actions.childElementCount > 0) this.drawer.body.appendChild(actions);
|
|
6539
6661
|
const list = doc.createElement("div");
|
|
6540
6662
|
list.className = "vela-md-list";
|
|
6541
6663
|
const current = this.opts.priceStyles().find((s) => s.id === this.opts.priceStyle());
|
|
@@ -6556,8 +6678,10 @@ var MoreDrawer = class {
|
|
|
6556
6678
|
})
|
|
6557
6679
|
);
|
|
6558
6680
|
}
|
|
6559
|
-
|
|
6560
|
-
|
|
6681
|
+
if (this.opts.alerts) {
|
|
6682
|
+
const alertCount = this.opts.alerts().length;
|
|
6683
|
+
list.appendChild(this.row(doc, "Alerts", { icon: "bell", value: alertCount > 0 ? String(alertCount) : void 0, chevron: true, onClick: () => this.show("alerts") }));
|
|
6684
|
+
}
|
|
6561
6685
|
for (const act of this.opts.actions()) {
|
|
6562
6686
|
list.appendChild(
|
|
6563
6687
|
this.row(doc, act.label, {
|
|
@@ -6638,7 +6762,7 @@ var MoreDrawer = class {
|
|
|
6638
6762
|
this.drawer.body.appendChild(list);
|
|
6639
6763
|
}
|
|
6640
6764
|
renderAlerts(doc) {
|
|
6641
|
-
const alerts = this.opts.alerts();
|
|
6765
|
+
const alerts = this.opts.alerts?.() ?? [];
|
|
6642
6766
|
if (alerts.length === 0) {
|
|
6643
6767
|
const empty = doc.createElement("div");
|
|
6644
6768
|
empty.className = "vela-md-empty";
|
|
@@ -7197,6 +7321,9 @@ var VelaWorkspace = class {
|
|
|
7197
7321
|
const optLayout = opts.layout === false || opts.layout === void 0 ? "4" : opts.layout;
|
|
7198
7322
|
this.def = this.resolveLayout(this.monoLayout ? "1" : boot?.layout && ensureLayout(boot.layout) ? boot.layout : optLayout);
|
|
7199
7323
|
this.alertCap = Math.max(1, opts.alertCap ?? ALERT_CAP);
|
|
7324
|
+
this.topbarComp = resolveTopbarComposition(opts.topbar);
|
|
7325
|
+
this.indicatorsOverride = topbarActionOverride("indicators");
|
|
7326
|
+
this.screenshotOverride = topbarActionOverride("screenshot");
|
|
7200
7327
|
if (boot?.trackSizes) for (const [id, ts] of Object.entries(boot.trackSizes)) this.trackSizes.set(id, ts);
|
|
7201
7328
|
if (boot?.ext) this.extState = { ...boot.ext };
|
|
7202
7329
|
if (boot?.charts) for (const { id, ...cs } of boot.charts) this.pool.set(id, cs);
|
|
@@ -7213,6 +7340,8 @@ var VelaWorkspace = class {
|
|
|
7213
7340
|
});
|
|
7214
7341
|
this.symbolPicker = new SymbolPicker({
|
|
7215
7342
|
host: this.root,
|
|
7343
|
+
// Row icons come from each descriptor's OWNING provider (resolveSymbolIcon).
|
|
7344
|
+
iconFor: (d) => this.feed.symbolIconOf(d),
|
|
7216
7345
|
onSelect: (ticker) => this.active.setSymbol(ticker),
|
|
7217
7346
|
onOpenChange: (open) => {
|
|
7218
7347
|
if (open) for (const cell of this.cells()) cell.chart.renderer.closeDialogs();
|
|
@@ -7220,7 +7349,7 @@ var VelaWorkspace = class {
|
|
|
7220
7349
|
}
|
|
7221
7350
|
});
|
|
7222
7351
|
this.symbolPicker.setSource(() => this.feed.symbols());
|
|
7223
|
-
this.indicatorPicker = opts.indicatorPicker !== false ? new IndicatorPicker({
|
|
7352
|
+
this.indicatorPicker = opts.indicatorPicker !== false && !this.indicatorsOverride && topbarHas(this.topbarComp, "indicators") ? new IndicatorPicker({
|
|
7224
7353
|
host: this.root,
|
|
7225
7354
|
library: () => this.active.libraryRows(),
|
|
7226
7355
|
onChart: () => this.active.onChartRows(),
|
|
@@ -7239,6 +7368,9 @@ var VelaWorkspace = class {
|
|
|
7239
7368
|
});
|
|
7240
7369
|
this.topbar = new Topbar(this.root, {
|
|
7241
7370
|
symbol: "",
|
|
7371
|
+
// RAW option, not the resolved lists — the bar distinguishes a host-declared
|
|
7372
|
+
// side (list is law) from a default one (an override's `order` may flow).
|
|
7373
|
+
composition: opts.topbar,
|
|
7242
7374
|
onSymbolClick: () => this.symbolPicker.open(),
|
|
7243
7375
|
...picker ? { onIndicatorsClick: () => picker.open() } : {},
|
|
7244
7376
|
onUndoClick: () => this.active.history.undo(),
|
|
@@ -7296,7 +7428,7 @@ var VelaWorkspace = class {
|
|
|
7296
7428
|
context: () => this.context(),
|
|
7297
7429
|
changed: () => this.markStateDirty()
|
|
7298
7430
|
});
|
|
7299
|
-
this.objectTree = new ObjectTree(main);
|
|
7431
|
+
this.objectTree = new ObjectTree(main, (sym) => this.feed.symbolIcon(sym));
|
|
7300
7432
|
this.dataWindow = new DataWindow(main);
|
|
7301
7433
|
this.dock.addBuiltIn({ id: "dataWindow", title: "Data window", icon: "datawindow", order: 10, panel: this.dataWindow, onChart: (c) => this.dataWindow.onChart(c) });
|
|
7302
7434
|
this.dock.addBuiltIn({ id: "objects", title: "Object tree", icon: "objects", order: 20, panel: this.objectTree, onChart: (c) => this.objectTree.onChart(c) });
|
|
@@ -7368,7 +7500,9 @@ var VelaWorkspace = class {
|
|
|
7368
7500
|
timeframe: "60",
|
|
7369
7501
|
onSymbolClick: () => this.symbolPicker.open(),
|
|
7370
7502
|
onTimeframeClick: () => this.openTimeframeDrawer(),
|
|
7371
|
-
|
|
7503
|
+
// Same visibility truth as the desktop bar: composition-hidden
|
|
7504
|
+
// indicators lose their mobile stop too; an override takes it over.
|
|
7505
|
+
...topbarHas(this.topbarComp, "indicators") && (picker || this.indicatorsOverride) ? { onIndicatorsClick: this.indicatorsOverride ? () => this.runOverride(this.indicatorsOverride) : () => picker.open() } : {},
|
|
7372
7506
|
getContext: () => this.context(),
|
|
7373
7507
|
...this.drawingsEnabled ? { onDrawingsClick: () => this.openDrawingsDrawer() } : {},
|
|
7374
7508
|
onMoreClick: () => this.openMoreDrawer(),
|
|
@@ -8264,27 +8398,32 @@ var VelaWorkspace = class {
|
|
|
8264
8398
|
this.drawingsDrawer.open();
|
|
8265
8399
|
}
|
|
8266
8400
|
openMoreDrawer() {
|
|
8401
|
+
const has = (id) => topbarHas(this.topbarComp, id);
|
|
8267
8402
|
this.moreDrawer ?? (this.moreDrawer = new MoreDrawer({
|
|
8268
8403
|
host: this.root,
|
|
8269
|
-
onUndo: () => this.active.history.undo(),
|
|
8270
|
-
|
|
8271
|
-
onScreenshot: () => this.active.downloadScreenshot(),
|
|
8404
|
+
...has("undo-redo") ? { onUndo: () => this.active.history.undo(), onRedo: () => this.active.history.redo() } : {},
|
|
8405
|
+
...has("screenshot") ? { onScreenshot: this.screenshotOverride ? () => this.runOverride(this.screenshotOverride) : () => this.active.downloadScreenshot() } : {},
|
|
8272
8406
|
canUndo: () => this.active.history.canUndo,
|
|
8273
8407
|
canRedo: () => this.active.history.canRedo,
|
|
8274
8408
|
priceStyles: () => priceStyleIds().map((id) => ({ id, label: priceStyleLabel(id), icon: priceStyleIcon(id) })),
|
|
8275
8409
|
priceStyle: () => this.active.priceStyle,
|
|
8276
8410
|
onPriceStyle: (id) => this.active.setPriceStyle(id),
|
|
8277
|
-
panels: () => [...this.dock.list()],
|
|
8411
|
+
panels: () => has("panels") ? [...this.dock.list()] : [],
|
|
8278
8412
|
onTogglePanel: (id) => this.dock.toggle(id),
|
|
8279
|
-
alerts: () => this.alerts.map((a) => ({ title: `${a.source} \xB7 ${a.title}`, message: a.message, time: a.time })),
|
|
8413
|
+
...has("alerts") ? { alerts: () => this.alerts.map((a) => ({ title: `${a.source} \xB7 ${a.title}`, message: a.message, time: a.time })) } : {},
|
|
8280
8414
|
// Left-aligned actions have their own bottom-bar stop — only the rest
|
|
8281
|
-
// lands in the drawer, or every left action would appear twice.
|
|
8282
|
-
actions
|
|
8415
|
+
// lands in the drawer, or every left action would appear twice. Built-in-id
|
|
8416
|
+
// actions are slot OVERRIDES: they reach the drawer through the slot's own
|
|
8417
|
+
// routed button (screenshot) or stop (indicators), never as an extra row.
|
|
8418
|
+
actions: () => {
|
|
8419
|
+
const builtin = new Set(TOPBAR_BUILTIN_IDS);
|
|
8420
|
+
return widgetActions("topbar", this.context()).filter((a) => a.align !== "left" && !builtin.has(a.id)).map((a) => ({ label: a.label, icon: a.icon, run: () => a.run(this.context()) }));
|
|
8421
|
+
},
|
|
8283
8422
|
// The desktop layout dropdown's whole surface — the grid canvas, the
|
|
8284
8423
|
// non-canvas presets and the sync switches — relocated into the kebab
|
|
8285
8424
|
// drawer (the topbar is hidden on mobile). Same reads as the topbar block;
|
|
8286
|
-
// single-chart mode
|
|
8287
|
-
layout: this.monoLayout ? void 0 : {
|
|
8425
|
+
// single-chart mode and a composition without 'layout' omit it here too.
|
|
8426
|
+
layout: this.monoLayout || !has("layout") ? void 0 : {
|
|
8288
8427
|
shape: () => layoutShape(this.def),
|
|
8289
8428
|
presets: () => layouts().filter((l) => layoutShape(l) === null).map((l) => ({ id: l.id, label: l.label, checked: l.id === this.def.id })),
|
|
8290
8429
|
onSelectGrid: (rows, cols) => this.setLayout(layoutForGrid(rows, cols)),
|
|
@@ -8351,9 +8490,23 @@ var VelaWorkspace = class {
|
|
|
8351
8490
|
}
|
|
8352
8491
|
}
|
|
8353
8492
|
}
|
|
8493
|
+
/** Invoke a slot override the way its button would: fresh context, `when` respected. */
|
|
8494
|
+
runOverride(action) {
|
|
8495
|
+
const ctx = this.context();
|
|
8496
|
+
if (!action.when || action.when(ctx)) action.run(ctx);
|
|
8497
|
+
}
|
|
8354
8498
|
/** The default shortcut set — every binding acts on the ACTIVE cell. */
|
|
8355
8499
|
registerDefaultKeys() {
|
|
8356
|
-
this.
|
|
8500
|
+
if (topbarHas(this.topbarComp, "screenshot")) {
|
|
8501
|
+
const ov = this.screenshotOverride;
|
|
8502
|
+
this.keymap.register({
|
|
8503
|
+
id: "chart.screenshot",
|
|
8504
|
+
keys: "mod+alt+s",
|
|
8505
|
+
label: ov ? ov.label : "Download a chart screenshot",
|
|
8506
|
+
category: "Chart",
|
|
8507
|
+
run: ov ? () => this.runOverride(ov) : () => this.active.downloadScreenshot()
|
|
8508
|
+
});
|
|
8509
|
+
}
|
|
8357
8510
|
this.keymap.register({ id: "chart.reset-view", keys: "alt+r", label: "Reset view (all history)", category: "Chart", run: () => this.active.chart.setVisibleRangePreset("ALL") });
|
|
8358
8511
|
this.keymap.register({ id: "chart.toggle-log", keys: "alt+l", label: "Toggle logarithmic scale", category: "Chart", run: () => this.active.chart.renderer.set("logScale", !this.active.chart.renderer.get("logScale")) });
|
|
8359
8512
|
this.keymap.register({
|
|
@@ -8393,7 +8546,10 @@ var VelaWorkspace = class {
|
|
|
8393
8546
|
this.keymap.register({ id: "view.zoom-out", keys: "mod+arrowdown", label: "Zoom out", category: "Chart", run: () => this.glider.zoom(ZOOM_OUT) });
|
|
8394
8547
|
this.keymap.register({ id: "view.pan-left", keys: "mod+arrowleft", label: "Pan toward history", category: "Chart", run: () => this.active.chart.panBy(-PAN_FAST) });
|
|
8395
8548
|
this.keymap.register({ id: "view.pan-right", keys: "mod+arrowright", label: "Pan toward now", category: "Chart", run: () => this.active.chart.panBy(PAN_FAST) });
|
|
8396
|
-
|
|
8549
|
+
const indOv = this.indicatorsOverride;
|
|
8550
|
+
if (indOv && topbarHas(this.topbarComp, "indicators")) {
|
|
8551
|
+
this.keymap.register({ id: "indicators.open", keys: "/", label: indOv.label, category: "Indicators", run: () => this.runOverride(indOv) });
|
|
8552
|
+
} else if (this.indicatorPicker) {
|
|
8397
8553
|
this.keymap.register({ id: "indicators.open", keys: "/", label: "Open the indicator picker", category: "Indicators", run: () => this.indicatorPicker?.open() });
|
|
8398
8554
|
}
|
|
8399
8555
|
this.keymap.register({
|