@luxalgo/vela 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/{DataProvider-DTOEXKTI.d.ts → DataProvider-Cqc_BaJ9.d.ts} +1 -1
- package/dist/{DataProvider-gnzv-dgc.d.cts → DataProvider-DgS2UyMc.d.cts} +1 -1
- package/dist/{chunk-RRFGWZNJ.js → chunk-D6WM5IUE.js} +786 -864
- package/dist/{chunk-LJLZR44Y.js → chunk-EMS6PO2T.js} +5 -7
- package/dist/{chunk-Y7TK4ZWK.js → chunk-JBQUY2RI.js} +126 -19
- package/dist/chunk-OGRQW3M6.js +1328 -0
- package/dist/{chunk-P556H6EA.js → chunk-QWIEKZRE.js} +59 -34
- package/dist/{chunk-B5TIKQJ5.js → chunk-ZNL2X6U2.js} +16 -2
- package/dist/{contributions-Ci_i3IN2.d.cts → contributions-4Nh1jbvb.d.cts} +2 -2
- package/dist/{contributions-b6AVrqqG.d.ts → contributions-DuIxJWeH.d.ts} +2 -2
- package/dist/{history-Cf1lVsap.d.cts → history-FsmvCIo4.d.ts} +5 -3
- package/dist/{history-Ci3M0xLV.d.ts → history-HofqYEh2.d.cts} +5 -3
- package/dist/index.cjs +2783 -1611
- package/dist/index.d.cts +14 -8
- package/dist/index.d.ts +14 -8
- 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-DfH4Y-Om.d.cts → plugin-DJR6z3e0.d.cts} +8 -7
- package/dist/{plugin-CnNd7cuF.d.ts → plugin-N_EaXN4-.d.ts} +8 -7
- 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 +2777 -1605
- package/dist/vela.global.min.js +315 -66
- package/dist/widget.cjs +2358 -1052
- package/dist/widget.d.cts +26 -8
- package/dist/widget.d.ts +26 -8
- package/dist/widget.js +27 -9
- package/dist/workspace.cjs +2264 -946
- package/dist/workspace.d.cts +10 -5
- package/dist/workspace.d.ts +10 -5
- package/dist/workspace.js +25 -7
- package/package.json +1 -1
- package/dist/chunk-ATPU5CI6.js +0 -81
|
@@ -80,6 +80,10 @@ registerIcon(
|
|
|
80
80
|
S('<path d="M8 1.8 14 5 8 8.2 2 5z"/><path d="M2 8l6 3.2L14 8" opacity="0.7"/><path d="M2 11l6 3.2 6-3.2" opacity="0.4"/>')
|
|
81
81
|
);
|
|
82
82
|
registerIcon("clock", S('<circle cx="8" cy="8" r="6.2"/><path d="M8 4.8V8l2.4 1.6"/>'));
|
|
83
|
+
registerIcon(
|
|
84
|
+
"calendar",
|
|
85
|
+
S('<rect x="2.2" y="3.2" width="11.6" height="10.6" rx="1.4"/><path d="M5.2 1.8v2.8M10.8 1.8v2.8M2.2 6.8h11.6"/>')
|
|
86
|
+
);
|
|
83
87
|
registerIcon("datawindow", S('<rect x="1.8" y="2.5" width="12.4" height="11" rx="1.5"/><path d="M4.5 5.5h4M4.5 8h7M4.5 10.5h5.5"/>'));
|
|
84
88
|
registerIcon("camera", S('<path d="M5.5 4 6.5 2.5h3L10.5 4h3A1 1 0 0 1 14.5 5v7.5a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z"/><circle cx="8" cy="8.5" r="2.6"/>'));
|
|
85
89
|
registerIcon(
|
|
@@ -223,12 +227,6 @@ function mix(base, top, alpha) {
|
|
|
223
227
|
const c = (x, y) => Math.round(x + (y - x) * alpha);
|
|
224
228
|
return `rgb(${c(b.r, t.r)},${c(b.g, t.g)},${c(b.b, t.b)})`;
|
|
225
229
|
}
|
|
226
|
-
function toHex6(color, fallback = "#888888") {
|
|
227
|
-
const c = parseRgb(color);
|
|
228
|
-
if (!c) return fallback;
|
|
229
|
-
const h = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
|
|
230
|
-
return `#${h(c.r)}${h(c.g)}${h(c.b)}`;
|
|
231
|
-
}
|
|
232
230
|
function isDarkColor(color) {
|
|
233
231
|
const c = parseRgb(color);
|
|
234
232
|
if (!c) return true;
|
|
@@ -256,4 +254,4 @@ function iconEl(id, doc = document) {
|
|
|
256
254
|
return span;
|
|
257
255
|
}
|
|
258
256
|
|
|
259
|
-
export { ACCENT, ACCENT_BRIGHT, BEARISH, BULLISH, CATEGORICAL, CROSSHAIR, HIGHLIGHT, INFO, INVALID, MARKER, NEUTRAL, SERIES_LINE, SESSION_OFF, SESSION_POST, SESSION_PRE, SLATE, SLATE_DEEP, TRADE_EXIT, TRADE_LONG, TRADE_SHORT, VALID, WARNING, categoricalColor, icon, iconAt, iconEl, iconMarkup, injectStyles, isDarkColor, mix, registerIcon, svg16, svg24, svg24Solid,
|
|
257
|
+
export { ACCENT, ACCENT_BRIGHT, BEARISH, BULLISH, CATEGORICAL, CROSSHAIR, HIGHLIGHT, INFO, INVALID, MARKER, NEUTRAL, SERIES_LINE, SESSION_OFF, SESSION_POST, SESSION_PRE, SLATE, SLATE_DEEP, TRADE_EXIT, TRADE_LONG, TRADE_SHORT, VALID, WARNING, categoricalColor, icon, iconAt, iconEl, iconMarkup, injectStyles, isDarkColor, mix, registerIcon, svg16, svg24, svg24Solid, withAlpha };
|
|
@@ -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-D6WM5IUE.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) => ({
|
|
@@ -1216,17 +1319,17 @@ var STYLE_ID4 = "vela-widget-watermark";
|
|
|
1216
1319
|
var CSS4 = `
|
|
1217
1320
|
.vela-watermark {
|
|
1218
1321
|
position: absolute;
|
|
1219
|
-
/* Insets follow the renderer-published gutters
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
bottom: 0;
|
|
1322
|
+
/* Insets follow the renderer-published gutters AND the price-pane vertical
|
|
1323
|
+
* bounds (mount container), so the mark centers on the PRICE PANE \u2014 never
|
|
1324
|
+
* the study panes below, the drawings toolbar, or the price scale. */
|
|
1325
|
+
top: var(--vela-price-pane-top, 0px);
|
|
1326
|
+
bottom: var(--vela-price-pane-bottom, 0px);
|
|
1225
1327
|
left: var(--vela-toolbar-gutter, 0px);
|
|
1226
1328
|
right: var(--vela-scale-gutter, 0px);
|
|
1227
1329
|
display: flex;
|
|
1228
1330
|
align-items: center;
|
|
1229
1331
|
justify-content: center;
|
|
1332
|
+
overflow: hidden;
|
|
1230
1333
|
pointer-events: none;
|
|
1231
1334
|
z-index: 1;
|
|
1232
1335
|
color: var(--vela-fg);
|
|
@@ -1281,7 +1384,7 @@ var Watermark = class {
|
|
|
1281
1384
|
this.text.textContent = symbol ? `${parseSymbol(symbol).ticker} \xB7 ${timeframeLabel(timeframe)}` : "";
|
|
1282
1385
|
this.fit();
|
|
1283
1386
|
}
|
|
1284
|
-
/** Measure the text at the cap and shrink it to the
|
|
1387
|
+
/** Measure the text at the cap and shrink it to the price pane's width. */
|
|
1285
1388
|
fit() {
|
|
1286
1389
|
if (this.el.clientWidth <= 0 || !this.text.textContent) return;
|
|
1287
1390
|
this.el.style.fontSize = `${MAX_FONT_PX}px`;
|
|
@@ -4140,6 +4243,10 @@ function sanitizeState(doc) {
|
|
|
4140
4243
|
const favs = d.favorites.filter((f) => typeof f === "string");
|
|
4141
4244
|
if (favs.length > 0) out.favorites = favs;
|
|
4142
4245
|
}
|
|
4246
|
+
if (Array.isArray(d.timeframeFavorites)) {
|
|
4247
|
+
const favs = d.timeframeFavorites.filter((f) => typeof f === "string");
|
|
4248
|
+
if (favs.length > 0) out.timeframeFavorites = favs;
|
|
4249
|
+
}
|
|
4143
4250
|
const sync = sanitizeSync(d.sync);
|
|
4144
4251
|
if (sync) out.sync = sync;
|
|
4145
4252
|
const tracks = sanitizeTrackSizes(d.trackSizes);
|