@luxalgo/vela 0.6.4 → 0.6.5

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/widget.cjs CHANGED
@@ -15331,6 +15331,12 @@ var SceneGraph = class {
15331
15331
  assignIndicatorZ(id) {
15332
15332
  if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.bottomZ() - 1);
15333
15333
  }
15334
+ /** Mount-time default for a LAYER-BACKED native (it paints on a canvas stacked above the
15335
+ * data canvas by default): top of the stack, so the recorded order tells the truth from
15336
+ * the first frame. Keeps an existing key, so a restored stack survives the remount. */
15337
+ assignIndicatorZTop(id) {
15338
+ if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.topZ() + 1);
15339
+ }
15334
15340
  forgetIndicatorZ(id) {
15335
15341
  this.seriesZ.delete(id);
15336
15342
  }
@@ -25087,6 +25093,19 @@ function rendererLayers() {
25087
25093
  return [...registry3.values()];
25088
25094
  }
25089
25095
 
25096
+ // src/renderers/native/core/layerStacking.ts
25097
+ function stackLayers(entries, candleZ) {
25098
+ const keyed = entries.map((e) => ({
25099
+ id: e.id,
25100
+ key: e.ownerZ ?? (e.placement === "below-data" ? -Infinity : candleZ)
25101
+ }));
25102
+ keyed.sort((a, b) => a.key - b.key);
25103
+ const below = [];
25104
+ const above = [];
25105
+ for (const k of keyed) (k.key < candleZ ? below : above).push(k.id);
25106
+ return { below, above };
25107
+ }
25108
+
25090
25109
  // src/renderers/native/chrome/luxalgo-logos.ts
25091
25110
  var LUXALGO_SYMBOL_SVG = '<svg viewBox="0 0 45 40" aria-hidden="true"><g fill="currentColor"><path d="m40.25 38 4.58-7.998L28.802 2l-16.03 28 9.16-.001 6.87-12z"/><path d="M34.525 32.002 9.33 31.997 27.655 0h-9.158L.18 31.993 4.759 40h34.347z"/></g></svg>';
