@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.
@@ -5699,6 +5699,12 @@ var SceneGraph = class {
5699
5699
  assignIndicatorZ(id) {
5700
5700
  if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.bottomZ() - 1);
5701
5701
  }
5702
+ /** Mount-time default for a LAYER-BACKED native (it paints on a canvas stacked above the
5703
+ * data canvas by default): top of the stack, so the recorded order tells the truth from
5704
+ * the first frame. Keeps an existing key, so a restored stack survives the remount. */
5705
+ assignIndicatorZTop(id) {
5706
+ if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.topZ() + 1);
5707
+ }
5702
5708
  forgetIndicatorZ(id) {
5703
5709
  this.seriesZ.delete(id);
5704
5710
  }
@@ -14717,6 +14723,19 @@ var VolumeRenderer = class {
14717
14723
  }
14718
14724
  };
14719
14725
 
14726
+ // src/renderers/native/core/layerStacking.ts
14727
+ function stackLayers(entries, candleZ) {
14728
+ const keyed = entries.map((e) => ({
14729
+ id: e.id,
14730
+ key: e.ownerZ ?? (e.placement === "below-data" ? -Infinity : candleZ)
14731
+ }));
14732
+ keyed.sort((a, b) => a.key - b.key);
14733
+ const below = [];
14734
+ const above = [];
14735
+ for (const k of keyed) (k.key < candleZ ? below : above).push(k.id);
14736
+ return { below, above };
14737
+ }
14738
+
14720
14739
  // src/renderers/native/chrome/luxalgo-logos.ts
14721
14740
  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>';
14722
14741
  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>';
@@ -15217,6 +15236,8 @@ var NativeRenderer = class {
15217
15236
  this.volumeRenderer = new VolumeRenderer();
15218
15237
  /** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
15219
15238
  this.extLayers = [];
15239
+ /** Last applied layer-canvas order (ids below + above the data canvas) — re-slotted only on change. */
15240
+ this.layerOrderSig = "";
15220
15241
  // The attribution mark (see chrome/AttributionMark + the NOTICE file): default-on;
15221
15242
  // disabling requires an equivalent visible attribution elsewhere in the host UI.
15222
15243
  this.attributionEl = null;
@@ -16147,9 +16168,7 @@ var NativeRenderer = class {
16147
16168
  if (el.getAttribute("data-vela-screenshot") === "under") rasterizeOverlay(ctx, el, frame);
16148
16169
  }
16149
16170
  }
