@luxalgo/vela 0.5.4 → 0.6.1
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-DgS2UyMc.d.cts → DataProvider-BNKtYU5V.d.cts} +22 -3
- package/dist/{DataProvider-Cqc_BaJ9.d.ts → DataProvider-gJjN_0eh.d.ts} +22 -3
- package/dist/{chunk-D6WM5IUE.js → chunk-4MQEG67Z.js} +77 -18
- package/dist/{chunk-JBQUY2RI.js → chunk-6LZJRO2S.js} +48 -18
- package/dist/{contributions-DuIxJWeH.d.ts → contributions-D64UA74H.d.cts} +9 -2
- package/dist/{contributions-4Nh1jbvb.d.cts → contributions-lPAgo9Cu.d.ts} +9 -2
- package/dist/{history-FsmvCIo4.d.ts → history-CVoFDJ2D.d.ts} +21 -3
- package/dist/{history-HofqYEh2.d.cts → history-fSMXVLt1.d.cts} +21 -3
- package/dist/index.cjs +77 -18
- package/dist/index.d.cts +14 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.js +1 -1
- package/dist/{options-BlQ00Fju.d.cts → options-BqGeFHtp.d.cts} +15 -1
- package/dist/{options-BlQ00Fju.d.ts → options-BqGeFHtp.d.ts} +15 -1
- package/dist/{plugin-DJR6z3e0.d.cts → plugin-D5ni4WEJ.d.cts} +3 -3
- package/dist/{plugin-N_EaXN4-.d.ts → plugin-o5HJH46Z.d.ts} +3 -3
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- 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.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/vela.global.js +77 -18
- package/dist/vela.global.min.js +29 -29
- package/dist/widget.cjs +175 -40
- package/dist/widget.d.cts +23 -15
- package/dist/widget.d.ts +23 -15
- package/dist/widget.js +56 -10
- package/dist/workspace.cjs +172 -36
- package/dist/workspace.d.cts +20 -5
- package/dist/workspace.d.ts +20 -5
- package/dist/workspace.js +52 -5
- package/package.json +1 -1
package/dist/widget.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Glider, WidgetHistory, localStorageAdapter, decodeState, prefixedSymbol, SymbolPicker, IndicatorPicker, TimeframeQuick, Topbar, PanelDock, ObjectTree, DataWindow, ChartContextMenu, Toast, Watermark, Statusline, Bottombar, MobileBar, DrawingPill, LayoutModeController, ZOOM_IN, ZOOM_OUT, PAN_FAST, ShortcutsHelp, TimeframeDrawer, RANGE_PRESETS, DrawingsDrawer, MoreDrawer, priceStyleIcon, priceStyleLabel, TimezoneDrawer, PriceScaleDrawer, indicatorLedger, sanitizeState, legacyWidgetState, parsePersisted, statuslineInkOf, toolShortcutHints, resolveIndicators, encodeState } from './chunk-
|
|
2
|
-
export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, legacyWidgetState, loadPersisted, localStorageAdapter, parsePersisted, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, savePersisted, timeframeLabel, timeframeMs } from './chunk-
|
|
3
|
-
import { registerBuiltinChartTypes, resolveTheme, parseSymbol, buildToolbar, priceStyleIds, Vela, normalizeTimezone } from './chunk-
|
|
4
|
-
export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-
|
|
1
|
+
import { Glider, WidgetHistory, localStorageAdapter, decodeState, prefixedSymbol, normalizeSession, SymbolPicker, IndicatorPicker, TimeframeQuick, Topbar, PanelDock, ObjectTree, DataWindow, ChartContextMenu, Toast, Watermark, Statusline, Bottombar, MobileBar, DrawingPill, LayoutModeController, ZOOM_IN, ZOOM_OUT, PAN_FAST, ShortcutsHelp, TimeframeDrawer, RANGE_PRESETS, DrawingsDrawer, MoreDrawer, priceStyleIcon, priceStyleLabel, TimezoneDrawer, PriceScaleDrawer, indicatorLedger, sanitizeState, legacyWidgetState, parsePersisted, statuslineInkOf, toolShortcutHints, resolveIndicators, encodeState } from './chunk-6LZJRO2S.js';
|
|
2
|
+
export { Bottombar, ChartContextMenu, DataWindow, IndicatorPicker, ObjectTree, PanelDock, RANGE_PRESETS, ShortcutsHelp, Statusline, SymbolPicker, TimeframeQuick, Topbar, Watermark, dataWindowSections, decimalsFor, decodeState, encodeState, filterSymbols, fmtChange, fmtPrice, legacyWidgetState, loadPersisted, localStorageAdapter, parsePersisted, parseTimeframe, priceStyleLabel, resolveIndicators, sanitizeState, savePersisted, timeframeLabel, timeframeMs } from './chunk-6LZJRO2S.js';
|
|
3
|
+
import { registerBuiltinChartTypes, resolveTheme, parseSymbol, buildToolbar, priceStyleIds, Vela, normalizeTimezone } from './chunk-4MQEG67Z.js';
|
|
4
|
+
export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-4MQEG67Z.js';
|
|
5
5
|
import { widgetActions, widgetAttachments, legendActionsProviderFor, resolveEngines } from './chunk-ZNL2X6U2.js';
|
|
6
6
|
export { DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, SidePanel, clampPanelWidth, registerSidePanel, registerWidgetAction, registerWidgetAttachment, sidePanels, unregisterSidePanel, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments } from './chunk-ZNL2X6U2.js';
|
|
7
7
|
import { KeymapManager, Menu, isEditableTarget } from './chunk-QWIEKZRE.js';
|
|
@@ -14,7 +14,8 @@ var PARAMS = [
|
|
|
14
14
|
["timeframe", "interval"],
|
|
15
15
|
["priceStyle", "style"],
|
|
16
16
|
["timezone", "tz"],
|
|
17
|
-
["bars", "bars"]
|
|
17
|
+
["bars", "bars"],
|
|
18
|
+
["session", "session"]
|
|
18
19
|
];
|
|
19
20
|
function readUrlState(search) {
|
|
20
21
|
const out = {};
|
|
@@ -146,6 +147,7 @@ var VelaWidget = class {
|
|
|
146
147
|
this.priceStyle = fromUrl.priceStyle ?? bootCell?.priceStyle ?? opts.priceStyle ?? "candles";
|
|
147
148
|
this.timezone = fromUrl.timezone ?? boot?.timezone ?? opts.timezone ?? "Etc/UTC";
|
|
148
149
|
this.bars = Number(fromUrl.bars ?? bootCell?.bars ?? opts.bars ?? 1e3);
|
|
150
|
+
this.session = normalizeSession(fromUrl.session ?? bootCell?.session ?? opts.session);
|
|
149
151
|
this.watermarkOn = bootCell?.watermark !== void 0 ? bootCell.watermark : opts.watermark !== false;
|
|
150
152
|
this.indicatorTitlesOn = bootCell?.indicatorTitles ?? true;
|
|
151
153
|
this.indicatorValuesOn = bootCell?.indicatorValues ?? true;
|
|
@@ -252,6 +254,7 @@ var VelaWidget = class {
|
|
|
252
254
|
timezone: this.timezone,
|
|
253
255
|
onRange: (preset) => this.applyRange(preset),
|
|
254
256
|
onTimezone: (zone) => this.setTimezone(zone),
|
|
257
|
+
onSession: (session) => this.setSession(session),
|
|
255
258
|
onSettingsClick: () => this.inner?.renderer.openSettings()
|
|
256
259
|
}) : null;
|
|
257
260
|
this.mobileBar = opts.bottombar !== false ? new MobileBar(this.root, {
|
|
@@ -481,13 +484,15 @@ var VelaWidget = class {
|
|
|
481
484
|
this.statusline?.setMeta(tf, this.providerLabel());
|
|
482
485
|
}
|
|
483
486
|
/**
|
|
484
|
-
* The
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
+
* The venue label to show: the LISTING prefix the resolved symbol's data declares
|
|
488
|
+
* (`NASDAQ` for AAPL), else the provider that RESOLVED the symbol — never the one
|
|
489
|
+
* configured or typed. They differ whenever the `provider` option is just a default,
|
|
490
|
+
* the prefix was a listing venue, or the spelling isn't registered — the status line
|
|
491
|
+
* must not claim a venue that served nothing.
|
|
487
492
|
*/
|
|
488
493
|
providerLabel() {
|
|
489
|
-
const
|
|
490
|
-
return
|
|
494
|
+
const display = this.inner?.data.displayPrefix(this.symbol);
|
|
495
|
+
return display ?? parseSymbol(this.symbol).provider ?? "";
|
|
491
496
|
}
|
|
492
497
|
setTimeframe(tf) {
|
|
493
498
|
if (tf === this.timeframe || this.destroyed) return;
|
|
@@ -498,6 +503,30 @@ var VelaWidget = class {
|
|
|
498
503
|
this.markStateDirty();
|
|
499
504
|
void this.inner?.setMarket({ timeframe: tf, bars: this.bars });
|
|
500
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Switch the shown trading session (RTH/ETH) in place — a full reload, like a
|
|
508
|
+
* timeframe change (the two sessions are different bar series). No-op on a chart
|
|
509
|
+
* already showing that session; meaningless on continuous markets (the toggle is
|
|
510
|
+
* disabled there, but the API tolerates the call — the provider ignores the flag).
|
|
511
|
+
*/
|
|
512
|
+
setSession(session) {
|
|
513
|
+
if (session === this.session || session === "regular" && this.session === void 0 || this.destroyed) return;
|
|
514
|
+
this.session = session;
|
|
515
|
+
this.refreshSessionToggle();
|
|
516
|
+
this.markStateDirty();
|
|
517
|
+
void this.inner?.setMarket({ session });
|
|
518
|
+
}
|
|
519
|
+
/** Re-derive the RTH/ETH toggle's posture from the ACTIVE symbol's metadata: enabled
|
|
520
|
+
* iff the resolved symbol declares a real session vocabulary (not `24x7`). */
|
|
521
|
+
refreshSessionToggle() {
|
|
522
|
+
const chart = this.inner;
|
|
523
|
+
if (!chart || !this.bottombar) return;
|
|
524
|
+
void chart.data.symbolInfo(this.symbol).then((si) => {
|
|
525
|
+
if (this.destroyed || this.inner !== chart) return;
|
|
526
|
+
const enabled = typeof si?.session === "string" && si.session !== "" && si.session !== "24x7";
|
|
527
|
+
this.bottombar?.setSession({ session: this.session ?? "regular", enabled });
|
|
528
|
+
});
|
|
529
|
+
}
|
|
501
530
|
/** Star/unstar a timeframe — the topbar chips and dropdown stars follow, and the
|
|
502
531
|
* set persists with the rest of the shell state. */
|
|
503
532
|
setTimeframeFavorite(tf, on) {
|
|
@@ -544,6 +573,8 @@ var VelaWidget = class {
|
|
|
544
573
|
cell.timeframe = live?.timeframe ?? this.timeframe;
|
|
545
574
|
cell.priceStyle = this.priceStyle;
|
|
546
575
|
if (this.bars > 0) cell.bars = this.bars;
|
|
576
|
+
const session = normalizeSession(live?.session ?? this.session);
|
|
577
|
+
if (session === "extended") cell.session = session;
|
|
547
578
|
cell.watermark = this.watermarkOn;
|
|
548
579
|
cell.indicatorTitles = this.indicatorTitlesOn;
|
|
549
580
|
cell.indicatorValues = this.indicatorValuesOn;
|
|
@@ -610,9 +641,15 @@ var VelaWidget = class {
|
|
|
610
641
|
const symbol = fromUrl.symbol ?? prefixedSymbol(cell);
|
|
611
642
|
const timeframe = fromUrl.timeframe ?? cell.timeframe;
|
|
612
643
|
const bars = Number(fromUrl.bars ?? cell.bars ?? 0);
|
|
644
|
+
const session = normalizeSession(fromUrl.session ?? cell.session);
|
|
613
645
|
const next = {};
|
|
614
646
|
if (symbol && symbol !== this.symbol) next.symbol = symbol;
|
|
615
647
|
if (timeframe && timeframe !== this.timeframe) next.timeframe = timeframe;
|
|
648
|
+
if (session && session !== (this.session ?? "regular")) {
|
|
649
|
+
this.session = session;
|
|
650
|
+
next.session = session;
|
|
651
|
+
this.refreshSessionToggle();
|
|
652
|
+
}
|
|
616
653
|
if (bars > 0 && bars !== this.bars) {
|
|
617
654
|
this.bars = bars;
|
|
618
655
|
next.bars = Math.max(bars, this.rangeBars);
|
|
@@ -839,6 +876,7 @@ var VelaWidget = class {
|
|
|
839
876
|
symbol: this.symbol,
|
|
840
877
|
timeframe: this.timeframe,
|
|
841
878
|
bars: Math.max(this.bars, this.rangeBars),
|
|
879
|
+
session: this.session,
|
|
842
880
|
volume: this.ledgerVolume ?? chartOpts.volume,
|
|
843
881
|
// A pending range chip frames the FIRST paint (no preview flash, no re-frame).
|
|
844
882
|
...this.pendingRange ? { visibleRange: this.pendingRange.preset } : {}
|
|
@@ -847,6 +885,11 @@ var VelaWidget = class {
|
|
|
847
885
|
for (const [language, make] of Object.entries(resolveEngines(engines))) chart.registerEngine(language, make());
|
|
848
886
|
this.inner = chart;
|
|
849
887
|
this.symbolPicker.setSource(() => chart.data.symbols());
|
|
888
|
+
void chart.data.ready().then(() => {
|
|
889
|
+
if (this.inner !== chart) return;
|
|
890
|
+
this.statusline?.setMeta(this.timeframe, this.providerLabel());
|
|
891
|
+
this.refreshSessionToggle();
|
|
892
|
+
});
|
|
850
893
|
if (this.layoutCtl !== void 0) chart.renderer.setLayoutMode(this.layoutCtl.current);
|
|
851
894
|
this.objectTree.setSymbol(this.symbol);
|
|
852
895
|
chart.renderer.setLegendActions(legendActionsProviderFor(chart, () => this.context()));
|
|
@@ -922,6 +965,7 @@ var VelaWidget = class {
|
|
|
922
965
|
this.statusline?.setSymbol(symbol);
|
|
923
966
|
this.objectTree.setSymbol(symbol);
|
|
924
967
|
this.watermark?.update(symbol, this.timeframe);
|
|
968
|
+
this.refreshSessionToggle();
|
|
925
969
|
}
|
|
926
970
|
if (timeframe !== this.timeframe) this.syncTimeframeChrome(timeframe);
|
|
927
971
|
});
|
|
@@ -1219,6 +1263,8 @@ var VelaWidget = class {
|
|
|
1219
1263
|
priceStyle: this.priceStyle,
|
|
1220
1264
|
timezone: this.timezone,
|
|
1221
1265
|
bars: String(this.bars),
|
|
1266
|
+
// Empty clears the param — the default (regular) keeps links lean.
|
|
1267
|
+
session: this.session === "extended" ? "extended" : "",
|
|
1222
1268
|
watermark: this.watermarkOn ? "1" : "0",
|
|
1223
1269
|
favorites: this.favs.join(",")
|
|
1224
1270
|
});
|
package/dist/workspace.cjs
CHANGED
|
@@ -152,7 +152,7 @@ var ProviderRegistry = class {
|
|
|
152
152
|
/** Register (or replace) a provider; returns a promise that settles when its index is built. */
|
|
153
153
|
register(rawName, provider) {
|
|
154
154
|
const name = normName(rawName);
|
|
155
|
-
const entry = { name, provider, index: null, descriptors: [], settled: false, settle: Promise.resolve() };
|
|
155
|
+
const entry = { name, provider, index: null, prefixIndex: null, byTicker: null, descriptors: [], settled: false, settle: Promise.resolve() };
|
|
156
156
|
this.entries.set(name, entry);
|
|
157
157
|
entry.settle = this.buildIndex(entry);
|
|
158
158
|
queueMicrotask(() => this.notify(false));
|
|
@@ -165,6 +165,9 @@ var ProviderRegistry = class {
|
|
|
165
165
|
const symbols = await entry.provider.listSymbols();
|
|
166
166
|
entry.descriptors = symbols;
|
|
167
167
|
entry.index = new Set(symbols.map((s) => normTicker(s.ticker)));
|
|
168
|
+
entry.byTicker = new Map(symbols.map((s) => [normTicker(s.ticker), s]));
|
|
169
|
+
entry.prefixIndex = /* @__PURE__ */ new Map();
|
|
170
|
+
for (const s of symbols) if (s.prefix) entry.prefixIndex.set(`${normName(s.prefix)}:${normTicker(s.ticker)}`, s);
|
|
168
171
|
}
|
|
169
172
|
} catch {
|
|
170
173
|
entry.index = null;
|
|
@@ -200,14 +203,25 @@ var ProviderRegistry = class {
|
|
|
200
203
|
}
|
|
201
204
|
/**
|
|
202
205
|
* Resolve a symbol to `{ provider, ticker }`, or null when nothing can serve it
|
|
203
|
-
* yet. Explicit prefix → that provider (must be registered)
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
206
|
+
* yet. Explicit prefix → that provider (must be registered), else a provider whose
|
|
207
|
+
* index declares that LISTING prefix for that ticker (`NASDAQ:AAPL` routes to the
|
|
208
|
+
* provider serving Nasdaq-listed AAPL — TradingView-strict, so `NYSE:AAPL` resolves
|
|
209
|
+
* to nothing rather than auto-correcting). Bare → first indexed provider
|
|
210
|
+
* (registration order, `opts.default` first) that contains the ticker;
|
|
211
|
+
* `opts.lenient` additionally allows a sole registered provider before its index
|
|
212
|
+
* exists.
|
|
207
213
|
*/
|
|
208
214
|
resolve(raw, opts = {}) {
|
|
209
215
|
const { provider, ticker } = this.parse(raw);
|
|
210
|
-
if (provider)
|
|
216
|
+
if (provider) {
|
|
217
|
+
if (this.entries.has(provider)) return { provider, ticker };
|
|
218
|
+
const key = `${provider}:${normTicker(ticker)}`;
|
|
219
|
+
for (const name of this.candidateOrder(opts.default)) {
|
|
220
|
+
const d = this.entries.get(name).prefixIndex?.get(key);
|
|
221
|
+
if (d) return { provider: name, ticker: d.ticker };
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
211
225
|
const norm = normTicker(ticker);
|
|
212
226
|
for (const name of this.candidateOrder(opts.default)) {
|
|
213
227
|
if (this.entries.get(name).index?.has(norm)) return { provider: name, ticker };
|
|
@@ -273,6 +287,22 @@ var ProviderRegistry = class {
|
|
|
273
287
|
}
|
|
274
288
|
return names;
|
|
275
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* The DISPLAY prefix for a resolved symbol — the descriptor's LISTING prefix when the
|
|
292
|
+
* data declares one, else the provider name. This is the single seam every label
|
|
293
|
+
* (legend chip, committed/persisted `PREFIX:TICKER` strings) derives from, which is
|
|
294
|
+
* what makes a non-canonical spelling impossible to display: the form always comes
|
|
295
|
+
* from the data, never from what was typed.
|
|
296
|
+
*/
|
|
297
|
+
displayPrefixOf(resolved) {
|
|
298
|
+
const d = this.entries.get(resolved.provider)?.byTicker?.get(normTicker(resolved.ticker));
|
|
299
|
+
return d?.prefix ?? resolved.provider;
|
|
300
|
+
}
|
|
301
|
+
/** The canonical `PREFIX:TICKER` string for a resolved symbol (descriptor spellings). */
|
|
302
|
+
canonicalSymbol(resolved) {
|
|
303
|
+
const d = this.entries.get(resolved.provider)?.byTicker?.get(normTicker(resolved.ticker));
|
|
304
|
+
return `${d?.prefix ?? resolved.provider}:${d?.ticker ?? resolved.ticker}`;
|
|
305
|
+
}
|
|
276
306
|
/** Indexed symbols for one provider (or all, concatenated) — for autocomplete. */
|
|
277
307
|
symbolsOf(rawName) {
|
|
278
308
|
if (rawName != null) return this.entries.get(normName(rawName))?.descriptors ?? [];
|
|
@@ -296,8 +326,8 @@ var ProviderRegistry = class {
|
|
|
296
326
|
};
|
|
297
327
|
|
|
298
328
|
// src/data/BarStore.ts
|
|
299
|
-
function seriesKey(provider, symbol, timeframe) {
|
|
300
|
-
return `${provider}|${symbol}|${timeframe}`;
|
|
329
|
+
function seriesKey(provider, symbol, timeframe, session) {
|
|
330
|
+
return `${provider}|${symbol}|${timeframe}${session && session !== "regular" ? `|${session}` : ""}`;
|
|
301
331
|
}
|
|
302
332
|
function symbolOf(key) {
|
|
303
333
|
return key.split("|")[1] ?? "";
|
|
@@ -395,9 +425,9 @@ function timeframeToMs(timeframe) {
|
|
|
395
425
|
}
|
|
396
426
|
|
|
397
427
|
// src/data/CachingDataFeed.ts
|
|
398
|
-
function cacheKey(symbol, timeframe) {
|
|
428
|
+
function cacheKey(symbol, timeframe, session) {
|
|
399
429
|
const { provider, ticker } = parseSymbol(symbol);
|
|
400
|
-
return seriesKey(provider ?? "", ticker, timeframe);
|
|
430
|
+
return seriesKey(provider ?? "", ticker, timeframe, session);
|
|
401
431
|
}
|
|
402
432
|
var PAGE_BARS = 1e4;
|
|
403
433
|
var SINGLE_FETCH_BARS = PAGE_BARS + 1;
|
|
@@ -409,7 +439,7 @@ var CachingDataFeed = class {
|
|
|
409
439
|
async load(cfg) {
|
|
410
440
|
if (cfg.data && cfg.data.length > 0) return this.inner.load(cfg);
|
|
411
441
|
const symbol = cfg.symbol ?? "TEST";
|
|
412
|
-
const key = cacheKey(symbol, cfg.timeframe ?? "60");
|
|
442
|
+
const key = cacheKey(symbol, cfg.timeframe ?? "60", cfg.session);
|
|
413
443
|
this.store.retainSymbol(symbol);
|
|
414
444
|
const cached = this.store.get(key);
|
|
415
445
|
const n = cfg.bars ?? 500;
|
|
@@ -438,7 +468,7 @@ var CachingDataFeed = class {
|
|
|
438
468
|
*/
|
|
439
469
|
async loadRange(cfg, range) {
|
|
440
470
|
if (!this.inner.loadRange) return this.inner.load(cfg);
|
|
441
|
-
const key = cacheKey(cfg.symbol ?? "TEST", cfg.timeframe ?? "60");
|
|
471
|
+
const key = cacheKey(cfg.symbol ?? "TEST", cfg.timeframe ?? "60", cfg.session);
|
|
442
472
|
let cached = this.store.get(key);
|
|
443
473
|
let newest = cached?.[cached.length - 1];
|
|
444
474
|
if (range.to != null && newest && range.to < newest.time) {
|
|
@@ -596,6 +626,20 @@ var MultiProviderFeed = class {
|
|
|
596
626
|
resolveSymbol(raw) {
|
|
597
627
|
return this.registry.resolve(raw, { default: this.primaryProvider });
|
|
598
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* The DISPLAY prefix for `raw` — the descriptor's LISTING prefix when the data
|
|
631
|
+
* declares one (`NASDAQ` for AAPL), else the resolved provider name. Null while
|
|
632
|
+
* nothing resolves the symbol.
|
|
633
|
+
*/
|
|
634
|
+
displayPrefix(raw) {
|
|
635
|
+
const resolved = this.resolveSymbol(raw);
|
|
636
|
+
return resolved ? this.registry.displayPrefixOf(resolved) : null;
|
|
637
|
+
}
|
|
638
|
+
/** The canonical `PREFIX:TICKER` form of `raw`, or null while unresolvable. */
|
|
639
|
+
canonicalSymbol(raw) {
|
|
640
|
+
const resolved = this.resolveSymbol(raw);
|
|
641
|
+
return resolved ? this.registry.canonicalSymbol(resolved) : null;
|
|
642
|
+
}
|
|
599
643
|
/** The registered provider INSTANCE under `name` (undefined if unknown). */
|
|
600
644
|
providerInstance(name) {
|
|
601
645
|
return this.registry.get(name);
|
|
@@ -731,13 +775,13 @@ var RegistryFetchFeed = class {
|
|
|
731
775
|
const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
|
|
732
776
|
const provider = this.registry.get(name ?? "");
|
|
733
777
|
if (!provider) return Promise.resolve([]);
|
|
734
|
-
return safeBars(provider, ticker, cfg.timeframe ?? "60", { limit: cfg.bars ?? 500 });
|
|
778
|
+
return safeBars(provider, ticker, cfg.timeframe ?? "60", { limit: cfg.bars ?? 500, session: cfg.session });
|
|
735
779
|
}
|
|
736
780
|
loadRange(cfg, range) {
|
|
737
781
|
const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
|
|
738
782
|
const provider = this.registry.get(name ?? "");
|
|
739
783
|
if (!provider) return Promise.resolve([]);
|
|
740
|
-
return safeBars(provider, ticker, cfg.timeframe ?? "60", range);
|
|
784
|
+
return safeBars(provider, ticker, cfg.timeframe ?? "60", { ...range, session: cfg.session });
|
|
741
785
|
}
|
|
742
786
|
subscribe(cfg, onBar) {
|
|
743
787
|
const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
|
|
@@ -745,13 +789,13 @@ var RegistryFetchFeed = class {
|
|
|
745
789
|
if (!provider) return () => {
|
|
746
790
|
};
|
|
747
791
|
const tf = cfg.timeframe ?? "60";
|
|
748
|
-
if (provider.subscribe) return provider.subscribe(ticker, tf, onBar);
|
|
792
|
+
if (provider.subscribe) return provider.subscribe(ticker, tf, onBar, cfg.session ? { session: cfg.session } : void 0);
|
|
749
793
|
let stopped = false;
|
|
750
794
|
let timer = null;
|
|
751
795
|
const poll = async () => {
|
|
752
796
|
if (stopped) return;
|
|
753
797
|
try {
|
|
754
|
-
const bars = await provider.getBars(ticker, tf, { limit: 2 });
|
|
798
|
+
const bars = await provider.getBars(ticker, tf, { limit: 2, session: cfg.session });
|
|
755
799
|
if (stopped) return;
|
|
756
800
|
for (const b of bars) onBar(b);
|
|
757
801
|
} catch {
|
|
@@ -4730,10 +4774,12 @@ var CSS4 = `
|
|
|
4730
4774
|
color: var(--vela-fg-muted);
|
|
4731
4775
|
font-size: 11px;
|
|
4732
4776
|
font-weight: 600;
|
|
4733
|
-
cursor:
|
|
4734
|
-
opacity: 0.55;
|
|
4777
|
+
cursor: pointer;
|
|
4735
4778
|
}
|
|
4736
|
-
.vela-bb-session-btn
|
|
4779
|
+
.vela-bb-session-btn:disabled { cursor: not-allowed; opacity: 0.55; }
|
|
4780
|
+
.vela-bb-session-btn:not(:disabled):hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
4781
|
+
.vela-bb-session-btn.is-active { color: var(--vela-fg); background: var(--vela-surface-elev); }
|
|
4782
|
+
.vela-bb-session-btn.is-active:disabled { opacity: 0.8; }
|
|
4737
4783
|
.vela-bb-settings {
|
|
4738
4784
|
all: unset;
|
|
4739
4785
|
display: inline-flex;
|
|
@@ -4753,6 +4799,8 @@ var Bottombar = class {
|
|
|
4753
4799
|
constructor(host, opts) {
|
|
4754
4800
|
this.settingsTip = null;
|
|
4755
4801
|
this.rangeButtons = /* @__PURE__ */ new Map();
|
|
4802
|
+
this.sessionButtons = /* @__PURE__ */ new Map();
|
|
4803
|
+
this.sessionEl = null;
|
|
4756
4804
|
this.timer = null;
|
|
4757
4805
|
this.timezone = opts.timezone;
|
|
4758
4806
|
const doc = host.ownerDocument;
|
|
@@ -4782,12 +4830,19 @@ var Bottombar = class {
|
|
|
4782
4830
|
this.tzButton.append(this.clockEl, this.tzLabelEl);
|
|
4783
4831
|
const session = doc.createElement("span");
|
|
4784
4832
|
session.className = "vela-bb-session";
|
|
4833
|
+
this.sessionEl = session;
|
|
4785
4834
|
session.title = "Session \u2014 stocks & ETFs only";
|
|
4786
|
-
for (const [
|
|
4835
|
+
for (const [key, label] of [["regular", "RTH"], ["extended", "ETH"]]) {
|
|
4787
4836
|
const b = doc.createElement("button");
|
|
4788
|
-
b.className = "vela-bb-session-btn" + (
|
|
4837
|
+
b.className = "vela-bb-session-btn" + (key === "regular" ? " is-active" : "");
|
|
4789
4838
|
b.textContent = label;
|
|
4790
4839
|
b.disabled = true;
|
|
4840
|
+
b.addEventListener("click", () => {
|
|
4841
|
+
if (b.disabled) return;
|
|
4842
|
+
this.setSession({ session: key, enabled: true });
|
|
4843
|
+
opts.onSession?.(key);
|
|
4844
|
+
});
|
|
4845
|
+
this.sessionButtons.set(key, b);
|
|
4791
4846
|
session.appendChild(b);
|
|
4792
4847
|
}
|
|
4793
4848
|
const settingsBtn = doc.createElement("button");
|
|
@@ -4825,6 +4880,19 @@ var Bottombar = class {
|
|
|
4825
4880
|
else delete b.dataset.active;
|
|
4826
4881
|
}
|
|
4827
4882
|
}
|
|
4883
|
+
/**
|
|
4884
|
+
* Reflect the ACTIVE chart's session posture. `enabled: false` (a continuous
|
|
4885
|
+
* market, or metadata not landed yet) shows the disabled RTH-active stub — the
|
|
4886
|
+
* pre-session-model look, byte-for-byte. Enabled, the chips become clickable and
|
|
4887
|
+
* the active one tracks the chart's current session.
|
|
4888
|
+
*/
|
|
4889
|
+
setSession(state) {
|
|
4890
|
+
if (this.sessionEl) this.sessionEl.title = state.enabled ? "Session \u2014 regular (RTH) vs extended (ETH) hours" : "Session \u2014 stocks & ETFs only";
|
|
4891
|
+
for (const [key, b] of this.sessionButtons) {
|
|
4892
|
+
b.disabled = !state.enabled;
|
|
4893
|
+
b.classList.toggle("is-active", state.enabled ? key === state.session : key === "regular");
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4828
4896
|
destroy() {
|
|
4829
4897
|
if (this.timer !== null) clearInterval(this.timer);
|
|
4830
4898
|
this.tzMenu.destroy();
|
|
@@ -12423,10 +12491,11 @@ function parseQuery(raw, venues) {
|
|
|
12423
12491
|
function onlyOne(matches) {
|
|
12424
12492
|
return matches.length === 1 ? matches[0] : null;
|
|
12425
12493
|
}
|
|
12494
|
+
var venueOf = (s) => s.prefix ?? s.provider;
|
|
12426
12495
|
function filterSymbols(list, query, limit = 100) {
|
|
12427
|
-
const venues = [...new Set(list.
|
|
12496
|
+
const venues = [...new Set(list.flatMap((s) => [venueOf(s)?.toLowerCase(), s.provider?.toLowerCase()]).filter((p) => !!p))];
|
|
12428
12497
|
const { scope, term } = parseQuery(query, venues);
|
|
12429
|
-
const pool = scope ? list.filter((s) => s.provider?.toLowerCase() === scope) : list;
|
|
12498
|
+
const pool = scope ? list.filter((s) => venueOf(s)?.toLowerCase() === scope || s.provider?.toLowerCase() === scope) : list;
|
|
12430
12499
|
const q = term.toUpperCase();
|
|
12431
12500
|
if (!q) {
|
|
12432
12501
|
if (scope) return [...pool].sort((a, b) => a.ticker.localeCompare(b.ticker)).slice(0, limit);
|
|
@@ -12445,7 +12514,7 @@ function filterSymbols(list, query, limit = 100) {
|
|
|
12445
12514
|
if (t.startsWith(q)) prefix.push(s);
|
|
12446
12515
|
else if (t.includes(q)) substr.push(s);
|
|
12447
12516
|
else if ((s.description ?? "").toUpperCase().includes(q)) desc.push(s);
|
|
12448
|
-
else if (!scope && s.provider?.toLowerCase().includes(qLower)) venue.push(s);
|
|
12517
|
+
else if (!scope && (venueOf(s)?.toLowerCase().includes(qLower) || s.provider?.toLowerCase().includes(qLower))) venue.push(s);
|
|
12449
12518
|
if (prefix.length >= limit) break;
|
|
12450
12519
|
}
|
|
12451
12520
|
return [...prefix, ...substr, ...desc, ...venue].slice(0, limit);
|
|
@@ -12597,14 +12666,14 @@ var SymbolPicker = class {
|
|
|
12597
12666
|
else if (e.key === "ArrowUp") this.moveHighlight(-1);
|
|
12598
12667
|
else if (e.key === "Enter") {
|
|
12599
12668
|
const pick = this.rows[this.highlighted];
|
|
12600
|
-
if (pick) this.select(pick.ticker, pick.provider, opts.onSelect);
|
|
12669
|
+
if (pick) this.select(pick.ticker, pick.prefix ?? pick.provider, opts.onSelect);
|
|
12601
12670
|
return;
|
|
12602
12671
|
} else return;
|
|
12603
12672
|
e.preventDefault();
|
|
12604
12673
|
});
|
|
12605
12674
|
this.list.addEventListener("click", (e) => {
|
|
12606
12675
|
const row = e.target.closest(".vela-sp-row");
|
|
12607
|
-
if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.
|
|
12676
|
+
if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.venue, opts.onSelect);
|
|
12608
12677
|
});
|
|
12609
12678
|
}
|
|
12610
12679
|
/** Wire where symbols come from (re-called on every widget rebuild). */
|
|
@@ -12621,9 +12690,9 @@ var SymbolPicker = class {
|
|
|
12621
12690
|
destroy() {
|
|
12622
12691
|
this.dialog.destroy();
|
|
12623
12692
|
}
|
|
12624
|
-
select(ticker,
|
|
12693
|
+
select(ticker, venue, onSelect) {
|
|
12625
12694
|
this.close();
|
|
12626
|
-
onSelect(
|
|
12695
|
+
onSelect(venue ? `${venue}:${ticker}` : ticker);
|
|
12627
12696
|
}
|
|
12628
12697
|
moveHighlight(delta) {
|
|
12629
12698
|
if (!this.rows.length) return;
|
|
@@ -12656,7 +12725,8 @@ var SymbolPicker = class {
|
|
|
12656
12725
|
const row = doc.createElement("div");
|
|
12657
12726
|
row.className = "vela-sp-row";
|
|
12658
12727
|
row.dataset.ticker = s.ticker;
|
|
12659
|
-
|
|
12728
|
+
const venue = s.prefix ?? s.provider;
|
|
12729
|
+
if (venue) row.dataset.venue = venue;
|
|
12660
12730
|
const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
|
|
12661
12731
|
const main = doc.createElement("span");
|
|
12662
12732
|
main.className = "vela-sp-main";
|
|
@@ -12668,11 +12738,11 @@ var SymbolPicker = class {
|
|
|
12668
12738
|
d.textContent = s.description ?? (s.type ?? "");
|
|
12669
12739
|
main.append(t, d);
|
|
12670
12740
|
row.append(av, main);
|
|
12671
|
-
if (
|
|
12741
|
+
if (venue) {
|
|
12672
12742
|
const badge = doc.createElement("span");
|
|
12673
12743
|
badge.className = "vela-sp-badge";
|
|
12674
|
-
badge.dataset.p = s.provider;
|
|
12675
|
-
badge.textContent =
|
|
12744
|
+
badge.dataset.p = s.provider ?? venue;
|
|
12745
|
+
badge.textContent = venue;
|
|
12676
12746
|
row.appendChild(badge);
|
|
12677
12747
|
}
|
|
12678
12748
|
this.list.appendChild(row);
|
|
@@ -16212,6 +16282,7 @@ function sanitizeCell(raw) {
|
|
|
16212
16282
|
if (typeof c.timeframe === "string") out.timeframe = c.timeframe;
|
|
16213
16283
|
if (typeof c.priceStyle === "string") out.priceStyle = c.priceStyle;
|
|
16214
16284
|
if (typeof c.bars === "number" && Number.isFinite(c.bars) && c.bars > 0) out.bars = c.bars;
|
|
16285
|
+
if (c.session === "regular" || c.session === "extended") out.session = c.session;
|
|
16215
16286
|
if (typeof c.watermark === "boolean") out.watermark = c.watermark;
|
|
16216
16287
|
if (typeof c.indicatorTitles === "boolean") out.indicatorTitles = c.indicatorTitles;
|
|
16217
16288
|
if (typeof c.indicatorValues === "boolean") out.indicatorValues = c.indicatorValues;
|
|
@@ -16845,7 +16916,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
16845
16916
|
* an in-flight `setMarket` immediately (the config mutates before the load). */
|
|
16846
16917
|
marketSnapshot() {
|
|
16847
16918
|
const m = this.config.market;
|
|
16848
|
-
return { symbol: m.symbol, provider: parseSymbol(m.symbol ?? "").provider ?? void 0, timeframe: m.timeframe, bars: m.bars, offline: m.data !== void 0 };
|
|
16919
|
+
return { symbol: m.symbol, provider: parseSymbol(m.symbol ?? "").provider ?? void 0, timeframe: m.timeframe, bars: m.bars, session: m.session, offline: m.data !== void 0 };
|
|
16849
16920
|
}
|
|
16850
16921
|
/**
|
|
16851
16922
|
* Switch the chart's market IN PLACE — no destroy/recreate. The renderer stays
|
|
@@ -16864,7 +16935,9 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
16864
16935
|
*/
|
|
16865
16936
|
async setMarket(next) {
|
|
16866
16937
|
const m = this.config.market;
|
|
16867
|
-
const identityChanged = next.symbol !== void 0 && next.symbol !== m.symbol || next.timeframe !== void 0 && next.timeframe !== m.timeframe ||
|
|
16938
|
+
const identityChanged = next.symbol !== void 0 && next.symbol !== m.symbol || next.timeframe !== void 0 && next.timeframe !== m.timeframe || // A session switch changes WHICH bars exist (RTH vs ETH) — a full reload,
|
|
16939
|
+
// exactly like a timeframe change; the cache keys the sessions apart.
|
|
16940
|
+
next.session !== void 0 && next.session !== m.session || next.data !== void 0;
|
|
16868
16941
|
const depthChanged = next.bars !== void 0 && next.bars !== m.bars;
|
|
16869
16942
|
const depthOnly = depthChanged && !identityChanged && this.rawBars.length > 0 && !m.data?.length && ((next.bars ?? 0) <= this.rawBars.length || typeof this.feed.loadRange === "function");
|
|
16870
16943
|
if (!identityChanged && !depthChanged) {
|
|
@@ -16886,6 +16959,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
16886
16959
|
if (next.symbol !== void 0) m.symbol = next.symbol;
|
|
16887
16960
|
if (next.timeframe !== void 0) m.timeframe = next.timeframe;
|
|
16888
16961
|
if (next.bars !== void 0) m.bars = next.bars;
|
|
16962
|
+
if (next.session !== void 0) m.session = next.session;
|
|
16889
16963
|
if (next.data !== void 0) m.data = next.data;
|
|
16890
16964
|
else if (next.symbol !== void 0) delete m.data;
|
|
16891
16965
|
m.visibleRange = next.visibleRange;
|
|
@@ -18267,6 +18341,17 @@ var DataControl = class {
|
|
|
18267
18341
|
resolve(symbol) {
|
|
18268
18342
|
return this.registry?.resolveSymbol(symbol) ?? null;
|
|
18269
18343
|
}
|
|
18344
|
+
/**
|
|
18345
|
+
* The DISPLAY prefix for `symbol` — the listing venue its descriptor declares
|
|
18346
|
+
* (`NASDAQ` for AAPL) or the resolved provider name. Null while unresolvable.
|
|
18347
|
+
*/
|
|
18348
|
+
displayPrefix(symbol) {
|
|
18349
|
+
return this.registry?.displayPrefix(symbol) ?? null;
|
|
18350
|
+
}
|
|
18351
|
+
/** The canonical `PREFIX:TICKER` form of `symbol`, or null while unresolvable. */
|
|
18352
|
+
canonicalSymbol(symbol) {
|
|
18353
|
+
return this.registry?.canonicalSymbol(symbol) ?? null;
|
|
18354
|
+
}
|
|
18270
18355
|
/**
|
|
18271
18356
|
* The registered provider INSTANCE under `name` — the seam for EXTENDED provider
|
|
18272
18357
|
* surfaces: a provider may implement interfaces beyond the `DataProvider` port
|
|
@@ -33946,6 +34031,7 @@ var Vela = class {
|
|
|
33946
34031
|
symbol: options.symbol,
|
|
33947
34032
|
timeframe: options.timeframe,
|
|
33948
34033
|
bars: options.bars,
|
|
34034
|
+
session: options.session,
|
|
33949
34035
|
visibleRange: options.visibleRange,
|
|
33950
34036
|
data: options.data
|
|
33951
34037
|
},
|
|
@@ -34236,6 +34322,9 @@ function resolveElement(container) {
|
|
|
34236
34322
|
return element;
|
|
34237
34323
|
}
|
|
34238
34324
|
|
|
34325
|
+
// src/core/options.ts
|
|
34326
|
+
var normalizeSession = (v) => v === "regular" || v === "extended" ? v : void 0;
|
|
34327
|
+
|
|
34239
34328
|
// src/widget/format.ts
|
|
34240
34329
|
function decimalsFor(ref) {
|
|
34241
34330
|
const a = Math.abs(ref);
|
|
@@ -34914,6 +35003,7 @@ function seedDefaults(opts) {
|
|
|
34914
35003
|
timeframe: opts.timeframe,
|
|
34915
35004
|
bars: opts.bars,
|
|
34916
35005
|
priceStyle: opts.priceStyle,
|
|
35006
|
+
session: opts.session,
|
|
34917
35007
|
data: opts.data,
|
|
34918
35008
|
visibleRange: opts.visibleRange
|
|
34919
35009
|
};
|
|
@@ -34942,6 +35032,8 @@ var ChartCell = class {
|
|
|
34942
35032
|
this.lastCrossPrice = null;
|
|
34943
35033
|
/** The bottombar range chip this cell is framed on (null = none). */
|
|
34944
35034
|
this.activeRangeId = null;
|
|
35035
|
+
/** Latched verdict of {@link sessionAvailable} (async metadata, sticky per symbol). */
|
|
35036
|
+
this.sessionAvailableFlag = false;
|
|
34945
35037
|
this.manifest = [];
|
|
34946
35038
|
/** A restored ledger's manifest entry NAMES, waiting for the manifest to resolve
|
|
34947
35039
|
* (a pool/persisted cell can be built before the shared manifest has loaded). */
|
|
@@ -34961,7 +35053,7 @@ var ChartCell = class {
|
|
|
34961
35053
|
this.destroyed = false;
|
|
34962
35054
|
this.appTheme = deps.theme;
|
|
34963
35055
|
const symbol = prefixedSymbol(seed);
|
|
34964
|
-
this.state = { symbol, provider: parseSymbol(symbol ?? "").provider ?? void 0, timeframe: seed.timeframe, priceStyle: seed.priceStyle, bars: seed.bars };
|
|
35056
|
+
this.state = { symbol, provider: parseSymbol(symbol ?? "").provider ?? void 0, timeframe: seed.timeframe, priceStyle: seed.priceStyle, bars: seed.bars, session: normalizeSession(seed.session) };
|
|
34965
35057
|
const doc = gridHost.ownerDocument;
|
|
34966
35058
|
this.host = doc.createElement("div");
|
|
34967
35059
|
this.host.className = "vela-cell";
|
|
@@ -34978,6 +35070,7 @@ var ChartCell = class {
|
|
|
34978
35070
|
timeframe: seed.timeframe,
|
|
34979
35071
|
bars: seed.bars,
|
|
34980
35072
|
priceStyle: seed.priceStyle,
|
|
35073
|
+
session: normalizeSession(seed.session),
|
|
34981
35074
|
data: seed.data,
|
|
34982
35075
|
visibleRange: seed.visibleRange,
|
|
34983
35076
|
theme: deps.theme,
|
|
@@ -35036,6 +35129,10 @@ var ChartCell = class {
|
|
|
35036
35129
|
this.statusline = deps.statusline ? new Statusline(this.host, symbol ?? "") : null;
|
|
35037
35130
|
this.statusline?.setMeta(seed.timeframe ?? "60", this.state.provider ?? "");
|
|
35038
35131
|
this.statusline?.onChart(this.inner);
|
|
35132
|
+
void this.inner.data.ready().then(() => {
|
|
35133
|
+
if (this.inner && this.state.symbol) this.statusline?.setMeta(this.state.timeframe ?? "60", this.inner.data.displayPrefix(this.state.symbol) ?? this.state.provider ?? "");
|
|
35134
|
+
this.refreshSessionAvailable();
|
|
35135
|
+
});
|
|
35039
35136
|
this.syncStatuslineColors();
|
|
35040
35137
|
this.contextMenu = new ChartContextMenu(this.host, {
|
|
35041
35138
|
resetView: () => {
|
|
@@ -35156,14 +35253,48 @@ var ChartCell = class {
|
|
|
35156
35253
|
this.state.symbol = symbol2;
|
|
35157
35254
|
this.state.provider = parseSymbol(symbol2).provider ?? void 0;
|
|
35158
35255
|
this.state.timeframe = timeframe;
|
|
35256
|
+
this.state.session = normalizeSession(this.inner?.market.session);
|
|
35159
35257
|
this.watermark?.update(symbol2, timeframe);
|
|
35160
35258
|
this.statusline?.setSymbol(symbol2);
|
|
35161
|
-
this.statusline?.setMeta(timeframe, this.inner?.data.
|
|
35259
|
+
this.statusline?.setMeta(timeframe, this.inner?.data.displayPrefix(symbol2) ?? this.state.provider ?? "");
|
|
35162
35260
|
if (this.inner) this.statusline?.onChart(this.inner);
|
|
35163
35261
|
this.refreshNativeCatalog();
|
|
35262
|
+
this.refreshSessionAvailable();
|
|
35164
35263
|
this.deps.onMarketChanged(this.id);
|
|
35165
35264
|
});
|
|
35166
35265
|
}
|
|
35266
|
+
/**
|
|
35267
|
+
* Does this cell's market HAVE sessions (RTH/ETH meaningful)? Derived from the
|
|
35268
|
+
* symbol's own metadata (`syminfo.session !== '24x7'`), asynchronously — the
|
|
35269
|
+
* workspace re-projects the shared bottombar when the verdict lands or changes.
|
|
35270
|
+
*/
|
|
35271
|
+
get sessionAvailable() {
|
|
35272
|
+
return this.sessionAvailableFlag;
|
|
35273
|
+
}
|
|
35274
|
+
/** This cell's shown session (`regular` when unset — the provider default). */
|
|
35275
|
+
get session() {
|
|
35276
|
+
return normalizeSession(this.state.session) ?? "regular";
|
|
35277
|
+
}
|
|
35278
|
+
/** Switch this cell's shown session in place (a reload — RTH and ETH are different bars). */
|
|
35279
|
+
setSession(session) {
|
|
35280
|
+
if (session === this.session) return;
|
|
35281
|
+
this.state.session = session;
|
|
35282
|
+
this.deps.onStateDirty();
|
|
35283
|
+
void this.inner?.setMarket({ session });
|
|
35284
|
+
}
|
|
35285
|
+
refreshSessionAvailable() {
|
|
35286
|
+
const chart = this.inner;
|
|
35287
|
+
const symbol = this.state.symbol;
|
|
35288
|
+
if (!chart || !symbol) return;
|
|
35289
|
+
void chart.data.symbolInfo(symbol).then((si) => {
|
|
35290
|
+
if (this.inner !== chart) return;
|
|
35291
|
+
const available = typeof si?.session === "string" && si.session !== "" && si.session !== "24x7";
|
|
35292
|
+
if (available !== this.sessionAvailableFlag) {
|
|
35293
|
+
this.sessionAvailableFlag = available;
|
|
35294
|
+
this.deps.onMarketChanged(this.id);
|
|
35295
|
+
}
|
|
35296
|
+
});
|
|
35297
|
+
}
|
|
35167
35298
|
/** Show/hide this cell's symbol watermark (persisted per cell). */
|
|
35168
35299
|
setWatermarkVisible(visible) {
|
|
35169
35300
|
this.watermarkOn = visible;
|
|
@@ -36021,6 +36152,9 @@ var VelaWorkspace = class {
|
|
|
36021
36152
|
this.bottombar?.setActiveRange(preset.id);
|
|
36022
36153
|
},
|
|
36023
36154
|
onTimezone: (zone) => this.setTimezone(zone),
|
|
36155
|
+
// RTH/ETH acts on the ACTIVE cell (like the range chips): sessions
|
|
36156
|
+
// are a per-chart market dimension, not a shell preference.
|
|
36157
|
+
onSession: (session) => this.active.setSession(session),
|
|
36024
36158
|
onSettingsClick: () => this.active.chart.renderer.openSettings()
|
|
36025
36159
|
}) : null;
|
|
36026
36160
|
this.mobileBar = opts.bottombar !== false ? new MobileBar(this.root, {
|
|
@@ -36353,6 +36487,7 @@ var VelaWorkspace = class {
|
|
|
36353
36487
|
this.objectTree.setSymbol(cell.symbol);
|
|
36354
36488
|
this.dock.onChart(cell.chart);
|
|
36355
36489
|
this.bottombar?.setActiveRange(cell.activeRangeId);
|
|
36490
|
+
this.bottombar?.setSession({ session: cell.session, enabled: cell.sessionAvailable });
|
|
36356
36491
|
this.indicatorPicker?.sync();
|
|
36357
36492
|
this.glider.stop();
|
|
36358
36493
|
const d = cell.chart.drawings;
|
|
@@ -36754,6 +36889,7 @@ var VelaWorkspace = class {
|
|
|
36754
36889
|
this.mobileBar?.setSymbol(cell.symbol);
|
|
36755
36890
|
this.mobileBar?.setTimeframe(cell.timeframe);
|
|
36756
36891
|
this.objectTree.setSymbol(cell.symbol);
|
|
36892
|
+
this.bottombar?.setSession({ session: cell.session, enabled: cell.sessionAvailable });
|
|
36757
36893
|
}
|
|
36758
36894
|
/** Trigger ② — a cell's indicator ledger changed: count + picker only if active. */
|
|
36759
36895
|
onCellIndicatorsChanged(id) {
|