25092
25111
  var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M 614 94.500 L 614 189 633.500 189 L 653 189 653 94.500 L 653 0 633.500 0 L 614 0 614 94.500 M 0 97.500 L 0 189 63.023 189 L 126.045 189 125.773 170.750 L 125.500 152.500 84.250 152.239 L 43 151.978 43 78.989 L 43 6 21.500 6 L 0 6 0 97.500 M 450.387 96.451 C 421.871 146.253, 398.306 186.994, 398.020 186.987 C 397.423 186.972, 362 124.724, 362 123.691 C 362 123.316, 370.100 108.818, 380 91.472 C 389.900 74.127, 398 59.500, 398 58.968 C 398 58.380, 391.007 58, 380.210 58 L 362.420 58 348.060 83.138 C 334.400 107.052, 333.637 108.160, 332.399 105.888 C 331.682 104.575, 325.287 93.375, 318.187 81 L 305.277 58.500 287.139 58.227 C 277.162 58.078, 269 58.186, 269 58.469 C 269 58.751, 277.154 73.274, 287.120 90.741 C 297.086 108.209, 305.261 122.983, 305.287 123.574 C 305.312 124.164, 297.318 138.789, 287.522 156.074 C 277.725 173.358, 269.524 187.838, 269.296 188.250 C 269.068 188.662, 276.977 189, 286.870 189 L 304.859 189 319.163 163.959 C 331.321 142.674, 333.647 139.165, 334.670 140.563 C 335.331 141.468, 341.864 152.737, 349.186 165.606 L 362.500 189.003 402.442 188.752 L 442.384 188.500 472.080 136.695 C 488.413 108.203, 501.939 84.729, 502.138 84.531 C 502.337 84.333, 516 107.757, 532.500 136.583 L 562.500 188.994 584.750 188.997 C 596.987 188.999, 607 188.854, 607 188.675 C 607 188.319, 503.014 6.686, 502.532 6.201 C 502.369 6.037, 478.904 46.649, 450.387 96.451 M 717.500 55.017 C 697.175 57.389, 680.641 68.752, 672.036 86.264 C 665.866 98.818, 663.840 108.024, 663.840 123.500 C 663.840 133.409, 664.328 138.355, 665.892 144.300 C 672.076 167.810, 689.191 184.757, 711.529 189.489 C 730.549 193.518, 747.264 189.540, 761.282 177.648 C 764.944 174.542, 768.178 172, 768.470 172 C 769.345 172, 769.030 185.018, 767.979 192.285 C 765.618 208.613, 755.148 218.672, 738.473 220.632 C 732.586 221.324, 723.136 219.599, 717.709 216.843 C 712.693 214.295, 706.636 207.946, 703.972 202.443 C 702.789 199.999, 701.749 198, 701.661 198.001 C 700.201 198.007, 665.596 205.071, 665.230 205.437 C 664.326 206.341, 669.156 218.313, 672.607 223.721 C 682.146 238.672, 700.008 248.257, 723.500 251.033 C 758.132 255.124, 788.930 241.875, 800.287 218 C 806.923 204.047, 807 203.001, 807 126.542 L 807 58 788.500 58 L 770 58 770 66.500 C 770 71.175, 769.641 75, 769.203 75 C 768.765 75, 766.627 73.142, 764.453 70.872 C 752.805 58.708, 735.614 52.903, 717.500 55.017 M 878.500 55.031 C 850.107 59.412, 830.755 75.064, 822.285 100.500 C 819.905 107.644, 819.614 110.051, 819.565 123 C 819.501 139.581, 820.786 145.825, 826.763 158 C 832.104 168.880, 842.842 179.424, 854.077 184.822 C 867.651 191.343, 873.132 192.462, 891.500 192.462 C 909.868 192.462, 915.349 191.343, 928.923 184.822 C 944.653 177.265, 956.667 162.306, 961.710 144 C 963.074 139.048, 963.481 134.047, 963.418 123 C 963.346 110.140, 963.038 107.595, 960.694 100.500 C 953.325 78.193, 936.695 62.854, 913.500 56.969 C 904.262 54.625, 887.240 53.682, 878.500 55.031 M 134.015 102.750 C 134.027 139.250, 134.310 148.689, 135.549 153.947 C 141.139 177.663, 159.815 193, 183.105 193 C 196.453 193, 210.163 186.171, 217.936 175.650 C 219.826 173.093, 221.738 171, 222.186 171 C 222.634 171, 223 175.050, 223 180 L 223 189 242.507 189 L 262.014 189 261.757 123.750 L 261.500 58.500 242.500 58.500 L 223.500 58.500 223 99.500 C 222.513 139.406, 222.441 140.610, 220.289 144.634 C 209.729 164.377, 183.014 164.880, 174.299 145.500 C 172.677 141.895, 172.469 137.382, 172.190 99.750 L 171.880 58 152.940 58 L 134 58 134.015 102.750 M 729.932 87.998 C 719.311 89.821, 710.155 97.164, 705.141 107.880 C 702.851 112.772, 702.510 114.798, 702.505 123.521 C 702.500 132.834, 702.732 134.014, 705.787 140.218 C 709.761 148.290, 715.989 154.097, 723.853 157.063 C 731.409 159.912, 744.359 159.466, 750.795 156.136 C 767.289 147.601, 774.103 127.713, 767.019 108.780 C 761.433 93.851, 746.051 85.232, 729.932 87.998 M 883.972 88.348 C 865.749 92.310, 854.873 109.966, 858.119 130.316 C 860.933 147.963, 872.346 158.830, 889.084 159.799 C 900.714 160.472, 906.867 158.234, 914.650 150.497 C 919.856 145.323, 921.123 143.340, 922.900 137.587 C 925.412 129.456, 925.655 118.570, 923.489 111.275 C 918.479 94.405, 901.478 84.541, 883.972 88.348"/></svg>';
