@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.
Files changed (37) hide show
  1. package/dist/{DataProvider-DgS2UyMc.d.cts → DataProvider-BNKtYU5V.d.cts} +22 -3
  2. package/dist/{DataProvider-Cqc_BaJ9.d.ts → DataProvider-gJjN_0eh.d.ts} +22 -3
  3. package/dist/{chunk-D6WM5IUE.js → chunk-4MQEG67Z.js} +77 -18
  4. package/dist/{chunk-JBQUY2RI.js → chunk-6LZJRO2S.js} +48 -18
  5. package/dist/{contributions-DuIxJWeH.d.ts → contributions-D64UA74H.d.cts} +9 -2
  6. package/dist/{contributions-4Nh1jbvb.d.cts → contributions-lPAgo9Cu.d.ts} +9 -2
  7. package/dist/{history-FsmvCIo4.d.ts → history-CVoFDJ2D.d.ts} +21 -3
  8. package/dist/{history-HofqYEh2.d.cts → history-fSMXVLt1.d.cts} +21 -3
  9. package/dist/index.cjs +77 -18
  10. package/dist/index.d.cts +14 -6
  11. package/dist/index.d.ts +14 -6
  12. package/dist/index.js +1 -1
  13. package/dist/{options-BlQ00Fju.d.cts → options-BqGeFHtp.d.cts} +15 -1
  14. package/dist/{options-BlQ00Fju.d.ts → options-BqGeFHtp.d.ts} +15 -1
  15. package/dist/{plugin-DJR6z3e0.d.cts → plugin-D5ni4WEJ.d.cts} +3 -3
  16. package/dist/{plugin-N_EaXN4-.d.ts → plugin-o5HJH46Z.d.ts} +3 -3
  17. package/dist/plugin.d.cts +4 -4
  18. package/dist/plugin.d.ts +4 -4
  19. package/dist/providers/binance.d.cts +2 -2
  20. package/dist/providers/binance.d.ts +2 -2
  21. package/dist/providers/coinbase.d.cts +2 -2
  22. package/dist/providers/coinbase.d.ts +2 -2
  23. package/dist/providers/hyperliquid.d.cts +2 -2
  24. package/dist/providers/hyperliquid.d.ts +2 -2
  25. package/dist/ui.d.cts +1 -1
  26. package/dist/ui.d.ts +1 -1
  27. package/dist/vela.global.js +77 -18
  28. package/dist/vela.global.min.js +29 -29
  29. package/dist/widget.cjs +175 -40
  30. package/dist/widget.d.cts +23 -15
  31. package/dist/widget.d.ts +23 -15
  32. package/dist/widget.js +56 -10
  33. package/dist/workspace.cjs +172 -36
  34. package/dist/workspace.d.cts +20 -5
  35. package/dist/workspace.d.ts +20 -5
  36. package/dist/workspace.js +52 -5
  37. package/package.json +1 -1
