@ericchen1990/pano-view 3.2.3 → 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.mjs CHANGED
@@ -9716,8 +9716,8 @@ function usePanoViewer() {
9716
9716
  }
9717
9717
 
9718
9718
  // src/sphere-tile/sphere-tile.tsx
9719
- import { useFrame as useFrame9, useThree as useThree13 } from "@react-three/fiber";
9720
- import { memo, useCallback as useCallback9, useEffect as useEffect22, useMemo as useMemo14, useRef as useRef23, useState as useState10 } from "react";
9719
+ import { useFrame as useFrame9, useThree as useThree14 } from "@react-three/fiber";
9720
+ import { memo, useCallback as useCallback9, useEffect as useEffect23, useMemo as useMemo15, useRef as useRef23, useState as useState10 } from "react";
9721
9721
  import { BackSide as BackSide2, Frustum, Matrix4 as Matrix42, PerspectiveCamera as PerspectiveCamera3 } from "three";
9722
9722
 
9723
9723
  // src/asset-url.ts
@@ -10014,6 +10014,9 @@ var TileTextureManager = class {
10014
10014
  this.generation = 0;
10015
10015
  this.options = options;
10016
10016
  }
10017
+ get memoryBudgetBytes() {
10018
+ return this.options.maxBytes;
10019
+ }
10017
10020
  resume() {
10018
10021
  this.disposed = false;
10019
10022
  this.drain();
@@ -10081,7 +10084,7 @@ var TileTextureManager = class {
10081
10084
  this.totalBytes = 0;
10082
10085
  }
10083
10086
  /**
10084
- * Releases textures that are no longer rendered by any Tile instance.
10087
+ * Releases textures that are no longer rendered by any tile source.
10085
10088
  * Scene transitions call this after the outgoing panorama has been
10086
10089
  * snapshotted, so the incoming scene never competes with stale GPU tiles.
10087
10090
  */
@@ -10215,8 +10218,44 @@ var TileTextureManager = class {
10215
10218
  }
10216
10219
  };
10217
10220
 