@@ -25587,6 +25606,8 @@ var NativeRenderer = class {
25587
25606
  this.volumeRenderer = new VolumeRenderer();
25588
25607
  /** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
25589
25608
  this.extLayers = [];
25609
+ /** Last applied layer-canvas order (ids below + above the data canvas) — re-slotted only on change. */
25610
+ this.layerOrderSig = "";
25590
25611
  // The attribution mark (see chrome/AttributionMark + the NOTICE file): default-on;
25591
25612
  // disabling requires an equivalent visible attribution elsewhere in the host UI.
25592
25613
  this.attributionEl = null;
@@ -26517,9 +26538,7 @@ var NativeRenderer = class {
26517
26538
  if (el.getAttribute("data-vela-screenshot") === "under") rasterizeOverlay(ctx, el, frame);
26518
26539
  }
26519
26540
  }
26520
- const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
26521
- const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
26522
- for (const canvas of [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas]) {
26541
+ for (const canvas of [...this.canvasPile(), this.chromeCanvas, this.drawingsCanvas]) {
26523
26542
  if (canvas && canvas.width > 0 && canvas.height > 0) ctx.drawImage(canvas, 0, 0);
26524
26543
  }
26525
26544
  if (frame) {
@@ -26653,6 +26672,7 @@ var NativeRenderer = class {
26653
26672
  const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
26654
26673
  const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
26655
26674
  this.plot.append(...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
26675
+ this.layerOrderSig = "";
26656
26676
  this.wrapper.appendChild(this.plot);
26657
26677
  this.factoryConfig = this.getConfig();
26658
26678
  this.attributionEl = this.buildAttributionEl();
@@ -27181,7 +27201,8 @@ var NativeRenderer = class {
27181
27201
  mountIndicator(model) {
27182
27202
  this.scene.indicators.set(model.id, model);
27183
27203
  this.refreshAnchorOffset(model);
27184
- this.scene.assignIndicatorZ(model.id);
27204
+ if (model.native && this.extLayers.some((l) => l.def.id === model.native.type)) this.scene.assignIndicatorZTop(model.id);
27205
+ else this.scene.assignIndicatorZ(model.id);
27185
27206
  this.inputsUI.upsert(model.id, model.shorttitle ?? model.title, model.inputs, model.inputValues, model.paneId, {
27186
27207
  native: !!model.native,
27187
27208
  ...model.shorttitle ? { settingsTitle: model.title } : {}
@@ -28000,10 +28021,17 @@ var NativeRenderer = class {
28000
28021
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
28001
28022
  for (const l of this.extLayers) {
28002
28023
  if (!l.def.repaintOnCursor) continue;
28003
- l.instance.render(this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs));
28024
+ const lp = this.layerPane(l.def.id) ?? pane;
28025
+ if (lp.collapsed) continue;
28026
+ l.instance.render(this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs));
28004
28027
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
28005
28028
  }
28006
28029
  }
28030
+ /** Blank one SDK layer canvas (a collapsed host pane suppresses the layer's painting). */
28031
+ clearLayerCanvas(canvas) {
28032
+ if (canvas.width === 0 || canvas.height === 0) return;
28033
+ canvas.getContext("2d")?.clearRect(0, 0, canvas.width, canvas.height);
28034
+ }
28007
28035
  /** One frame's args for an SDK renderer layer (shared by the data + cursor paint paths). */
28008
28036
  extLayerArgs(id, scale, bounds, nowMs) {
28009
28037
  return {
@@ -28022,6 +28050,7 @@ var NativeRenderer = class {
28022
28050
  }
28023
28051
  /** Paint the below-data (L-1) + geometry (L0) + chrome (L1) layers from the current scene/coords. */
28024
28052
  paintData() {
28053
+ this.syncLayerCanvasOrder();
28025
28054
  this.stampScaleInvert();
28026
28055
  this.backend.modelAlpha = this.modelAlpha;
28027
28056
  this.backend.candleBodyAlpha = this.candleBodyAlpha;
@@ -28052,9 +28081,14 @@ var NativeRenderer = class {
28052
28081
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
28053
28082
  let folded = null;
28054
28083
  for (const l of this.extLayers) {
28055
- const args = this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs);
28084
+ const lp = this.layerPane(l.def.id) ?? pane;
28085
+ if (lp.collapsed) {
28086
+ this.clearLayerCanvas(l.canvas);
28087
+ continue;
28088
+ }
28089
+ const args = this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs);
28056
28090
  l.instance.render(args);
28057
- folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
28091
+ if (lp === pane) folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
28058
28092
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
28059
28093
  }
28060
28094
  if (folded) {
@@ -28216,6 +28250,9 @@ var NativeRenderer = class {
28216
28250
  pane.scaleTarget = { min: 0, max: maxVol / VOLUME_PANE_FILL_FRAC };
28217
28251
  pane.axisFormat = "volume";
28218
28252
  }
28253
+ } else if (this.layerNativesOwnPane(pane, masterModels)) {
28254
+ pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
28255
+ pane.percentBaseline = this.bars[i0]?.close ?? 0;
28219
28256
  }
28220
28257
  if (pane === pricePane && this.scene.tradeMarkers.visible && pane.bounds.height > 0) {
28221
28258
  const th = this.tradesScaleHints(vr);
@@ -28302,6 +28339,59 @@ var NativeRenderer = class {
28302
28339
  }
28303
28340
  return null;
28304
28341
  }
28342
+ /** The mounted native indicator that OWNS an SDK layer — the one whose type equals the
28343
+ * layer id (the id doubles as the data channel, so the pairing is the SDK's own
28344
+ * contract). Null for chart-type channels and while the owner is hidden (a hidden
28345
+ * indicator leaves the scene; its cleared data channel paints nothing anyway). */
28346
+ layerOwner(layerId) {
28347
+ for (const m of this.scene.indicators.values()) {
28348
+ if (m.native?.type === layerId) return m;
28349
+ }
28350
+ return null;
28351
+ }
28352
+ /** The pane an SDK layer paints on: its owner's pane, else the price pane. */
28353
+ layerPane(layerId) {
28354
+ const owner = this.layerOwner(layerId);
28355
+ const paneId = owner ? owner.paneId ?? PRICE_PANE_ID : PRICE_PANE_ID;
28356
+ return this.scene.panes.get(paneId) ?? null;
28357
+ }
28358
+ /** The SDK layer canvases split around the data canvas, each side back-to-front:
28359
+ * owned layers by their owner's z key against the candles' (an indicator restacked
28360
+ * below the candles takes its layer canvas along), unowned by declared placement. */
28361
+ orderedLayerCanvases() {
28362
+ const byId = new Map(this.extLayers.map((l) => [l.def.id, l.canvas]));
28363
+ const { below, above } = stackLayers(
28364
+ this.extLayers.map((l) => {
28365
+ const owner = this.layerOwner(l.def.id);
28366
+ return {
28367
+ id: l.def.id,
28368
+ placement: l.def.placement === "below-data" ? "below-data" : "above-data",
28369
+ ownerZ: owner ? this.scene.zOf(owner.id) : null
28370
+ };
28371
+ }),
28372
+ this.scene.candleZ
28373
+ );
28374
+ return { below: below.map((id) => byId.get(id)), above: above.map((id) => byId.get(id)) };
28375
+ }
28376
+ /** The full canvas pile in paint order (layers + data/volume/vpvr) — what the DOM
28377
+ * stacking and the screenshot compositor must both follow. */
28378
+ canvasPile() {
28379
+ const { below, above } = this.orderedLayerCanvases();
28380
+ return [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above];
28381
+ }
28382
+ /** Re-slot the SDK layer canvases in the plot when the computed order changed (a z
28383
+ * write, a restored config, an indicator mount/remove/restack). Runs at the top of
28384
+ * every data frame; a no-op when the signature is unchanged. Re-inserting an
28385
+ * absolutely-positioned, pointer-transparent canvas repaints nothing by itself. */
28386
+ syncLayerCanvasOrder() {
28387
+ if (this.extLayers.length === 0 || !this.plot) return;
28388
+ const { below, above } = this.orderedLayerCanvases();
28389
+ const sig = [...below.map((c) => this.extLayers.find((l) => l.canvas === c).def.id), "|", ...above.map((c) => this.extLayers.find((l) => l.canvas === c).def.id)].join(",");
28390
+ if (sig === this.layerOrderSig) return;
28391
+ this.layerOrderSig = sig;
28392
+ for (const c of below) this.plot.insertBefore(c, this.dataCanvas);
28393
+ for (const c of above) this.plot.insertBefore(c, this.chromeCanvas);
28394
+ }
28305
28395
  /** True when an active volume layer is this study pane's ONLY content — so its scale should
28306
28396
  * come from volume, not the empty {0,1} placeholder. (In the price pane, or alongside a real
28307
28397
  * series, volume stays a bottom overlay and the master scale wins.) */
@@ -28311,6 +28401,15 @@ var NativeRenderer = class {
28311
28401
  if (this.nativeLayerPane("volume") !== pane) return false;
28312
28402
  return masterModels.every((m) => m.native?.type === "volume");
28313
28403
  }
28404
+ /** True when this study pane's master content is only SDK-layer natives (series-less
28405
+ * models whose type names a mounted layer) — its scale then follows the visible bars
28406
+ * (see the call site). Any real master series takes over the scale as usual. */
28407
+ layerNativesOwnPane(pane, masterModels) {
28408
+ if (pane.kind === "price" || masterModels.length === 0) return false;
28409
+ return masterModels.every(
28410
+ (m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type)
28411
+ );
28412
+ }
28314
28413
  /** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
28315
28414
  * band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
28316
28415
  * Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
@@ -31708,6 +31807,7 @@ var CSS8 = `
31708
31807
  .vela-sp-badge[data-p='hyperliquid'] { color: #50d2c1; } /* palette-exempt: venue brand mark */
31709
31808
  .vela-sp-empty { padding: var(--vela-space-3); color: var(--vela-fg-muted); text-align: center; }
31710
31809
  `;
31810
+ var PAGE = 100;
31711
31811
  var SymbolPicker = class {
31712
31812
  constructor(opts) {
31713
31813
  this.source = () => [];
@@ -31715,6 +31815,7 @@ var SymbolPicker = class {
31715
31815
  this.highlighted = 0;
31716
31816
  this.seed = "";
31717
31817
  this.activeTab = "All";
31818
+ this.visible = PAGE;
31718
31819
  const doc = (opts.host ?? document.body).ownerDocument;
31719
31820
  injectStyles(STYLE_ID11, CSS8, doc);
31720
31821
  this.input = doc.createElement("input");
@@ -31741,6 +31842,12 @@ var SymbolPicker = class {
31741
31842
  }
31742
31843
  this.list = doc.createElement("div");
31743
31844
  this.list.className = "vela-sp-list";
31845
+ this.list.addEventListener("scroll", () => {
31846
+ if (this.rows.length < this.visible) return;
31847
+ if (this.list.scrollTop + this.list.clientHeight < this.list.scrollHeight - 200) return;
31848
+ this.visible += PAGE;
31849
+ this.grow();
31850
+ });
31744
31851
  this.dialog = new Dialog({
31745
31852
  title: "Symbol Search",
31746
31853
  host: opts.host,
@@ -31806,11 +31913,15 @@ var SymbolPicker = class {
31806
31913
  } else delete el.dataset.highlighted;
31807
31914
  });
31808
31915
  }
31809
- refresh() {
31810
- const doc = this.list.ownerDocument;
31916
+ computeRows() {
31811
31917
  const TAB_TYPES = { Crypto: ["crypto"], Stocks: ["stock"], ETFs: ["etf"], Forex: ["forex"], Commodities: ["commodity"] };
31812
31918
  const pool = this.activeTab === "All" ? this.source() : this.source().filter((s) => TAB_TYPES[this.activeTab]?.includes((s.type ?? "").toLowerCase()) || this.activeTab === "Crypto" && (s.type ?? "").toLowerCase() === "futures");
31813
- this.rows = filterSymbols(pool, this.input.value);
31919
+ return filterSymbols(pool, this.input.value, this.visible);
31920
+ }
31921
+ refresh() {
31922
+ const doc = this.list.ownerDocument;
31923
+ this.visible = PAGE;
31924
+ this.rows = this.computeRows();
31814
31925
  this.highlighted = 0;
31815
31926
  this.list.replaceChildren();
31816
31927
  if (!this.rows.length) {
@@ -31820,34 +31931,42 @@ var SymbolPicker = class {
31820
31931
  this.list.appendChild(empty);
31821
31932
  return;
31822
31933
  }
31823
- for (const s of this.rows) {
31824
- const row = doc.createElement("div");
31825
- row.className = "vela-sp-row";
31826
- row.dataset.ticker = s.ticker;
31827
- const venue = s.prefix ?? s.provider;
31828
- if (venue) row.dataset.venue = venue;
31829
- const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
31830
- const main = doc.createElement("span");
31831
- main.className = "vela-sp-main";
31832
- const t = doc.createElement("span");
31833
- t.className = "vela-sp-ticker";
31834
- t.textContent = s.ticker;
31835
- const d = doc.createElement("span");
31836
- d.className = "vela-sp-desc";
31837
- d.textContent = s.description ?? (s.type ?? "");
31838
- main.append(t, d);
31839
- row.append(av, main);
31840
- if (venue) {
31841
- const badge = doc.createElement("span");
31842
- badge.className = "vela-sp-badge";
31843
- badge.dataset.p = s.provider ?? venue;
31844
- badge.textContent = venue;
31845
- row.appendChild(badge);
31846
- }
31847
- this.list.appendChild(row);
31848
- }
31934
+ for (const s of this.rows) this.list.appendChild(this.rowEl(s));
31849
31935
  this.renderHighlight();
31850
31936
  }
31937
+ /** Append the page the grown `visible` just uncovered — rows already on screen stay put. */
31938
+ grow() {
31939
+ const already = this.rows.length;
31940
+ this.rows = this.computeRows();
31941
+ for (const s of this.rows.slice(already)) this.list.appendChild(this.rowEl(s));
31942
+ }
31943
+ rowEl(s) {
31944
+ const doc = this.list.ownerDocument;
31945
+ const row = doc.createElement("div");
31946
+ row.className = "vela-sp-row";
31947
+ row.dataset.ticker = s.ticker;
31948
+ const venue = s.prefix ?? s.provider;
31949
+ if (venue) row.dataset.venue = venue;
31950
+ const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
31951
+ const main = doc.createElement("span");
31952
+ main.className = "vela-sp-main";
31953
+ const t = doc.createElement("span");
31954
+ t.className = "vela-sp-ticker";
31955
+ t.textContent = s.ticker;
31956
+ const d = doc.createElement("span");
31957
+ d.className = "vela-sp-desc";
31958
+ d.textContent = s.description ?? (s.type ?? "");
31959
+ main.append(t, d);
31960
+ row.append(av, main);
31961
+ if (venue) {
31962
+ const badge = doc.createElement("span");
31963
+ badge.className = "vela-sp-badge";
31964
+ badge.dataset.p = s.provider ?? venue;
31965
+ badge.textContent = venue;
31966
+ row.appendChild(badge);
31967
+ }
31968
+ return row;
31969
+ }
31851
31970
  };
31852
31971
 
31853
31972
  // src/widget/object-tree-model.ts
package/dist/widget.d.cts CHANGED
@@ -521,6 +521,7 @@ declare class SymbolPicker {
521
521
  private seed;
522
522
  private activeTab;
523
523
  private tabs;
524
+ private visible;
524
525
  constructor(opts: SymbolPickerOptions);
525
526
  /** Wire where symbols come from (re-called on every widget rebuild). */
526
527
  setSource(source: () => readonly SymbolDescriptor[]): void;
@@ -530,7 +531,11 @@ declare class SymbolPicker {
530
531
  private select;
531
532
  private moveHighlight;
532
533
  private renderHighlight;
534
+ private computeRows;
533
535
  private refresh;
536
+ /** Append the page the grown `visible` just uncovered — rows already on screen stay put. */
537
+ private grow;
538
+ private rowEl;
534
539
  }
535
540
 
536
541
  interface IndicatorRow {
package/dist/widget.d.ts CHANGED
@@ -521,6 +521,7 @@ declare class SymbolPicker {
521
521
  private seed;
522
522
  private activeTab;
523
523
  private tabs;
524
+ private visible;
524
525
  constructor(opts: SymbolPickerOptions);
525
526
  /** Wire where symbols come from (re-called on every widget rebuild). */
526
527
  setSource(source: () => readonly SymbolDescriptor[]): void;
@@ -530,7 +531,11 @@ declare class SymbolPicker {
530
531
  private select;
531
532
  private moveHighlight;
532
533
  private renderHighlight;
534
+ private computeRows;
533
535
  private refresh;
536
+ /** Append the page the grown `visible` just uncovered — rows already on screen stay put. */
537
+ private grow;
538
+ private rowEl;
534
539
  }
535
540
 
536
541
  interface IndicatorRow {
package/dist/widget.js CHANGED
@@ -1,7 +1,7 @@
1
- import { SessionShadingTracker, Glider, WidgetHistory, localStorageAdapter, decodeState, prefixedSymbol, normalizeSession, SymbolPicker, IndicatorPicker, TimeframeQuick, Topbar, PanelDock, ObjectTree, DataWindow, ChartContextMenu, Toast, Watermark, Statusline, MarketStatusTracker, Bottombar, MobileBar, DrawingPill, LayoutModeController, ZOOM_IN, ZOOM_OUT, PAN_FAST, ShortcutsHelp, TimeframeDrawer, RANGE_PRESETS, DrawingsDrawer, MoreDrawer, priceStyleIcon, priceStyleLabel, TimezoneDrawer, PriceScaleDrawer, timeframeMs, indicatorLedger, sanitizeState, legacyWidgetState, parsePersisted, statuslineInkOf, toolShortcutHints, resolveIndicators, encodeState } from './chunk-3QX6QOCX.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-3QX6QOCX.js';
3
- import { registerBuiltinChartTypes, resolveTheme, parseSymbol, buildToolbar, priceStyleIds, Vela, normalizeTimezone } from './chunk-6FA4TDUF.js';
4
- export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-6FA4TDUF.js';
1
+ import { SessionShadingTracker, Glider, WidgetHistory, localStorageAdapter, decodeState, prefixedSymbol, normalizeSession, SymbolPicker, IndicatorPicker, TimeframeQuick, Topbar, PanelDock, ObjectTree, DataWindow, ChartContextMenu, Toast, Watermark, Statusline, MarketStatusTracker, Bottombar, MobileBar, DrawingPill, LayoutModeController, ZOOM_IN, ZOOM_OUT, PAN_FAST, ShortcutsHelp, TimeframeDrawer, RANGE_PRESETS, DrawingsDrawer, MoreDrawer, priceStyleIcon, priceStyleLabel, TimezoneDrawer, PriceScaleDrawer, timeframeMs, indicatorLedger, sanitizeState, legacyWidgetState, parsePersisted, statuslineInkOf, toolShortcutHints, resolveIndicators, encodeState } from './chunk-N6MBJHMV.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-N6MBJHMV.js';
3
+ import { registerBuiltinChartTypes, resolveTheme, parseSymbol, buildToolbar, priceStyleIds, Vela, normalizeTimezone } from './chunk-FQBT4NFM.js';
4
+ export { TIMEZONES, normalizeTimezone, tzButtonLabel, tzMenuLabel, tzOffset } from './chunk-FQBT4NFM.js';
5
5
  import { widgetActions, widgetAttachments, legendActionsProviderFor, resolveEngines } from './chunk-BEJ57HP4.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-BEJ57HP4.js';
7
7
  import { KeymapManager, isEditableTarget } from './chunk-2R7BANEM.js';