package/dist/widget.cjs CHANGED
@@ -6936,7 +6936,7 @@ var ProviderRegistry = class {
6936
6936
  /** Register (or replace) a provider; returns a promise that settles when its index is built. */
6937
6937
  register(rawName, provider) {
6938
6938
  const name = normName(rawName);
6939
- const entry = { name, provider, index: null, descriptors: [], settled: false, settle: Promise.resolve() };
6939
+ const entry = { name, provider, index: null, prefixIndex: null, byTicker: null, descriptors: [], settled: false, settle: Promise.resolve() };
6940
6940
  this.entries.set(name, entry);
6941
6941
  entry.settle = this.buildIndex(entry);
6942
6942
  queueMicrotask(() => this.notify(false));
@@ -6949,6 +6949,9 @@ var ProviderRegistry = class {
6949
6949
  const symbols = await entry.provider.listSymbols();
6950
6950
  entry.descriptors = symbols;
6951
6951
  entry.index = new Set(symbols.map((s) => normTicker(s.ticker)));
6952
+ entry.byTicker = new Map(symbols.map((s) => [normTicker(s.ticker), s]));
6953
+ entry.prefixIndex = /* @__PURE__ */ new Map();
6954
+ for (const s of symbols) if (s.prefix) entry.prefixIndex.set(`${normName(s.prefix)}:${normTicker(s.ticker)}`, s);
6952
6955
  }
6953
6956
  } catch {
6954
6957
  entry.index = null;
@@ -6984,14 +6987,25 @@ var ProviderRegistry = class {
6984
6987
  }
6985
6988
  /**
6986
6989
  * Resolve a symbol to `{ provider, ticker }`, or null when nothing can serve it
6987
- * yet. Explicit prefix → that provider (must be registered). Bare first
6988
- * indexed provider (registration order, `opts.default` first) that contains the
6989
- * ticker; `opts.lenient` additionally allows a sole registered provider before
6990
- * its index exists.
6990
+ * yet. Explicit prefix → that provider (must be registered), else a provider whose
6991
+ * index declares that LISTING prefix for that ticker (`NASDAQ:AAPL` routes to the
6992
+ * provider serving Nasdaq-listed AAPL TradingView-strict, so `NYSE:AAPL` resolves
6993
+ * to nothing rather than auto-correcting). Bare → first indexed provider
6994
+ * (registration order, `opts.default` first) that contains the ticker;
6995
+ * `opts.lenient` additionally allows a sole registered provider before its index
6996
+ * exists.
6991
6997
  */
6992
6998
  resolve(raw, opts = {}) {
6993
6999
  const { provider, ticker } = this.parse(raw);
6994
- if (provider) return this.entries.has(provider) ? { provider, ticker } : null;
7000
+ if (provider) {
7001
+ if (this.entries.has(provider)) return { provider, ticker };
7002
+ const key = `${provider}:${normTicker(ticker)}`;
7003
+ for (const name of this.candidateOrder(opts.default)) {
7004
+ const d = this.entries.get(name).prefixIndex?.get(key);
7005
+ if (d) return { provider: name, ticker: d.ticker };
7006
+ }
7007
+ return null;
7008
+ }
6995
7009
  const norm = normTicker(ticker);
6996
7010
  for (const name of this.candidateOrder(opts.default)) {
6997
7011
  if (this.entries.get(name).index?.has(norm)) return { provider: name, ticker };
@@ -7057,6 +7071,22 @@ var ProviderRegistry = class {
7057
7071
  }
7058
7072
  return names;
7059
7073
  }
7074
+ /**
7075
+ * The DISPLAY prefix for a resolved symbol — the descriptor's LISTING prefix when the
7076
+ * data declares one, else the provider name. This is the single seam every label
7077
+ * (legend chip, committed/persisted `PREFIX:TICKER` strings) derives from, which is
7078
+ * what makes a non-canonical spelling impossible to display: the form always comes
7079
+ * from the data, never from what was typed.
7080
+ */
7081
+ displayPrefixOf(resolved) {
7082
+ const d = this.entries.get(resolved.provider)?.byTicker?.get(normTicker(resolved.ticker));
7083
+ return d?.prefix ?? resolved.provider;
7084
+ }
7085
+ /** The canonical `PREFIX:TICKER` string for a resolved symbol (descriptor spellings). */
7086
+ canonicalSymbol(resolved) {
7087
+ const d = this.entries.get(resolved.provider)?.byTicker?.get(normTicker(resolved.ticker));
7088
+ return `${d?.prefix ?? resolved.provider}:${d?.ticker ?? resolved.ticker}`;
7089
+ }
7060
7090
  /** Indexed symbols for one provider (or all, concatenated) — for autocomplete. */
7061
7091
  symbolsOf(rawName) {
7062
7092
  if (rawName != null) return this.entries.get(normName(rawName))?.descriptors ?? [];
@@ -7516,7 +7546,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
7516
7546
  * an in-flight `setMarket` immediately (the config mutates before the load). */
7517
7547
  marketSnapshot() {
7518
7548
  const m = this.config.market;
7519
- return { symbol: m.symbol, provider: parseSymbol(m.symbol ?? "").provider ?? void 0, timeframe: m.timeframe, bars: m.bars, offline: m.data !== void 0 };
7549
+ 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 };
7520
7550
  }
7521
7551
  /**
7522
7552
  * Switch the chart's market IN PLACE — no destroy/recreate. The renderer stays
@@ -7535,7 +7565,9 @@ var EngineOrchestrator = class _EngineOrchestrator {
7535
7565
  */
7536
7566
  async setMarket(next) {
7537
7567
  const m = this.config.market;
7538
- const identityChanged = next.symbol !== void 0 && next.symbol !== m.symbol || next.timeframe !== void 0 && next.timeframe !== m.timeframe || next.data !== void 0;
7568
+ 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,
7569
+ // exactly like a timeframe change; the cache keys the sessions apart.
7570
+ next.session !== void 0 && next.session !== m.session || next.data !== void 0;
7539
7571
  const depthChanged = next.bars !== void 0 && next.bars !== m.bars;
7540
7572
  const depthOnly = depthChanged && !identityChanged && this.rawBars.length > 0 && !m.data?.length && ((next.bars ?? 0) <= this.rawBars.length || typeof this.feed.loadRange === "function");
7541
7573
  if (!identityChanged && !depthChanged) {
@@ -7557,6 +7589,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
7557
7589
  if (next.symbol !== void 0) m.symbol = next.symbol;
7558
7590
  if (next.timeframe !== void 0) m.timeframe = next.timeframe;
7559
7591
  if (next.bars !== void 0) m.bars = next.bars;
7592
+ if (next.session !== void 0) m.session = next.session;
7560
7593
  if (next.data !== void 0) m.data = next.data;
7561
7594
  else if (next.symbol !== void 0) delete m.data;
7562
7595
  m.visibleRange = next.visibleRange;
@@ -8910,8 +8943,8 @@ var PanesControl = class {
8910
8943
  };
8911
8944
 
8912
8945
  // src/data/BarStore.ts
8913
- function seriesKey(provider, symbol, timeframe) {
8914
- return `${provider}|${symbol}|${timeframe}`;
8946
+ function seriesKey(provider, symbol, timeframe, session) {
8947
+ return `${provider}|${symbol}|${timeframe}${session && session !== "regular" ? `|${session}` : ""}`;
8915
8948
  }
8916
8949
  function symbolOf(key) {
8917
8950
  return key.split("|")[1] ?? "";
@@ -8994,9 +9027,9 @@ var BarStore = class {
8994
9027
  var sharedBarStore = new BarStore();
8995
9028
 
8996
9029
  // src/data/CachingDataFeed.ts
8997
- function cacheKey(symbol, timeframe) {
9030
+ function cacheKey(symbol, timeframe, session) {
8998
9031
  const { provider, ticker } = parseSymbol(symbol);
8999
- return seriesKey(provider ?? "", ticker, timeframe);
9032
+ return seriesKey(provider ?? "", ticker, timeframe, session);
9000
9033
  }
9001
9034
  var PAGE_BARS = 1e4;
9002
9035
  var SINGLE_FETCH_BARS = PAGE_BARS + 1;
@@ -9008,7 +9041,7 @@ var CachingDataFeed = class {
9008
9041
  async load(cfg) {
9009
9042
  if (cfg.data && cfg.data.length > 0) return this.inner.load(cfg);
9010
9043
  const symbol = cfg.symbol ?? "TEST";
9011
- const key = cacheKey(symbol, cfg.timeframe ?? "60");
9044
+ const key = cacheKey(symbol, cfg.timeframe ?? "60", cfg.session);
9012
9045
  this.store.retainSymbol(symbol);
9013
9046
  const cached = this.store.get(key);
9014
9047
  const n = cfg.bars ?? 500;
@@ -9037,7 +9070,7 @@ var CachingDataFeed = class {
9037
9070
  */
9038
9071
  async loadRange(cfg, range) {
9039
9072
  if (!this.inner.loadRange) return this.inner.load(cfg);
9040
- const key = cacheKey(cfg.symbol ?? "TEST", cfg.timeframe ?? "60");
9073
+ const key = cacheKey(cfg.symbol ?? "TEST", cfg.timeframe ?? "60", cfg.session);
9041
9074
  let cached = this.store.get(key);
9042
9075
  let newest = cached?.[cached.length - 1];
9043
9076
  if (range.to != null && newest && range.to < newest.time) {
@@ -9195,6 +9228,20 @@ var MultiProviderFeed = class {
9195
9228
  resolveSymbol(raw) {
9196
9229
  return this.registry.resolve(raw, { default: this.primaryProvider });
9197
9230
  }
9231
+ /**
9232
+ * The DISPLAY prefix for `raw` — the descriptor's LISTING prefix when the data
9233
+ * declares one (`NASDAQ` for AAPL), else the resolved provider name. Null while
9234
+ * nothing resolves the symbol.
9235
+ */
9236
+ displayPrefix(raw) {
9237
+ const resolved = this.resolveSymbol(raw);
9238
+ return resolved ? this.registry.displayPrefixOf(resolved) : null;
9239
+ }
9240
+ /** The canonical `PREFIX:TICKER` form of `raw`, or null while unresolvable. */
9241
+ canonicalSymbol(raw) {
9242
+ const resolved = this.resolveSymbol(raw);
9243
+ return resolved ? this.registry.canonicalSymbol(resolved) : null;
9244
+ }
9198
9245
  /** The registered provider INSTANCE under `name` (undefined if unknown). */
9199
9246
  providerInstance(name) {
9200
9247
  return this.registry.get(name);
@@ -9330,13 +9377,13 @@ var RegistryFetchFeed = class {
9330
9377
  const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
9331
9378
  const provider = this.registry.get(name ?? "");
9332
9379
  if (!provider) return Promise.resolve([]);
9333
- return safeBars(provider, ticker, cfg.timeframe ?? "60", { limit: cfg.bars ?? 500 });
9380
+ return safeBars(provider, ticker, cfg.timeframe ?? "60", { limit: cfg.bars ?? 500, session: cfg.session });
9334
9381
  }
9335
9382
  loadRange(cfg, range) {
9336
9383
  const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
9337
9384
  const provider = this.registry.get(name ?? "");
9338
9385
  if (!provider) return Promise.resolve([]);
9339
- return safeBars(provider, ticker, cfg.timeframe ?? "60", range);
9386
+ return safeBars(provider, ticker, cfg.timeframe ?? "60", { ...range, session: cfg.session });
9340
9387
  }
9341
9388
  subscribe(cfg, onBar) {
9342
9389
  const { provider: name, ticker } = parseSymbol(cfg.symbol ?? "");
@@ -9344,13 +9391,13 @@ var RegistryFetchFeed = class {
9344
9391
  if (!provider) return () => {
9345
9392
  };
9346
9393
  const tf = cfg.timeframe ?? "60";
9347
- if (provider.subscribe) return provider.subscribe(ticker, tf, onBar);
9394
+ if (provider.subscribe) return provider.subscribe(ticker, tf, onBar, cfg.session ? { session: cfg.session } : void 0);
9348
9395
  let stopped = false;
9349
9396
  let timer = null;
9350
9397
  const poll = async () => {
9351
9398
  if (stopped) return;
9352
9399
  try {
9353
- const bars = await provider.getBars(ticker, tf, { limit: 2 });
9400
+ const bars = await provider.getBars(ticker, tf, { limit: 2, session: cfg.session });
9354
9401
  if (stopped) return;
9355
9402
  for (const b of bars) onBar(b);
9356
9403
  } catch {
@@ -9400,6 +9447,17 @@ var DataControl = class {
9400
9447
  resolve(symbol) {
9401
9448
  return this.registry?.resolveSymbol(symbol) ?? null;
9402
9449
  }
9450
+ /**
9451
+ * The DISPLAY prefix for `symbol` — the listing venue its descriptor declares
9452
+ * (`NASDAQ` for AAPL) or the resolved provider name. Null while unresolvable.
9453
+ */
9454
+ displayPrefix(symbol) {
9455
+ return this.registry?.displayPrefix(symbol) ?? null;
9456
+ }
9457
+ /** The canonical `PREFIX:TICKER` form of `symbol`, or null while unresolvable. */
9458
+ canonicalSymbol(symbol) {
9459
+ return this.registry?.canonicalSymbol(symbol) ?? null;
9460
+ }
9403
9461
  /**
9404
9462
  * The registered provider INSTANCE under `name` — the seam for EXTENDED provider
9405
9463
  * surfaces: a provider may implement interfaces beyond the `DataProvider` port
@@ -27623,6 +27681,7 @@ var Vela = class {
27623
27681
  symbol: options.symbol,
27624
27682
  timeframe: options.timeframe,
27625
27683
  bars: options.bars,
27684
+ session: options.session,
27626
27685
  visibleRange: options.visibleRange,
27627
27686
  data: options.data
27628
27687
  },
@@ -27912,6 +27971,9 @@ function resolveElement(container) {
27912
27971
  if (!element) throw new Error(`[vela] container not found for selector "${container}"`);
27913
27972
  return element;
27914
27973
  }
27974
+
27975
+ // src/core/options.ts
27976
+ var normalizeSession = (v) => v === "regular" || v === "extended" ? v : void 0;
27915
27977
  function runMachine(machine5, props, render) {
27916
27978
  const m = new vanilla.VanillaMachine(machine5, props);
27917
27979
  const unsub = m.subscribe(render);
@@ -30471,10 +30533,12 @@ var CSS7 = `
30471
30533
  color: var(--vela-fg-muted);
30472
30534
  font-size: 11px;
30473
30535
  font-weight: 600;
30474
- cursor: not-allowed;
30475
- opacity: 0.55;
30536
+ cursor: pointer;
30476
30537
  }
30477
- .vela-bb-session-btn.is-active { color: var(--vela-fg); background: var(--vela-surface-elev); opacity: 0.8; }
30538
+ .vela-bb-session-btn:disabled { cursor: not-allowed; opacity: 0.55; }
30539
+ .vela-bb-session-btn:not(:disabled):hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
30540
+ .vela-bb-session-btn.is-active { color: var(--vela-fg); background: var(--vela-surface-elev); }
30541
+ .vela-bb-session-btn.is-active:disabled { opacity: 0.8; }
30478
30542
  .vela-bb-settings {
30479
30543
  all: unset;
30480
30544
  display: inline-flex;
@@ -30494,6 +30558,8 @@ var Bottombar = class {
30494
30558
  constructor(host, opts) {
30495
30559
  this.settingsTip = null;
30496
30560
  this.rangeButtons = /* @__PURE__ */ new Map();
30561
+ this.sessionButtons = /* @__PURE__ */ new Map();
30562
+ this.sessionEl = null;
30497
30563
  this.timer = null;
30498
30564
  this.timezone = opts.timezone;
30499
30565
  const doc = host.ownerDocument;
@@ -30523,12 +30589,19 @@ var Bottombar = class {
30523
30589
  this.tzButton.append(this.clockEl, this.tzLabelEl);
30524
30590
  const session = doc.createElement("span");
30525
30591
  session.className = "vela-bb-session";
30592
+ this.sessionEl = session;
30526
30593
  session.title = "Session \u2014 stocks & ETFs only";
30527
- for (const [label, active] of [["RTH", true], ["ETH", false]]) {
30594
+ for (const [key, label] of [["regular", "RTH"], ["extended", "ETH"]]) {
30528
30595
  const b = doc.createElement("button");
30529
- b.className = "vela-bb-session-btn" + (active ? " is-active" : "");
30596
+ b.className = "vela-bb-session-btn" + (key === "regular" ? " is-active" : "");
30530
30597
  b.textContent = label;
30531
30598
  b.disabled = true;
30599
+ b.addEventListener("click", () => {
30600
+ if (b.disabled) return;
30601
+ this.setSession({ session: key, enabled: true });
30602
+ opts.onSession?.(key);
30603
+ });
30604
+ this.sessionButtons.set(key, b);
30532
30605
  session.appendChild(b);
30533
30606
  }
30534
30607
  const settingsBtn = doc.createElement("button");
@@ -30566,6 +30639,19 @@ var Bottombar = class {
30566
30639
  else delete b.dataset.active;
30567
30640
  }
30568
30641
  }
30642
+ /**
30643
+ * Reflect the ACTIVE chart's session posture. `enabled: false` (a continuous
30644
+ * market, or metadata not landed yet) shows the disabled RTH-active stub — the
30645
+ * pre-session-model look, byte-for-byte. Enabled, the chips become clickable and
30646
+ * the active one tracks the chart's current session.
30647
+ */
30648
+ setSession(state) {
30649
+ if (this.sessionEl) this.sessionEl.title = state.enabled ? "Session \u2014 regular (RTH) vs extended (ETH) hours" : "Session \u2014 stocks & ETFs only";
30650
+ for (const [key, b] of this.sessionButtons) {
30651
+ b.disabled = !state.enabled;
30652
+ b.classList.toggle("is-active", state.enabled ? key === state.session : key === "regular");
30653
+ }
30654
+ }
30569
30655
  destroy() {
30570
30656
  if (this.timer !== null) clearInterval(this.timer);
30571
30657
  this.tzMenu.destroy();
@@ -30608,10 +30694,11 @@ function parseQuery(raw, venues) {
30608
30694
  function onlyOne(matches) {
30609
30695
  return matches.length === 1 ? matches[0] : null;
30610
30696
  }
30697
+ var venueOf = (s) => s.prefix ?? s.provider;
30611
30698
  function filterSymbols(list, query, limit = 100) {
30612
- const venues = [...new Set(list.map((s) => s.provider?.toLowerCase()).filter((p) => !!p))];
30699
+ const venues = [...new Set(list.flatMap((s) => [venueOf(s)?.toLowerCase(), s.provider?.toLowerCase()]).filter((p) => !!p))];
30613
30700
  const { scope, term } = parseQuery(query, venues);
30614
- const pool = scope ? list.filter((s) => s.provider?.toLowerCase() === scope) : list;
30701
+ const pool = scope ? list.filter((s) => venueOf(s)?.toLowerCase() === scope || s.provider?.toLowerCase() === scope) : list;
30615
30702
  const q = term.toUpperCase();
30616
30703
  if (!q) {
30617
30704
  if (scope) return [...pool].sort((a, b) => a.ticker.localeCompare(b.ticker)).slice(0, limit);
@@ -30630,7 +30717,7 @@ function filterSymbols(list, query, limit = 100) {
30630
30717
  if (t.startsWith(q)) prefix.push(s);
30631
30718
  else if (t.includes(q)) substr.push(s);
30632
30719
  else if ((s.description ?? "").toUpperCase().includes(q)) desc.push(s);
30633
- else if (!scope && s.provider?.toLowerCase().includes(qLower)) venue.push(s);
30720
+ else if (!scope && (venueOf(s)?.toLowerCase().includes(qLower) || s.provider?.toLowerCase().includes(qLower))) venue.push(s);
30634
30721
  if (prefix.length >= limit) break;
30635
30722
  }
30636
30723
  return [...prefix, ...substr, ...desc, ...venue].slice(0, limit);
@@ -30782,14 +30869,14 @@ var SymbolPicker = class {
30782
30869
  else if (e.key === "ArrowUp") this.moveHighlight(-1);
30783
30870
  else if (e.key === "Enter") {
30784
30871
  const pick = this.rows[this.highlighted];
30785
- if (pick) this.select(pick.ticker, pick.provider, opts.onSelect);
30872
+ if (pick) this.select(pick.ticker, pick.prefix ?? pick.provider, opts.onSelect);
30786
30873
  return;
30787
30874
  } else return;
30788
30875
  e.preventDefault();
30789
30876
  });
30790
30877
  this.list.addEventListener("click", (e) => {
30791
30878
  const row = e.target.closest(".vela-sp-row");
30792
- if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.provider, opts.onSelect);
30879
+ if (row?.dataset.ticker) this.select(row.dataset.ticker, row.dataset.venue, opts.onSelect);
30793
30880
  });
30794
30881
  }
30795
30882
  /** Wire where symbols come from (re-called on every widget rebuild). */
@@ -30806,9 +30893,9 @@ var SymbolPicker = class {
30806
30893
  destroy() {
30807
30894
  this.dialog.destroy();
30808
30895
  }
30809
- select(ticker, provider, onSelect) {
30896
+ select(ticker, venue, onSelect) {
30810
30897
  this.close();
30811
- onSelect(provider ? `${provider.toLowerCase()}:${ticker}` : ticker);
30898
+ onSelect(venue ? `${venue}:${ticker}` : ticker);
30812
30899
  }
30813
30900
  moveHighlight(delta) {
30814
30901
  if (!this.rows.length) return;
@@ -30841,7 +30928,8 @@ var SymbolPicker = class {
30841
30928
  const row = doc.createElement("div");
30842
30929
  row.className = "vela-sp-row";
30843
30930
  row.dataset.ticker = s.ticker;
30844
- if (s.provider) row.dataset.provider = s.provider;
30931
+ const venue = s.prefix ?? s.provider;
30932
+ if (venue) row.dataset.venue = venue;
30845
30933
  const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
30846
30934
  const main = doc.createElement("span");
30847
30935
  main.className = "vela-sp-main";
@@ -30853,11 +30941,11 @@ var SymbolPicker = class {
30853
30941
  d.textContent = s.description ?? (s.type ?? "");
30854
30942
  main.append(t, d);
30855
30943
  row.append(av, main);
30856
- if (s.provider) {
30944
+ if (venue) {
30857
30945
  const badge = doc.createElement("span");
30858
30946
  badge.className = "vela-sp-badge";
30859
- badge.dataset.p = s.provider;
30860
- badge.textContent = s.provider;
30947
+ badge.dataset.p = s.provider ?? venue;
30948
+ badge.textContent = venue;
30861
30949
  row.appendChild(badge);
30862
30950
  }
30863
30951
  this.list.appendChild(row);
@@ -34630,6 +34718,7 @@ function sanitizeCell(raw) {
34630
34718
  if (typeof c.timeframe === "string") out.timeframe = c.timeframe;
34631
34719
  if (typeof c.priceStyle === "string") out.priceStyle = c.priceStyle;
34632
34720
  if (typeof c.bars === "number" && Number.isFinite(c.bars) && c.bars > 0) out.bars = c.bars;
34721
+ if (c.session === "regular" || c.session === "extended") out.session = c.session;
34633
34722
  if (typeof c.watermark === "boolean") out.watermark = c.watermark;
34634
34723
  if (typeof c.indicatorTitles === "boolean") out.indicatorTitles = c.indicatorTitles;
34635
34724
  if (typeof c.indicatorValues === "boolean") out.indicatorValues = c.indicatorValues;
@@ -34696,7 +34785,8 @@ var PARAMS = [
34696
34785
  ["timeframe", "interval"],
34697
34786
  ["priceStyle", "style"],
34698
34787
  ["timezone", "tz"],
34699
- ["bars", "bars"]
34788
+ ["bars", "bars"],
34789
+ ["session", "session"]
34700
34790
  ];
34701
34791
  function readUrlState(search) {
34702
34792
  const out = {};
@@ -35093,6 +35183,7 @@ var VelaWidget = class {
35093
35183
  this.priceStyle = fromUrl.priceStyle ?? bootCell?.priceStyle ?? opts.priceStyle ?? "candles";
35094
35184
  this.timezone = fromUrl.timezone ?? boot?.timezone ?? opts.timezone ?? "Etc/UTC";
35095
35185
  this.bars = Number(fromUrl.bars ?? bootCell?.bars ?? opts.bars ?? 1e3);
35186
+ this.session = normalizeSession(fromUrl.session ?? bootCell?.session ?? opts.session);
35096
35187
  this.watermarkOn = bootCell?.watermark !== void 0 ? bootCell.watermark : opts.watermark !== false;
35097
35188
  this.indicatorTitlesOn = bootCell?.indicatorTitles ?? true;
35098
35189
  this.indicatorValuesOn = bootCell?.indicatorValues ?? true;
@@ -35199,6 +35290,7 @@ var VelaWidget = class {
35199
35290
  timezone: this.timezone,
35200
35291
  onRange: (preset) => this.applyRange(preset),
35201
35292
  onTimezone: (zone) => this.setTimezone(zone),
35293
+ onSession: (session) => this.setSession(session),
35202
35294
  onSettingsClick: () => this.inner?.renderer.openSettings()
35203
35295
  }) : null;
35204
35296
  this.mobileBar = opts.bottombar !== false ? new MobileBar(this.root, {
@@ -35428,13 +35520,15 @@ var VelaWidget = class {
35428
35520
  this.statusline?.setMeta(tf, this.providerLabel());
35429
35521
  }
35430
35522
  /**
35431
- * The provider name to show: the one that RESOLVED the symbol, not the one configured.
35432
- * They differ whenever the `provider` option is just a default (or names something that
35433
- * isn't registered) the status line must not claim a venue that served nothing.
35523
+ * The venue label to show: the LISTING prefix the resolved symbol's data declares
35524
+ * (`NASDAQ` for AAPL), else the provider that RESOLVED the symbol never the one
35525
+ * configured or typed. They differ whenever the `provider` option is just a default,
35526
+ * the prefix was a listing venue, or the spelling isn't registered — the status line
35527
+ * must not claim a venue that served nothing.
35434
35528
  */
35435
35529
  providerLabel() {
35436
- const resolved = this.inner?.data.resolve(this.symbol)?.provider;
35437
- return resolved ?? parseSymbol(this.symbol).provider ?? "";
35530
+ const display = this.inner?.data.displayPrefix(this.symbol);
35531
+ return display ?? parseSymbol(this.symbol).provider ?? "";
35438
35532
  }
35439
35533
  setTimeframe(tf) {
35440
35534
  if (tf === this.timeframe || this.destroyed) return;
@@ -35445,6 +35539,30 @@ var VelaWidget = class {
35445
35539
  this.markStateDirty();
35446
35540
  void this.inner?.setMarket({ timeframe: tf, bars: this.bars });
35447
35541
  }
35542
+ /**
35543
+ * Switch the shown trading session (RTH/ETH) in place — a full reload, like a
35544
+ * timeframe change (the two sessions are different bar series). No-op on a chart
35545
+ * already showing that session; meaningless on continuous markets (the toggle is
35546
+ * disabled there, but the API tolerates the call — the provider ignores the flag).
35547
+ */
35548
+ setSession(session) {
35549
+ if (session === this.session || session === "regular" && this.session === void 0 || this.destroyed) return;
35550
+ this.session = session;
35551
+ this.refreshSessionToggle();
35552
+ this.markStateDirty();
35553
+ void this.inner?.setMarket({ session });
35554
+ }
35555
+ /** Re-derive the RTH/ETH toggle's posture from the ACTIVE symbol's metadata: enabled
35556
+ * iff the resolved symbol declares a real session vocabulary (not `24x7`). */
35557
+ refreshSessionToggle() {
35558
+ const chart = this.inner;
35559
+ if (!chart || !this.bottombar) return;
35560
+ void chart.data.symbolInfo(this.symbol).then((si) => {
35561
+ if (this.destroyed || this.inner !== chart) return;
35562
+ const enabled = typeof si?.session === "string" && si.session !== "" && si.session !== "24x7";
35563
+ this.bottombar?.setSession({ session: this.session ?? "regular", enabled });
35564
+ });
35565
+ }
35448
35566
  /** Star/unstar a timeframe — the topbar chips and dropdown stars follow, and the
35449
35567
  * set persists with the rest of the shell state. */
35450
35568
  setTimeframeFavorite(tf, on) {
@@ -35491,6 +35609,8 @@ var VelaWidget = class {
35491
35609
  cell.timeframe = live?.timeframe ?? this.timeframe;
35492
35610
  cell.priceStyle = this.priceStyle;
35493
35611
  if (this.bars > 0) cell.bars = this.bars;
35612
+ const session = normalizeSession(live?.session ?? this.session);
35613
+ if (session === "extended") cell.session = session;
35494
35614
  cell.watermark = this.watermarkOn;
35495
35615
  cell.indicatorTitles = this.indicatorTitlesOn;
35496
35616
  cell.indicatorValues = this.indicatorValuesOn;
@@ -35557,9 +35677,15 @@ var VelaWidget = class {
35557
35677
  const symbol = fromUrl.symbol ?? prefixedSymbol(cell);
35558
35678
  const timeframe = fromUrl.timeframe ?? cell.timeframe;
35559
35679
  const bars = Number(fromUrl.bars ?? cell.bars ?? 0);
35680
+ const session = normalizeSession(fromUrl.session ?? cell.session);
35560
35681
  const next = {};
35561
35682
  if (symbol && symbol !== this.symbol) next.symbol = symbol;
35562
35683
  if (timeframe && timeframe !== this.timeframe) next.timeframe = timeframe;
35684
+ if (session && session !== (this.session ?? "regular")) {
35685
+ this.session = session;
35686
+ next.session = session;
35687
+ this.refreshSessionToggle();
35688
+ }
35563
35689
  if (bars > 0 && bars !== this.bars) {
35564
35690
  this.bars = bars;
35565
35691
  next.bars = Math.max(bars, this.rangeBars);
@@ -35786,6 +35912,7 @@ var VelaWidget = class {
35786
35912
  symbol: this.symbol,
35787
35913
  timeframe: this.timeframe,
35788
35914
  bars: Math.max(this.bars, this.rangeBars),
35915
+ session: this.session,
35789
35916
  volume: this.ledgerVolume ?? chartOpts.volume,
35790
35917
  // A pending range chip frames the FIRST paint (no preview flash, no re-frame).
35791
35918
  ...this.pendingRange ? { visibleRange: this.pendingRange.preset } : {}
@@ -35794,6 +35921,11 @@ var VelaWidget = class {
35794
35921
  for (const [language, make] of Object.entries(resolveEngines(engines))) chart.registerEngine(language, make());
35795
35922
  this.inner = chart;
35796
35923
  this.symbolPicker.setSource(() => chart.data.symbols());
35924
+ void chart.data.ready().then(() => {
35925
+ if (this.inner !== chart) return;
35926
+ this.statusline?.setMeta(this.timeframe, this.providerLabel());
35927
+ this.refreshSessionToggle();
35928
+ });
35797
35929
  if (this.layoutCtl !== void 0) chart.renderer.setLayoutMode(this.layoutCtl.current);
35798
35930
  this.objectTree.setSymbol(this.symbol);
35799
35931
  chart.renderer.setLegendActions(legendActionsProviderFor(chart, () => this.context()));
@@ -35869,6 +36001,7 @@ var VelaWidget = class {
35869
36001
  this.statusline?.setSymbol(symbol);
35870
36002
  this.objectTree.setSymbol(symbol);
35871
36003
  this.watermark?.update(symbol, this.timeframe);
36004
+ this.refreshSessionToggle();
35872
36005
  }
35873
36006
  if (timeframe !== this.timeframe) this.syncTimeframeChrome(timeframe);
35874
36007
  });
@@ -36166,6 +36299,8 @@ var VelaWidget = class {
36166
36299
  priceStyle: this.priceStyle,
36167
36300
  timezone: this.timezone,
36168
36301
  bars: String(this.bars),
36302
+ // Empty clears the param — the default (regular) keeps links lean.
36303
+ session: this.session === "extended" ? "extended" : "",
36169
36304
  watermark: this.watermarkOn ? "1" : "0",
36170
36305
  favorites: this.favs.join(",")
36171
36306
  });
package/dist/widget.d.cts CHANGED
@@ -1,10 +1,10 @@
1
- import { W as WidgetContext, V as Vela, y as SidePanelButton } from './contributions-4Nh1jbvb.cjs';
2
- export { ah as DEFAULT_PANEL_ORDER, z as SidePanelDescriptor, A as SidePanelHandle, U as WidgetActionDescriptor, X as WidgetActionTarget, Y as WidgetAttachment, a4 as registerSidePanel, a5 as registerWidgetAction, a6 as registerWidgetAttachment, a8 as sidePanels, ac as unregisterSidePanel, ad as unregisterWidgetAction, ae as unregisterWidgetAttachment, af as widgetActions, ag as widgetAttachments } from './contributions-4Nh1jbvb.cjs';
3
- import { b as VelaOptions, q as DataWindowReadout } from './options-BlQ00Fju.cjs';
1
+ import { W as WidgetContext, V as Vela, y as SidePanelButton } from './contributions-D64UA74H.cjs';
2
+ export { ah as DEFAULT_PANEL_ORDER, z as SidePanelDescriptor, A as SidePanelHandle, U as WidgetActionDescriptor, X as WidgetActionTarget, Y as WidgetAttachment, a4 as registerSidePanel, a5 as registerWidgetAction, a6 as registerWidgetAttachment, a8 as sidePanels, ac as unregisterSidePanel, ad as unregisterWidgetAction, ae as unregisterWidgetAttachment, af as widgetActions, ag as widgetAttachments } from './contributions-D64UA74H.cjs';
3
+ import { b as VelaOptions, d as MarketSession, r as DataWindowReadout } from './options-BqGeFHtp.cjs';
4
4
  import { K as KeymapManager } from './keymap-CGOz5F5f.cjs';
5
- import { W as WidgetHistory, b as VelaShellOptions, e as WorkspaceState, a as RangePreset, i as PersistedState, P as PanelsState } from './history-HofqYEh2.cjs';
6
- export { B as Bottombar, j as BottombarOptions, C as CellState, f as ChartState, I as IndicatorLoader, k as IndicatorManifest, l as IndicatorManifestEntry, m as RANGE_PRESETS, R as ResolvedIndicator, V as VelaStorage, n as WidgetStorage, g as decodeState, h as encodeState, o as legacyWidgetState, p as loadPersisted, q as localStorageAdapter, r as parsePersisted, t as resolveIndicators, s as sanitizeState, u as savePersisted } from './history-HofqYEh2.cjs';
7
- import { a as SymbolDescriptor } from './DataProvider-DgS2UyMc.cjs';
5
+ import { W as WidgetHistory, b as VelaShellOptions, e as WorkspaceState, a as RangePreset, i as PersistedState, P as PanelsState } from './history-fSMXVLt1.cjs';
6
+ export { B as Bottombar, j as BottombarOptions, C as CellState, f as ChartState, I as IndicatorLoader, k as IndicatorManifest, l as IndicatorManifestEntry, m as RANGE_PRESETS, R as ResolvedIndicator, V as VelaStorage, n as WidgetStorage, g as decodeState, h as encodeState, o as legacyWidgetState, p as loadPersisted, q as localStorageAdapter, r as parsePersisted, t as resolveIndicators, s as sanitizeState, u as savePersisted } from './history-fSMXVLt1.cjs';
7
+ import { a as SymbolDescriptor } from './DataProvider-BNKtYU5V.cjs';
8
8
  import { d as SidePanel } from './side-panel-CT9ZwIGz.cjs';
9
9
  export { D as DEFAULT_PANEL_MAX_WIDTH, a as DEFAULT_PANEL_MIN_WIDTH, b as DEFAULT_PANEL_WIDTH, S as SidePanelOptions, c as clampPanelWidth } from './side-panel-CT9ZwIGz.cjs';
10
10
 
@@ -51,6 +51,8 @@ declare class VelaWidget {
51
51
  private priceStyle;
52
52
  private timezone;
53
53
  private bars;
54
+ /** The shown trading session — undefined = the provider default (regular). */
55
+ private session;
54
56
  private watermarkOn;
55
57
  /** Indicator titles (the in-chart legend rows) shown — reapplied across rebuilds. */
56
58
  private indicatorTitlesOn;
@@ -141,12 +143,24 @@ declare class VelaWidget {
141
143
  * drifting out of sync with the topbar. */
142
144
  private syncTimeframeChrome;
143
145
  /**
144
- * The provider name to show: the one that RESOLVED the symbol, not the one configured.
145
- * They differ whenever the `provider` option is just a default (or names something that
146
- * isn't registered) the status line must not claim a venue that served nothing.
146
+ * The venue label to show: the LISTING prefix the resolved symbol's data declares
147
+ * (`NASDAQ` for AAPL), else the provider that RESOLVED the symbol never the one
148
+ * configured or typed. They differ whenever the `provider` option is just a default,
149
+ * the prefix was a listing venue, or the spelling isn't registered — the status line
150
+ * must not claim a venue that served nothing.
147
151
  */
148
152
  private providerLabel;
149
153
  setTimeframe(tf: string): void;
154
+ /**
155
+ * Switch the shown trading session (RTH/ETH) in place — a full reload, like a
156
+ * timeframe change (the two sessions are different bar series). No-op on a chart
157
+ * already showing that session; meaningless on continuous markets (the toggle is
158
+ * disabled there, but the API tolerates the call — the provider ignores the flag).
159
+ */
160
+ setSession(session: MarketSession): void;
161
+ /** Re-derive the RTH/ETH toggle's posture from the ACTIVE symbol's metadata: enabled
162
+ * iff the resolved symbol declares a real session vocabulary (not `24x7`). */
163
+ private refreshSessionToggle;
150
164
  /** Star/unstar a timeframe — the topbar chips and dropdown stars follow, and the
151
165
  * set persists with the rest of the shell state. */
152
166
  private setTimeframeFavorite;
@@ -465,12 +479,6 @@ declare class Watermark {
465
479
  destroy(): void;
466
480
  }
467
481
 
468
- /**
469
- * Rank: ticker prefix > ticker substring > description substring > venue-name substring (typing
470
- * `binance` surfaces that venue's symbols after any literal matches). An optional venue prefix
471
- * (see {@link parseQuery}) scopes the pool first — venue alone browses it whole, alphabetically.
472
- * Pure — unit-tested.
473
- */
474
482
  declare function filterSymbols(list: readonly SymbolDescriptor[], query: string, limit?: number): SymbolDescriptor[];
475
483
  interface SymbolPickerOptions {
476
484
  /** `provider` is the venue of the chosen row — absent only for a source that has none. */
package/dist/widget.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { W as WidgetContext, V as Vela, y as SidePanelButton } from './contributions-DuIxJWeH.js';
2
- export { ah as DEFAULT_PANEL_ORDER, z as SidePanelDescriptor, A as SidePanelHandle, U as WidgetActionDescriptor, X as WidgetActionTarget, Y as WidgetAttachment, a4 as registerSidePanel, a5 as registerWidgetAction, a6 as registerWidgetAttachment, a8 as sidePanels, ac as unregisterSidePanel, ad as unregisterWidgetAction, ae as unregisterWidgetAttachment, af as widgetActions, ag as widgetAttachments } from './contributions-DuIxJWeH.js';
3
- import { b as VelaOptions, q as DataWindowReadout } from './options-BlQ00Fju.js';
1
+ import { W as WidgetContext, V as Vela, y as SidePanelButton } from './contributions-lPAgo9Cu.js';
2
+ export { ah as DEFAULT_PANEL_ORDER, z as SidePanelDescriptor, A as SidePanelHandle, U as WidgetActionDescriptor, X as WidgetActionTarget, Y as WidgetAttachment, a4 as registerSidePanel, a5 as registerWidgetAction, a6 as registerWidgetAttachment, a8 as sidePanels, ac as unregisterSidePanel, ad as unregisterWidgetAction, ae as unregisterWidgetAttachment, af as widgetActions, ag as widgetAttachments } from './contributions-lPAgo9Cu.js';
3
+ import { b as VelaOptions, d as MarketSession, r as DataWindowReadout } from './options-BqGeFHtp.js';
4
4
  import { K as KeymapManager } from './keymap-CGOz5F5f.js';
5
- import { W as WidgetHistory, b as VelaShellOptions, e as WorkspaceState, a as RangePreset, i as PersistedState, P as PanelsState } from './history-FsmvCIo4.js';
6
- export { B as Bottombar, j as BottombarOptions, C as CellState, f as ChartState, I as IndicatorLoader, k as IndicatorManifest, l as IndicatorManifestEntry, m as RANGE_PRESETS, R as ResolvedIndicator, V as VelaStorage, n as WidgetStorage, g as decodeState, h as encodeState, o as legacyWidgetState, p as loadPersisted, q as localStorageAdapter, r as parsePersisted, t as resolveIndicators, s as sanitizeState, u as savePersisted } from './history-FsmvCIo4.js';
7
- import { a as SymbolDescriptor } from './DataProvider-Cqc_BaJ9.js';
5
+ import { W as WidgetHistory, b as VelaShellOptions, e as WorkspaceState, a as RangePreset, i as PersistedState, P as PanelsState } from './history-CVoFDJ2D.js';
6
+ export { B as Bottombar, j as BottombarOptions, C as CellState, f as ChartState, I as IndicatorLoader, k as IndicatorManifest, l as IndicatorManifestEntry, m as RANGE_PRESETS, R as ResolvedIndicator, V as VelaStorage, n as WidgetStorage, g as decodeState, h as encodeState, o as legacyWidgetState, p as loadPersisted, q as localStorageAdapter, r as parsePersisted, t as resolveIndicators, s as sanitizeState, u as savePersisted } from './history-CVoFDJ2D.js';
7
+ import { a as SymbolDescriptor } from './DataProvider-gJjN_0eh.js';
8
8
  import { d as SidePanel } from './side-panel-CT9ZwIGz.js';
9
9
  export { D as DEFAULT_PANEL_MAX_WIDTH, a as DEFAULT_PANEL_MIN_WIDTH, b as DEFAULT_PANEL_WIDTH, S as SidePanelOptions, c as clampPanelWidth } from './side-panel-CT9ZwIGz.js';
10
10
 
@@ -51,6 +51,8 @@ declare class VelaWidget {
51
51
  private priceStyle;
52
52
  private timezone;
53
53
  private bars;
54
+ /** The shown trading session — undefined = the provider default (regular). */
55
+ private session;
54
56
  private watermarkOn;
55
57
  /** Indicator titles (the in-chart legend rows) shown — reapplied across rebuilds. */
56
58
  private indicatorTitlesOn;
@@ -141,12 +143,24 @@ declare class VelaWidget {
141
143
  * drifting out of sync with the topbar. */
142
144
  private syncTimeframeChrome;
143
145
  /**
144
- * The provider name to show: the one that RESOLVED the symbol, not the one configured.
145
- * They differ whenever the `provider` option is just a default (or names something that
146
- * isn't registered) the status line must not claim a venue that served nothing.
146
+ * The venue label to show: the LISTING prefix the resolved symbol's data declares
147
+ * (`NASDAQ` for AAPL), else the provider that RESOLVED the symbol never the one
148
+ * configured or typed. They differ whenever the `provider` option is just a default,
149
+ * the prefix was a listing venue, or the spelling isn't registered — the status line
150
+ * must not claim a venue that served nothing.
147
151
  */
148
152
  private providerLabel;
149
153
  setTimeframe(tf: string): void;
154
+ /**
155
+ * Switch the shown trading session (RTH/ETH) in place — a full reload, like a
156
+ * timeframe change (the two sessions are different bar series). No-op on a chart
157
+ * already showing that session; meaningless on continuous markets (the toggle is
158
+ * disabled there, but the API tolerates the call — the provider ignores the flag).
159
+ */
160
+ setSession(session: MarketSession): void;
161
+ /** Re-derive the RTH/ETH toggle's posture from the ACTIVE symbol's metadata: enabled
162
+ * iff the resolved symbol declares a real session vocabulary (not `24x7`). */
163
+ private refreshSessionToggle;
150
164
  /** Star/unstar a timeframe — the topbar chips and dropdown stars follow, and the
151
165
  * set persists with the rest of the shell state. */
152
166
  private setTimeframeFavorite;
@@ -465,12 +479,6 @@ declare class Watermark {
465
479
  destroy(): void;
466
480
  }
467
481
 
468
- /**
469
- * Rank: ticker prefix > ticker substring > description substring > venue-name substring (typing
470
- * `binance` surfaces that venue's symbols after any literal matches). An optional venue prefix
471
- * (see {@link parseQuery}) scopes the pool first — venue alone browses it whole, alphabetically.
472
- * Pure — unit-tested.
473
- */
474
482
  declare function filterSymbols(list: readonly SymbolDescriptor[], query: string, limit?: number): SymbolDescriptor[];
475
483
  interface SymbolPickerOptions {
476
484
  /** `provider` is the venue of the chosen row — absent only for a source that has none. */