10221
+ // src/tile/texture-manager-context.tsx
10222
+ import { useThree as useThree13 } from "@react-three/fiber";
10223
+ import {
10224
+ createContext as createContext15,
10225
+ useContext as useContext21,
10226
+ useEffect as useEffect21,
10227
+ useMemo as useMemo14
10228
+ } from "react";
10229
+ import { jsx as jsx15 } from "react/jsx-runtime";
10230
+ var TileTextureManagerContext = createContext15(null);
10231
+ function TileTextureManagerProvider({
10232
+ children,
10233
+ maxTextureMemoryMb = 128,
10234
+ maxConcurrentLoads = 4
10235
+ }) {
10236
+ const gl = useThree13((state) => state.gl);
10237
+ const anisotropy = gl.capabilities.getMaxAnisotropy();
10238
+ const manager = useMemo14(
10239
+ () => new TileTextureManager({
10240
+ anisotropy,
10241
+ maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
10242
+ concurrency: Math.max(1, Math.floor(maxConcurrentLoads)),
10243
+ retryCount: 1
10244
+ }),
10245
+ [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
10246
+ );
10247
+ useEffect21(() => {
10248
+ manager.resume();
10249
+ return () => manager.dispose();
10250
+ }, [manager]);
10251
+ return /* @__PURE__ */ jsx15(TileTextureManagerContext.Provider, { value: manager, children });
10252
+ }
10253
+ function useSharedTileTextureManager() {
10254
+ return useContext21(TileTextureManagerContext);
10255
+ }
10256
+
10218
10257
  // src/tile/texture-hooks.ts
10219
- import { useEffect as useEffect21, useRef as useRef22, useState as useState9 } from "react";
10258
+ import { useEffect as useEffect22, useRef as useRef22, useState as useState9 } from "react";
10220
10259
  import {
10221
10260
  ClampToEdgeWrapping as ClampToEdgeWrapping2,
10222
10261
  LinearFilter as LinearFilter2,
@@ -10227,7 +10266,7 @@ function usePreviewTexture(url, anisotropy, onError) {
10227
10266
  const [texture, setTexture] = useState9(null);
10228
10267
  const onErrorRef = useRef22(onError);
10229
10268
  onErrorRef.current = onError;
10230
- useEffect21(() => {
10269
+ useEffect22(() => {
10231
10270
  if (!url) {
10232
10271
  setTexture(null);
10233
10272
  return;
@@ -10264,7 +10303,7 @@ function usePreviewTexture(url, anisotropy, onError) {
10264
10303
  active = false;
10265
10304
  };
10266
10305
  }, [anisotropy, url]);
10267
- useEffect21(
10306
+ useEffect22(
10268
10307
  () => () => {
10269
10308
  texture?.dispose();
10270
10309
  },
@@ -10280,7 +10319,7 @@ function useManagedTexture(manager, key, url, address) {
10280
10319
  snapshot: manager.getSnapshot(key)
10281
10320
  }));
10282
10321
  const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
10283
- useEffect21(
10322
+ useEffect22(
10284
10323
  () => manager.acquire(key, url, address, (nextSnapshot) => {
10285
10324
  setManagedState({
10286
10325
  manager,
@@ -10428,14 +10467,14 @@ function preferredSphereLevel(levels, height, dpr, verticalFov) {
10428
10467
  }
10429
10468
 
10430
10469
  // src/sphere-tile/sphere-tile.tsx
10431
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
10470
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
10432
10471
  function SpherePatch({
10433
10472
  address,
10434
10473
  texture,
10435
10474
  yawOffset,
10436
10475
  preview = false
10437
10476
  }) {
10438
- const geometry = useMemo14(
10477
+ const geometry = useMemo15(
10439
10478
  () => sphereTileGeometry(
10440
10479
  address,
10441
10480
  yawOffset,
@@ -10443,13 +10482,13 @@ function SpherePatch({
10443
10482
  ),
10444
10483
  [address, yawOffset, preview]
10445
10484
  );
10446
- useEffect22(() => () => geometry.dispose(), [geometry]);
10447
- return /* @__PURE__ */ jsx15(
10485
+ useEffect23(() => () => geometry.dispose(), [geometry]);
10486
+ return /* @__PURE__ */ jsx16(
10448
10487
  "mesh",
10449
10488
  {
10450
10489
  geometry,
10451
10490
  renderOrder: preview ? -1e3 : -900 + address.level,
10452
- children: /* @__PURE__ */ jsx15(
10491
+ children: /* @__PURE__ */ jsx16(
10453
10492
  PanoBasicMaterial,
10454
10493
  {
10455
10494
  map: texture,
@@ -10476,10 +10515,10 @@ var ManagedPatch = memo(function ManagedPatch2({
10476
10515
  url,
10477
10516
  address
10478
10517
  );
10479
- useEffect22(() => {
10518
+ useEffect23(() => {
10480
10519
  if (snapshot.texture) ready();
10481
10520
  }, [snapshot.texture, ready]);
10482
- return snapshot.texture ? /* @__PURE__ */ jsx15(
10521
+ return snapshot.texture ? /* @__PURE__ */ jsx16(
10483
10522
  SpherePatch,
10484
10523
  {
10485
10524
  address,
@@ -10507,7 +10546,7 @@ function SphereTileSource({
10507
10546
  onTileError,
10508
10547
  onLevelChange
10509
10548
  }) {
10510
- const { camera, gl, size } = useThree13();
10549
+ const { camera, gl, size } = useThree14();
10511
10550
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10512
10551
  const preview = usePreviewTexture(
10513
10552
  resolveAssetUrl(baseUrl, previewUrl),
@@ -10521,14 +10560,15 @@ function SphereTileSource({
10521
10560
  onLevelChange
10522
10561
  });
10523
10562
  callbacks.current = { onReady, onLoadProgress, onTileError, onLevelChange };
10524
- const budget = Math.max(
10563
+ const sharedManager = useSharedTileTextureManager();
10564
+ const ownedBudget = Math.max(
10525
10565
  1,
10526
10566
  Number.isFinite(maxTextureMemoryMb) ? maxTextureMemoryMb : 128
10527
10567
  ) * 1024 * 1024;
10528
- const manager = useMemo14(
10568
+ const ownedManager = useMemo15(
10529
10569
  () => new TileTextureManager({
10530
10570
  anisotropy,
10531
- maxBytes: budget,
10571
+ maxBytes: ownedBudget,
10532
10572
  concurrency: Math.max(
10533
10573
  1,
10534
10574
  Math.floor(
@@ -10542,8 +10582,10 @@ function SphereTileSource({
10542
10582
  onProgress: (progress) => callbacks.current.onLoadProgress?.(progress),
10543
10583
  onError: (error) => callbacks.current.onTileError?.(error)
10544
10584
  }),
10545
- [anisotropy, budget, maxConcurrentLoads, retryCount]
10585
+ [anisotropy, ownedBudget, maxConcurrentLoads, retryCount]
10546
10586
  );
10587
+ const manager = sharedManager ?? ownedManager;
10588
+ const budget = manager.memoryBudgetBytes;
10547
10589
  const readyRef = useRef23(false);
10548
10590
  const ready = useCallback9(() => {
10549
10591
  if (!readyRef.current) {
@@ -10551,21 +10593,27 @@ function SphereTileSource({
10551
10593
  callbacks.current.onReady?.();
10552
10594
  }
10553
10595
  }, []);
10554
- useEffect22(() => {
10596
+ useEffect23(() => {
10555
10597
  if (preview) ready();
10556
10598
  }, [preview, ready]);
10557
- useEffect22(() => {
10599
+ useEffect23(() => {
10558
10600
  manager.resume();
10559
- return () => manager.dispose();
10560
- }, [manager]);
10561
- const resolveUrl = useMemo14(
10601
+ return () => {
10602
+ if (manager === ownedManager) {
10603
+ manager.dispose();
10604
+ return;
10605
+ }
10606
+ queueMicrotask(() => manager.releaseUnused());
10607
+ };
10608
+ }, [manager, ownedManager]);
10609
+ const resolveUrl = useMemo15(
10562
10610
  () => (address) => resolveAssetUrl(
10563
10611
  baseUrl,
10564
10612
  resolveTileUrl ? resolveTileUrl(address) : resolveSphereTemplateUrl(urlTemplate, address, indexBase)
10565
10613
  ),
10566
10614
  [baseUrl, resolveTileUrl, urlTemplate, indexBase]
10567
10615
  );
10568
- const candidates = useMemo14(
10616
+ const candidates = useMemo15(
10569
10617
  () => config.levels.map(
10570
10618
  (level, index) => sphereTileAddresses(level, index, config.tileSize).map((address) => ({
10571
10619
  address,
@@ -10574,12 +10622,12 @@ function SphereTileSource({
10574
10622
  ),
10575
10623
  [config, yawOffset]
10576
10624
  );
10577
- const previewAddress = useMemo14(
10625
+ const previewAddress = useMemo15(
10578
10626
  () => sphereTileAddresses({ width: 2, height: 1 }, 0, 2)[0],
10579
10627
  []
10580
10628
  );
10581
10629
  const [layers, setLayers] = useState10([]);
10582
- const scratch = useMemo14(
10630
+ const scratch = useMemo15(
10583
10631
  () => ({
10584
10632
  camera: new PerspectiveCamera3(),
10585
10633
  frustum: new Frustum(),
@@ -10649,7 +10697,7 @@ function SphereTileSource({
10649
10697
  }
10650
10698
  });
10651
10699
  return /* @__PURE__ */ jsxs9("group", { visible, children: [
10652
- preview ? /* @__PURE__ */ jsx15(
10700
+ preview ? /* @__PURE__ */ jsx16(
10653
10701
  SpherePatch,
10654
10702
  {
10655
10703
  address: previewAddress,
@@ -10659,7 +10707,7 @@ function SphereTileSource({
10659
10707
  }
10660
10708
  ) : null,
10661
10709
  loadMode !== "preview" ? layers.flatMap(
10662
- (layer) => layer.addresses.map((address) => /* @__PURE__ */ jsx15(
10710
+ (layer) => layer.addresses.map((address) => /* @__PURE__ */ jsx16(
10663
10711
  ManagedPatch,
10664
10712
  {
10665
10713
  address,
@@ -10675,14 +10723,14 @@ function SphereTileSource({
10675
10723
  }
10676
10724
  function SphereTile({ multires, ...props }) {
10677
10725
  const token = typeof multires === "string" ? multires : JSON.stringify(multires);
10678
- const config = useMemo14(() => parseSphereMultires(multires), [token]);
10726
+ const config = useMemo15(() => parseSphereMultires(multires), [token]);
10679
10727
  const sourceKey = `${props.baseUrl}\0${token}\0${props.previewUrl}\0${props.urlTemplate ?? ""}\0${props.indexBase ?? 1}`;
10680
- return /* @__PURE__ */ jsx15(SphereTileSource, { ...props, config }, sourceKey);
10728
+ return /* @__PURE__ */ jsx16(SphereTileSource, { ...props, config }, sourceKey);
10681
10729
  }
10682
10730
 
10683
10731
  // src/sphere.tsx
10684
- import { useThree as useThree14 } from "@react-three/fiber";
10685
- import { useEffect as useEffect23, useMemo as useMemo15, useRef as useRef24, useState as useState11 } from "react";
10732
+ import { useThree as useThree15 } from "@react-three/fiber";
10733
+ import { useEffect as useEffect24, useMemo as useMemo16, useRef as useRef24, useState as useState11 } from "react";
10686
10734
  import {
10687
10735
  BackSide as BackSide3,
10688
10736
  LinearFilter as LinearFilter3,
@@ -10692,14 +10740,14 @@ import {
10692
10740
  SRGBColorSpace as SRGBColorSpace3,
10693
10741
  TextureLoader as TextureLoader3
10694
10742
  } from "three";
10695
- import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
10743
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
10696
10744
  function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10697
10745
  const [texture, setTexture] = useState11(null);
10698
10746
  const onLoadRef = useRef24(onLoad);
10699
10747
  const onErrorRef = useRef24(onError);
10700
10748
  onLoadRef.current = onLoad;
10701
10749
  onErrorRef.current = onError;
10702
- useEffect23(() => {
10750
+ useEffect24(() => {
10703
10751
  if (!url) {
10704
10752
  setTexture(null);
10705
10753
  return;
@@ -10738,7 +10786,7 @@ function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10738
10786
  active = false;
10739
10787
  };
10740
10788
  }, [anisotropy, generateMipmaps, url]);
10741
- useEffect23(
10789
+ useEffect24(
10742
10790
  () => () => {
10743
10791
  texture?.dispose();
10744
10792
  },
@@ -10756,9 +10804,9 @@ function Sphere({
10756
10804
  onPreviewError,
10757
10805
  visible = true
10758
10806
  }) {
10759
- const gl = useThree14((state) => state.gl);
10807
+ const gl = useThree15((state) => state.gl);
10760
10808
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10761
- const resolvedPreviewUrl = useMemo15(
10809
+ const resolvedPreviewUrl = useMemo16(
10762
10810
  () => resolveUrlAgainstFile(src, previewUrl),
10763
10811
  [previewUrl, src]
10764
10812
  );
@@ -10778,10 +10826,10 @@ function Sphere({
10778
10826
  );
10779
10827
  const displayTexture = texture ?? previewTexture;
10780
10828
  const readyRef = useRef24(false);
10781
- useEffect23(() => {
10829
+ useEffect24(() => {
10782
10830
  readyRef.current = false;
10783
10831
  }, [resolvedPreviewUrl, src]);
10784
- useEffect23(() => {
10832
+ useEffect24(() => {
10785
10833
  if (!displayTexture || readyRef.current) {
10786
10834
  return;
10787
10835
  }
@@ -10797,8 +10845,8 @@ function Sphere({
10797
10845
  rotation: [0, MathUtils6.degToRad(90 + yawOffset), 0],
10798
10846
  visible,
10799
10847
  children: [
10800
- /* @__PURE__ */ jsx16("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10801
- /* @__PURE__ */ jsx16(
10848
+ /* @__PURE__ */ jsx17("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
10849
+ /* @__PURE__ */ jsx17(
10802
10850
  PanoBasicMaterial,
10803
10851
  {
10804
10852
  map: displayTexture,
@@ -10812,13 +10860,13 @@ function Sphere({
10812
10860
  }
10813
10861
 
10814
10862
  // src/video/pano-video.tsx
10815
- import { useThree as useThree15 } from "@react-three/fiber";
10863
+ import { useThree as useThree16 } from "@react-three/fiber";
10816
10864
  import {
10817
10865
  useCallback as useCallback10,
10818
- useContext as useContext21,
10819
- useEffect as useEffect24,
10866
+ useContext as useContext22,
10867
+ useEffect as useEffect25,
10820
10868
  useLayoutEffect as useLayoutEffect3,
10821
- useMemo as useMemo16,
10869
+ useMemo as useMemo17,
10822
10870
  useRef as useRef25,
10823
10871
  useState as useState12
10824
10872
  } from "react";
@@ -10832,7 +10880,7 @@ import {
10832
10880
  TextureLoader as TextureLoader4,
10833
10881
  VideoTexture
10834
10882
  } from "three";
10835
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
10883
+ import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
10836
10884
  var EMPTY_TRACKS = [];
10837
10885
  var EMPTY_RATES = DEFAULT_PANO_VIDEO_PLAYBACK_RATES;
10838
10886
  function createEmptySnapshot() {
@@ -10916,11 +10964,11 @@ function applyEquirectangularLayout(texture, anisotropy) {
10916
10964
  texture.needsUpdate = true;
10917
10965
  }
10918
10966
  function usePosterTexture(poster, onError) {
10919
- const gl = useThree15((state) => state.gl);
10967
+ const gl = useThree16((state) => state.gl);
10920
10968
  const [texture, setTexture] = useState12(null);
10921
10969
  const onErrorRef = useRef25(onError);
10922
10970
  onErrorRef.current = onError;
10923
- useEffect24(() => {
10971
+ useEffect25(() => {
10924
10972
  let active = true;
10925
10973
  setTexture(null);
10926
10974
  if (!poster) {
@@ -10957,7 +11005,7 @@ function usePosterTexture(poster, onError) {
10957
11005
  active = false;
10958
11006
  };
10959
11007
  }, [gl, poster]);
10960
- useEffect24(
11008
+ useEffect25(
10961
11009
  () => () => {
10962
11010
  texture?.dispose();
10963
11011
  },
@@ -11015,16 +11063,16 @@ function PanoVideo({
11015
11063
  onTrackChange
11016
11064
  }) {
11017
11065
  usePanoMediaActivationIntent(autoPlay);
11018
- const gl = useThree15((state) => state.gl);
11019
- const host = useContext21(PanoVideoHostContext);
11020
- const overlay = useContext21(PanoChromeOverlayContext);
11021
- const variants = useMemo16(
11066
+ const gl = useThree16((state) => state.gl);
11067
+ const host = useContext22(PanoVideoHostContext);
11068
+ const overlay = useContext22(PanoChromeOverlayContext);
11069
+ const variants = useMemo17(
11022
11070
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
11023
11071
  [poster, src, variantsProp]
11024
11072
  );
11025
11073
  const tracks = tracksProp ?? EMPTY_TRACKS;
11026
11074
  const captionsEnabled = captions !== false;
11027
- const captionAppearance = useMemo16(
11075
+ const captionAppearance = useMemo17(
11028
11076
  () => resolvePanoVideoCaptionAppearance(
11029
11077
  typeof captions === "object" ? captions : void 0
11030
11078
  ),
@@ -11089,7 +11137,7 @@ function PanoVideo({
11089
11137
  onEndedRef.current = onEnded;
11090
11138
  onVariantChangeRef.current = onVariantChange;
11091
11139
  onTrackChangeRef.current = onTrackChange;
11092
- const resource = useMemo16(() => {
11140
+ const resource = useMemo17(() => {
11093
11141
  if (typeof document === "undefined") {
11094
11142
  return null;
11095
11143
  }
@@ -11106,7 +11154,7 @@ function PanoVideo({
11106
11154
  texture.generateMipmaps = false;
11107
11155
  return { video, texture };
11108
11156
  }, []);
11109
- useEffect24(
11157
+ useEffect25(
11110
11158
  () => () => {
11111
11159
  if (!resource) {
11112
11160
  return;
@@ -11121,13 +11169,13 @@ function PanoVideo({
11121
11169
  },
11122
11170
  [resource]
11123
11171
  );
11124
- useEffect24(() => {
11172
+ useEffect25(() => {
11125
11173
  if (!resource) {
11126
11174
  return;
11127
11175
  }
11128
11176
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
11129
11177
  }, [gl, resource]);
11130
- useEffect24(() => {
11178
+ useEffect25(() => {
11131
11179
  if (!resource || !overlay?.overlayElement) {
11132
11180
  return;
11133
11181
  }
@@ -11202,7 +11250,7 @@ function PanoVideo({
11202
11250
  captionAppearance
11203
11251
  ]
11204
11252
  );
11205
- const controller = useMemo16(
11253
+ const controller = useMemo17(
11206
11254
  () => ({
11207
11255
  subscribe: (onStoreChange) => {
11208
11256
  const store = storeRef.current;
@@ -11280,10 +11328,10 @@ function PanoVideo({
11280
11328
  };
11281
11329
  emitSnapshot();
11282
11330
  }, [emitSnapshot, playbackRates, resource]);
11283
- useEffect24(() => {
11331
+ useEffect25(() => {
11284
11332
  emitSnapshot();
11285
11333
  }, [emitSnapshot]);
11286
- useEffect24(() => {
11334
+ useEffect25(() => {
11287
11335
  if (!resource || !activeVariant || !mediaKey) {
11288
11336
  return;
11289
11337
  }
@@ -11340,7 +11388,7 @@ function PanoVideo({
11340
11388
  video.removeEventListener("error", handleError);
11341
11389
  };
11342
11390
  }, [mediaKey, resource]);
11343
- useEffect24(() => {
11391
+ useEffect25(() => {
11344
11392
  if (!resource) {
11345
11393
  return;
11346
11394
  }
@@ -11432,7 +11480,7 @@ function PanoVideo({
11432
11480
  }
11433
11481
  };
11434
11482
  }, [emitSnapshot, resource]);
11435
- useEffect24(() => {
11483
+ useEffect25(() => {
11436
11484
  if (!resource) {
11437
11485
  return;
11438
11486
  }
@@ -11452,7 +11500,7 @@ function PanoVideo({
11452
11500
  }
11453
11501
  video.preload = preload;
11454
11502
  }, [loop, muted, playsInline, preload, resource, volume]);
11455
- useEffect24(() => {
11503
+ useEffect25(() => {
11456
11504
  if (!resource) {
11457
11505
  return;
11458
11506
  }
@@ -11502,7 +11550,7 @@ function PanoVideo({
11502
11550
  video.removeEventListener("ratechange", handleVolume);
11503
11551
  };
11504
11552
  }, [emitSnapshot, resource]);
11505
- useEffect24(() => {
11553
+ useEffect25(() => {
11506
11554
  if (!resource) {
11507
11555
  return;
11508
11556
  }
@@ -11537,7 +11585,7 @@ function PanoVideo({
11537
11585
  video.textTracks.removeEventListener("change", applyModes);
11538
11586
  };
11539
11587
  }, [emitSnapshot, resource, trackId, tracks]);
11540
- useEffect24(() => {
11588
+ useEffect25(() => {
11541
11589
  if (!captionsEnabled) {
11542
11590
  setTrackId(null);
11543
11591
  return;
@@ -11555,8 +11603,8 @@ function PanoVideo({
11555
11603
  rotation: [0, MathUtils7.degToRad(90 + yawOffset), 0],
11556
11604
  visible,
11557
11605
  children: [
11558
- /* @__PURE__ */ jsx17("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
11559
- /* @__PURE__ */ jsx17(
11606
+ /* @__PURE__ */ jsx18("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
11607
+ /* @__PURE__ */ jsx18(
11560
11608
  PanoBasicMaterial,
11561
11609
  {
11562
11610
  map: activeTexture,
@@ -11567,7 +11615,7 @@ function PanoVideo({
11567
11615
  ]
11568
11616
  }
11569
11617
  ) : null,
11570
- captionsEnabled ? /* @__PURE__ */ jsx17(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
11618
+ captionsEnabled ? /* @__PURE__ */ jsx18(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
11571
11619
  ] });
11572
11620
  }
11573
11621
 
@@ -11635,8 +11683,8 @@ function isSnapshotTransition(preset) {
11635
11683
  }
11636
11684
 
11637
11685
  // src/scene-transitions/particle-overlay.tsx
11638
- import { useFrame as useFrame10, useThree as useThree16 } from "@react-three/fiber";
11639
- import { useEffect as useEffect25, useMemo as useMemo17, useRef as useRef26 } from "react";
11686
+ import { useFrame as useFrame10, useThree as useThree17 } from "@react-three/fiber";
11687
+ import { useEffect as useEffect26, useMemo as useMemo18, useRef as useRef26 } from "react";
11640
11688
  import {
11641
11689
  DoubleSide as DoubleSide2,
11642
11690
  InstancedBufferAttribute,
@@ -11699,7 +11747,7 @@ function overlayProgress(elapsedTime, duration, startTime) {
11699
11747
  }
11700
11748
 
11701
11749
  // src/scene-transitions/particle-overlay.tsx
11702
- import { jsx as jsx18 } from "react/jsx-runtime";
11750
+ import { jsx as jsx19 } from "react/jsx-runtime";
11703
11751
  var PARTICLE_GRID = 48;
11704
11752
  var PARTICLE_COUNT = PARTICLE_GRID * PARTICLE_GRID;
11705
11753
  var PARTICLE_VERTEX_SHADER = `
@@ -11775,13 +11823,13 @@ function ParticleOverlay({
11775
11823
  runId,
11776
11824
  onFinish
11777
11825
  }) {
11778
- const { camera } = useThree16();
11826
+ const { camera } = useThree17();
11779
11827
  const meshRef = useRef26(null);
11780
11828
  const startRef = useRef26(null);
11781
11829
  const finishedRef = useRef26(false);
11782
11830
  const directionRef = useRef26(new Vector310());
11783
- const geometry = useMemo17(() => createParticleGeometry(), []);
11784
- const material = useMemo17(
11831
+ const geometry = useMemo18(() => createParticleGeometry(), []);
11832
+ const material = useMemo18(
11785
11833
  () => new ShaderMaterial2({
11786
11834
  transparent: true,
11787
11835
  depthTest: false,
@@ -11797,9 +11845,9 @@ function ParticleOverlay({
11797
11845
  }),
11798
11846
  [snapshot.texture]
11799
11847
  );
11800
- useEffect25(() => () => geometry.dispose(), [geometry]);
11801
- useEffect25(() => () => material.dispose(), [material]);
11802
- useEffect25(() => {
11848
+ useEffect26(() => () => geometry.dispose(), [geometry]);
11849
+ useEffect26(() => () => material.dispose(), [material]);
11850
+ useEffect26(() => {
11803
11851
  startRef.current = null;
11804
11852
  finishedRef.current = false;
11805
11853
  material.uniforms.progress.value = 0;
@@ -11821,7 +11869,7 @@ function ParticleOverlay({
11821
11869
  onFinish();
11822
11870
  }
11823
11871
  });
11824
- return /* @__PURE__ */ jsx18(
11872
+ return /* @__PURE__ */ jsx19(
11825
11873
  "mesh",
11826
11874
  {
11827
11875
  ref: meshRef,
@@ -11834,8 +11882,8 @@ function ParticleOverlay({
11834
11882
  }
11835
11883
 
11836
11884
  // src/scene-transitions/snapshot-overlay.tsx
11837
- import { useFrame as useFrame11, useThree as useThree17 } from "@react-three/fiber";
11838
- import { useEffect as useEffect26, useMemo as useMemo18, useRef as useRef27 } from "react";
11885
+ import { useFrame as useFrame11, useThree as useThree18 } from "@react-three/fiber";
11886
+ import { useEffect as useEffect27, useMemo as useMemo19, useRef as useRef27 } from "react";
11839
11887
  import { Vector3 as Vector311 } from "three";
11840
11888
 
11841
11889
  // src/scene-transitions/snapshot-shaders.ts
@@ -12115,7 +12163,7 @@ function createSnapshotMaterial(preset, texture) {
12115
12163
  }
12116
12164
 
12117
12165
  // src/scene-transitions/snapshot-overlay.tsx
12118
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
12166
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
12119
12167
  function SnapshotOverlay({
12120
12168
  snapshot,
12121
12169
  transition,
@@ -12123,19 +12171,19 @@ function SnapshotOverlay({
12123
12171
  runId,
12124
12172
  onFinish
12125
12173
  }) {
12126
- const { camera, gl } = useThree17();
12174
+ const { camera, gl } = useThree18();
12127
12175
  const meshRef = useRef27(null);
12128
12176
  const startRef = useRef27(null);
12129
12177
  const finishedRef = useRef27(false);
12130
12178
  const directionRef = useRef27(new Vector311());
12131
- const material = useMemo18(() => {
12179
+ const material = useMemo19(() => {
12132
12180
  if (!isSnapshotTransition(transition.preset)) {
12133
12181
  return null;
12134
12182
  }
12135
12183
  return createSnapshotMaterial(transition.preset, snapshot.texture);
12136
12184
  }, [snapshot.texture, transition.preset]);
12137
- useEffect26(() => () => material?.dispose(), [material]);
12138
- useEffect26(() => {
12185
+ useEffect27(() => () => material?.dispose(), [material]);
12186
+ useEffect27(() => {
12139
12187
  startRef.current = null;
12140
12188
  finishedRef.current = false;
12141
12189
  if (material) {
@@ -12165,20 +12213,20 @@ function SnapshotOverlay({
12165
12213
  return null;
12166
12214
  }
12167
12215
  return /* @__PURE__ */ jsxs12("mesh", { ref: meshRef, renderOrder: OVERLAY_RENDER_ORDER, frustumCulled: false, children: [
12168
- /* @__PURE__ */ jsx19("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
12169
- /* @__PURE__ */ jsx19("primitive", { attach: "material", object: material })
12216
+ /* @__PURE__ */ jsx20("primitive", { attach: "geometry", object: FULLSCREEN_PLANE }),
12217
+ /* @__PURE__ */ jsx20("primitive", { attach: "material", object: material })
12170
12218
  ] });
12171
12219
  }
12172
12220
 
12173
12221
  // src/scene-transitions/transition-overlay.tsx
12174
- import { jsx as jsx20 } from "react/jsx-runtime";
12222
+ import { jsx as jsx21 } from "react/jsx-runtime";
12175
12223
  function TransitionOverlay(props) {
12176
12224
  const { preset } = props.transition;
12177
12225
  switch (preset) {
12178
12226
  case "none":
12179
12227
  return null;
12180
12228
  case "particles":
12181
- return /* @__PURE__ */ jsx20(ParticleOverlay, { ...props });
12229
+ return /* @__PURE__ */ jsx21(ParticleOverlay, { ...props });
12182
12230
  case "crossfade":
12183
12231
  case "zoom":
12184
12232
  case "blackout":
@@ -12206,7 +12254,7 @@ function TransitionOverlay(props) {
12206
12254
  case "zoomBlur":
12207
12255
  case "hexDissolve":
12208
12256
  case "filmBurn":
12209
- return /* @__PURE__ */ jsx20(SnapshotOverlay, { ...props });
12257
+ return /* @__PURE__ */ jsx21(SnapshotOverlay, { ...props });
12210
12258
  default: {
12211
12259
  const exhaustive = preset;
12212
12260
  void exhaustive;
@@ -12215,44 +12263,6 @@ function TransitionOverlay(props) {
12215
12263
  }
12216
12264
  }
12217
12265
 
12218
- // src/tile/texture-manager-context.tsx
12219
- import { useThree as useThree18 } from "@react-three/fiber";
12220
- import {
12221
- createContext as createContext15,
12222
- useContext as useContext22,
12223
- useEffect as useEffect27,
12224
- useMemo as useMemo19
12225
- } from "react";
12226
- import { jsx as jsx21 } from "react/jsx-runtime";
12227
- var TileTextureManagerContext = createContext15(
12228
- null
12229
- );
12230
- function TileTextureManagerProvider({
12231
- children,
12232
- maxTextureMemoryMb = 128,
12233
- maxConcurrentLoads = 4
12234
- }) {
12235
- const gl = useThree18((state) => state.gl);
12236
- const anisotropy = gl.capabilities.getMaxAnisotropy();
12237
- const manager = useMemo19(
12238
- () => new TileTextureManager({
12239
- anisotropy,
12240
- maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
12241
- concurrency: Math.max(1, Math.floor(maxConcurrentLoads)),
12242
- retryCount: 1
12243
- }),
12244
- [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
12245
- );
12246
- useEffect27(() => {
12247
- manager.resume();
12248
- return () => manager.dispose();
12249
- }, [manager]);
12250
- return /* @__PURE__ */ jsx21(TileTextureManagerContext.Provider, { value: manager, children });
12251
- }
12252
- function useSharedTileTextureManager() {
12253
- return useContext22(TileTextureManagerContext);
12254
- }
12255
-
12256
12266
  // src/tile/tile.tsx
12257
12267
  import { useFrame as useFrame12, useThree as useThree19 } from "@react-three/fiber";
12258
12268
  import { memo as memo2, useEffect as useEffect28, useMemo as useMemo20, useRef as useRef28, useState as useState13 } from "react";
@@ -12939,6 +12949,19 @@ function SceneSource({
12939
12949
  }
12940
12950
  );
12941
12951
  }
12952
+ case "sphereTile": {
12953
+ const { id: _id, type: _type, ...tileProps } = scene;
12954
+ return /* @__PURE__ */ jsx23(
12955
+ SphereTile,
12956
+ {
12957
+ ...tileProps,
12958
+ visible,
12959
+ loadMode: baseOnly ? "base" : "full",
12960
+ onReady,
12961
+ onPreviewError: onError
12962
+ }
12963
+ );
12964
+ }
12942
12965
  default: {
12943
12966
  const exhaustive = scene;
12944
12967
  void exhaustive;
@@ -13005,6 +13028,7 @@ function ScenesController({
13005
13028
  activeSceneId,
13006
13029
  transition: transitionInput,
13007
13030
  renderHotspots,
13031
+ onSceneReady,
13008
13032
  onTransitionEnd,
13009
13033
  onTransitionError,
13010
13034
  snapshotMaxPixels = 3686400,
@@ -13040,6 +13064,7 @@ function ScenesController({
13040
13064
  const phaseRef = useRef29(phase);
13041
13065
  const liveSlotRef = useRef29(liveSlot);
13042
13066
  const transitionFromRef = useRef29(initialScene);
13067
+ const readySceneRef = useRef29(null);
13043
13068
  slotsRef.current = slots;
13044
13069
  phaseRef.current = phase;
13045
13070
  if (externalPhaseRef) {
@@ -13080,6 +13105,7 @@ function ScenesController({
13080
13105
  return;
13081
13106
  }
13082
13107
  if (phaseRef.current === "transitioning" || phaseRef.current === "capturing") {
13108
+ if (requestedSceneIdRef.current === requested.id) return;
13083
13109
  requestedSceneIdRef.current = requested.id;
13084
13110
  captureReasonRef.current = "interrupt";
13085
13111
  setPhase("recapturing");
@@ -13088,7 +13114,16 @@ function ScenesController({
13088
13114
  }
13089
13115
  startPreload(requested);
13090
13116
  }, [activeSceneId, pendingSlot, reportError, sceneMap, startPreload]);
13117
+ const notifySceneReady = useCallback11((scene) => {
13118
+ if (!scene || readySceneRef.current === scene) return;
13119
+ readySceneRef.current = scene;
13120
+ onSceneReady?.(scene.id);
13121
+ }, [onSceneReady]);
13091
13122
  const handleReady = useCallback11((slot) => {
13123
+ if (slot === liveSlotRef.current && pendingSlot === null && phaseRef.current === "idle") {
13124
+ notifySceneReady(slotsRef.current[slot]);
13125
+ return;
13126
+ }
13092
13127
  if (slot !== pendingSlot || phaseRef.current !== "preloading") {
13093
13128
  return;
13094
13129
  }
@@ -13104,6 +13139,7 @@ function ScenesController({
13104
13139
  setActiveSlot(slot);
13105
13140
  setPendingSlot(null);
13106
13141
  setPhase("idle");
13142
+ notifySceneReady(nextScene);
13107
13143
  if (previousScene && nextScene) {
13108
13144
  onTransitionEnd?.({
13109
13145
  previousSceneId: previousScene.id,
@@ -13118,7 +13154,7 @@ function ScenesController({
13118
13154
  captureReasonRef.current = "start";
13119
13155
  setPhase("capturing");
13120
13156
  setCaptureRequestId((current) => current + 1);
13121
- }, [controlsRef, onTransitionEnd, pendingSlot, transition.preset]);
13157
+ }, [controlsRef, notifySceneReady, onTransitionEnd, pendingSlot, transition.preset]);
13122
13158
  const handleCaptured = useCallback11((nextSnapshot) => {
13123
13159
  setSnapshot((current) => {
13124
13160
  current?.dispose();
@@ -13136,11 +13172,12 @@ function ScenesController({
13136
13172
  });
13137
13173
  manager?.releaseUnused();
13138
13174
  if (pendingSlot !== null) {
13175
+ notifySceneReady(slotsRef.current[pendingSlot]);
13139
13176
  setLiveSlot(pendingSlot);
13140
13177
  }
13141
13178
  setPhase("transitioning");
13142
13179
  setTransitionRunId((current) => current + 1);
13143
- }, [manager, pendingSlot, sceneMap, startPreload]);
13180
+ }, [manager, notifySceneReady, pendingSlot, sceneMap, startPreload]);
13144
13181
  const finishTransition = useCallback11(() => {
13145
13182
  const nextSlot = pendingSlot;
13146
13183
  const previousScene = transitionFromRef.current;
@@ -13183,6 +13220,7 @@ function ScenesController({
13183
13220
  setPhase("idle");
13184
13221
  };
13185
13222
  const onContextRestored = () => {
13223
+ readySceneRef.current = null;
13186
13224
  manager?.resume();
13187
13225
  setRecoveryKey((current) => current + 1);
13188
13226
  };