@ericchen1990/pano-view 3.2.2 → 3.3.0

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/index.cjs CHANGED
@@ -7548,6 +7548,7 @@ var PanoViewer = (0, import_react26.forwardRef)(
7548
7548
  import_fiber10.Canvas,
7549
7549
  {
7550
7550
  "aria-label": `${ariaLabel} canvas`,
7551
+ resize: { debounce: 0 },
7551
7552
  camera: {
7552
7553
  far: DEFAULT_PANORAMA_CAMERA_FAR,
7553
7554
  fov: normalizedInitialView.fov,
@@ -9698,8 +9699,8 @@ function usePanoViewer() {
9698
9699
  }
9699
9700
 
9700
9701
  // src/sphere-tile/sphere-tile.tsx
9701
- var import_fiber15 = require("@react-three/fiber");
9702
- var import_react38 = require("react");
9702
+ var import_fiber16 = require("@react-three/fiber");
9703
+ var import_react39 = require("react");
9703
9704
  var import_three14 = require("three");
9704
9705
 
9705
9706
  // src/asset-url.ts
@@ -9988,6 +9989,9 @@ var TileTextureManager = class {
9988
9989
  this.generation = 0;
9989
9990
  this.options = options;
9990
9991
  }
9992
+ get memoryBudgetBytes() {
9993
+ return this.options.maxBytes;
9994
+ }
9991
9995
  resume() {
9992
9996
  this.disposed = false;
9993
9997
  this.drain();
@@ -10055,7 +10059,7 @@ var TileTextureManager = class {
10055
10059
  this.totalBytes = 0;
10056
10060
  }
10057
10061
  /**
10058
- * Releases textures that are no longer rendered by any Tile instance.
10062
+ * Releases textures that are no longer rendered by any tile source.
10059
10063
  * Scene transitions call this after the outgoing panorama has been
10060
10064
  * snapshotted, so the incoming scene never competes with stale GPU tiles.
10061
10065
  */
@@ -10189,14 +10193,45 @@ var TileTextureManager = class {
10189
10193
  }
10190
10194
  };
10191
10195
 
10192
- // src/tile/texture-hooks.ts
10196
+ // src/tile/texture-manager-context.tsx
10197
+ var import_fiber15 = require("@react-three/fiber");
10193
10198
  var import_react37 = require("react");
10199
+ var import_jsx_runtime15 = require("react/jsx-runtime");
10200
+ var TileTextureManagerContext = (0, import_react37.createContext)(null);
10201
+ function TileTextureManagerProvider({
10202
+ children,
10203
+ maxTextureMemoryMb = 128,
10204
+ maxConcurrentLoads = 4
10205
+ }) {
10206
+ const gl = (0, import_fiber15.useThree)((state) => state.gl);
10207
+ const anisotropy = gl.capabilities.getMaxAnisotropy();
10208
+ const manager = (0, import_react37.useMemo)(
10209
+ () => new TileTextureManager({
10210
+ anisotropy,
10211
+ maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
10212
+ concurrency: Math.max(1, Math.floor(maxConcurrentLoads)),
10213
+ retryCount: 1
10214
+ }),
10215
+ [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
10216
+ );
10217
+ (0, import_react37.useEffect)(() => {
10218
+ manager.resume();
10219
+ return () => manager.dispose();
10220
+ }, [manager]);
10221
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TileTextureManagerContext.Provider, { value: manager, children });
10222
+ }
10223
+ function useSharedTileTextureManager() {
10224
+ return (0, import_react37.useContext)(TileTextureManagerContext);
10225
+ }
10226
+
10227
+ // src/tile/texture-hooks.ts
10228
+ var import_react38 = require("react");
10194
10229
  var import_three12 = require("three");
10195
10230
  function usePreviewTexture(url, anisotropy, onError) {
10196
- const [texture, setTexture] = (0, import_react37.useState)(null);
10197
- const onErrorRef = (0, import_react37.useRef)(onError);
10231
+ const [texture, setTexture] = (0, import_react38.useState)(null);
10232
+ const onErrorRef = (0, import_react38.useRef)(onError);
10198
10233
  onErrorRef.current = onError;
10199
- (0, import_react37.useEffect)(() => {
10234
+ (0, import_react38.useEffect)(() => {
10200
10235
  if (!url) {
10201
10236
  setTexture(null);
10202
10237
  return;
@@ -10233,7 +10268,7 @@ function usePreviewTexture(url, anisotropy, onError) {
10233
10268
  active = false;
10234
10269
  };
10235
10270
  }, [anisotropy, url]);
10236
- (0, import_react37.useEffect)(
10271
+ (0, import_react38.useEffect)(
10237
10272
  () => () => {
10238
10273
  texture?.dispose();
10239
10274
  },
@@ -10242,14 +10277,14 @@ function usePreviewTexture(url, anisotropy, onError) {
10242
10277
  return texture;
10243
10278
  }
10244
10279
  function useManagedTexture(manager, key, url, address) {
10245
- const [managedState, setManagedState] = (0, import_react37.useState)(() => ({
10280
+ const [managedState, setManagedState] = (0, import_react38.useState)(() => ({
10246
10281
  manager,
10247
10282
  key,
10248
10283
  url,
10249
10284
  snapshot: manager.getSnapshot(key)
10250
10285
  }));
10251
10286
  const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
10252
- (0, import_react37.useEffect)(
10287
+ (0, import_react38.useEffect)(
10253
10288
  () => manager.acquire(key, url, address, (nextSnapshot) => {
10254
10289
  setManagedState({
10255
10290
  manager,
@@ -10391,14 +10426,14 @@ function preferredSphereLevel(levels, height, dpr, verticalFov) {
10391
10426
  }
10392
10427
 
10393
10428
  // src/sphere-tile/sphere-tile.tsx
10394
- var import_jsx_runtime15 = require("react/jsx-runtime");
10429
+ var import_jsx_runtime16 = require("react/jsx-runtime");
10395
10430
  function SpherePatch({
10396
10431
  address,
10397
10432
  texture,
10398
10433
  yawOffset,
10399
10434
  preview = false
10400
10435
  }) {
10401
- const geometry = (0, import_react38.useMemo)(
10436
+ const geometry = (0, import_react39.useMemo)(
10402
10437
  () => sphereTileGeometry(
10403
10438
  address,
10404
10439
  yawOffset,
@@ -10406,13 +10441,13 @@ function SpherePatch({
10406
10441
  ),
10407
10442
  [address, yawOffset, preview]
10408
10443
  );
10409
- (0, import_react38.useEffect)(() => () => geometry.dispose(), [geometry]);
10410
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10444
+ (0, import_react39.useEffect)(() => () => geometry.dispose(), [geometry]);
10445
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10411
10446
  "mesh",
10412
10447
  {
10413
10448
  geometry,
10414
10449
  renderOrder: preview ? -1e3 : -900 + address.level,
10415
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10450
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10416
10451
  PanoBasicMaterial,
10417
10452
  {
10418
10453
  map: texture,
@@ -10425,7 +10460,7 @@ function SpherePatch({
10425
10460
  }
10426
10461
  );
10427
10462
  }
10428
- var ManagedPatch = (0, import_react38.memo)(function ManagedPatch2({
10463
+ var ManagedPatch = (0, import_react39.memo)(function ManagedPatch2({
10429
10464
  address,
10430
10465
  manager,
10431
10466
  resolveUrl,
@@ -10439,10 +10474,10 @@ var ManagedPatch = (0, import_react38.memo)(function ManagedPatch2({
10439
10474
  url,
10440
10475
  address
10441
10476
  );
10442
- (0, import_react38.useEffect)(() => {
10477
+ (0, import_react39.useEffect)(() => {
10443
10478
  if (snapshot.texture) ready();
10444
10479
  }, [snapshot.texture, ready]);
10445
- return snapshot.texture ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10480
+ return snapshot.texture ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10446
10481
  SpherePatch,
10447
10482
  {
10448
10483
  address,
@@ -10470,28 +10505,29 @@ function SphereTileSource({
10470
10505
  onTileError,
10471
10506
  onLevelChange
10472
10507
  }) {
10473
- const { camera, gl, size } = (0, import_fiber15.useThree)();
10508
+ const { camera, gl, size } = (0, import_fiber16.useThree)();
10474
10509
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10475
10510
  const preview = usePreviewTexture(
10476
10511
  resolveAssetUrl(baseUrl, previewUrl),
10477
10512
  anisotropy,
10478
10513
  onPreviewError
10479
10514
  );
10480
- const callbacks = (0, import_react38.useRef)({
10515
+ const callbacks = (0, import_react39.useRef)({
10481
10516
  onReady,
10482
10517
  onLoadProgress,
10483
10518
  onTileError,
10484
10519
  onLevelChange
10485
10520
  });
10486
10521
  callbacks.current = { onReady, onLoadProgress, onTileError, onLevelChange };
10487
- const budget = Math.max(
10522
+ const sharedManager = useSharedTileTextureManager();
10523
+ const ownedBudget = Math.max(
10488
10524
  1,
10489
10525
  Number.isFinite(maxTextureMemoryMb) ? maxTextureMemoryMb : 128
10490
10526
  ) * 1024 * 1024;
10491
- const manager = (0, import_react38.useMemo)(
10527
+ const ownedManager = (0, import_react39.useMemo)(
10492
10528
  () => new TileTextureManager({
10493
10529
  anisotropy,
10494
- maxBytes: budget,
10530
+ maxBytes: ownedBudget,
10495
10531
  concurrency: Math.max(
10496
10532
  1,
10497
10533
  Math.floor(
@@ -10505,30 +10541,38 @@ function SphereTileSource({
10505
10541
  onProgress: (progress) => callbacks.current.onLoadProgress?.(progress),
10506
10542
  onError: (error) => callbacks.current.onTileError?.(error)
10507
10543
  }),
10508
- [anisotropy, budget, maxConcurrentLoads, retryCount]
10544
+ [anisotropy, ownedBudget, maxConcurrentLoads, retryCount]
10509
10545
  );
10510
- const readyRef = (0, import_react38.useRef)(false);
10511
- const ready = (0, import_react38.useCallback)(() => {
10546
+ const manager = sharedManager ?? ownedManager;
10547
+ const budget = manager.memoryBudgetBytes;
10548
+ const readyRef = (0, import_react39.useRef)(false);
10549
+ const ready = (0, import_react39.useCallback)(() => {
10512
10550
  if (!readyRef.current) {
10513
10551
  readyRef.current = true;
10514
10552
  callbacks.current.onReady?.();
10515
10553
  }
10516
10554
  }, []);
10517
- (0, import_react38.useEffect)(() => {
10555
+ (0, import_react39.useEffect)(() => {
10518
10556
  if (preview) ready();
10519
10557
  }, [preview, ready]);
10520
- (0, import_react38.useEffect)(() => {
10558
+ (0, import_react39.useEffect)(() => {
10521
10559
  manager.resume();
10522
- return () => manager.dispose();
10523
- }, [manager]);
10524
- const resolveUrl = (0, import_react38.useMemo)(
10560
+ return () => {
10561
+ if (manager === ownedManager) {
10562
+ manager.dispose();
10563
+ return;
10564
+ }
10565
+ queueMicrotask(() => manager.releaseUnused());
10566
+ };
10567
+ }, [manager, ownedManager]);
10568
+ const resolveUrl = (0, import_react39.useMemo)(
10525
10569
  () => (address) => resolveAssetUrl(
10526
10570
  baseUrl,
10527
10571
  resolveTileUrl ? resolveTileUrl(address) : resolveSphereTemplateUrl(urlTemplate, address, indexBase)
10528
10572
  ),
10529
10573
  [baseUrl, resolveTileUrl, urlTemplate, indexBase]
10530
10574
  );
10531
- const candidates = (0, import_react38.useMemo)(
10575
+ const candidates = (0, import_react39.useMemo)(
10532
10576
  () => config.levels.map(
10533
10577
  (level, index) => sphereTileAddresses(level, index, config.tileSize).map((address) => ({
10534
10578
  address,
@@ -10537,12 +10581,12 @@ function SphereTileSource({
10537
10581
  ),
10538
10582
  [config, yawOffset]
10539
10583
  );
10540
- const previewAddress = (0, import_react38.useMemo)(
10584
+ const previewAddress = (0, import_react39.useMemo)(
10541
10585
  () => sphereTileAddresses({ width: 2, height: 1 }, 0, 2)[0],
10542
10586
  []
10543
10587
  );
10544
- const [layers, setLayers] = (0, import_react38.useState)([]);
10545
- const scratch = (0, import_react38.useMemo)(
10588
+ const [layers, setLayers] = (0, import_react39.useState)([]);
10589
+ const scratch = (0, import_react39.useMemo)(
10546
10590
  () => ({
10547
10591
  camera: new import_three14.PerspectiveCamera(),
10548
10592
  frustum: new import_three14.Frustum(),
@@ -10560,7 +10604,7 @@ function SphereTileSource({
10560
10604
  }),
10561
10605
  [camera, candidates]
10562
10606
  );
10563
- (0, import_fiber15.useFrame)(() => {
10607
+ (0, import_fiber16.useFrame)(() => {
10564
10608
  if (!(camera instanceof import_three14.PerspectiveCamera)) return;
10565
10609
  const now = performance.now(), dpr = gl.getPixelRatio();
10566
10610
  const reset = scratch.manager !== manager || scratch.loadMode !== loadMode;
@@ -10611,8 +10655,8 @@ function SphereTileSource({
10611
10655
  setLayers(next);
10612
10656
  }
10613
10657
  });
10614
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("group", { visible, children: [
10615
- preview ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10658
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("group", { visible, children: [
10659
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10616
10660
  SpherePatch,
10617
10661
  {
10618
10662
  address: previewAddress,
@@ -10622,7 +10666,7 @@ function SphereTileSource({
10622
10666
  }
10623
10667
  ) : null,
10624
10668
  loadMode !== "preview" ? layers.flatMap(
10625
- (layer) => layer.addresses.map((address) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
10669
+ (layer) => layer.addresses.map((address) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10626
10670
  ManagedPatch,
10627
10671
  {
10628
10672
  address,
@@ -10638,23 +10682,23 @@ function SphereTileSource({
10638
10682
  }
10639
10683
  function SphereTile({ multires, ...props }) {
10640
10684
  const token = typeof multires === "string" ? multires : JSON.stringify(multires);
10641
- const config = (0, import_react38.useMemo)(() => parseSphereMultires(multires), [token]);
10685
+ const config = (0, import_react39.useMemo)(() => parseSphereMultires(multires), [token]);
10642
10686
  const sourceKey = `${props.baseUrl}\0${token}\0${props.previewUrl}\0${props.urlTemplate ?? ""}\0${props.indexBase ?? 1}`;
10643
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SphereTileSource, { ...props, config }, sourceKey);
10687
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SphereTileSource, { ...props, config }, sourceKey);
10644
10688
  }
10645
10689
 
10646
10690
  // src/sphere.tsx
10647
- var import_fiber16 = require("@react-three/fiber");
10648
- var import_react39 = require("react");
10691
+ var import_fiber17 = require("@react-three/fiber");
10692
+ var import_react40 = require("react");
10649
10693
  var import_three15 = require("three");
10650
- var import_jsx_runtime16 = require("react/jsx-runtime");
10694
+ var import_jsx_runtime17 = require("react/jsx-runtime");
10651
10695
  function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10652
- const [texture, setTexture] = (0, import_react39.useState)(null);
10653
- const onLoadRef = (0, import_react39.useRef)(onLoad);
10654
- const onErrorRef = (0, import_react39.useRef)(onError);
10696
+ const [texture, setTexture] = (0, import_react40.useState)(null);
10697
+ const onLoadRef = (0, import_react40.useRef)(onLoad);
10698
+ const onErrorRef = (0, import_react40.useRef)(onError);
10655
10699
  onLoadRef.current = onLoad;
10656
10700
  onErrorRef.current = onError;
10657
- (0, import_react39.useEffect)(() => {
10701
+ (0, import_react40.useEffect)(() => {
10658
10702
  if (!url) {
10659
10703
  setTexture(null);
10660
10704
  return;
@@ -10693,7 +10737,7 @@ function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10693
10737
  active = false;
10694
10738
  };
10695
10739
  }, [anisotropy, generateMipmaps, url]);
10696
- (0, import_react39.useEffect)(
10740
+ (0, import_react40.useEffect)(
10697
10741
  () => () => {
10698
10742
  texture?.dispose();
10699
10743
  },
@@ -10711,9 +10755,9 @@ function Sphere({
10711
10755
  onPreviewError,
10712
10756
  visible = true
10713
10757
  }) {
10714
- const gl = (0, import_fiber16.useThree)((state) => state.gl);
10758
+ const gl = (0, import_fiber17.useThree)((state) => state.gl);
10715
10759
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10716
- const resolvedPreviewUrl = (0, import_react39.useMemo)(
10760
+ const resolvedPreviewUrl = (0, import_react40.useMemo)(
10717
10761
  () => resolveUrlAgainstFile(src, previewUrl),
10718
10762
  [previewUrl, src]
10719
10763
  );
@@ -10732,11 +10776,11 @@ function Sphere({
10732
10776
  onPreviewError
10733
10777
  );
10734
10778
  const displayTexture = texture ?? previewTexture;
10735
- const readyRef = (0, import_react39.useRef)(false);
10736
- (0, import_react39.useEffect)(() => {
10779
+ const readyRef = (0, import_react40.useRef)(false);
10780
+ (0, import_react40.useEffect)(() => {
10737
10781
  readyRef.current = false;
10738
10782
  }, [resolvedPreviewUrl, src]);
10739
- (0, import_react39.useEffect)(() => {
10783
+ (0, import_react40.useEffect)(() => {
10740
10784
  if (!displayTexture || readyRef.current) {
10741
10785
  return;
10742
10786
  }
@@ -10746,14 +10790,14 @@ function Sphere({
10746
10790
  if (!displayTexture) {
10747
10791
  return null;
10748
10792
  }
10749
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
10793
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
10750
10794
  "mesh",
10751
10795
  {
10752
10796
  rotation: [0, import_three15.MathUtils.degToRad(90 + yawOffset), 0],
10753
10797
  visible,
10754
10798
  children: [
10755
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10756
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
10799
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10800
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
10757
10801
  PanoBasicMaterial,
10758
10802
  {
10759
10803
  map: displayTexture,
@@ -10767,10 +10811,10 @@ function Sphere({
10767
10811
  }
10768
10812
 
10769
10813
  // src/video/pano-video.tsx
10770
- var import_fiber17 = require("@react-three/fiber");
10771
- var import_react40 = require("react");
10814
+ var import_fiber18 = require("@react-three/fiber");
10815
+ var import_react41 = require("react");
10772
10816
  var import_three16 = require("three");
10773
- var import_jsx_runtime17 = require("react/jsx-runtime");
10817
+ var import_jsx_runtime18 = require("react/jsx-runtime");
10774
10818
  var EMPTY_TRACKS = [];
10775
10819
  var EMPTY_RATES = DEFAULT_PANO_VIDEO_PLAYBACK_RATES;
10776
10820
  function createEmptySnapshot() {
@@ -10854,11 +10898,11 @@ function applyEquirectangularLayout(texture, anisotropy) {
10854
10898
  texture.needsUpdate = true;
10855
10899
  }
10856
10900
  function usePosterTexture(poster, onError) {
10857
- const gl = (0, import_fiber17.useThree)((state) => state.gl);
10858
- const [texture, setTexture] = (0, import_react40.useState)(null);
10859
- const onErrorRef = (0, import_react40.useRef)(onError);
10901
+ const gl = (0, import_fiber18.useThree)((state) => state.gl);
10902
+ const [texture, setTexture] = (0, import_react41.useState)(null);
10903
+ const onErrorRef = (0, import_react41.useRef)(onError);
10860
10904
  onErrorRef.current = onError;
10861
- (0, import_react40.useEffect)(() => {
10905
+ (0, import_react41.useEffect)(() => {
10862
10906
  let active = true;
10863
10907
  setTexture(null);
10864
10908
  if (!poster) {
@@ -10895,7 +10939,7 @@ function usePosterTexture(poster, onError) {
10895
10939
  active = false;
10896
10940
  };
10897
10941
  }, [gl, poster]);
10898
- (0, import_react40.useEffect)(
10942
+ (0, import_react41.useEffect)(
10899
10943
  () => () => {
10900
10944
  texture?.dispose();
10901
10945
  },
@@ -10953,39 +10997,39 @@ function PanoVideo({
10953
10997
  onTrackChange
10954
10998
  }) {
10955
10999
  usePanoMediaActivationIntent(autoPlay);
10956
- const gl = (0, import_fiber17.useThree)((state) => state.gl);
10957
- const host = (0, import_react40.useContext)(PanoVideoHostContext);
10958
- const overlay = (0, import_react40.useContext)(PanoChromeOverlayContext);
10959
- const variants = (0, import_react40.useMemo)(
11000
+ const gl = (0, import_fiber18.useThree)((state) => state.gl);
11001
+ const host = (0, import_react41.useContext)(PanoVideoHostContext);
11002
+ const overlay = (0, import_react41.useContext)(PanoChromeOverlayContext);
11003
+ const variants = (0, import_react41.useMemo)(
10960
11004
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
10961
11005
  [poster, src, variantsProp]
10962
11006
  );
10963
11007
  const tracks = tracksProp ?? EMPTY_TRACKS;
10964
11008
  const captionsEnabled = captions !== false;
10965
- const captionAppearance = (0, import_react40.useMemo)(
11009
+ const captionAppearance = (0, import_react41.useMemo)(
10966
11010
  () => resolvePanoVideoCaptionAppearance(
10967
11011
  typeof captions === "object" ? captions : void 0
10968
11012
  ),
10969
11013
  [captions]
10970
11014
  );
10971
- const [variantId, setVariantId] = (0, import_react40.useState)(
11015
+ const [variantId, setVariantId] = (0, import_react41.useState)(
10972
11016
  () => resolvePanoVideoVariantId(variants, defaultVariantId)
10973
11017
  );
10974
- const [trackId, setTrackId] = (0, import_react40.useState)(
11018
+ const [trackId, setTrackId] = (0, import_react41.useState)(
10975
11019
  () => captionsEnabled ? defaultPanoVideoTrackId(tracks) : null
10976
11020
  );
10977
- const [ready, setReady] = (0, import_react40.useState)(false);
10978
- const autoPlayRef = (0, import_react40.useRef)(autoPlay);
11021
+ const [ready, setReady] = (0, import_react41.useState)(false);
11022
+ const autoPlayRef = (0, import_react41.useRef)(autoPlay);
10979
11023
  autoPlayRef.current = autoPlay;
10980
- const blockedRef = (0, import_react40.useRef)(false);
10981
- const captionAppearanceOverrideRef = (0, import_react40.useRef)(
11024
+ const blockedRef = (0, import_react41.useRef)(false);
11025
+ const captionAppearanceOverrideRef = (0, import_react41.useRef)(
10982
11026
  {}
10983
11027
  );
10984
- const mediaCapabilitiesRef = (0, import_react40.useRef)({
11028
+ const mediaCapabilitiesRef = (0, import_react41.useRef)({
10985
11029
  volumeAdjustable: true,
10986
11030
  playbackRates: [...DEFAULT_PANO_VIDEO_PLAYBACK_RATES]
10987
11031
  });
10988
- const storeRef = (0, import_react40.useRef)(null);
11032
+ const storeRef = (0, import_react41.useRef)(null);
10989
11033
  if (!storeRef.current) {
10990
11034
  storeRef.current = {
10991
11035
  snapshot: {
@@ -10995,7 +11039,7 @@ function PanoVideo({
10995
11039
  listeners: /* @__PURE__ */ new Set()
10996
11040
  };
10997
11041
  }
10998
- const actionsRef = (0, import_react40.useRef)({
11042
+ const actionsRef = (0, import_react41.useRef)({
10999
11043
  play: async () => {
11000
11044
  },
11001
11045
  pause: () => {
@@ -11015,19 +11059,19 @@ function PanoVideo({
11015
11059
  setCaptionAppearance: (_appearance) => {
11016
11060
  }
11017
11061
  });
11018
- const onLoadRef = (0, import_react40.useRef)(onLoad);
11019
- const onErrorRef = (0, import_react40.useRef)(onError);
11020
- const onPlaybackStateChangeRef = (0, import_react40.useRef)(onPlaybackStateChange);
11021
- const onEndedRef = (0, import_react40.useRef)(onEnded);
11022
- const onVariantChangeRef = (0, import_react40.useRef)(onVariantChange);
11023
- const onTrackChangeRef = (0, import_react40.useRef)(onTrackChange);
11062
+ const onLoadRef = (0, import_react41.useRef)(onLoad);
11063
+ const onErrorRef = (0, import_react41.useRef)(onError);
11064
+ const onPlaybackStateChangeRef = (0, import_react41.useRef)(onPlaybackStateChange);
11065
+ const onEndedRef = (0, import_react41.useRef)(onEnded);
11066
+ const onVariantChangeRef = (0, import_react41.useRef)(onVariantChange);
11067
+ const onTrackChangeRef = (0, import_react41.useRef)(onTrackChange);
11024
11068
  onLoadRef.current = onLoad;
11025
11069
  onErrorRef.current = onError;
11026
11070
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
11027
11071
  onEndedRef.current = onEnded;
11028
11072
  onVariantChangeRef.current = onVariantChange;
11029
11073
  onTrackChangeRef.current = onTrackChange;
11030
- const resource = (0, import_react40.useMemo)(() => {
11074
+ const resource = (0, import_react41.useMemo)(() => {
11031
11075
  if (typeof document === "undefined") {
11032
11076
  return null;
11033
11077
  }
@@ -11044,7 +11088,7 @@ function PanoVideo({
11044
11088
  texture.generateMipmaps = false;
11045
11089
  return { video, texture };
11046
11090
  }, []);
11047
- (0, import_react40.useEffect)(
11091
+ (0, import_react41.useEffect)(
11048
11092
  () => () => {
11049
11093
  if (!resource) {
11050
11094
  return;
@@ -11059,13 +11103,13 @@ function PanoVideo({
11059
11103
  },
11060
11104
  [resource]
11061
11105
  );
11062
- (0, import_react40.useEffect)(() => {
11106
+ (0, import_react41.useEffect)(() => {
11063
11107
  if (!resource) {
11064
11108
  return;
11065
11109
  }
11066
11110
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
11067
11111
  }, [gl, resource]);
11068
- (0, import_react40.useEffect)(() => {
11112
+ (0, import_react41.useEffect)(() => {
11069
11113
  if (!resource || !overlay?.overlayElement) {
11070
11114
  return;
11071
11115
  }
@@ -11088,7 +11132,7 @@ function PanoVideo({
11088
11132
  activeVariant?.poster ?? poster,
11089
11133
  onError
11090
11134
  );
11091
- const emitSnapshot = (0, import_react40.useCallback)(
11135
+ const emitSnapshot = (0, import_react41.useCallback)(
11092
11136
  (patch) => {
11093
11137
  const store = storeRef.current;
11094
11138
  const video = resource?.video;
@@ -11140,7 +11184,7 @@ function PanoVideo({
11140
11184
  captionAppearance
11141
11185
  ]
11142
11186
  );
11143
- const controller = (0, import_react40.useMemo)(
11187
+ const controller = (0, import_react41.useMemo)(
11144
11188
  () => ({
11145
11189
  subscribe: (onStoreChange) => {
11146
11190
  const store = storeRef.current;
@@ -11192,7 +11236,7 @@ function PanoVideo({
11192
11236
  }),
11193
11237
  []
11194
11238
  );
11195
- (0, import_react40.useLayoutEffect)(() => {
11239
+ (0, import_react41.useLayoutEffect)(() => {
11196
11240
  if (!host || variants.length === 0) {
11197
11241
  return;
11198
11242
  }
@@ -11205,7 +11249,7 @@ function PanoVideo({
11205
11249
  notifyPanoVideoHost(host);
11206
11250
  };
11207
11251
  }, [captionsEnabled, controller, controls, host, variants.length]);
11208
- (0, import_react40.useLayoutEffect)(() => {
11252
+ (0, import_react41.useLayoutEffect)(() => {
11209
11253
  if (!resource) {
11210
11254
  return;
11211
11255
  }
@@ -11218,10 +11262,10 @@ function PanoVideo({
11218
11262
  };
11219
11263
  emitSnapshot();
11220
11264
  }, [emitSnapshot, playbackRates, resource]);
11221
- (0, import_react40.useEffect)(() => {
11265
+ (0, import_react41.useEffect)(() => {
11222
11266
  emitSnapshot();
11223
11267
  }, [emitSnapshot]);
11224
- (0, import_react40.useEffect)(() => {
11268
+ (0, import_react41.useEffect)(() => {
11225
11269
  if (!resource || !activeVariant || !mediaKey) {
11226
11270
  return;
11227
11271
  }
@@ -11278,7 +11322,7 @@ function PanoVideo({
11278
11322
  video.removeEventListener("error", handleError);
11279
11323
  };
11280
11324
  }, [mediaKey, resource]);
11281
- (0, import_react40.useEffect)(() => {
11325
+ (0, import_react41.useEffect)(() => {
11282
11326
  if (!resource) {
11283
11327
  return;
11284
11328
  }
@@ -11370,7 +11414,7 @@ function PanoVideo({
11370
11414
  }
11371
11415
  };
11372
11416
  }, [emitSnapshot, resource]);
11373
- (0, import_react40.useEffect)(() => {
11417
+ (0, import_react41.useEffect)(() => {
11374
11418
  if (!resource) {
11375
11419
  return;
11376
11420
  }
@@ -11390,7 +11434,7 @@ function PanoVideo({
11390
11434
  }
11391
11435
  video.preload = preload;
11392
11436
  }, [loop, muted, playsInline, preload, resource, volume]);
11393
- (0, import_react40.useEffect)(() => {
11437
+ (0, import_react41.useEffect)(() => {
11394
11438
  if (!resource) {
11395
11439
  return;
11396
11440
  }
@@ -11440,7 +11484,7 @@ function PanoVideo({
11440
11484
  video.removeEventListener("ratechange", handleVolume);
11441
11485
  };
11442
11486
  }, [emitSnapshot, resource]);
11443
- (0, import_react40.useEffect)(() => {
11487
+ (0, import_react41.useEffect)(() => {
11444
11488
  if (!resource) {
11445
11489
  return;
11446
11490
  }
@@ -11475,7 +11519,7 @@ function PanoVideo({
11475
11519
  video.textTracks.removeEventListener("change", applyModes);
11476
11520
  };
11477
11521
  }, [emitSnapshot, resource, trackId, tracks]);
11478
- (0, import_react40.useEffect)(() => {
11522
+ (0, import_react41.useEffect)(() => {
11479
11523
  if (!captionsEnabled) {
11480
11524
  setTrackId(null);
11481
11525
  return;
@@ -11486,15 +11530,15 @@ function PanoVideo({
11486
11530
  if (variants.length === 0) {
11487
11531
  return null;
11488
11532
  }
11489
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
11490
- activeTexture ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
11533
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
11534
+ activeTexture ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
11491
11535
  "mesh",
11492
11536
  {
11493
11537
  rotation: [0, import_three16.MathUtils.degToRad(90 + yawOffset), 0],
11494
11538
  visible,
11495
11539
  children: [
11496
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
11497
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
11540
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
11541
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11498
11542
  PanoBasicMaterial,
11499
11543
  {
11500
11544
  map: activeTexture,
@@ -11505,7 +11549,7 @@ function PanoVideo({
11505
11549
  ]
11506
11550
  }
11507
11551
  ) : null,
11508
- captionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
11552
+ captionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
11509
11553
  ] });
11510
11554
  }
11511
11555
 
@@ -11561,8 +11605,8 @@ function isSnapshotTransition(preset) {
11561
11605
  }
11562
11606
 
11563
11607
  // src/scene-transitions/particle-overlay.tsx
11564
- var import_fiber18 = require("@react-three/fiber");
11565
- var import_react41 = require("react");
11608
+ var import_fiber19 = require("@react-three/fiber");
11609
+ var import_react42 = require("react");
11566
11610
  var import_three18 = require("three");
11567
11611
 
11568
11612
  // src/scene-transitions/glsl.ts
@@ -11617,7 +11661,7 @@ function overlayProgress(elapsedTime, duration, startTime) {
11617
11661
  }
11618
11662
 
11619
11663
  // src/scene-transitions/particle-overlay.tsx
11620
- var import_jsx_runtime18 = require("react/jsx-runtime");
11664
+ var import_jsx_runtime19 = require("react/jsx-runtime");
11621
11665
  var PARTICLE_GRID = 48;
11622
11666
  var PARTICLE_COUNT = PARTICLE_GRID * PARTICLE_GRID;
11623
11667
  var PARTICLE_VERTEX_SHADER = `
@@ -11693,13 +11737,13 @@ function ParticleOverlay({
11693
11737
  runId,
11694
11738
  onFinish
11695
11739
  }) {
11696
- const { camera } = (0, import_fiber18.useThree)();
11697
- const meshRef = (0, import_react41.useRef)(null);
11698
- const startRef = (0, import_react41.useRef)(null);
11699
- const finishedRef = (0, import_react41.useRef)(false);
11700
- const directionRef = (0, import_react41.useRef)(new import_three18.Vector3());
11701
- const geometry = (0, import_react41.useMemo)(() => createParticleGeometry(), []);
11702
- const material = (0, import_react41.useMemo)(
11740
+ const { camera } = (0, import_fiber19.useThree)();
11741
+ const meshRef = (0, import_react42.useRef)(null);
11742
+ const startRef = (0, import_react42.useRef)(null);
11743
+ const finishedRef = (0, import_react42.useRef)(false);
11744
+ const directionRef = (0, import_react42.useRef)(new import_three18.Vector3());
11745
+ const geometry = (0, import_react42.useMemo)(() => createParticleGeometry(), []);
11746
+ const material = (0, import_react42.useMemo)(
11703
11747
  () => new import_three18.ShaderMaterial({
11704
11748
  transparent: true,
11705
11749
  depthTest: false,
@@ -11715,14 +11759,14 @@ function ParticleOverlay({
11715
11759
  }),
11716
11760
  [snapshot.texture]
11717
11761
  );
11718
- (0, import_react41.useEffect)(() => () => geometry.dispose(), [geometry]);
11719
- (0, import_react41.useEffect)(() => () => material.dispose(), [material]);
11720
- (0, import_react41.useEffect)(() => {
11762
+ (0, import_react42.useEffect)(() => () => geometry.dispose(), [geometry]);
11763
+ (0, import_react42.useEffect)(() => () => material.dispose(), [material]);
11764
+ (0, import_react42.useEffect)(() => {
11721
11765
  startRef.current = null;
11722
11766
  finishedRef.current = false;
11723
11767
  material.uniforms.progress.value = 0;
11724
11768
  }, [material, runId]);
11725
- (0, import_fiber18.useFrame)((state) => {
11769
+ (0, import_fiber19.useFrame)((state) => {
11726
11770
  const mesh = meshRef.current;
11727
11771
  if (!mesh || !isPerspectiveCamera(camera)) {
11728
11772
  return;
@@ -11739,7 +11783,7 @@ function ParticleOverlay({
11739
11783
  onFinish();
11740
11784
  }
11741
11785
  });
11742
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
11786
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
11743
11787
  "mesh",
11744
11788
  {
11745
11789
  ref: meshRef,
@@ -11752,8 +11796,8 @@ function ParticleOverlay({
11752
11796
  }
11753
11797
 
11754
11798
  // src/scene-transitions/snapshot-overlay.tsx
11755
- var import_fiber19 = require("@react-three/fiber");
11756
- var import_react42 = require("react");
11799
+ var import_fiber20 = require("@react-three/fiber");
11800
+ var import_react43 = require("react");
11757
11801
  var import_three20 = require("three");
11758
11802
 
11759
11803
  // src/scene-transitions/snapshot-shaders.ts
@@ -12033,7 +12077,7 @@ function createSnapshotMaterial(preset, texture) {
12033
12077
  }
12034
12078
 
12035
12079
  // src/scene-transitions/snapshot-overlay.tsx
12036
- var import_jsx_runtime19 = require("react/jsx-runtime");
12080
+ var import_jsx_runtime20 = require("react/jsx-runtime");
12037
12081
  function SnapshotOverlay({
12038
12082
  snapshot,
12039
12083
  transition,
@@ -12041,26 +12085,26 @@ function SnapshotOverlay({
12041
12085
  runId,
12042
12086
  onFinish
12043
12087
  }) {
12044
- const { camera, gl } = (0, import_fiber19.useThree)();
12045
- const meshRef = (0, import_react42.useRef)(null);
12046
- const startRef = (0, import_react42.useRef)(null);
12047
- const finishedRef = (0, import_react42.useRef)(false);
12048
- const directionRef = (0, import_react42.useRef)(new import_three20.Vector3());
12049
- const material = (0, import_react42.useMemo)(() => {
12088
+ const { camera, gl } = (0, import_fiber20.useThree)();
12089
+ const meshRef = (0, import_react43.useRef)(null);
12090
+ const startRef = (0, import_react43.useRef)(null);
12091
+ const finishedRef = (0, import_react43.useRef)(false);
12092
+ const directionRef = (0, import_react43.useRef)(new import_three20.Vector3());
12093
+ const material = (0, import_react43.useMemo)(() => {
12050
12094
  if (!isSnapshotTransition(transition.preset)) {
12051
12095
  return null;
12052
12096
  }
12053
12097
  return createSnapshotMaterial(transition.preset, snapshot.texture);
12054
12098
  }, [snapshot.texture, transition.preset]);
12055
- (0, import_react42.useEffect)(() => () => material?.dispose(), [material]);
12056
- (0, import_react42.useEffect)(() => {
12099
+ (0, import_react43.useEffect)(() => () => material?.dispose(), [material]);
12100
+ (0, import_react43.useEffect)(() => {
12057
12101
  startRef.current = null;
12058
12102
  finishedRef.current = false;
12059
12103
  if (material) {
12060
12104
  material.uniforms.progress.value = 0;
12061
12105
  }
12062
12106
  }, [material, runId]);
12063
- (0, import_fiber19.useFrame)((state) => {
12107
+ (0, import_fiber20.useFrame)((state) => {
12064
12108
  const mesh = meshRef.current;
12065
12109
  if (!mesh || !material || !isPerspectiveCamera(camera)) {
12066
12110
  return;
@@ -12082,21 +12126,21 @@ function SnapshotOverlay({
12082
12126
  if (!material) {
12083
12127
  return null;
12084
12128
  }
12085
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("mesh", { ref: meshRef, renderOrder: OVERLAY_RENDER_ORDER, frustumCulled: false, children: [
12086
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
12087
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("primitive", { attach: "material", object: material })
12129
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("mesh", { ref: meshRef, renderOrder: OVERLAY_RENDER_ORDER, frustumCulled: false, children: [
12130
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
12131
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("primitive", { attach: "material", object: material })
12088
12132
  ] });
12089
12133
  }
12090
12134
 
12091
12135
  // src/scene-transitions/transition-overlay.tsx
12092
- var import_jsx_runtime20 = require("react/jsx-runtime");
12136
+ var import_jsx_runtime21 = require("react/jsx-runtime");
12093
12137
  function TransitionOverlay(props) {
12094
12138
  const { preset } = props.transition;
12095
12139
  switch (preset) {
12096
12140
  case "none":
12097
12141
  return null;
12098
12142
  case "particles":
12099
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ParticleOverlay, { ...props });
12143
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ParticleOverlay, { ...props });
12100
12144
  case "crossfade":
12101
12145
  case "zoom":
12102
12146
  case "blackout":
@@ -12124,7 +12168,7 @@ function TransitionOverlay(props) {
12124
12168
  case "zoomBlur":
12125
12169
  case "hexDissolve":
12126
12170
  case "filmBurn":
12127
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SnapshotOverlay, { ...props });
12171
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SnapshotOverlay, { ...props });
12128
12172
  default: {
12129
12173
  const exhaustive = preset;
12130
12174
  void exhaustive;
@@ -12133,39 +12177,6 @@ function TransitionOverlay(props) {
12133
12177
  }
12134
12178
  }
12135
12179
 
12136
- // src/tile/texture-manager-context.tsx
12137
- var import_fiber20 = require("@react-three/fiber");
12138
- var import_react43 = require("react");
12139
- var import_jsx_runtime21 = require("react/jsx-runtime");
12140
- var TileTextureManagerContext = (0, import_react43.createContext)(
12141
- null
12142
- );
12143
- function TileTextureManagerProvider({
12144
- children,
12145
- maxTextureMemoryMb = 128,
12146
- maxConcurrentLoads = 4
12147
- }) {
12148
- const gl = (0, import_fiber20.useThree)((state) => state.gl);
12149
- const anisotropy = gl.capabilities.getMaxAnisotropy();
12150
- const manager = (0, import_react43.useMemo)(
12151
- () => new TileTextureManager({
12152
- anisotropy,
12153
- maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
12154
- concurrency: Math.max(1, Math.floor(maxConcurrentLoads)),
12155
- retryCount: 1
12156
- }),
12157
- [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
12158
- );
12159
- (0, import_react43.useEffect)(() => {
12160
- manager.resume();
12161
- return () => manager.dispose();
12162
- }, [manager]);
12163
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TileTextureManagerContext.Provider, { value: manager, children });
12164
- }
12165
- function useSharedTileTextureManager() {
12166
- return (0, import_react43.useContext)(TileTextureManagerContext);
12167
- }
12168
-
12169
12180
  // src/tile/tile.tsx
12170
12181
  var import_fiber21 = require("@react-three/fiber");
12171
12182
  var import_react44 = require("react");
@@ -12841,6 +12852,19 @@ function SceneSource({
12841
12852
  }
12842
12853
  );
12843
12854
  }
12855
+ case "sphereTile": {
12856
+ const { id: _id, type: _type, ...tileProps } = scene;
12857
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12858
+ SphereTile,
12859
+ {
12860
+ ...tileProps,
12861
+ visible,
12862
+ loadMode: baseOnly ? "base" : "full",
12863
+ onReady,
12864
+ onPreviewError: onError
12865
+ }
12866
+ );
12867
+ }
12844
12868
  default: {
12845
12869
  const exhaustive = scene;
12846
12870
  void exhaustive;
@@ -12907,6 +12931,7 @@ function ScenesController({
12907
12931
  activeSceneId,
12908
12932
  transition: transitionInput,
12909
12933
  renderHotspots,
12934
+ onSceneReady,
12910
12935
  onTransitionEnd,
12911
12936
  onTransitionError,
12912
12937
  snapshotMaxPixels = 3686400,
@@ -12942,6 +12967,7 @@ function ScenesController({
12942
12967
  const phaseRef = (0, import_react45.useRef)(phase);
12943
12968
  const liveSlotRef = (0, import_react45.useRef)(liveSlot);
12944
12969
  const transitionFromRef = (0, import_react45.useRef)(initialScene);
12970
+ const readySceneRef = (0, import_react45.useRef)(null);
12945
12971
  slotsRef.current = slots;
12946
12972
  phaseRef.current = phase;
12947
12973
  if (externalPhaseRef) {
@@ -12982,6 +13008,7 @@ function ScenesController({
12982
13008
  return;
12983
13009
  }
12984
13010
  if (phaseRef.current === "transitioning" || phaseRef.current === "capturing") {
13011
+ if (requestedSceneIdRef.current === requested.id) return;
12985
13012
  requestedSceneIdRef.current = requested.id;
12986
13013
  captureReasonRef.current = "interrupt";
12987
13014
  setPhase("recapturing");
@@ -12990,7 +13017,16 @@ function ScenesController({
12990
13017
  }
12991
13018
  startPreload(requested);
12992
13019
  }, [activeSceneId, pendingSlot, reportError, sceneMap, startPreload]);
13020
+ const notifySceneReady = (0, import_react45.useCallback)((scene) => {
13021
+ if (!scene || readySceneRef.current === scene) return;
13022
+ readySceneRef.current = scene;
13023
+ onSceneReady?.(scene.id);
13024
+ }, [onSceneReady]);
12993
13025
  const handleReady = (0, import_react45.useCallback)((slot) => {
13026
+ if (slot === liveSlotRef.current && pendingSlot === null && phaseRef.current === "idle") {
13027
+ notifySceneReady(slotsRef.current[slot]);
13028
+ return;
13029
+ }
12994
13030
  if (slot !== pendingSlot || phaseRef.current !== "preloading") {
12995
13031
  return;
12996
13032
  }
@@ -13006,6 +13042,7 @@ function ScenesController({
13006
13042
  setActiveSlot(slot);
13007
13043
  setPendingSlot(null);
13008
13044
  setPhase("idle");
13045
+ notifySceneReady(nextScene);
13009
13046
  if (previousScene && nextScene) {
13010
13047
  onTransitionEnd?.({
13011
13048
  previousSceneId: previousScene.id,
@@ -13020,7 +13057,7 @@ function ScenesController({
13020
13057
  captureReasonRef.current = "start";
13021
13058
  setPhase("capturing");
13022
13059
  setCaptureRequestId((current) => current + 1);
13023
- }, [controlsRef, onTransitionEnd, pendingSlot, transition.preset]);
13060
+ }, [controlsRef, notifySceneReady, onTransitionEnd, pendingSlot, transition.preset]);
13024
13061
  const handleCaptured = (0, import_react45.useCallback)((nextSnapshot) => {
13025
13062
  setSnapshot((current) => {
13026
13063
  current?.dispose();
@@ -13038,11 +13075,12 @@ function ScenesController({
13038
13075
  });
13039
13076
  manager?.releaseUnused();
13040
13077
  if (pendingSlot !== null) {
13078
+ notifySceneReady(slotsRef.current[pendingSlot]);
13041
13079
  setLiveSlot(pendingSlot);
13042
13080
  }
13043
13081
  setPhase("transitioning");
13044
13082
  setTransitionRunId((current) => current + 1);
13045
- }, [manager, pendingSlot, sceneMap, startPreload]);
13083
+ }, [manager, notifySceneReady, pendingSlot, sceneMap, startPreload]);
13046
13084
  const finishTransition = (0, import_react45.useCallback)(() => {
13047
13085
  const nextSlot = pendingSlot;
13048
13086
  const previousScene = transitionFromRef.current;
@@ -13085,6 +13123,7 @@ function ScenesController({
13085
13123
  setPhase("idle");
13086
13124
  };
13087
13125
  const onContextRestored = () => {
13126
+ readySceneRef.current = null;
13088
13127
  manager?.resume();
13089
13128
  setRecoveryKey((current) => current + 1);
13090
13129
  };