16150
- const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
16151
- const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
16152
- for (const canvas of [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas]) {
16171
+ for (const canvas of [...this.canvasPile(), this.chromeCanvas, this.drawingsCanvas]) {
16153
16172
  if (canvas && canvas.width > 0 && canvas.height > 0) ctx.drawImage(canvas, 0, 0);
16154
16173
  }
16155
16174
  if (frame) {
@@ -16283,6 +16302,7 @@ var NativeRenderer = class {
16283
16302
  const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
16284
16303
  const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
16285
16304
  this.plot.append(...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
16305
+ this.layerOrderSig = "";
16286
16306
  this.wrapper.appendChild(this.plot);
16287
16307
  this.factoryConfig = this.getConfig();
16288
16308
  this.attributionEl = this.buildAttributionEl();
@@ -16811,7 +16831,8 @@ var NativeRenderer = class {
16811
16831
  mountIndicator(model) {
16812
16832
  this.scene.indicators.set(model.id, model);
16813
16833
  this.refreshAnchorOffset(model);
16814
- this.scene.assignIndicatorZ(model.id);
16834
+ if (model.native && this.extLayers.some((l) => l.def.id === model.native.type)) this.scene.assignIndicatorZTop(model.id);
16835
+ else this.scene.assignIndicatorZ(model.id);
16815
16836
  this.inputsUI.upsert(model.id, model.shorttitle ?? model.title, model.inputs, model.inputValues, model.paneId, {
16816
16837
  native: !!model.native,
16817
16838
  ...model.shorttitle ? { settingsTitle: model.title } : {}
@@ -17630,10 +17651,17 @@ var NativeRenderer = class {
17630
17651
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
17631
17652
  for (const l of this.extLayers) {
17632
17653
  if (!l.def.repaintOnCursor) continue;
17633
- l.instance.render(this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs));
17654
+ const lp = this.layerPane(l.def.id) ?? pane;
17655
+ if (lp.collapsed) continue;
17656
+ l.instance.render(this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs));
17634
17657
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
17635
17658
  }
17636
17659
  }
17660
+ /** Blank one SDK layer canvas (a collapsed host pane suppresses the layer's painting). */
17661
+ clearLayerCanvas(canvas) {
17662
+ if (canvas.width === 0 || canvas.height === 0) return;
17663
+ canvas.getContext("2d")?.clearRect(0, 0, canvas.width, canvas.height);
17664
+ }
17637
17665
  /** One frame's args for an SDK renderer layer (shared by the data + cursor paint paths). */
17638
17666
  extLayerArgs(id, scale, bounds, nowMs) {
17639
17667
  return {
@@ -17652,6 +17680,7 @@ var NativeRenderer = class {
17652
17680
  }
17653
17681
  /** Paint the below-data (L-1) + geometry (L0) + chrome (L1) layers from the current scene/coords. */
17654
17682
  paintData() {
17683
+ this.syncLayerCanvasOrder();
17655
17684
  this.stampScaleInvert();
17656
17685
  this.backend.modelAlpha = this.modelAlpha;
17657
17686
  this.backend.candleBodyAlpha = this.candleBodyAlpha;
@@ -17682,9 +17711,14 @@ var NativeRenderer = class {
17682
17711
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
17683
17712
  let folded = null;
17684
17713
  for (const l of this.extLayers) {
17685
- const args = this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs);
17714
+ const lp = this.layerPane(l.def.id) ?? pane;
17715
+ if (lp.collapsed) {
17716
+ this.clearLayerCanvas(l.canvas);
17717
+ continue;
17718
+ }
17719
+ const args = this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs);
17686
17720
  l.instance.render(args);
17687
- folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
17721
+ if (lp === pane) folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
17688
17722
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
17689
17723
  }
17690
17724
  if (folded) {
@@ -17846,6 +17880,9 @@ var NativeRenderer = class {
17846
17880
  pane.scaleTarget = { min: 0, max: maxVol / VOLUME_PANE_FILL_FRAC };
17847
17881
  pane.axisFormat = "volume";
17848
17882
  }
17883
+ } else if (this.layerNativesOwnPane(pane, masterModels)) {
17884
+ pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
17885
+ pane.percentBaseline = this.bars[i0]?.close ?? 0;
17849
17886
  }
17850
17887
  if (pane === pricePane && this.scene.tradeMarkers.visible && pane.bounds.height > 0) {
17851
17888
  const th = this.tradesScaleHints(vr);
@@ -17932,6 +17969,59 @@ var NativeRenderer = class {
17932
17969
  }
17933
17970
  return null;
17934
17971
  }
17972
+ /** The mounted native indicator that OWNS an SDK layer — the one whose type equals the
17973
+ * layer id (the id doubles as the data channel, so the pairing is the SDK's own
17974
+ * contract). Null for chart-type channels and while the owner is hidden (a hidden
17975
+ * indicator leaves the scene; its cleared data channel paints nothing anyway). */
17976
+ layerOwner(layerId) {
17977
+ for (const m of this.scene.indicators.values()) {
17978
+ if (m.native?.type === layerId) return m;
17979
+ }
17980
+ return null;
17981
+ }
17982
+ /** The pane an SDK layer paints on: its owner's pane, else the price pane. */
17983
+ layerPane(layerId) {
17984
+ const owner = this.layerOwner(layerId);
17985
+ const paneId = owner ? owner.paneId ?? PRICE_PANE_ID : PRICE_PANE_ID;
17986
+ return this.scene.panes.get(paneId) ?? null;
17987
+ }
17988
+ /** The SDK layer canvases split around the data canvas, each side back-to-front:
17989
+ * owned layers by their owner's z key against the candles' (an indicator restacked
17990
+ * below the candles takes its layer canvas along), unowned by declared placement. */
17991
+ orderedLayerCanvases() {
17992
+ const byId = new Map(this.extLayers.map((l) => [l.def.id, l.canvas]));
17993
+ const { below, above } = stackLayers(
17994
+ this.extLayers.map((l) => {
17995
+ const owner = this.layerOwner(l.def.id);
17996
+ return {
17997
+ id: l.def.id,
17998
+ placement: l.def.placement === "below-data" ? "below-data" : "above-data",
17999
+ ownerZ: owner ? this.scene.zOf(owner.id) : null
18000
+ };
18001
+ }),
18002
+ this.scene.candleZ
18003
+ );
18004
+ return { below: below.map((id) => byId.get(id)), above: above.map((id) => byId.get(id)) };
18005
+ }
18006
+ /** The full canvas pile in paint order (layers + data/volume/vpvr) — what the DOM
18007
+ * stacking and the screenshot compositor must both follow. */
18008
+ canvasPile() {
18009
+ const { below, above } = this.orderedLayerCanvases();
18010
+ return [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above];
18011
+ }
18012
+ /** Re-slot the SDK layer canvases in the plot when the computed order changed (a z
18013
+ * write, a restored config, an indicator mount/remove/restack). Runs at the top of
18014
+ * every data frame; a no-op when the signature is unchanged. Re-inserting an
18015
+ * absolutely-positioned, pointer-transparent canvas repaints nothing by itself. */
18016
+ syncLayerCanvasOrder() {
18017
+ if (this.extLayers.length === 0 || !this.plot) return;
18018
+ const { below, above } = this.orderedLayerCanvases();
18019
+ 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(",");
18020
+ if (sig === this.layerOrderSig) return;
18021
+ this.layerOrderSig = sig;
18022
+ for (const c of below) this.plot.insertBefore(c, this.dataCanvas);
18023
+ for (const c of above) this.plot.insertBefore(c, this.chromeCanvas);
18024
+ }
17935
18025
  /** True when an active volume layer is this study pane's ONLY content — so its scale should
17936
18026
  * come from volume, not the empty {0,1} placeholder. (In the price pane, or alongside a real
17937
18027
  * series, volume stays a bottom overlay and the master scale wins.) */
@@ -17941,6 +18031,15 @@ var NativeRenderer = class {
17941
18031
  if (this.nativeLayerPane("volume") !== pane) return false;
17942
18032
  return masterModels.every((m) => m.native?.type === "volume");
17943
18033
  }
18034
+ /** True when this study pane's master content is only SDK-layer natives (series-less
18035
+ * models whose type names a mounted layer) — its scale then follows the visible bars
18036
+ * (see the call site). Any real master series takes over the scale as usual. */
18037
+ layerNativesOwnPane(pane, masterModels) {
18038
+ if (pane.kind === "price" || masterModels.length === 0) return false;
18039
+ return masterModels.every(
18040
+ (m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type)
18041
+ );
18042
+ }
17944
18043
  /** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
17945
18044
  * band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
17946
18045
  * Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
@@ -1,4 +1,4 @@
1
- import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-6FA4TDUF.js';
1
+ import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-FQBT4NFM.js';
2
2
  import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-BEJ57HP4.js';
3
3
  import { Tooltip, Drawer } from './chunk-2R7BANEM.js';
4
4
  import { Menu, Dialog } from './chunk-OICPLNU7.js';
@@ -1751,6 +1751,7 @@ var CSS6 = `
1751
1751
  .vela-sp-badge[data-p='hyperliquid'] { color: #50d2c1; } /* palette-exempt: venue brand mark */
1752
1752
  .vela-sp-empty { padding: var(--vela-space-3); color: var(--vela-fg-muted); text-align: center; }
1753
1753
  `;
1754
+ var PAGE = 100;
1754
1755
  var SymbolPicker = class {
1755
1756
  constructor(opts) {
1756
1757
  this.source = () => [];
@@ -1758,6 +1759,7 @@ var SymbolPicker = class {
1758
1759
  this.highlighted = 0;
1759
1760
  this.seed = "";
1760
1761
  this.activeTab = "All";
1762
+ this.visible = PAGE;
1761
1763
  const doc = (opts.host ?? document.body).ownerDocument;
1762
1764
  injectStyles(STYLE_ID6, CSS6, doc);
1763
1765
  this.input = doc.createElement("input");
@@ -1784,6 +1786,12 @@ var SymbolPicker = class {
1784
1786
  }
1785
1787
  this.list = doc.createElement("div");
1786
1788
  this.list.className = "vela-sp-list";
1789
+ this.list.addEventListener("scroll", () => {
1790
+ if (this.rows.length < this.visible) return;
1791
+ if (this.list.scrollTop + this.list.clientHeight < this.list.scrollHeight - 200) return;
1792
+ this.visible += PAGE;
1793
+ this.grow();
1794
+ });
1787
1795
  this.dialog = new Dialog({
1788
1796
  title: "Symbol Search",
1789
1797
  host: opts.host,
@@ -1849,11 +1857,15 @@ var SymbolPicker = class {
1849
1857
  } else delete el.dataset.highlighted;
1850
1858
  });
1851
1859
  }
1852
- refresh() {
1853
- const doc = this.list.ownerDocument;
1860
+ computeRows() {
1854
1861
  const TAB_TYPES = { Crypto: ["crypto"], Stocks: ["stock"], ETFs: ["etf"], Forex: ["forex"], Commodities: ["commodity"] };
1855
1862
  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");
1856
- this.rows = filterSymbols(pool, this.input.value);
1863
+ return filterSymbols(pool, this.input.value, this.visible);
1864
+ }
1865
+ refresh() {
1866
+ const doc = this.list.ownerDocument;
1867
+ this.visible = PAGE;
1868
+ this.rows = this.computeRows();
1857
1869
  this.highlighted = 0;
1858
1870
  this.list.replaceChildren();
1859
1871
  if (!this.rows.length) {
@@ -1863,34 +1875,42 @@ var SymbolPicker = class {
1863
1875
  this.list.appendChild(empty);
1864
1876
  return;
1865
1877
  }
1866
- for (const s of this.rows) {
1867
- const row = doc.createElement("div");
1868
- row.className = "vela-sp-row";
1869
- row.dataset.ticker = s.ticker;
1870
- const venue = s.prefix ?? s.provider;
1871
- if (venue) row.dataset.venue = venue;
1872
- const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
1873
- const main = doc.createElement("span");
1874
- main.className = "vela-sp-main";
1875
- const t = doc.createElement("span");
1876
- t.className = "vela-sp-ticker";
1877
- t.textContent = s.ticker;
1878
- const d = doc.createElement("span");
1879
- d.className = "vela-sp-desc";
1880
- d.textContent = s.description ?? (s.type ?? "");
1881
- main.append(t, d);
1882
- row.append(av, main);
1883
- if (venue) {
1884
- const badge = doc.createElement("span");
1885
- badge.className = "vela-sp-badge";
1886
- badge.dataset.p = s.provider ?? venue;
1887
- badge.textContent = venue;
1888
- row.appendChild(badge);
1889
- }
1890
- this.list.appendChild(row);
1891
- }
1878
+ for (const s of this.rows) this.list.appendChild(this.rowEl(s));
1892
1879
  this.renderHighlight();
1893
1880
  }
1881
+ /** Append the page the grown `visible` just uncovered — rows already on screen stay put. */
1882
+ grow() {
1883
+ const already = this.rows.length;
1884
+ this.rows = this.computeRows();
1885
+ for (const s of this.rows.slice(already)) this.list.appendChild(this.rowEl(s));
1886
+ }
1887
+ rowEl(s) {
1888
+ const doc = this.list.ownerDocument;
1889
+ const row = doc.createElement("div");
1890
+ row.className = "vela-sp-row";
1891
+ row.dataset.ticker = s.ticker;
1892
+ const venue = s.prefix ?? s.provider;
1893
+ if (venue) row.dataset.venue = venue;
1894
+ const av = tickerIconEl(doc, baseOf(s), s.ticker, "vela-sp-avatar");
1895
+ const main = doc.createElement("span");
1896
+ main.className = "vela-sp-main";
1897
+ const t = doc.createElement("span");
1898
+ t.className = "vela-sp-ticker";
1899
+ t.textContent = s.ticker;
1900
+ const d = doc.createElement("span");
1901
+ d.className = "vela-sp-desc";
1902
+ d.textContent = s.description ?? (s.type ?? "");
1903
+ main.append(t, d);
1904
+ row.append(av, main);
1905
+ if (venue) {
1906
+ const badge = doc.createElement("span");
1907
+ badge.className = "vela-sp-badge";
1908
+ badge.dataset.p = s.provider ?? venue;
1909
+ badge.textContent = venue;
1910
+ row.appendChild(badge);
1911
+ }
1912
+ return row;
1913
+ }
1894
1914
  };
1895
1915
 
1896
1916
  // src/widget/object-tree-model.ts
package/dist/index.cjs CHANGED
@@ -15344,6 +15344,12 @@ var SceneGraph = class {
15344
15344
  assignIndicatorZ(id) {
15345
15345
  if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.bottomZ() - 1);
15346
15346
  }
15347
+ /** Mount-time default for a LAYER-BACKED native (it paints on a canvas stacked above the
15348
+ * data canvas by default): top of the stack, so the recorded order tells the truth from
15349
+ * the first frame. Keeps an existing key, so a restored stack survives the remount. */
15350
+ assignIndicatorZTop(id) {
15351
+ if (!this.seriesZ.has(id)) this.seriesZ.set(id, this.topZ() + 1);
15352
+ }
15347
15353
  forgetIndicatorZ(id) {
15348
15354
  this.seriesZ.delete(id);
15349
15355
  }
@@ -25102,6 +25108,19 @@ function rendererLayers() {
25102
25108
  return [...registry3.values()];
25103
25109
  }
25104
25110
 
25111
+ // src/renderers/native/core/layerStacking.ts
25112
+ function stackLayers(entries, candleZ) {
25113
+ const keyed = entries.map((e) => ({
25114
+ id: e.id,
25115
+ key: e.ownerZ ?? (e.placement === "below-data" ? -Infinity : candleZ)
25116
+ }));
25117
+ keyed.sort((a, b) => a.key - b.key);
25118
+ const below = [];
25119
+ const above = [];
25120
+ for (const k of keyed) (k.key < candleZ ? below : above).push(k.id);
25121
+ return { below, above };
25122
+ }
25123
+
25105
25124
  // src/renderers/native/chrome/luxalgo-logos.ts
25106
25125
  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>';
25107
25126
  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>';
@@ -25602,6 +25621,8 @@ var NativeRenderer = class {
25602
25621
  this.volumeRenderer = new VolumeRenderer();
25603
25622
  /** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
25604
25623
  this.extLayers = [];
25624
+ /** Last applied layer-canvas order (ids below + above the data canvas) — re-slotted only on change. */
25625
+ this.layerOrderSig = "";
25605
25626
  // The attribution mark (see chrome/AttributionMark + the NOTICE file): default-on;
25606
25627
  // disabling requires an equivalent visible attribution elsewhere in the host UI.
25607
25628
  this.attributionEl = null;
@@ -26532,9 +26553,7 @@ var NativeRenderer = class {
26532
26553
  if (el.getAttribute("data-vela-screenshot") === "under") rasterizeOverlay(ctx, el, frame);
26533
26554
  }
26534
26555
  }
26535
- const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
26536
- const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
26537
- for (const canvas of [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas]) {
26556
+ for (const canvas of [...this.canvasPile(), this.chromeCanvas, this.drawingsCanvas]) {
26538
26557
  if (canvas && canvas.width > 0 && canvas.height > 0) ctx.drawImage(canvas, 0, 0);
26539
26558
  }
26540
26559
  if (frame) {
@@ -26668,6 +26687,7 @@ var NativeRenderer = class {
26668
26687
  const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
26669
26688
  const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
26670
26689
  this.plot.append(...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
26690
+ this.layerOrderSig = "";
26671
26691
  this.wrapper.appendChild(this.plot);
26672
26692
  this.factoryConfig = this.getConfig();
26673
26693
  this.attributionEl = this.buildAttributionEl();
@@ -27196,7 +27216,8 @@ var NativeRenderer = class {
27196
27216
  mountIndicator(model) {
27197
27217
  this.scene.indicators.set(model.id, model);
27198
27218
  this.refreshAnchorOffset(model);
27199
- this.scene.assignIndicatorZ(model.id);
27219
+ if (model.native && this.extLayers.some((l) => l.def.id === model.native.type)) this.scene.assignIndicatorZTop(model.id);
27220
+ else this.scene.assignIndicatorZ(model.id);
27200
27221
  this.inputsUI.upsert(model.id, model.shorttitle ?? model.title, model.inputs, model.inputValues, model.paneId, {
27201
27222
  native: !!model.native,
27202
27223
  ...model.shorttitle ? { settingsTitle: model.title } : {}
@@ -28015,10 +28036,17 @@ var NativeRenderer = class {
28015
28036
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
28016
28037
  for (const l of this.extLayers) {
28017
28038
  if (!l.def.repaintOnCursor) continue;
28018
- l.instance.render(this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs));
28039
+ const lp = this.layerPane(l.def.id) ?? pane;
28040
+ if (lp.collapsed) continue;
28041
+ l.instance.render(this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs));
28019
28042
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
28020
28043
  }
28021
28044
  }
28045
+ /** Blank one SDK layer canvas (a collapsed host pane suppresses the layer's painting). */
28046
+ clearLayerCanvas(canvas) {
28047
+ if (canvas.width === 0 || canvas.height === 0) return;
28048
+ canvas.getContext("2d")?.clearRect(0, 0, canvas.width, canvas.height);
28049
+ }
28022
28050
  /** One frame's args for an SDK renderer layer (shared by the data + cursor paint paths). */
28023
28051
  extLayerArgs(id, scale, bounds, nowMs) {
28024
28052
  return {
@@ -28037,6 +28065,7 @@ var NativeRenderer = class {
28037
28065
  }
28038
28066
  /** Paint the below-data (L-1) + geometry (L0) + chrome (L1) layers from the current scene/coords. */
28039
28067
  paintData() {
28068
+ this.syncLayerCanvasOrder();
28040
28069
  this.stampScaleInvert();
28041
28070
  this.backend.modelAlpha = this.modelAlpha;
28042
28071
  this.backend.candleBodyAlpha = this.candleBodyAlpha;
@@ -28067,9 +28096,14 @@ var NativeRenderer = class {
28067
28096
  const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
28068
28097
  let folded = null;
28069
28098
  for (const l of this.extLayers) {
28070
- const args = this.extLayerArgs(l.def.id, pane.scale, pane.bounds, nowMs);
28099
+ const lp = this.layerPane(l.def.id) ?? pane;
28100
+ if (lp.collapsed) {
28101
+ this.clearLayerCanvas(l.canvas);
28102
+ continue;
28103
+ }
28104
+ const args = this.extLayerArgs(l.def.id, lp.scale, lp.bounds, nowMs);
28071
28105
  l.instance.render(args);
28072
- folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
28106
+ if (lp === pane) folded = foldBaseModulation(folded, l.instance.modulateBase?.(args) ?? null);
28073
28107
  if (this.animZoom && l.instance.animating?.()) this.animator.start();
28074
28108
  }
28075
28109
  if (folded) {
@@ -28231,6 +28265,9 @@ var NativeRenderer = class {
28231
28265
  pane.scaleTarget = { min: 0, max: maxVol / VOLUME_PANE_FILL_FRAC };
28232
28266
  pane.axisFormat = "volume";
28233
28267
  }
28268
+ } else if (this.layerNativesOwnPane(pane, masterModels)) {
28269
+ pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
28270
+ pane.percentBaseline = this.bars[i0]?.close ?? 0;
28234
28271
  }
28235
28272
  if (pane === pricePane && this.scene.tradeMarkers.visible && pane.bounds.height > 0) {
28236
28273
  const th = this.tradesScaleHints(vr);
@@ -28317,6 +28354,59 @@ var NativeRenderer = class {
28317
28354
  }
28318
28355
  return null;
28319
28356
  }
28357
+ /** The mounted native indicator that OWNS an SDK layer — the one whose type equals the
28358
+ * layer id (the id doubles as the data channel, so the pairing is the SDK's own
28359
+ * contract). Null for chart-type channels and while the owner is hidden (a hidden
28360
+ * indicator leaves the scene; its cleared data channel paints nothing anyway). */
28361
+ layerOwner(layerId) {
28362
+ for (const m of this.scene.indicators.values()) {
28363
+ if (m.native?.type === layerId) return m;
28364
+ }
28365
+ return null;
28366
+ }
28367
+ /** The pane an SDK layer paints on: its owner's pane, else the price pane. */
28368
+ layerPane(layerId) {
28369
+ const owner = this.layerOwner(layerId);
28370
+ const paneId = owner ? owner.paneId ?? PRICE_PANE_ID : PRICE_PANE_ID;
28371
+ return this.scene.panes.get(paneId) ?? null;
28372
+ }
28373
+ /** The SDK layer canvases split around the data canvas, each side back-to-front:
28374
+ * owned layers by their owner's z key against the candles' (an indicator restacked
28375
+ * below the candles takes its layer canvas along), unowned by declared placement. */
28376
+ orderedLayerCanvases() {
28377
+ const byId = new Map(this.extLayers.map((l) => [l.def.id, l.canvas]));
28378
+ const { below, above } = stackLayers(
28379
+ this.extLayers.map((l) => {
28380
+ const owner = this.layerOwner(l.def.id);
28381
+ return {
28382
+ id: l.def.id,
28383
+ placement: l.def.placement === "below-data" ? "below-data" : "above-data",
28384
+ ownerZ: owner ? this.scene.zOf(owner.id) : null
28385
+ };
28386
+ }),
28387
+ this.scene.candleZ
28388
+ );
28389
+ return { below: below.map((id) => byId.get(id)), above: above.map((id) => byId.get(id)) };
28390
+ }
28391
+ /** The full canvas pile in paint order (layers + data/volume/vpvr) — what the DOM
28392
+ * stacking and the screenshot compositor must both follow. */
28393
+ canvasPile() {
28394
+ const { below, above } = this.orderedLayerCanvases();
28395
+ return [...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above];
28396
+ }
28397
+ /** Re-slot the SDK layer canvases in the plot when the computed order changed (a z
28398
+ * write, a restored config, an indicator mount/remove/restack). Runs at the top of
28399
+ * every data frame; a no-op when the signature is unchanged. Re-inserting an
28400
+ * absolutely-positioned, pointer-transparent canvas repaints nothing by itself. */
28401
+ syncLayerCanvasOrder() {
28402
+ if (this.extLayers.length === 0 || !this.plot) return;
28403
+ const { below, above } = this.orderedLayerCanvases();
28404
+ 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(",");
28405
+ if (sig === this.layerOrderSig) return;
28406
+ this.layerOrderSig = sig;
28407
+ for (const c of below) this.plot.insertBefore(c, this.dataCanvas);
28408
+ for (const c of above) this.plot.insertBefore(c, this.chromeCanvas);
28409
+ }
28320
28410
  /** True when an active volume layer is this study pane's ONLY content — so its scale should
28321
28411
  * come from volume, not the empty {0,1} placeholder. (In the price pane, or alongside a real
28322
28412
  * series, volume stays a bottom overlay and the master scale wins.) */
@@ -28326,6 +28416,15 @@ var NativeRenderer = class {
28326
28416
  if (this.nativeLayerPane("volume") !== pane) return false;
28327
28417
  return masterModels.every((m) => m.native?.type === "volume");
28328
28418
  }
28419
+ /** True when this study pane's master content is only SDK-layer natives (series-less
28420
+ * models whose type names a mounted layer) — its scale then follows the visible bars
28421
+ * (see the call site). Any real master series takes over the scale as usual. */
28422
+ layerNativesOwnPane(pane, masterModels) {
28423
+ if (pane.kind === "price" || masterModels.length === 0) return false;
28424
+ return masterModels.every(
28425
+ (m) => m.series.length === 0 && !!m.native && this.extLayers.some((l) => l.def.id === m.native.type)
28426
+ );
28427
+ }
28329
28428
  /** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
28330
28429
  * band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
28331
28430
  * Top-to-bottom order. Every pane is independent — the price pane from the scene setting,
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@ export { B as BarsChangeReason, C as ContextSelect, b as DataControl, c as Drawi
3
3
  import { D as Drawing, S as SerializedDrawing, U as Unsubscribe, L as LineStyle, P as PriceStyle, I as IChartRenderer, R as RendererCapabilities, e as RendererDisplayOptions, f as IndicatorRenderHandle, g as IndicatorStatus, c as VelaTheme, O as OHLCV, h as Pane, i as PaneAction, j as MoveTarget, k as IndicatorModel, l as ScenePatch, m as InputValue, n as SymbolPickerFn, o as LegendActionView, p as InputChangeEvent, T as ThemeName, C as CrosshairEvent, q as ClickEvent, A as AxisLongPressEvent, a as VisibleRange, r as DataWindowReadout, s as IDrawingsRendererPort, t as Millis, u as SnapMode, v as DrawingTypeKey, w as ToolbarDefinition, M as MarketConfig } from './options-YdiaaVjt.cjs';
4
4
  export { x as AddIndicatorOptions, B as Background, y as BoxFontFamily, z as BoxHAlign, E as BoxTextSize, F as BoxVAlign, G as CandleBarColor, H as CandleSeries, J as CandleStyle, K as DataWindowGroup, Q as DataWindowOHLC, W as DataWindowRow, X as DirtyRange, Y as DrawingBox, Z as DrawingExtend, _ as DrawingIntent, $ as DrawingLabel, a0 as DrawingLine, a1 as DrawingLinefill, a2 as DrawingMode, a3 as DrawingPoint, a4 as DrawingPolyline, a5 as DrawingStyle, a6 as DrawingTable, a7 as DrawingText, a8 as DrawingXLoc, a9 as DrawingsOption, aa as Fill, ab as FillGradientStop, ac as IndicatorMeta, ad as InputCondition, ae as InputSchema, af as InputType, ag as InputWhen, ah as LabelStyle, ai as LabelYLoc, aj as LineLikeKind, ak as LineLikeSeries, al as LineLikeStyle, am as MarkerPoint, an as MarkerSeries, ao as MarketSnapshot, ap as MarketSwitch, aq as PaneHint, ar as PaneKind, as as PolylinePoint, at as PriceLine, au as Projector, av as ProviderName, aw as RendererConstructor, ax as Scene, ay as SchemaPatch, az as SeriesKind, aA as SeriesPoint, aB as SeriesSpec, aC as SeriesValueDelta, aD as SettingsField, aE as SettingsSchema, aF as TableCell, aG as TableMerge, aH as TablePosition, aI as ToolbarGroupConfig, aJ as TradeExecution, aK as ValuePatch, b as VelaOptions, V as VisibleRangePreset, aL as buildToolbar, aM as defaultToolbar, aN as inputVisible } from './options-YdiaaVjt.cjs';
5
5
  import { M as MarketDataFeed, D as DataProvider, P as ProviderInfo, a as SymbolDescriptor, S as SymbolInfo, b as ProviderCapabilities, B as BarRange } from './DataProvider-DrN7ZIou.cjs';
6
- export { A as ACCENT, a as ACCENT_BRIGHT, B as BEARISH, b as BULLISH, c as BarTransform, d as BasePaintingModulation, C as CATEGORICAL, e as CROSSHAIR, f as ChartTypeDefinition, g as ChartTypeSettingsInstance, h as ChartTypeSettingsSection, i as ChartTypeSettingsSubsection, D as DrawingTypeMeta, H as HIGHLIGHT, I as INFO, j as INVALID, k as IdentifiableKind, M as MARKER, N as NEUTRAL, l as NormalizedSettingsRow, R as RendererLayerArgs, m as RendererLayerDefinition, n as RendererLayerInstance, S as SERIES_LINE, o as SESSION_OFF, p as SESSION_POST, q as SESSION_PRE, r as SLATE, s as SLATE_DEEP, t as SeriesDataEngine, u as SeriesDataEngineHost, v as SettingsInlineControl, w as SettingsRowCondition, x as SettingsRowDescriptor, y as SettingsRowInlineNumber, z as SettingsRowSwatch, E as SettingsRowValueKey, F as SettingsRowWhen, G as SettingsRowWidth, J as SettingsSelectOption, K as SettingsValueRow, T as TRADE_EXIT, L as TRADE_LONG, O as TRADE_SHORT, V as VALID, W as WARNING, P as categoricalColor, Q as chartType, U as chartTypes, X as createDrawing, Y as deserializeDrawing, Z as drawingTypes, _ as getDrawingType, $ as normalizeSettingsRow, a0 as registerChartType, a1 as registerDrawingType, a2 as registerRendererDefaults, a3 as registerRendererLayer, a4 as rendererDefaults, a5 as rendererLayers, a6 as settingsRowValueKeys, a7 as stableSeriesId, a8 as tickerModifierIds, a9 as unregisterChartType, aa as unregisterRendererDefaults, ab as unregisterRendererLayer } from './plugin-CkiStRaI.cjs';
6
+ export { A as ACCENT, a as ACCENT_BRIGHT, B as BEARISH, b as BULLISH, c as BarTransform, d as BasePaintingModulation, C as CATEGORICAL, e as CROSSHAIR, f as ChartTypeDefinition, g as ChartTypeSettingsInstance, h as ChartTypeSettingsSection, i as ChartTypeSettingsSubsection, D as DrawingTypeMeta, H as HIGHLIGHT, I as INFO, j as INVALID, k as IdentifiableKind, M as MARKER, N as NEUTRAL, l as NormalizedSettingsRow, R as RendererLayerArgs, m as RendererLayerDefinition, n as RendererLayerInstance, S as SERIES_LINE, o as SESSION_OFF, p as SESSION_POST, q as SESSION_PRE, r as SLATE, s as SLATE_DEEP, t as SeriesDataEngine, u as SeriesDataEngineHost, v as SettingsInlineControl, w as SettingsRowCondition, x as SettingsRowDescriptor, y as SettingsRowInlineNumber, z as SettingsRowSwatch, E as SettingsRowValueKey, F as SettingsRowWhen, G as SettingsRowWidth, J as SettingsSelectOption, K as SettingsValueRow, T as TRADE_EXIT, L as TRADE_LONG, O as TRADE_SHORT, V as VALID, W as WARNING, P as categoricalColor, Q as chartType, U as chartTypes, X as createDrawing, Y as deserializeDrawing, Z as drawingTypes, _ as getDrawingType, $ as normalizeSettingsRow, a0 as registerChartType, a1 as registerDrawingType, a2 as registerRendererDefaults, a3 as registerRendererLayer, a4 as rendererDefaults, a5 as rendererLayers, a6 as settingsRowValueKeys, a7 as stableSeriesId, a8 as tickerModifierIds, a9 as unregisterChartType, aa as unregisterRendererDefaults, ab as unregisterRendererLayer } from './plugin-DO_DX0gp.cjs';
7
7
  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';
8
8
  export { i as iconMarkup, r as registerIcon } from './icons-BZYbJXSV.cjs';
9
9
  export { a as KeyBindingDescriptor, R as ResolvedBinding } from './keymap-CGOz5F5f.cjs';
@@ -342,6 +342,8 @@ declare class NativeRenderer implements IChartRenderer {
342
342
  private readonly volumeRenderer;
343
343
  /** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
344
344
  private extLayers;
345
+ /** Last applied layer-canvas order (ids below + above the data canvas) — re-slotted only on change. */
346
+ private layerOrderSig;
345
347
  private attributionEl;
346
348
  private attributionEnabled;
347
349
  /** Host-supplied mark shown INSTEAD of the built-in one (`attribution: '<html>'`). */
@@ -800,6 +802,8 @@ declare class NativeRenderer implements IChartRenderer {
800
802
  private renderFrame;
801
803
  /** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
802
804
  private repaintCursorLayers;
805
+ /** Blank one SDK layer canvas (a collapsed host pane suppresses the layer's painting). */
806
+ private clearLayerCanvas;
803
807
  /** One frame's args for an SDK renderer layer (shared by the data + cursor paint paths). */
804
808
  private extLayerArgs;
805
809
  /** Paint the below-data (L-1) + geometry (L0) + chrome (L1) layers from the current scene/coords. */
@@ -857,10 +861,33 @@ declare class NativeRenderer implements IChartRenderer {
857
861
  private paneMasterTitle;
858
862
  /** The pane a bespoke-layer native indicator (volume/vpvr) currently lives in (null = not mounted). */
859
863
  private nativeLayerPane;
864
+ /** The mounted native indicator that OWNS an SDK layer — the one whose type equals the
865
+ * layer id (the id doubles as the data channel, so the pairing is the SDK's own
866
+ * contract). Null for chart-type channels and while the owner is hidden (a hidden
867
+ * indicator leaves the scene; its cleared data channel paints nothing anyway). */
868
+ private layerOwner;
869
+ /** The pane an SDK layer paints on: its owner's pane, else the price pane. */
870
+ private layerPane;
871
+ /** The SDK layer canvases split around the data canvas, each side back-to-front:
872
+ * owned layers by their owner's z key against the candles' (an indicator restacked
873
+ * below the candles takes its layer canvas along), unowned by declared placement. */
874
+ private orderedLayerCanvases;
875
+ /** The full canvas pile in paint order (layers + data/volume/vpvr) — what the DOM
876
+ * stacking and the screenshot compositor must both follow. */
877
+ private canvasPile;
878
+ /** Re-slot the SDK layer canvases in the plot when the computed order changed (a z
879
+ * write, a restored config, an indicator mount/remove/restack). Runs at the top of
880
+ * every data frame; a no-op when the signature is unchanged. Re-inserting an
881
+ * absolutely-positioned, pointer-transparent canvas repaints nothing by itself. */
882
+ private syncLayerCanvasOrder;
860
883
  /** True when an active volume layer is this study pane's ONLY content — so its scale should
861
884
  * come from volume, not the empty {0,1} placeholder. (In the price pane, or alongside a real
862
885
  * series, volume stays a bottom overlay and the master scale wins.) */
863
886
  private volumeOwnsPane;
887
+ /** True when this study pane's master content is only SDK-layer natives (series-less
888
+ * models whose type names a mounted layer) — its scale then follows the visible bars
889
+ * (see the call site). Any real master series takes over the scale as usual. */
890
+ private layerNativesOwnPane;
864
891
  /** Per-pane scale state for a host UI (e.g. a price-axis context menu): the pane's pixel
865
892
  * band (`top`/`height`, so a click y maps to a pane) plus its current axis `mode`/`log`.
866
893
  * Top-to-bottom order. Every pane is independent — the price pane from the scene setting,