@ericchen1990/pano-view 3.9.0 → 3.10.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
@@ -1291,8 +1291,8 @@ var require_howler = __commonJS({
1291
1291
  break;
1292
1292
  }
1293
1293
  }
1294
- if (cache && remCache) {
1295
- delete cache[self._src];
1294
+ if (cache2 && remCache) {
1295
+ delete cache2[self._src];
1296
1296
  }
1297
1297
  Howler4.noAudio = false;
1298
1298
  self._state = "unloaded";
@@ -1547,7 +1547,7 @@ var require_howler = __commonJS({
1547
1547
  _refreshBuffer: function(sound) {
1548
1548
  var self = this;
1549
1549
  sound._node.bufferSource = Howler4.ctx.createBufferSource();
1550
- sound._node.bufferSource.buffer = cache[self._src];
1550
+ sound._node.bufferSource.buffer = cache2[self._src];
1551
1551
  if (sound._panner) {
1552
1552
  sound._node.bufferSource.connect(sound._panner);
1553
1553
  } else {
@@ -1709,11 +1709,11 @@ var require_howler = __commonJS({
1709
1709
  self._node.removeEventListener("ended", self._endFn, false);
1710
1710
  }
1711
1711
  };
1712
- var cache = {};
1712
+ var cache2 = {};
1713
1713
  var loadBuffer = function(self) {
1714
1714
  var url = self._src;
1715
- if (cache[url]) {
1716
- self._duration = cache[url].duration;
1715
+ if (cache2[url]) {
1716
+ self._duration = cache2[url].duration;
1717
1717
  loadSound(self);
1718
1718
  return;
1719
1719
  }
@@ -1747,7 +1747,7 @@ var require_howler = __commonJS({
1747
1747
  self._html5 = true;
1748
1748
  self._webAudio = false;
1749
1749
  self._sounds = [];
1750
- delete cache[url];
1750
+ delete cache2[url];
1751
1751
  self.load();
1752
1752
  }
1753
1753
  };
@@ -1767,7 +1767,7 @@ var require_howler = __commonJS({
1767
1767
  };
1768
1768
  var success = function(buffer) {
1769
1769
  if (buffer && self._sounds.length > 0) {
1770
- cache[self._src] = buffer;
1770
+ cache2[self._src] = buffer;
1771
1771
  loadSound(self, buffer);
1772
1772
  } else {
1773
1773
  error();
@@ -2260,6 +2260,7 @@ __export(index_exports, {
2260
2260
  DEFAULT_PANO_VIDEO_PLAYBACK_RATES: () => DEFAULT_PANO_VIDEO_PLAYBACK_RATES,
2261
2261
  DEFAULT_WEBVR_CHROME_APPEARANCE: () => DEFAULT_WEBVR_CHROME_APPEARANCE,
2262
2262
  DEFAULT_WEBVR_CURSOR_DWELL_MS: () => DEFAULT_WEBVR_CURSOR_DWELL_MS,
2263
+ EMPTY_WEBVR_STATE: () => EMPTY_WEBVR_STATE,
2263
2264
  GraphicHotspot: () => GraphicHotspot,
2264
2265
  Gyro: () => Gyro,
2265
2266
  IframeHotspot: () => IframeHotspot,
@@ -2274,6 +2275,7 @@ __export(index_exports, {
2274
2275
  PanoFilter: () => PanoFilter,
2275
2276
  PanoMap: () => PanoMap,
2276
2277
  PanoMapOverlay: () => PanoMapOverlay,
2278
+ PanoOverlay: () => PanoOverlay,
2277
2279
  PanoOverlayPortalProvider: () => PanoOverlayPortalProvider,
2278
2280
  PanoSceneMenu: () => PanoSceneMenu,
2279
2281
  PanoVideo: () => PanoVideo,
@@ -2387,7 +2389,7 @@ function limitedYawDelta(from, to, range, shortestPath = true) {
2387
2389
  // src/pano-viewer.tsx
2388
2390
  var import_fiber10 = require("@react-three/fiber");
2389
2391
  var import_xr3 = require("@react-three/xr");
2390
- var import_react29 = require("react");
2392
+ var import_react30 = require("react");
2391
2393
 
2392
2394
  // src/background-audio-host.ts
2393
2395
  var import_react = require("react");
@@ -3939,6 +3941,18 @@ var import_react7 = require("react");
3939
3941
  // src/webvr/host.ts
3940
3942
  var import_react6 = require("react");
3941
3943
  var WebVRRuntimeContext = (0, import_react6.createContext)(null);
3944
+ var EMPTY_WEBVR_STATE = Object.freeze({
3945
+ available: false,
3946
+ enabled: false,
3947
+ mode: null
3948
+ });
3949
+ function createWebVRState(available, mode) {
3950
+ return Object.freeze({
3951
+ available,
3952
+ enabled: mode !== null,
3953
+ mode
3954
+ });
3955
+ }
3942
3956
  function createWebVRHost() {
3943
3957
  return {
3944
3958
  controller: null,
@@ -3948,18 +3962,28 @@ function createWebVRHost() {
3948
3962
  chrome: true,
3949
3963
  mode: null,
3950
3964
  setupOpen: false,
3951
- settings: { screensize: "auto", profileId: "cardboard-v1" }
3965
+ settings: { screensize: "auto", profileId: "cardboard-v1" },
3966
+ detectedScreensize: null
3952
3967
  },
3968
+ state: EMPTY_WEBVR_STATE,
3953
3969
  listeners: /* @__PURE__ */ new Set()
3954
3970
  };
3955
3971
  }
3956
3972
  function updateWebVRHost(host, snapshot) {
3957
- host.snapshot = { ...host.snapshot, ...snapshot };
3973
+ const previous = host.snapshot;
3974
+ const next = { ...previous, ...snapshot };
3975
+ host.snapshot = next;
3958
3976
  host.revision += 1;
3977
+ if (previous.available !== next.available || previous.mode !== next.mode) {
3978
+ host.state = createWebVRState(next.available, next.mode);
3979
+ }
3959
3980
  for (const listener of host.listeners) {
3960
3981
  listener();
3961
3982
  }
3962
3983
  }
3984
+ function getWebVRState(host) {
3985
+ return host.state;
3986
+ }
3963
3987
  function subscribeWebVRHost(host, listener) {
3964
3988
  host.listeners.add(listener);
3965
3989
  return () => {
@@ -6190,8 +6214,50 @@ function createPanoModalHost() {
6190
6214
  };
6191
6215
  }
6192
6216
 
6193
- // src/overlay/pano-overlay.tsx
6217
+ // src/overlay/partition.ts
6194
6218
  var import_react17 = require("react");
6219
+ var PANO_OVERLAY_TYPE = /* @__PURE__ */ Symbol.for("pano-view.PanoOverlay");
6220
+ function isPanoOverlayType(type) {
6221
+ return typeof type === "function" && type[PANO_OVERLAY_TYPE] === true;
6222
+ }
6223
+ function isPanoOverlayElement(node) {
6224
+ return (0, import_react17.isValidElement)(node) && isPanoOverlayType(node.type);
6225
+ }
6226
+ function isFragmentElement(node) {
6227
+ return (0, import_react17.isValidElement)(node) && node.type === import_react17.Fragment;
6228
+ }
6229
+ function flattenViewerChildren(children) {
6230
+ const flat = [];
6231
+ for (const item of import_react17.Children.toArray(children)) {
6232
+ if (isFragmentElement(item)) {
6233
+ flat.push(...flattenViewerChildren(item.props.children));
6234
+ } else {
6235
+ flat.push(item);
6236
+ }
6237
+ }
6238
+ return flat;
6239
+ }
6240
+ function partitionPanoOverlay(children) {
6241
+ const items = flattenViewerChildren(children);
6242
+ const overlayElements = [];
6243
+ const sceneItems = [];
6244
+ let found = false;
6245
+ for (const item of items) {
6246
+ if (isPanoOverlayElement(item)) {
6247
+ found = true;
6248
+ overlayElements.push(item);
6249
+ } else {
6250
+ sceneItems.push(item);
6251
+ }
6252
+ }
6253
+ if (!found) {
6254
+ return { sceneChildren: children, overlayElements: [] };
6255
+ }
6256
+ return { sceneChildren: sceneItems, overlayElements };
6257
+ }
6258
+
6259
+ // src/overlay/pano-overlay.tsx
6260
+ var import_react18 = require("react");
6195
6261
 
6196
6262
  // src/overlay/visible-rect.ts
6197
6263
  function getOverlayVisibleRect(rect, viewport) {
@@ -6204,33 +6270,49 @@ function getOverlayVisibleRect(rect, viewport) {
6204
6270
 
6205
6271
  // src/overlay/pano-overlay.tsx
6206
6272
  var import_jsx_runtime5 = require("react/jsx-runtime");
6207
- var PanoOverlayContext = (0, import_react17.createContext)(null);
6208
- var PanoModalHostContext = (0, import_react17.createContext)(null);
6273
+ var PanoOverlayContext = (0, import_react18.createContext)(null);
6274
+ var PanoModalHostContext = (0, import_react18.createContext)(null);
6275
+ var PanoOverlayOutletContext = (0, import_react18.createContext)(false);
6276
+ function PanoOverlay({ children }) {
6277
+ const inOutlet = (0, import_react18.useContext)(PanoOverlayOutletContext);
6278
+ if (!inOutlet) {
6279
+ throw new Error(
6280
+ "PanoOverlay must be a direct child of PanoViewer (or of a Fragment under PanoViewer), not nested inside scene components."
6281
+ );
6282
+ }
6283
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
6284
+ }
6285
+ PanoOverlay[PANO_OVERLAY_TYPE] = true;
6286
+ var OVERLAY_HOOK_SCOPE = "PanoOverlay or PanoViewer.renderOverlay";
6209
6287
  function usePanoOverlay() {
6210
- const context = (0, import_react17.useContext)(PanoOverlayContext);
6211
- if (!context) throw new Error("usePanoOverlay must be used inside PanoViewer.renderOverlay.");
6288
+ const context = (0, import_react18.useContext)(PanoOverlayContext);
6289
+ if (!context) {
6290
+ throw new Error(`usePanoOverlay must be used inside ${OVERLAY_HOOK_SCOPE}.`);
6291
+ }
6212
6292
  return context;
6213
6293
  }
6214
6294
  function usePanoModal(open) {
6215
- const host = (0, import_react17.useContext)(PanoModalHostContext);
6216
- if (!host) throw new Error("usePanoModal must be used inside PanoViewer.renderOverlay.");
6217
- (0, import_react17.useLayoutEffect)(() => open ? host.acquire() : void 0, [host, open]);
6295
+ const host = (0, import_react18.useContext)(PanoModalHostContext);
6296
+ if (!host) {
6297
+ throw new Error(`usePanoModal must be used inside ${OVERLAY_HOOK_SCOPE}.`);
6298
+ }
6299
+ (0, import_react18.useLayoutEffect)(() => open ? host.acquire() : void 0, [host, open]);
6218
6300
  }
6219
6301
  function PanoOverlayPortalProvider({
6220
6302
  container,
6221
6303
  children
6222
6304
  }) {
6223
6305
  const context = usePanoOverlay();
6224
- const value = (0, import_react17.useMemo)(() => ({ ...context, portalContainer: container }), [context, container]);
6306
+ const value = (0, import_react18.useMemo)(() => ({ ...context, portalContainer: container }), [context, container]);
6225
6307
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PanoOverlayContext.Provider, { value, children });
6226
6308
  }
6227
6309
  function usePanoOverlayHost(isFullscreen) {
6228
- const [portalContainer, setPortalContainer] = (0, import_react17.useState)(null);
6229
- const [geometry, setGeometry] = (0, import_react17.useState)({
6310
+ const [portalContainer, setPortalContainer] = (0, import_react18.useState)(null);
6311
+ const [geometry, setGeometry] = (0, import_react18.useState)({
6230
6312
  size: { width: 0, height: 0 },
6231
6313
  visibleRect: { left: 0, top: 0, width: 0, height: 0 }
6232
6314
  });
6233
- (0, import_react17.useLayoutEffect)(() => {
6315
+ (0, import_react18.useLayoutEffect)(() => {
6234
6316
  if (!portalContainer) return;
6235
6317
  const win = portalContainer.ownerDocument.defaultView;
6236
6318
  if (!win) return;
@@ -6269,13 +6351,13 @@ function usePanoOverlayHost(isFullscreen) {
6269
6351
  win.visualViewport?.removeEventListener("scroll", scheduleMeasure);
6270
6352
  };
6271
6353
  }, [portalContainer]);
6272
- const context = (0, import_react17.useMemo)(() => ({ portalContainer, ...geometry, isFullscreen }), [portalContainer, geometry, isFullscreen]);
6354
+ const context = (0, import_react18.useMemo)(() => ({ portalContainer, ...geometry, isFullscreen }), [portalContainer, geometry, isFullscreen]);
6273
6355
  return { context, setPortalContainer };
6274
6356
  }
6275
6357
 
6276
6358
  // src/panorama-event-surface.tsx
6277
6359
  var import_fiber7 = require("@react-three/fiber");
6278
- var import_react18 = require("react");
6360
+ var import_react19 = require("react");
6279
6361
  var import_three5 = require("three");
6280
6362
 
6281
6363
  // src/hotspot/coordinates.ts
@@ -6374,9 +6456,9 @@ function makePanoramaPointerEvent(event) {
6374
6456
  };
6375
6457
  }
6376
6458
  function PanoramaEventSurface() {
6377
- const eventBus = (0, import_react18.useContext)(PanoEventBusContext);
6459
+ const eventBus = (0, import_react19.useContext)(PanoEventBusContext);
6378
6460
  const { camera, gl } = (0, import_fiber7.useThree)();
6379
- (0, import_react18.useEffect)(() => {
6461
+ (0, import_react19.useEffect)(() => {
6380
6462
  if (!eventBus) {
6381
6463
  return;
6382
6464
  }
@@ -6445,8 +6527,8 @@ function PanoramaEventSurface() {
6445
6527
  }
6446
6528
 
6447
6529
  // src/scenes-host.ts
6448
- var import_react19 = require("react");
6449
- var ScenesHostContext = (0, import_react19.createContext)(null);
6530
+ var import_react20 = require("react");
6531
+ var ScenesHostContext = (0, import_react20.createContext)(null);
6450
6532
  function createScenesHost() {
6451
6533
  return {
6452
6534
  controller: null,
@@ -6463,7 +6545,7 @@ function notifyScenesHost(host) {
6463
6545
 
6464
6546
  // src/touch-controls.tsx
6465
6547
  var import_fiber8 = require("@react-three/fiber");
6466
- var import_react20 = require("react");
6548
+ var import_react21 = require("react");
6467
6549
  var DEFAULT_ROTATE_SPEED3 = 0.35;
6468
6550
  function pointerAt2(event) {
6469
6551
  return event.timeStamp > 0 ? event.timeStamp : performance.now();
@@ -6481,18 +6563,18 @@ function TouchControls({
6481
6563
  pinchZoom = true
6482
6564
  }) {
6483
6565
  useClaimControlChannel("touch");
6484
- const controlsRef = (0, import_react20.useContext)(PanoramaViewContext);
6566
+ const controlsRef = (0, import_react21.useContext)(PanoramaViewContext);
6485
6567
  const { gl, camera } = (0, import_fiber8.useThree)();
6486
- const validateDragRef = (0, import_react20.useRef)(void 0);
6568
+ const validateDragRef = (0, import_react21.useRef)(void 0);
6487
6569
  (0, import_fiber8.useFrame)((_, delta) => validateDragRef.current?.(delta), -1);
6488
- const pointersRef = (0, import_react20.useRef)(/* @__PURE__ */ new Map());
6489
- const pinchDistanceRef = (0, import_react20.useRef)(null);
6490
- const optionsRef = (0, import_react20.useRef)({ rotateSpeed, invert, pinchZoom });
6570
+ const pointersRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
6571
+ const pinchDistanceRef = (0, import_react21.useRef)(null);
6572
+ const optionsRef = (0, import_react21.useRef)({ rotateSpeed, invert, pinchZoom });
6491
6573
  if (!controlsRef) {
6492
6574
  throw new Error("<TouchControls> must be rendered inside <PanoViewer>.");
6493
6575
  }
6494
6576
  optionsRef.current = { rotateSpeed, invert, pinchZoom };
6495
- (0, import_react20.useEffect)(() => {
6577
+ (0, import_react21.useEffect)(() => {
6496
6578
  const element = gl.domElement;
6497
6579
  const drag = createPointerDrag(camera, element, () => controlsRef.current, "touch");
6498
6580
  const pending = /* @__PURE__ */ new Set();
@@ -6667,17 +6749,17 @@ function TouchControls({
6667
6749
  }
6668
6750
 
6669
6751
  // src/video/chrome-overlay.tsx
6670
- var import_react21 = require("react");
6752
+ var import_react22 = require("react");
6671
6753
  var import_jsx_runtime7 = require("react/jsx-runtime");
6672
- var PanoChromeOverlayContext = (0, import_react21.createContext)(null);
6754
+ var PanoChromeOverlayContext = (0, import_react22.createContext)(null);
6673
6755
  function usePanoChromeOverlay() {
6674
- const [overlayElement, setOverlayElement] = (0, import_react21.useState)(
6756
+ const [overlayElement, setOverlayElement] = (0, import_react22.useState)(
6675
6757
  null
6676
6758
  );
6677
- const setOverlayNode = (0, import_react21.useCallback)((node) => {
6759
+ const setOverlayNode = (0, import_react22.useCallback)((node) => {
6678
6760
  setOverlayElement(node);
6679
6761
  }, []);
6680
- const api = (0, import_react21.useMemo)(
6762
+ const api = (0, import_react22.useMemo)(
6681
6763
  () => ({ overlayElement }),
6682
6764
  [overlayElement]
6683
6765
  );
@@ -6693,9 +6775,9 @@ function usePanoChromeOverlay() {
6693
6775
  }
6694
6776
 
6695
6777
  // src/video/host.ts
6696
- var import_react22 = require("react");
6697
- var PanoVideoHostContext = (0, import_react22.createContext)(null);
6698
- var DefaultPanoVideoControlsContext = (0, import_react22.createContext)(false);
6778
+ var import_react23 = require("react");
6779
+ var PanoVideoHostContext = (0, import_react23.createContext)(null);
6780
+ var DefaultPanoVideoControlsContext = (0, import_react23.createContext)(false);
6699
6781
  function createPanoVideoHost() {
6700
6782
  return {
6701
6783
  controller: null,
@@ -6740,8 +6822,8 @@ function isProduction3() {
6740
6822
  return nodeEnv === "production";
6741
6823
  }
6742
6824
  function useClaimPanoVideoControls() {
6743
- const host = (0, import_react22.useContext)(PanoVideoHostContext);
6744
- const isDefault = (0, import_react22.useContext)(DefaultPanoVideoControlsContext);
6825
+ const host = (0, import_react23.useContext)(PanoVideoHostContext);
6826
+ const isDefault = (0, import_react23.useContext)(DefaultPanoVideoControlsContext);
6745
6827
  if (!host || isDefault) {
6746
6828
  return;
6747
6829
  }
@@ -6754,10 +6836,10 @@ function useClaimPanoVideoControls() {
6754
6836
  }
6755
6837
 
6756
6838
  // src/video/pano-video-chrome-bridge.tsx
6757
- var import_react25 = require("react");
6839
+ var import_react26 = require("react");
6758
6840
 
6759
6841
  // src/video/pano-video-captions.tsx
6760
- var import_react23 = require("react");
6842
+ var import_react24 = require("react");
6761
6843
  var import_jsx_runtime8 = require("react/jsx-runtime");
6762
6844
  var DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE = {
6763
6845
  color: "#ffffff",
@@ -6781,7 +6863,7 @@ function resolvePanoVideoCaptionAppearance(appearance) {
6781
6863
  function PanoVideoCaptionsOverlay({
6782
6864
  controller
6783
6865
  }) {
6784
- const snapshot = (0, import_react23.useSyncExternalStore)(
6866
+ const snapshot = (0, import_react24.useSyncExternalStore)(
6785
6867
  controller.subscribe,
6786
6868
  controller.getSnapshot,
6787
6869
  controller.getSnapshot
@@ -6823,7 +6905,7 @@ function PanoVideoCaptions({
6823
6905
  }
6824
6906
 
6825
6907
  // src/video/pano-video-controls.tsx
6826
- var import_react24 = require("react");
6908
+ var import_react25 = require("react");
6827
6909
 
6828
6910
  // src/video/format.ts
6829
6911
  var DEFAULT_PANO_VIDEO_PLAYBACK_RATES = [0.5, 1, 1.25, 1.5, 2];
@@ -7275,7 +7357,7 @@ function VerticalVolumeSlider({
7275
7357
  onVolume,
7276
7358
  volume
7277
7359
  }) {
7278
- const trackRef = (0, import_react24.useRef)(null);
7360
+ const trackRef = (0, import_react25.useRef)(null);
7279
7361
  const applyPointer = (event) => {
7280
7362
  const track = trackRef.current;
7281
7363
  if (!track) {
@@ -7411,14 +7493,14 @@ function VolumeControl({
7411
7493
  volume,
7412
7494
  volumeAdjustable
7413
7495
  }) {
7414
- const rootRef = (0, import_react24.useRef)(null);
7415
- const [open, setOpen] = (0, import_react24.useState)(false);
7496
+ const rootRef = (0, import_react25.useRef)(null);
7497
+ const [open, setOpen] = (0, import_react25.useState)(false);
7416
7498
  const displayVolume = muted ? 0 : volume;
7417
7499
  const mutedOrSilent = muted || volume === 0;
7418
- (0, import_react24.useEffect)(() => {
7500
+ (0, import_react25.useEffect)(() => {
7419
7501
  onOpenChange(open);
7420
7502
  }, [onOpenChange, open]);
7421
- (0, import_react24.useEffect)(() => {
7503
+ (0, import_react25.useEffect)(() => {
7422
7504
  if (!open || !volumeAdjustable || hasFinePointerHover()) {
7423
7505
  return;
7424
7506
  }
@@ -7505,24 +7587,24 @@ function PanoVideoControlsHud({
7505
7587
  fullscreen,
7506
7588
  overlayElement
7507
7589
  }) {
7508
- const resolved = (0, import_react24.useMemo)(
7590
+ const resolved = (0, import_react25.useMemo)(
7509
7591
  () => resolveControlsAppearance(appearance),
7510
7592
  [appearance]
7511
7593
  );
7512
- const snapshot = (0, import_react24.useSyncExternalStore)(
7594
+ const snapshot = (0, import_react25.useSyncExternalStore)(
7513
7595
  controller.subscribe,
7514
7596
  controller.getSnapshot,
7515
7597
  controller.getSnapshot
7516
7598
  );
7517
- const [menu, setMenu] = (0, import_react24.useState)(null);
7518
- const [captionsPanel, setCaptionsPanel] = (0, import_react24.useState)("tracks");
7519
- const [visible, setVisible] = (0, import_react24.useState)(true);
7520
- const [volumeOpen, setVolumeOpen] = (0, import_react24.useState)(false);
7521
- const [overlayWidth, setOverlayWidth] = (0, import_react24.useState)(
7599
+ const [menu, setMenu] = (0, import_react25.useState)(null);
7600
+ const [captionsPanel, setCaptionsPanel] = (0, import_react25.useState)("tracks");
7601
+ const [visible, setVisible] = (0, import_react25.useState)(true);
7602
+ const [volumeOpen, setVolumeOpen] = (0, import_react25.useState)(false);
7603
+ const [overlayWidth, setOverlayWidth] = (0, import_react25.useState)(
7522
7604
  () => overlayElement.clientWidth
7523
7605
  );
7524
- const idleTimerRef = (0, import_react24.useRef)(0);
7525
- const barId = (0, import_react24.useId)();
7606
+ const idleTimerRef = (0, import_react25.useRef)(0);
7607
+ const barId = (0, import_react25.useId)();
7526
7608
  const rateMenuId = `${barId}-rate`;
7527
7609
  const variantMenuId = `${barId}-variant`;
7528
7610
  const captionsMenuId = `${barId}-captions`;
@@ -7530,7 +7612,7 @@ function PanoVideoControlsHud({
7530
7612
  const compact = overlayWidth < COMPACT_CONTROLS_WIDTH;
7531
7613
  const hideTime = overlayWidth < NARROW_CONTROLS_WIDTH;
7532
7614
  const showFullscreen = canEnterViewerFullscreen();
7533
- const bumpVisibility = (0, import_react24.useCallback)(
7615
+ const bumpVisibility = (0, import_react25.useCallback)(
7534
7616
  (playing) => {
7535
7617
  setVisible(true);
7536
7618
  window.clearTimeout(idleTimerRef.current);
@@ -7543,7 +7625,7 @@ function PanoVideoControlsHud({
7543
7625
  },
7544
7626
  [menu, volumeOpen]
7545
7627
  );
7546
- (0, import_react24.useEffect)(() => {
7628
+ (0, import_react25.useEffect)(() => {
7547
7629
  if (typeof ResizeObserver === "undefined") {
7548
7630
  return;
7549
7631
  }
@@ -7558,15 +7640,15 @@ function PanoVideoControlsHud({
7558
7640
  observer.disconnect();
7559
7641
  };
7560
7642
  }, [overlayElement]);
7561
- (0, import_react24.useEffect)(() => {
7643
+ (0, import_react25.useEffect)(() => {
7562
7644
  if (!compact && menu === "more") {
7563
7645
  setMenu(null);
7564
7646
  }
7565
7647
  }, [compact, menu]);
7566
- (0, import_react24.useEffect)(() => {
7648
+ (0, import_react25.useEffect)(() => {
7567
7649
  bumpVisibility(snapshot.playing);
7568
7650
  }, [bumpVisibility, snapshot.playing]);
7569
- (0, import_react24.useEffect)(() => {
7651
+ (0, import_react25.useEffect)(() => {
7570
7652
  const root = overlayElement.parentElement;
7571
7653
  if (!root) {
7572
7654
  return;
@@ -7625,7 +7707,7 @@ function PanoVideoControlsHud({
7625
7707
  document.removeEventListener("pointerdown", handlePointerDown, true);
7626
7708
  };
7627
7709
  }, [bumpVisibility, controller, menu, overlayElement, snapshot.playing, volumeOpen]);
7628
- (0, import_react24.useEffect)(
7710
+ (0, import_react25.useEffect)(
7629
7711
  () => () => {
7630
7712
  window.clearTimeout(idleTimerRef.current);
7631
7713
  },
@@ -7916,7 +7998,7 @@ function PanoVideoControlsHud({
7916
7998
  }
7917
7999
  function PanoVideoControls({ appearance }) {
7918
8000
  useClaimPanoVideoControls();
7919
- const host = (0, import_react24.useContext)(PanoVideoHostContext);
8001
+ const host = (0, import_react25.useContext)(PanoVideoHostContext);
7920
8002
  if (host && appearance) {
7921
8003
  host.controls = appearance;
7922
8004
  }
@@ -7930,7 +8012,7 @@ function PanoVideoChromeBridge({
7930
8012
  host,
7931
8013
  overlayElement
7932
8014
  }) {
7933
- (0, import_react25.useSyncExternalStore)(
8015
+ (0, import_react26.useSyncExternalStore)(
7934
8016
  (onStoreChange) => subscribePanoVideoHost(host, onStoreChange),
7935
8017
  () => getPanoVideoHostRevision(host),
7936
8018
  () => getPanoVideoHostRevision(host)
@@ -7954,7 +8036,7 @@ function PanoVideoChromeBridge({
7954
8036
  }
7955
8037
 
7956
8038
  // src/webvr/chrome.tsx
7957
- var import_react27 = require("react");
8039
+ var import_react28 = require("react");
7958
8040
 
7959
8041
  // src/webvr/appearance.ts
7960
8042
  var DEFAULT_WEBVR_CHROME_APPEARANCE = {
@@ -7966,7 +8048,7 @@ var DEFAULT_WEBVR_CHROME_APPEARANCE = {
7966
8048
  };
7967
8049
 
7968
8050
  // src/webvr/orientation.ts
7969
- var import_react26 = require("react");
8051
+ var import_react27 = require("react");
7970
8052
  function subscribePortraitOrientation(onChange) {
7971
8053
  if (typeof window === "undefined") {
7972
8054
  return () => {
@@ -7998,7 +8080,7 @@ function getPortraitOrientation() {
7998
8080
  return window.matchMedia("(orientation: portrait)").matches;
7999
8081
  }
8000
8082
  function usePortraitOrientation() {
8001
- return (0, import_react26.useSyncExternalStore)(
8083
+ return (0, import_react27.useSyncExternalStore)(
8002
8084
  subscribePortraitOrientation,
8003
8085
  getPortraitOrientation,
8004
8086
  () => false
@@ -8020,6 +8102,7 @@ var WEBVR_PROFILES = {
8020
8102
  fov: 80,
8021
8103
  ipdMm: 60,
8022
8104
  screenToLensMm: 42,
8105
+ trayDistanceMm: 0,
8023
8106
  k1: 0,
8024
8107
  k2: 0
8025
8108
  },
@@ -8029,6 +8112,7 @@ var WEBVR_PROFILES = {
8029
8112
  fov: 80,
8030
8113
  ipdMm: 60,
8031
8114
  screenToLensMm: 42,
8115
+ trayDistanceMm: 35,
8032
8116
  k1: 0.441,
8033
8117
  k2: 0.156
8034
8118
  },
@@ -8038,6 +8122,7 @@ var WEBVR_PROFILES = {
8038
8122
  fov: 90,
8039
8123
  ipdMm: 64,
8040
8124
  screenToLensMm: 39,
8125
+ trayDistanceMm: 0,
8041
8126
  k1: 0.336,
8042
8127
  k2: 0.553
8043
8128
  },
@@ -8047,6 +8132,7 @@ var WEBVR_PROFILES = {
8047
8132
  fov: 96,
8048
8133
  ipdMm: 63.5,
8049
8134
  screenToLensMm: 50,
8135
+ trayDistanceMm: 0,
8050
8136
  k1: 0.215,
8051
8137
  k2: 0.215
8052
8138
  },
@@ -8056,6 +8142,7 @@ var WEBVR_PROFILES = {
8056
8142
  fov: 90,
8057
8143
  ipdMm: 61.5,
8058
8144
  screenToLensMm: 44,
8145
+ trayDistanceMm: 0,
8059
8146
  k1: 0.385,
8060
8147
  k2: 0.593
8061
8148
  }
@@ -8106,12 +8193,28 @@ function saveWebVRSettings(settings) {
8106
8193
  } catch {
8107
8194
  }
8108
8195
  }
8196
+ function clearWebVRSettings() {
8197
+ if (typeof localStorage === "undefined") {
8198
+ return;
8199
+ }
8200
+ try {
8201
+ localStorage.removeItem(STORAGE_KEY);
8202
+ } catch {
8203
+ }
8204
+ }
8205
+ function resolveInitialWebVRSettings(screensize, profile) {
8206
+ return {
8207
+ screensize: screensize ?? DEFAULT_WEBVR_SETTINGS.screensize,
8208
+ profileId: typeof profile === "string" ? profile : DEFAULT_WEBVR_SETTINGS.profileId
8209
+ };
8210
+ }
8109
8211
  function clamp2(value, min, max) {
8110
8212
  return Math.max(min, Math.min(max, Number.isFinite(value) ? value : min));
8111
8213
  }
8112
8214
 
8113
8215
  // src/webvr/chrome.tsx
8114
8216
  var import_jsx_runtime11 = require("react/jsx-runtime");
8217
+ var SESSION_CHROME_IDLE_MS = 3e3;
8115
8218
  function resolveAppearance(value) {
8116
8219
  return {
8117
8220
  ...DEFAULT_WEBVR_CHROME_APPEARANCE,
@@ -8139,8 +8242,15 @@ function ChromeButton2({
8139
8242
  }
8140
8243
  );
8141
8244
  }
8245
+ function formatScreensizeLabel(settings, detectedScreensize) {
8246
+ if (settings.screensize === "auto") {
8247
+ return detectedScreensize === null ? "Auto" : `Auto \xB7 ${detectedScreensize.toFixed(1)}`;
8248
+ }
8249
+ return settings.screensize.toFixed(1);
8250
+ }
8142
8251
  function SetupPanel({
8143
8252
  appearance,
8253
+ detectedScreensize,
8144
8254
  host,
8145
8255
  settings
8146
8256
  }) {
@@ -8184,7 +8294,7 @@ function SetupPanel({
8184
8294
  children: "\u2039"
8185
8295
  }
8186
8296
  ),
8187
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { className: "pano-webvr-setup-value", children: settings.screensize === "auto" ? "Auto" : settings.screensize.toFixed(1) }),
8297
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { className: "pano-webvr-setup-value", children: formatScreensizeLabel(settings, detectedScreensize) }),
8188
8298
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8189
8299
  "button",
8190
8300
  {
@@ -8225,42 +8335,61 @@ function SetupPanel({
8225
8335
  )
8226
8336
  ] })
8227
8337
  ] }),
8338
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pano-webvr-setup-row", children: [
8339
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8340
+ ChromeButton2,
8341
+ {
8342
+ appearance,
8343
+ onClick: () => controller?.resetSettings(),
8344
+ children: "Reset"
8345
+ }
8346
+ ),
8347
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8348
+ ChromeButton2,
8349
+ {
8350
+ appearance,
8351
+ onClick: () => controller?.closeSetup(),
8352
+ children: "Close"
8353
+ }
8354
+ )
8355
+ ] })
8356
+ ]
8357
+ }
8358
+ );
8359
+ }
8360
+ function SessionActions({
8361
+ appearance,
8362
+ host,
8363
+ mode,
8364
+ visible
8365
+ }) {
8366
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
8367
+ "div",
8368
+ {
8369
+ "aria-hidden": !visible,
8370
+ className: visible ? "pano-webvr-toolbar-actions" : "pano-webvr-toolbar-actions pano-webvr-toolbar-actions-hidden",
8371
+ inert: !visible ? true : void 0,
8372
+ children: [
8373
+ mode !== "webxr" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8374
+ ChromeButton2,
8375
+ {
8376
+ appearance,
8377
+ onClick: () => host.controller?.openSetup(),
8378
+ children: "VR Setup"
8379
+ }
8380
+ ) : null,
8228
8381
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8229
8382
  ChromeButton2,
8230
8383
  {
8231
8384
  appearance,
8232
- onClick: () => controller?.closeSetup(),
8233
- children: "Close"
8385
+ onClick: () => void host.controller?.exitVR(),
8386
+ children: "Exit VR"
8234
8387
  }
8235
8388
  )
8236
8389
  ]
8237
8390
  }
8238
8391
  );
8239
8392
  }
8240
- function SessionActions({
8241
- appearance,
8242
- host,
8243
- mode
8244
- }) {
8245
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pano-webvr-toolbar-actions", children: [
8246
- mode !== "webxr" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8247
- ChromeButton2,
8248
- {
8249
- appearance,
8250
- onClick: () => host.controller?.openSetup(),
8251
- children: "VR Setup"
8252
- }
8253
- ) : null,
8254
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8255
- ChromeButton2,
8256
- {
8257
- appearance,
8258
- onClick: () => void host.controller?.exitVR(),
8259
- children: "Exit VR"
8260
- }
8261
- )
8262
- ] });
8263
- }
8264
8393
  function RotatePhoneIcon2() {
8265
8394
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
8266
8395
  "svg",
@@ -8291,18 +8420,81 @@ function PortraitPrompt() {
8291
8420
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "pano-webvr-portrait-text", children: "Rotate your phone to landscape" })
8292
8421
  ] });
8293
8422
  }
8423
+ function useSessionChromeVisibility(active, keepVisible, viewer) {
8424
+ const [visible, setVisible] = (0, import_react28.useState)(true);
8425
+ const idleTimerRef = (0, import_react28.useRef)(0);
8426
+ const bump = (0, import_react28.useCallback)(() => {
8427
+ setVisible(true);
8428
+ window.clearTimeout(idleTimerRef.current);
8429
+ if (!active || keepVisible) {
8430
+ return;
8431
+ }
8432
+ idleTimerRef.current = window.setTimeout(() => {
8433
+ setVisible(false);
8434
+ }, SESSION_CHROME_IDLE_MS);
8435
+ }, [active, keepVisible]);
8436
+ (0, import_react28.useEffect)(() => {
8437
+ if (!active) {
8438
+ window.clearTimeout(idleTimerRef.current);
8439
+ setVisible(true);
8440
+ return;
8441
+ }
8442
+ bump();
8443
+ }, [active, bump, keepVisible]);
8444
+ (0, import_react28.useEffect)(() => {
8445
+ if (!active || !viewer) {
8446
+ return;
8447
+ }
8448
+ const reveal = () => {
8449
+ bump();
8450
+ };
8451
+ viewer.addEventListener("pointerdown", reveal);
8452
+ viewer.addEventListener("wheel", reveal, { passive: true });
8453
+ viewer.addEventListener("keydown", reveal);
8454
+ return () => {
8455
+ viewer.removeEventListener("pointerdown", reveal);
8456
+ viewer.removeEventListener("wheel", reveal);
8457
+ viewer.removeEventListener("keydown", reveal);
8458
+ };
8459
+ }, [active, bump, viewer]);
8460
+ (0, import_react28.useEffect)(
8461
+ () => () => {
8462
+ window.clearTimeout(idleTimerRef.current);
8463
+ },
8464
+ []
8465
+ );
8466
+ return keepVisible || visible;
8467
+ }
8294
8468
  function WebVRChromeBridge({ host }) {
8295
- (0, import_react27.useSyncExternalStore)(
8469
+ (0, import_react28.useSyncExternalStore)(
8296
8470
  (listener) => subscribeWebVRHost(host, listener),
8297
8471
  () => host.revision,
8298
8472
  () => host.revision
8299
8473
  );
8300
8474
  const portrait = usePortraitOrientation();
8301
- const { available, chrome, mode, settings, setupOpen } = host.snapshot;
8302
- const [mounted, setMounted] = (0, import_react27.useState)(false);
8303
- (0, import_react27.useEffect)(() => {
8475
+ const {
8476
+ chrome,
8477
+ detectedScreensize,
8478
+ mode,
8479
+ settings,
8480
+ setupOpen
8481
+ } = host.snapshot;
8482
+ const [mounted, setMounted] = (0, import_react28.useState)(false);
8483
+ const [viewer, setViewer] = (0, import_react28.useState)(null);
8484
+ const toolbarRef = (0, import_react28.useRef)(null);
8485
+ (0, import_react28.useEffect)(() => {
8304
8486
  setMounted(true);
8305
8487
  }, []);
8488
+ (0, import_react28.useEffect)(() => {
8489
+ const node = toolbarRef.current?.closest("[data-pano-viewer]") ?? null;
8490
+ setViewer(node);
8491
+ }, [mode]);
8492
+ const sessionActive = mode !== null;
8493
+ const chromeVisible = useSessionChromeVisibility(
8494
+ sessionActive,
8495
+ setupOpen,
8496
+ viewer
8497
+ );
8306
8498
  if (chrome === false || !host.controller) {
8307
8499
  return null;
8308
8500
  }
@@ -8311,17 +8503,29 @@ function WebVRChromeBridge({ host }) {
8311
8503
  if (showPortraitPrompt) {
8312
8504
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PortraitPrompt, {});
8313
8505
  }
8506
+ if (!mode) {
8507
+ return null;
8508
+ }
8314
8509
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
8315
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pano-webvr-toolbar", children: mode ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SessionActions, { appearance, host, mode }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8316
- ChromeButton2,
8510
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pano-webvr-toolbar", ref: toolbarRef, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8511
+ SessionActions,
8317
8512
  {
8318
8513
  appearance,
8319
- onClick: () => void host.controller?.enterVR(),
8320
- children: available ? "Enter VR" : "VR unavailable"
8514
+ host,
8515
+ mode,
8516
+ visible: chromeVisible
8321
8517
  }
8322
8518
  ) }),
8323
- mode === "fake" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "pano-webvr-hint", style: { color: appearance.color }, children: "Simulated VR Mode \xB7 Click the view to lock the mouse, then move to look around. Use a headset or phone for real VR." }) : null,
8324
- setupOpen ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SetupPanel, { appearance, host, settings }) : null
8519
+ mode === "fake" && chromeVisible ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "pano-webvr-hint", style: { color: appearance.color }, children: "Simulated VR Mode \xB7 Click the view to lock the mouse, then move to look around. Use a headset or phone for real VR." }) : null,
8520
+ setupOpen ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8521
+ SetupPanel,
8522
+ {
8523
+ appearance,
8524
+ detectedScreensize,
8525
+ host,
8526
+ settings
8527
+ }
8528
+ ) : null
8325
8529
  ] });
8326
8530
  }
8327
8531
 
@@ -8354,12 +8558,12 @@ function projectStereoHtmlPosition(el, camera, size, eye) {
8354
8558
  // src/webvr/xr-origin.tsx
8355
8559
  var import_fiber9 = require("@react-three/fiber");
8356
8560
  var import_xr2 = require("@react-three/xr");
8357
- var import_react28 = require("react");
8561
+ var import_react29 = require("react");
8358
8562
  var import_three7 = require("three");
8359
8563
  var import_jsx_runtime12 = require("react/jsx-runtime");
8360
8564
  function PanoramaXROrigin() {
8361
- const originRef = (0, import_react28.useRef)(null);
8362
- const localHeadPositionRef = (0, import_react28.useRef)(new import_three7.Vector3());
8565
+ const originRef = (0, import_react29.useRef)(null);
8566
+ const localHeadPositionRef = (0, import_react29.useRef)(new import_three7.Vector3());
8363
8567
  const session2 = (0, import_xr2.useXR)((state) => state.session);
8364
8568
  const gl = (0, import_fiber9.useThree)((state) => state.gl);
8365
8569
  (0, import_fiber9.useFrame)(() => {
@@ -8424,7 +8628,7 @@ function PanoCursorTree({
8424
8628
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoCursorController, { cursors, children });
8425
8629
  }
8426
8630
  function PanoVideoHostReset() {
8427
- const host = (0, import_react29.useContext)(PanoVideoHostContext);
8631
+ const host = (0, import_react30.useContext)(PanoVideoHostContext);
8428
8632
  if (host) {
8429
8633
  host.controlClaims = 0;
8430
8634
  }
@@ -8437,7 +8641,7 @@ function DefaultControlChannels({
8437
8641
  keyboardChannel,
8438
8642
  fovSpeed
8439
8643
  }) {
8440
- const claims = (0, import_react29.useContext)(ControlClaimsContext);
8644
+ const claims = (0, import_react30.useContext)(ControlClaimsContext);
8441
8645
  if (!claims) {
8442
8646
  return null;
8443
8647
  }
@@ -8453,7 +8657,7 @@ function DefaultControlChannels({
8453
8657
  ) : null
8454
8658
  ] });
8455
8659
  }
8456
- var PanoViewer = (0, import_react29.forwardRef)(
8660
+ var PanoViewer = (0, import_react30.forwardRef)(
8457
8661
  function PanoViewer2({
8458
8662
  children,
8459
8663
  renderOverlay,
@@ -8474,37 +8678,37 @@ var PanoViewer = (0, import_react29.forwardRef)(
8474
8678
  "aria-label": ariaLabel = "Panorama viewer",
8475
8679
  ...divProps
8476
8680
  }, ref) {
8477
- const rootRef = (0, import_react29.useRef)(null);
8478
- const controlsRef = (0, import_react29.useRef)(null);
8479
- const modalHost = (0, import_react29.useMemo)(() => createPanoModalHost(), []);
8480
- const modalOpen = (0, import_react29.useSyncExternalStore)(modalHost.subscribe, modalHost.getSnapshot, () => false);
8481
- const setControlsRuntime = (0, import_react29.useCallback)((runtime) => {
8681
+ const rootRef = (0, import_react30.useRef)(null);
8682
+ const controlsRef = (0, import_react30.useRef)(null);
8683
+ const modalHost = (0, import_react30.useMemo)(() => createPanoModalHost(), []);
8684
+ const modalOpen = (0, import_react30.useSyncExternalStore)(modalHost.subscribe, modalHost.getSnapshot, () => false);
8685
+ const setControlsRuntime = (0, import_react30.useCallback)((runtime) => {
8482
8686
  controlsRef.current = runtime;
8483
8687
  modalHost.connect(runtime);
8484
8688
  }, [modalHost]);
8485
- const eventBus = (0, import_react29.useMemo)(() => createPanoEventBus(), []);
8689
+ const eventBus = (0, import_react30.useMemo)(() => createPanoEventBus(), []);
8486
8690
  const { controls: hotspotAccessibilityControls, registry } = useHotspotAccessibilityLayer();
8487
- const hotspotTargetRegistry = (0, import_react29.useMemo)(() => createHotspotTargetRegistry(), []);
8691
+ const hotspotTargetRegistry = (0, import_react30.useMemo)(() => createHotspotTargetRegistry(), []);
8488
8692
  const { api: contextMenuOverlayApi, overlay: contextMenuOverlay } = usePanoContextMenuOverlay();
8489
8693
  const { api: chromeOverlayApi, setOverlayNode, overlayElement } = usePanoChromeOverlay();
8490
- const videoHost = (0, import_react29.useMemo)(() => createPanoVideoHost(), []);
8491
- const filterHost = (0, import_react29.useMemo)(() => createPanoFilterHost(), []);
8492
- const webVRHost = (0, import_react29.useMemo)(() => createWebVRHost(), []);
8493
- const scenesHost = (0, import_react29.useMemo)(() => createScenesHost(), []);
8494
- const bgmHost = (0, import_react29.useMemo)(() => createBackgroundAudioHost(), []);
8495
- const mediaActivationHost = (0, import_react29.useMemo)(() => createPanoMediaActivationHost(), []);
8496
- const mediaActivationOptions = (0, import_react29.useMemo)(
8694
+ const videoHost = (0, import_react30.useMemo)(() => createPanoVideoHost(), []);
8695
+ const filterHost = (0, import_react30.useMemo)(() => createPanoFilterHost(), []);
8696
+ const webVRHost = (0, import_react30.useMemo)(() => createWebVRHost(), []);
8697
+ const scenesHost = (0, import_react30.useMemo)(() => createScenesHost(), []);
8698
+ const bgmHost = (0, import_react30.useMemo)(() => createBackgroundAudioHost(), []);
8699
+ const mediaActivationHost = (0, import_react30.useMemo)(() => createPanoMediaActivationHost(), []);
8700
+ const mediaActivationOptions = (0, import_react30.useMemo)(
8497
8701
  () => mediaActivation === false ? null : mediaActivation ?? {},
8498
8702
  [mediaActivation]
8499
8703
  );
8500
- const mediaActivationRevision = (0, import_react29.useSyncExternalStore)(
8704
+ const mediaActivationRevision = (0, import_react30.useSyncExternalStore)(
8501
8705
  (onStoreChange) => subscribePanoMediaActivation(mediaActivationHost, onStoreChange),
8502
8706
  () => getPanoMediaActivationRevision(mediaActivationHost),
8503
8707
  () => getPanoMediaActivationRevision(mediaActivationHost)
8504
8708
  );
8505
- const [mediaActivated, setMediaActivated] = (0, import_react29.useState)(false);
8506
- const stereoView = (0, import_react29.useMemo)(() => createWebVRStereoView(), []);
8507
- const xrStore = (0, import_react29.useMemo)(
8709
+ const [mediaActivated, setMediaActivated] = (0, import_react30.useState)(false);
8710
+ const stereoView = (0, import_react30.useMemo)(() => createWebVRStereoView(), []);
8711
+ const xrStore = (0, import_react30.useMemo)(
8508
8712
  () => (0, import_xr3.createXRStore)({
8509
8713
  anchors: false,
8510
8714
  bodyTracking: false,
@@ -8520,7 +8724,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8520
8724
  }),
8521
8725
  []
8522
8726
  );
8523
- const mediaActivationControls = (0, import_react29.useMemo)(
8727
+ const mediaActivationControls = (0, import_react30.useMemo)(
8524
8728
  () => ({
8525
8729
  resumeAudio: () => resumePanoAudio(),
8526
8730
  playVideo: async ({ unmute = false } = {}) => {
@@ -8538,18 +8742,18 @@ var PanoViewer = (0, import_react29.forwardRef)(
8538
8742
  }),
8539
8743
  [bgmHost, videoHost]
8540
8744
  );
8541
- const runMediaActivation = (0, import_react29.useCallback)(() => {
8745
+ const runMediaActivation = (0, import_react30.useCallback)(() => {
8542
8746
  const resume = mediaActivationControls.resumeAudio();
8543
8747
  mediaActivationOptions?.onActivate?.(mediaActivationControls);
8544
8748
  return resume;
8545
8749
  }, [mediaActivationControls, mediaActivationOptions]);
8546
- const activateMedia = (0, import_react29.useCallback)(() => {
8750
+ const activateMedia = (0, import_react30.useCallback)(() => {
8547
8751
  setMediaActivated(true);
8548
8752
  return runMediaActivation();
8549
8753
  }, [runMediaActivation]);
8550
8754
  const hasMediaActivationIntent = mediaActivationRevision >= 0 && hasPanoMediaActivationIntent(mediaActivationHost);
8551
8755
  const shouldActivateMedia = mediaActivationOptions !== null && hasMediaActivationIntent && !mediaActivated;
8552
- (0, import_react29.useEffect)(() => {
8756
+ (0, import_react30.useEffect)(() => {
8553
8757
  if (!shouldActivateMedia) {
8554
8758
  return;
8555
8759
  }
@@ -8624,14 +8828,14 @@ var PanoViewer = (0, import_react29.forwardRef)(
8624
8828
  videoHost
8625
8829
  ]);
8626
8830
  const limitsKey = JSON.stringify(viewLimits);
8627
- const parsedLimits = (0, import_react29.useMemo)(() => parseViewLimits(viewLimits), [limitsKey]);
8628
- const fallbackViewRef = (0, import_react29.useRef)(DEFAULT_VIEW);
8831
+ const parsedLimits = (0, import_react30.useMemo)(() => parseViewLimits(viewLimits), [limitsKey]);
8832
+ const fallbackViewRef = (0, import_react30.useRef)(DEFAULT_VIEW);
8629
8833
  const normalizedMinFov = Math.max(1, Math.min(minFov, maxFov - 1));
8630
8834
  const normalizedMaxFov = Math.min(
8631
8835
  179,
8632
8836
  Math.max(maxFov, normalizedMinFov + 1)
8633
8837
  );
8634
- const normalizedInitialView = (0, import_react29.useMemo)(
8838
+ const normalizedInitialView = (0, import_react30.useMemo)(
8635
8839
  () => ({
8636
8840
  yaw: initialView?.yaw ?? DEFAULT_VIEW.yaw,
8637
8841
  pitch: clampPanoPitch(initialView?.pitch ?? DEFAULT_VIEW.pitch),
@@ -8646,10 +8850,10 @@ var PanoViewer = (0, import_react29.forwardRef)(
8646
8850
  ]
8647
8851
  );
8648
8852
  fallbackViewRef.current = normalizedInitialView;
8649
- const controlClaims = (0, import_react29.useMemo)(() => createControlClaims(), []);
8853
+ const controlClaims = (0, import_react30.useMemo)(() => createControlClaims(), []);
8650
8854
  resetControlClaims(controlClaims);
8651
8855
  const controlsEnabled = controls !== false;
8652
- const controlOptions = (0, import_react29.useMemo)(
8856
+ const controlOptions = (0, import_react30.useMemo)(
8653
8857
  () => typeof controls === "object" ? controls : {},
8654
8858
  [controls]
8655
8859
  );
@@ -8657,7 +8861,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8657
8861
  const allModeInvert = controlOptions.invert === true;
8658
8862
  const topRotateSpeed = controlOptions.rotateSpeed ?? DEFAULT_POINTER_ROTATE_SPEED;
8659
8863
  const topZoomSpeed = controlOptions.zoomSpeed ?? DEFAULT_MOUSE_ZOOM_SPEED;
8660
- const mouseChannel = (0, import_react29.useMemo)(
8864
+ const mouseChannel = (0, import_react30.useMemo)(
8661
8865
  () => resolveChannel(controlOptions.mouse, {
8662
8866
  enabled: true,
8663
8867
  rotateSpeed: topRotateSpeed,
@@ -8673,7 +8877,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8673
8877
  topZoomSpeed
8674
8878
  ]
8675
8879
  );
8676
- const touchChannel = (0, import_react29.useMemo)(
8880
+ const touchChannel = (0, import_react30.useMemo)(
8677
8881
  () => resolveChannel(controlOptions.touch, {
8678
8882
  enabled: true,
8679
8883
  rotateSpeed: topRotateSpeed,
@@ -8682,13 +8886,13 @@ var PanoViewer = (0, import_react29.forwardRef)(
8682
8886
  }),
8683
8887
  [allModeInvert, controlOptions.touch, topRotateSpeed]
8684
8888
  );
8685
- const keyboardChannel = (0, import_react29.useMemo)(
8889
+ const keyboardChannel = (0, import_react30.useMemo)(
8686
8890
  () => resolveChannel(controlOptions.keyboard, {
8687
8891
  enabled: true
8688
8892
  }),
8689
8893
  [controlOptions.keyboard]
8690
8894
  );
8691
- const enterFullscreen = (0, import_react29.useCallback)(async () => {
8895
+ const enterFullscreen = (0, import_react30.useCallback)(async () => {
8692
8896
  if (typeof document === "undefined") {
8693
8897
  return;
8694
8898
  }
@@ -8707,7 +8911,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8707
8911
  acquireIphoneLandscapeFullscreen(root, "viewer");
8708
8912
  }
8709
8913
  }, []);
8710
- const exitFullscreen = (0, import_react29.useCallback)(async () => {
8914
+ const exitFullscreen = (0, import_react30.useCallback)(async () => {
8711
8915
  if (typeof document === "undefined") {
8712
8916
  return;
8713
8917
  }
@@ -8719,7 +8923,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8719
8923
  await exitElementFullscreen();
8720
8924
  }
8721
8925
  }, []);
8722
- const toggleFullscreen = (0, import_react29.useCallback)(async () => {
8926
+ const toggleFullscreen = (0, import_react30.useCallback)(async () => {
8723
8927
  if (typeof document === "undefined") {
8724
8928
  return;
8725
8929
  }
@@ -8733,17 +8937,21 @@ var PanoViewer = (0, import_react29.forwardRef)(
8733
8937
  }
8734
8938
  await enterFullscreen();
8735
8939
  }, [enterFullscreen, exitFullscreen]);
8736
- const [isViewerFullscreen, setIsViewerFullscreen] = (0, import_react29.useState)(false);
8940
+ const [isViewerFullscreen, setIsViewerFullscreen] = (0, import_react30.useState)(false);
8737
8941
  const { context: overlayContext, setPortalContainer } = usePanoOverlayHost(isViewerFullscreen);
8738
- const iphoneFullscreenAllowsScroll = (0, import_react29.useSyncExternalStore)(
8942
+ const { sceneChildren, overlayElements } = (0, import_react30.useMemo)(
8943
+ () => partitionPanoOverlay(children),
8944
+ [children]
8945
+ );
8946
+ const iphoneFullscreenAllowsScroll = (0, import_react30.useSyncExternalStore)(
8739
8947
  subscribeIphoneLandscapeFullscreen,
8740
8948
  () => getIphoneLandscapeFullscreenSnapshot().allowsDocumentScroll,
8741
8949
  () => false
8742
8950
  );
8743
- (0, import_react29.useEffect)(() => {
8951
+ (0, import_react30.useEffect)(() => {
8744
8952
  if (modalOpen) contextMenuOverlayApi.setSession(null);
8745
8953
  }, [contextMenuOverlayApi, modalOpen]);
8746
- (0, import_react29.useEffect)(() => {
8954
+ (0, import_react30.useEffect)(() => {
8747
8955
  if (typeof document === "undefined") {
8748
8956
  return;
8749
8957
  }
@@ -8770,7 +8978,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8770
8978
  unsubscribeIphone();
8771
8979
  };
8772
8980
  }, []);
8773
- (0, import_react29.useEffect)(() => {
8981
+ (0, import_react30.useEffect)(() => {
8774
8982
  const root = rootRef.current;
8775
8983
  return () => {
8776
8984
  if (root) {
@@ -8778,7 +8986,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8778
8986
  }
8779
8987
  };
8780
8988
  }, []);
8781
- (0, import_react29.useEffect)(() => {
8989
+ (0, import_react30.useEffect)(() => {
8782
8990
  const root = rootRef.current;
8783
8991
  if (!root) {
8784
8992
  return;
@@ -8791,7 +8999,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8791
8999
  root.removeEventListener("contextmenu", suppressNativeContextMenu, true);
8792
9000
  };
8793
9001
  }, []);
8794
- const contextMenuActions = (0, import_react29.useMemo)(
9002
+ const contextMenuActions = (0, import_react30.useMemo)(
8795
9003
  () => ({
8796
9004
  reset: () => {
8797
9005
  controlsRef.current?.reset();
@@ -8803,24 +9011,24 @@ var PanoViewer = (0, import_react29.forwardRef)(
8803
9011
  }),
8804
9012
  [isViewerFullscreen, toggleFullscreen]
8805
9013
  );
8806
- const contextMenuChannel = (0, import_react29.useMemo)(
9014
+ const contextMenuChannel = (0, import_react30.useMemo)(
8807
9015
  () => resolveContextMenuChannel(contextMenu),
8808
9016
  [contextMenu]
8809
9017
  );
8810
- const resolvedCursors = (0, import_react29.useMemo)(
9018
+ const resolvedCursors = (0, import_react30.useMemo)(
8811
9019
  () => resolvePanoCursors(cursors),
8812
9020
  [cursors]
8813
9021
  );
8814
9022
  const legacyAutoRotateOptions = controlOptions;
8815
- const [legacyAutoRotate, setLegacyAutoRotate] = (0, import_react29.useState)(
9023
+ const [legacyAutoRotate, setLegacyAutoRotate] = (0, import_react30.useState)(
8816
9024
  legacyAutoRotateOptions.autoRotate ?? false
8817
9025
  );
8818
- (0, import_react29.useEffect)(() => {
9026
+ (0, import_react30.useEffect)(() => {
8819
9027
  setLegacyAutoRotate(legacyAutoRotateOptions.autoRotate ?? false);
8820
9028
  }, [legacyAutoRotateOptions.autoRotate]);
8821
- const isFullscreenRef = (0, import_react29.useRef)(false);
9029
+ const isFullscreenRef = (0, import_react30.useRef)(false);
8822
9030
  isFullscreenRef.current = isViewerFullscreen;
8823
- (0, import_react29.useImperativeHandle)(
9031
+ (0, import_react30.useImperativeHandle)(
8824
9032
  ref,
8825
9033
  () => ({
8826
9034
  // -- View --
@@ -8858,6 +9066,8 @@ var PanoViewer = (0, import_react29.forwardRef)(
8858
9066
  isVRAvailable: () => webVRHost.snapshot.available,
8859
9067
  isVREnabled: () => webVRHost.snapshot.mode !== null,
8860
9068
  getVRMode: () => webVRHost.snapshot.mode,
9069
+ getVRState: () => webVRHost.state,
9070
+ subscribeVR: (onStoreChange) => subscribeWebVRHost(webVRHost, onStoreChange),
8861
9071
  requestVRPermission: () => webVRHost.controller?.requestPermission() ?? Promise.resolve(false),
8862
9072
  // -- Video --
8863
9073
  getVideo: () => videoHost.controller,
@@ -8901,7 +9111,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8901
9111
  webVRHost
8902
9112
  ]
8903
9113
  );
8904
- const rootStyle = (0, import_react29.useMemo)(
9114
+ const rootStyle = (0, import_react30.useMemo)(
8905
9115
  () => ({
8906
9116
  overflow: "hidden",
8907
9117
  position: "relative",
@@ -8990,7 +9200,7 @@ var PanoViewer = (0, import_react29.forwardRef)(
8990
9200
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoramaEventSurface, {}),
8991
9201
  contextMenuChannel.mount ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoContextMenu, { ...contextMenuChannel.props }) : null,
8992
9202
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ControlClaimsContext.Provider, { value: controlClaims, children: [
8993
- children,
9203
+ sceneChildren,
8994
9204
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
8995
9205
  DefaultControlChannels,
8996
9206
  {
@@ -9035,15 +9245,18 @@ var PanoViewer = (0, import_react29.forwardRef)(
9035
9245
  contextMenuOverlay,
9036
9246
  hotspotAccessibilityControls
9037
9247
  ] }),
9038
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoEventBusContext.Provider, { value: eventBus, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoModalHostContext.Provider, { value: modalHost, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoOverlayContext.Provider, { value: overlayContext, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
9248
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoEventBusContext.Provider, { value: eventBus, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoModalHostContext.Provider, { value: modalHost, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoOverlayContext.Provider, { value: overlayContext, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PanoOverlayOutletContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
9039
9249
  "div",
9040
9250
  {
9041
9251
  "data-pano-overlay": "",
9042
9252
  ref: setPortalContainer,
9043
9253
  style: { position: "absolute", inset: 0, zIndex: 30, pointerEvents: "none" },
9044
- children: renderOverlay?.(overlayContext)
9254
+ children: [
9255
+ renderOverlay?.(overlayContext),
9256
+ overlayElements
9257
+ ]
9045
9258
  }
9046
- ) }) }) })
9259
+ ) }) }) }) })
9047
9260
  ]
9048
9261
  }
9049
9262
  );
@@ -9136,18 +9349,18 @@ function hotspotAction(hotspot, trigger) {
9136
9349
  }
9137
9350
 
9138
9351
  // src/project-scenes.tsx
9139
- var import_react53 = require("react");
9352
+ var import_react54 = require("react");
9140
9353
 
9141
9354
  // src/scenes.tsx
9142
9355
  var import_fiber18 = require("@react-three/fiber");
9143
- var import_react40 = require("react");
9356
+ var import_react41 = require("react");
9144
9357
  var import_three21 = require("three");
9145
9358
 
9146
9359
  // src/scene-detail.tsx
9147
- var import_react30 = require("react");
9148
- var SceneDetailContext = (0, import_react30.createContext)(null);
9360
+ var import_react31 = require("react");
9361
+ var SceneDetailContext = (0, import_react31.createContext)(null);
9149
9362
  function useSceneDetailReporter() {
9150
- return (0, import_react30.useContext)(SceneDetailContext);
9363
+ return (0, import_react31.useContext)(SceneDetailContext);
9151
9364
  }
9152
9365
  function viewTilesAreDrawable(textures) {
9153
9366
  return textures.length > 0 && textures.every((texture) => texture != null);
@@ -9212,7 +9425,7 @@ function isSnapshotTransition(preset) {
9212
9425
 
9213
9426
  // src/scene-transitions/snapshot-hold.tsx
9214
9427
  var import_fiber11 = require("@react-three/fiber");
9215
- var import_react31 = require("react");
9428
+ var import_react32 = require("react");
9216
9429
  var import_three10 = require("three");
9217
9430
 
9218
9431
  // src/scene-transitions/overlay-utils.ts
@@ -9548,13 +9761,13 @@ function createSnapshotMaterial(preset, texture) {
9548
9761
  var import_jsx_runtime14 = require("react/jsx-runtime");
9549
9762
  function SnapshotHold({ snapshot }) {
9550
9763
  const { camera, gl } = (0, import_fiber11.useThree)();
9551
- const meshRef = (0, import_react31.useRef)(null);
9552
- const directionRef = (0, import_react31.useRef)(new import_three10.Vector3());
9553
- const material = (0, import_react31.useMemo)(
9764
+ const meshRef = (0, import_react32.useRef)(null);
9765
+ const directionRef = (0, import_react32.useRef)(new import_three10.Vector3());
9766
+ const material = (0, import_react32.useMemo)(
9554
9767
  () => createSnapshotMaterial("crossfade", snapshot.texture),
9555
9768
  [snapshot.texture]
9556
9769
  );
9557
- (0, import_react31.useEffect)(() => () => material.dispose(), [material]);
9770
+ (0, import_react32.useEffect)(() => () => material.dispose(), [material]);
9558
9771
  (0, import_fiber11.useFrame)(() => {
9559
9772
  const mesh = meshRef.current;
9560
9773
  if (!mesh || !isPerspectiveCamera(camera)) {
@@ -9632,7 +9845,7 @@ function phaseAfterSnapshot(preset, hold) {
9632
9845
 
9633
9846
  // src/scene-transitions/particle-overlay.tsx
9634
9847
  var import_fiber12 = require("@react-three/fiber");
9635
- var import_react32 = require("react");
9848
+ var import_react33 = require("react");
9636
9849
  var import_three11 = require("three");
9637
9850
  var import_jsx_runtime15 = require("react/jsx-runtime");
9638
9851
  var PARTICLE_GRID = 48;
@@ -9711,12 +9924,12 @@ function ParticleOverlay({
9711
9924
  onFinish
9712
9925
  }) {
9713
9926
  const { camera } = (0, import_fiber12.useThree)();
9714
- const meshRef = (0, import_react32.useRef)(null);
9715
- const startRef = (0, import_react32.useRef)(null);
9716
- const finishedRef = (0, import_react32.useRef)(false);
9717
- const directionRef = (0, import_react32.useRef)(new import_three11.Vector3());
9718
- const geometry = (0, import_react32.useMemo)(() => createParticleGeometry(), []);
9719
- const material = (0, import_react32.useMemo)(
9927
+ const meshRef = (0, import_react33.useRef)(null);
9928
+ const startRef = (0, import_react33.useRef)(null);
9929
+ const finishedRef = (0, import_react33.useRef)(false);
9930
+ const directionRef = (0, import_react33.useRef)(new import_three11.Vector3());
9931
+ const geometry = (0, import_react33.useMemo)(() => createParticleGeometry(), []);
9932
+ const material = (0, import_react33.useMemo)(
9720
9933
  () => new import_three11.ShaderMaterial({
9721
9934
  transparent: true,
9722
9935
  depthTest: false,
@@ -9732,9 +9945,9 @@ function ParticleOverlay({
9732
9945
  }),
9733
9946
  [snapshot.texture]
9734
9947
  );
9735
- (0, import_react32.useEffect)(() => () => geometry.dispose(), [geometry]);
9736
- (0, import_react32.useEffect)(() => () => material.dispose(), [material]);
9737
- (0, import_react32.useEffect)(() => {
9948
+ (0, import_react33.useEffect)(() => () => geometry.dispose(), [geometry]);
9949
+ (0, import_react33.useEffect)(() => () => material.dispose(), [material]);
9950
+ (0, import_react33.useEffect)(() => {
9738
9951
  startRef.current = null;
9739
9952
  finishedRef.current = false;
9740
9953
  material.uniforms.progress.value = 0;
@@ -9770,7 +9983,7 @@ function ParticleOverlay({
9770
9983
 
9771
9984
  // src/scene-transitions/snapshot-overlay.tsx
9772
9985
  var import_fiber13 = require("@react-three/fiber");
9773
- var import_react33 = require("react");
9986
+ var import_react34 = require("react");
9774
9987
  var import_three12 = require("three");
9775
9988
  var import_jsx_runtime16 = require("react/jsx-runtime");
9776
9989
  function SnapshotOverlay({
@@ -9781,18 +9994,18 @@ function SnapshotOverlay({
9781
9994
  onFinish
9782
9995
  }) {
9783
9996
  const { camera, gl } = (0, import_fiber13.useThree)();
9784
- const meshRef = (0, import_react33.useRef)(null);
9785
- const startRef = (0, import_react33.useRef)(null);
9786
- const finishedRef = (0, import_react33.useRef)(false);
9787
- const directionRef = (0, import_react33.useRef)(new import_three12.Vector3());
9788
- const material = (0, import_react33.useMemo)(() => {
9997
+ const meshRef = (0, import_react34.useRef)(null);
9998
+ const startRef = (0, import_react34.useRef)(null);
9999
+ const finishedRef = (0, import_react34.useRef)(false);
10000
+ const directionRef = (0, import_react34.useRef)(new import_three12.Vector3());
10001
+ const material = (0, import_react34.useMemo)(() => {
9789
10002
  if (!isSnapshotTransition(transition.preset)) {
9790
10003
  return null;
9791
10004
  }
9792
10005
  return createSnapshotMaterial(transition.preset, snapshot.texture);
9793
10006
  }, [snapshot.texture, transition.preset]);
9794
- (0, import_react33.useEffect)(() => () => material?.dispose(), [material]);
9795
- (0, import_react33.useEffect)(() => {
10007
+ (0, import_react34.useEffect)(() => () => material?.dispose(), [material]);
10008
+ (0, import_react34.useEffect)(() => {
9796
10009
  startRef.current = null;
9797
10010
  finishedRef.current = false;
9798
10011
  if (material) {
@@ -9874,11 +10087,11 @@ function TransitionOverlay(props) {
9874
10087
 
9875
10088
  // src/sphere.tsx
9876
10089
  var import_fiber14 = require("@react-three/fiber");
9877
- var import_react35 = require("react");
10090
+ var import_react36 = require("react");
9878
10091
  var import_three14 = require("three");
9879
10092
 
9880
10093
  // src/pano-filter/pano-basic-material.tsx
9881
- var import_react34 = require("react");
10094
+ var import_react35 = require("react");
9882
10095
  var import_three13 = require("three");
9883
10096
 
9884
10097
  // src/pano-filter/shaders.ts
@@ -10075,10 +10288,10 @@ function PanoBasicMaterial({
10075
10288
  const preset = snapshot.enabled && target === "panorama" ? snapshot.preset : "none";
10076
10289
  const matrix = snapshot.enabled && (target === "panorama" || snapshot.colorMatrixScope === "all") ? snapshot.colorMatrix : null;
10077
10290
  const hasMatrix = matrix !== null;
10078
- const currentRef = (0, import_react34.useRef)({ matrix, intensity: snapshot.intensity });
10291
+ const currentRef = (0, import_react35.useRef)({ matrix, intensity: snapshot.intensity });
10079
10292
  currentRef.current = { matrix, intensity: snapshot.intensity };
10080
- const shaderRef = (0, import_react34.useRef)(null);
10081
- const onBeforeCompile = (0, import_react34.useMemo)(() => {
10293
+ const shaderRef = (0, import_react35.useRef)(null);
10294
+ const onBeforeCompile = (0, import_react35.useMemo)(() => {
10082
10295
  const compile = (shader) => {
10083
10296
  shaderRef.current = shader;
10084
10297
  if (preset !== "none") {
@@ -10098,7 +10311,7 @@ function PanoBasicMaterial({
10098
10311
  compile.toString = () => `pano-filter:${preset}:matrix:${hasMatrix}`;
10099
10312
  return compile;
10100
10313
  }, [preset, hasMatrix]);
10101
- (0, import_react34.useLayoutEffect)(() => {
10314
+ (0, import_react35.useLayoutEffect)(() => {
10102
10315
  const shader = shaderRef.current;
10103
10316
  if (!shader) return;
10104
10317
  if (shader.uniforms.uPanoFilterIntensity)
@@ -10124,12 +10337,12 @@ function PanoBasicMaterial({
10124
10337
  // src/sphere.tsx
10125
10338
  var import_jsx_runtime19 = require("react/jsx-runtime");
10126
10339
  function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10127
- const [texture, setTexture] = (0, import_react35.useState)(null);
10128
- const onLoadRef = (0, import_react35.useRef)(onLoad);
10129
- const onErrorRef = (0, import_react35.useRef)(onError);
10340
+ const [texture, setTexture] = (0, import_react36.useState)(null);
10341
+ const onLoadRef = (0, import_react36.useRef)(onLoad);
10342
+ const onErrorRef = (0, import_react36.useRef)(onError);
10130
10343
  onLoadRef.current = onLoad;
10131
10344
  onErrorRef.current = onError;
10132
- (0, import_react35.useEffect)(() => {
10345
+ (0, import_react36.useEffect)(() => {
10133
10346
  if (!url) {
10134
10347
  setTexture(null);
10135
10348
  return;
@@ -10168,7 +10381,7 @@ function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
10168
10381
  active = false;
10169
10382
  };
10170
10383
  }, [anisotropy, generateMipmaps, url]);
10171
- (0, import_react35.useEffect)(
10384
+ (0, import_react36.useEffect)(
10172
10385
  () => () => {
10173
10386
  texture?.dispose();
10174
10387
  },
@@ -10188,7 +10401,7 @@ function Sphere({
10188
10401
  }) {
10189
10402
  const gl = (0, import_fiber14.useThree)((state) => state.gl);
10190
10403
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10191
- const resolvedPreviewUrl = (0, import_react35.useMemo)(
10404
+ const resolvedPreviewUrl = (0, import_react36.useMemo)(
10192
10405
  () => resolveUrlAgainstFile(src, previewUrl),
10193
10406
  [previewUrl, src]
10194
10407
  );
@@ -10207,12 +10420,12 @@ function Sphere({
10207
10420
  onPreviewError
10208
10421
  );
10209
10422
  const displayTexture = texture ?? previewTexture;
10210
- const readyRef = (0, import_react35.useRef)(false);
10423
+ const readyRef = (0, import_react36.useRef)(false);
10211
10424
  const detailReporter = useSceneDetailReporter();
10212
- (0, import_react35.useEffect)(() => {
10425
+ (0, import_react36.useEffect)(() => {
10213
10426
  readyRef.current = false;
10214
10427
  }, [resolvedPreviewUrl, src]);
10215
- (0, import_react35.useEffect)(() => {
10428
+ (0, import_react36.useEffect)(() => {
10216
10429
  if (!detailReporter) {
10217
10430
  return;
10218
10431
  }
@@ -10221,7 +10434,7 @@ function Sphere({
10221
10434
  detailReporter.report(false);
10222
10435
  };
10223
10436
  }, [detailReporter, texture]);
10224
- (0, import_react35.useEffect)(() => {
10437
+ (0, import_react36.useEffect)(() => {
10225
10438
  if (!displayTexture || readyRef.current) {
10226
10439
  return;
10227
10440
  }
@@ -10253,7 +10466,7 @@ function Sphere({
10253
10466
 
10254
10467
  // src/sphere-tile/sphere-tile.tsx
10255
10468
  var import_fiber16 = require("@react-three/fiber");
10256
- var import_react38 = require("react");
10469
+ var import_react39 = require("react");
10257
10470
  var import_three18 = require("three");
10258
10471
 
10259
10472
  // src/tile/texture-manager.ts
@@ -10263,6 +10476,20 @@ var EMPTY_TEXTURE_SNAPSHOT = {
10263
10476
  texture: null,
10264
10477
  error: null
10265
10478
  };
10479
+ var MIPMAP_MEMORY_FACTOR = 4 / 3;
10480
+ function mipmappedTextureBytes(width, height) {
10481
+ return Math.ceil(width * height * 4 * MIPMAP_MEMORY_FACTOR);
10482
+ }
10483
+ function configureMipmappedTexture(texture, anisotropy) {
10484
+ texture.colorSpace = import_three15.SRGBColorSpace;
10485
+ texture.wrapS = import_three15.ClampToEdgeWrapping;
10486
+ texture.wrapT = import_three15.ClampToEdgeWrapping;
10487
+ texture.magFilter = import_three15.LinearFilter;
10488
+ texture.minFilter = import_three15.LinearMipmapLinearFilter;
10489
+ texture.generateMipmaps = true;
10490
+ texture.anisotropy = Math.max(1, anisotropy);
10491
+ texture.needsUpdate = true;
10492
+ }
10266
10493
  var TileTextureManager = class {
10267
10494
  constructor(options) {
10268
10495
  this.loader = new import_three15.TextureLoader();
@@ -10414,7 +10641,10 @@ var TileTextureManager = class {
10414
10641
  entry.texture = texture;
10415
10642
  entry.status = "loaded";
10416
10643
  entry.error = null;
10417
- entry.bytes = entry.address.width * entry.address.height * 4;
10644
+ entry.bytes = mipmappedTextureBytes(
10645
+ entry.address.width,
10646
+ entry.address.height
10647
+ );
10418
10648
  entry.lastUsed = performance.now();
10419
10649
  this.totalBytes += entry.bytes;
10420
10650
  this.loaded += 1;
@@ -10437,14 +10667,7 @@ var TileTextureManager = class {
10437
10667
  }
10438
10668
  }
10439
10669
  configureTexture(texture) {
10440
- texture.colorSpace = import_three15.SRGBColorSpace;
10441
- texture.wrapS = import_three15.ClampToEdgeWrapping;
10442
- texture.wrapT = import_three15.ClampToEdgeWrapping;
10443
- texture.magFilter = import_three15.LinearFilter;
10444
- texture.minFilter = import_three15.LinearFilter;
10445
- texture.generateMipmaps = false;
10446
- texture.anisotropy = Math.min(4, this.options.anisotropy);
10447
- texture.needsUpdate = true;
10670
+ configureMipmappedTexture(texture, this.options.anisotropy);
10448
10671
  }
10449
10672
  evict() {
10450
10673
  if (this.totalBytes <= this.options.maxBytes) {
@@ -10483,9 +10706,9 @@ var TileTextureManager = class {
10483
10706
 
10484
10707
  // src/tile/texture-manager-context.tsx
10485
10708
  var import_fiber15 = require("@react-three/fiber");
10486
- var import_react36 = require("react");
10709
+ var import_react37 = require("react");
10487
10710
  var import_jsx_runtime20 = require("react/jsx-runtime");
10488
- var TileTextureManagerContext = (0, import_react36.createContext)(null);
10711
+ var TileTextureManagerContext = (0, import_react37.createContext)(null);
10489
10712
  function TileTextureManagerProvider({
10490
10713
  children,
10491
10714
  maxTextureMemoryMb = 128,
@@ -10493,7 +10716,7 @@ function TileTextureManagerProvider({
10493
10716
  }) {
10494
10717
  const gl = (0, import_fiber15.useThree)((state) => state.gl);
10495
10718
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10496
- const manager = (0, import_react36.useMemo)(
10719
+ const manager = (0, import_react37.useMemo)(
10497
10720
  () => new TileTextureManager({
10498
10721
  anisotropy,
10499
10722
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -10502,24 +10725,24 @@ function TileTextureManagerProvider({
10502
10725
  }),
10503
10726
  [anisotropy, maxConcurrentLoads, maxTextureMemoryMb]
10504
10727
  );
10505
- (0, import_react36.useEffect)(() => {
10728
+ (0, import_react37.useEffect)(() => {
10506
10729
  manager.resume();
10507
10730
  return () => manager.dispose();
10508
10731
  }, [manager]);
10509
10732
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TileTextureManagerContext.Provider, { value: manager, children });
10510
10733
  }
10511
10734
  function useSharedTileTextureManager() {
10512
- return (0, import_react36.useContext)(TileTextureManagerContext);
10735
+ return (0, import_react37.useContext)(TileTextureManagerContext);
10513
10736
  }
10514
10737
 
10515
10738
  // src/tile/texture-hooks.ts
10516
- var import_react37 = require("react");
10739
+ var import_react38 = require("react");
10517
10740
  var import_three16 = require("three");
10518
10741
  function usePreviewTexture(url, anisotropy, onError) {
10519
- const [texture, setTexture] = (0, import_react37.useState)(null);
10520
- const onErrorRef = (0, import_react37.useRef)(onError);
10742
+ const [texture, setTexture] = (0, import_react38.useState)(null);
10743
+ const onErrorRef = (0, import_react38.useRef)(onError);
10521
10744
  onErrorRef.current = onError;
10522
- (0, import_react37.useEffect)(() => {
10745
+ (0, import_react38.useEffect)(() => {
10523
10746
  if (!url) {
10524
10747
  setTexture(null);
10525
10748
  return;
@@ -10534,14 +10757,7 @@ function usePreviewTexture(url, anisotropy, onError) {
10534
10757
  loadedTexture.dispose();
10535
10758
  return;
10536
10759
  }
10537
- loadedTexture.colorSpace = import_three16.SRGBColorSpace;
10538
- loadedTexture.wrapS = import_three16.ClampToEdgeWrapping;
10539
- loadedTexture.wrapT = import_three16.ClampToEdgeWrapping;
10540
- loadedTexture.magFilter = import_three16.LinearFilter;
10541
- loadedTexture.minFilter = import_three16.LinearFilter;
10542
- loadedTexture.generateMipmaps = false;
10543
- loadedTexture.anisotropy = Math.min(4, anisotropy);
10544
- loadedTexture.needsUpdate = true;
10760
+ configureMipmappedTexture(loadedTexture, anisotropy);
10545
10761
  setTexture(loadedTexture);
10546
10762
  },
10547
10763
  void 0,
@@ -10556,7 +10772,7 @@ function usePreviewTexture(url, anisotropy, onError) {
10556
10772
  active = false;
10557
10773
  };
10558
10774
  }, [anisotropy, url]);
10559
- (0, import_react37.useEffect)(
10775
+ (0, import_react38.useEffect)(
10560
10776
  () => () => {
10561
10777
  texture?.dispose();
10562
10778
  },
@@ -10565,14 +10781,14 @@ function usePreviewTexture(url, anisotropy, onError) {
10565
10781
  return texture;
10566
10782
  }
10567
10783
  function useManagedTexture(manager, key, url, address) {
10568
- const [managedState, setManagedState] = (0, import_react37.useState)(() => ({
10784
+ const [managedState, setManagedState] = (0, import_react38.useState)(() => ({
10569
10785
  manager,
10570
10786
  key,
10571
10787
  url,
10572
10788
  snapshot: manager.getSnapshot(key)
10573
10789
  }));
10574
10790
  const snapshot = managedState.manager === manager && managedState.key === key && managedState.url === url ? managedState.snapshot : manager.getSnapshot(key);
10575
- (0, import_react37.useEffect)(
10791
+ (0, import_react38.useEffect)(
10576
10792
  () => manager.acquire(key, url, address, (nextSnapshot) => {
10577
10793
  setManagedState({
10578
10794
  manager,
@@ -10707,8 +10923,17 @@ function sphereTileGeometry(address, yawOffset2, radius = DEFAULT_PANORAMA_RADIU
10707
10923
  geometry.computeBoundingSphere();
10708
10924
  return geometry;
10709
10925
  }
10710
- function preferredSphereLevel(levels, height, dpr, verticalFov) {
10711
- const needed = height * dpr * 180 / Math.max(1, verticalFov);
10926
+ function tanHalfFov(verticalFov) {
10927
+ const fov = Math.min(179, Math.max(1, verticalFov));
10928
+ return Math.tan(import_three17.MathUtils.degToRad(fov) * 0.5);
10929
+ }
10930
+ function preferredSphereLevel(levels, height, dpr, verticalFov, width = height) {
10931
+ const pixelHeight = Math.max(1, height) * dpr;
10932
+ const pixelWidth = Math.max(1, width) * dpr;
10933
+ const tanHalfV = tanHalfFov(verticalFov);
10934
+ const tanHalfH = pixelWidth / pixelHeight * tanHalfV;
10935
+ const edgeScale = 1 + Math.max(tanHalfV, tanHalfH) ** 2;
10936
+ const needed = pixelHeight * 0.5 / tanHalfV * Math.PI * edgeScale;
10712
10937
  const index = levels.findIndex((level) => level.height >= needed);
10713
10938
  return index < 0 ? levels.length - 1 : index;
10714
10939
  }
@@ -10730,7 +10955,7 @@ function SpherePatch({
10730
10955
  yawOffset: yawOffset2,
10731
10956
  preview = false
10732
10957
  }) {
10733
- const geometry = (0, import_react38.useMemo)(
10958
+ const geometry = (0, import_react39.useMemo)(
10734
10959
  () => sphereTileGeometry(
10735
10960
  address,
10736
10961
  yawOffset2,
@@ -10738,7 +10963,7 @@ function SpherePatch({
10738
10963
  ),
10739
10964
  [address, yawOffset2, preview]
10740
10965
  );
10741
- (0, import_react38.useEffect)(() => () => geometry.dispose(), [geometry]);
10966
+ (0, import_react39.useEffect)(() => () => geometry.dispose(), [geometry]);
10742
10967
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
10743
10968
  "mesh",
10744
10969
  {
@@ -10757,7 +10982,7 @@ function SpherePatch({
10757
10982
  }
10758
10983
  );
10759
10984
  }
10760
- var ManagedPatch = (0, import_react38.memo)(function ManagedPatch2({
10985
+ var ManagedPatch = (0, import_react39.memo)(function ManagedPatch2({
10761
10986
  address,
10762
10987
  manager,
10763
10988
  resolveUrl,
@@ -10771,7 +10996,7 @@ var ManagedPatch = (0, import_react38.memo)(function ManagedPatch2({
10771
10996
  url,
10772
10997
  address
10773
10998
  );
10774
- (0, import_react38.useEffect)(() => {
10999
+ (0, import_react39.useEffect)(() => {
10775
11000
  if (snapshot.texture) ready();
10776
11001
  }, [snapshot.texture, ready]);
10777
11002
  return snapshot.texture ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -10809,7 +11034,7 @@ function SphereTileSource({
10809
11034
  anisotropy,
10810
11035
  onPreviewError
10811
11036
  );
10812
- const callbacks = (0, import_react38.useRef)({
11037
+ const callbacks = (0, import_react39.useRef)({
10813
11038
  onReady,
10814
11039
  onLoadProgress,
10815
11040
  onTileError,
@@ -10821,7 +11046,7 @@ function SphereTileSource({
10821
11046
  1,
10822
11047
  Number.isFinite(maxTextureMemoryMb) ? maxTextureMemoryMb : 128
10823
11048
  ) * 1024 * 1024;
10824
- const ownedManager = (0, import_react38.useMemo)(
11049
+ const ownedManager = (0, import_react39.useMemo)(
10825
11050
  () => new TileTextureManager({
10826
11051
  anisotropy,
10827
11052
  maxBytes: ownedBudget,
@@ -10842,17 +11067,17 @@ function SphereTileSource({
10842
11067
  );
10843
11068
  const manager = sharedManager ?? ownedManager;
10844
11069
  const budget = manager.memoryBudgetBytes;
10845
- const readyRef = (0, import_react38.useRef)(false);
10846
- const ready = (0, import_react38.useCallback)(() => {
11070
+ const readyRef = (0, import_react39.useRef)(false);
11071
+ const ready = (0, import_react39.useCallback)(() => {
10847
11072
  if (!readyRef.current) {
10848
11073
  readyRef.current = true;
10849
11074
  callbacks.current.onReady?.();
10850
11075
  }
10851
11076
  }, []);
10852
- (0, import_react38.useEffect)(() => {
11077
+ (0, import_react39.useEffect)(() => {
10853
11078
  if (preview) ready();
10854
11079
  }, [preview, ready]);
10855
- (0, import_react38.useEffect)(() => {
11080
+ (0, import_react39.useEffect)(() => {
10856
11081
  manager.resume();
10857
11082
  return () => {
10858
11083
  if (manager === ownedManager) {
@@ -10862,14 +11087,14 @@ function SphereTileSource({
10862
11087
  queueMicrotask(() => manager.releaseUnused());
10863
11088
  };
10864
11089
  }, [manager, ownedManager]);
10865
- const resolveUrl = (0, import_react38.useMemo)(
11090
+ const resolveUrl = (0, import_react39.useMemo)(
10866
11091
  () => (address) => resolveAssetUrl(
10867
11092
  baseUrl,
10868
11093
  resolveTileUrl ? resolveTileUrl(address) : resolveSphereTemplateUrl(urlTemplate, address, indexBase)
10869
11094
  ),
10870
11095
  [baseUrl, resolveTileUrl, urlTemplate, indexBase]
10871
11096
  );
10872
- const candidates = (0, import_react38.useMemo)(
11097
+ const candidates = (0, import_react39.useMemo)(
10873
11098
  () => config.levels.map(
10874
11099
  (level, index) => sphereTileAddresses(level, index, config.tileSize).map((address) => ({
10875
11100
  address,
@@ -10878,17 +11103,17 @@ function SphereTileSource({
10878
11103
  ),
10879
11104
  [config, yawOffset2]
10880
11105
  );
10881
- const previewAddress = (0, import_react38.useMemo)(
11106
+ const previewAddress = (0, import_react39.useMemo)(
10882
11107
  () => sphereTileAddresses({ width: 2, height: 1 }, 0, 2)[0],
10883
11108
  []
10884
11109
  );
10885
- const [layers, setLayers] = (0, import_react38.useState)([]);
11110
+ const [layers, setLayers] = (0, import_react39.useState)([]);
10886
11111
  const detailReporter = useSceneDetailReporter();
10887
- const detailReporterRef = (0, import_react38.useRef)(detailReporter);
11112
+ const detailReporterRef = (0, import_react39.useRef)(detailReporter);
10888
11113
  detailReporterRef.current = detailReporter;
10889
- const detailTokenRef = (0, import_react38.useRef)("");
10890
- const targetKeysRef = (0, import_react38.useRef)([]);
10891
- const scratch = (0, import_react38.useMemo)(
11114
+ const detailTokenRef = (0, import_react39.useRef)("");
11115
+ const targetKeysRef = (0, import_react39.useRef)([]);
11116
+ const scratch = (0, import_react39.useMemo)(
10892
11117
  () => ({
10893
11118
  camera: new import_three18.PerspectiveCamera(),
10894
11119
  frustum: new import_three18.Frustum(),
@@ -10936,7 +11161,13 @@ function SphereTileSource({
10936
11161
  scratch.camera.matrixWorldInverse
10937
11162
  );
10938
11163
  scratch.frustum.setFromProjectionMatrix(scratch.matrix);
10939
- let level = loadMode === "preview" ? -1 : loadMode === "base" ? 0 : preferredSphereLevel(config.levels, size.height, dpr, camera.fov);
11164
+ let level = loadMode === "preview" ? -1 : loadMode === "base" ? 0 : preferredSphereLevel(
11165
+ config.levels,
11166
+ size.height,
11167
+ dpr,
11168
+ camera.fov,
11169
+ size.width
11170
+ );
10940
11171
  const next = [];
10941
11172
  let residentBytes = 0;
10942
11173
  for (let index = 0; index <= level; index++) {
@@ -10944,7 +11175,7 @@ function SphereTileSource({
10944
11175
  ({ bounds }) => scratch.frustum.intersectsBox(bounds)
10945
11176
  ).map(({ address }) => address);
10946
11177
  const bytes = addresses.reduce(
10947
- (total, address) => total + address.width * address.height * 4,
11178
+ (total, address) => total + mipmappedTextureBytes(address.width, address.height),
10948
11179
  0
10949
11180
  );
10950
11181
  if (residentBytes + bytes > budget) {
@@ -11003,14 +11234,14 @@ function SphereTileSource({
11003
11234
  }
11004
11235
  function SphereTile({ multires, ...props }) {
11005
11236
  const token = typeof multires === "string" ? multires : JSON.stringify(multires);
11006
- const config = (0, import_react38.useMemo)(() => parseSphereMultires(multires), [token]);
11237
+ const config = (0, import_react39.useMemo)(() => parseSphereMultires(multires), [token]);
11007
11238
  const sourceKey = `${props.baseUrl}\0${token}\0${props.previewUrl}\0${props.urlTemplate ?? ""}\0${props.indexBase ?? 1}`;
11008
11239
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SphereTileSource, { ...props, config }, sourceKey);
11009
11240
  }
11010
11241
 
11011
11242
  // src/tile/tile.tsx
11012
11243
  var import_fiber17 = require("@react-three/fiber");
11013
- var import_react39 = require("react");
11244
+ var import_react40 = require("react");
11014
11245
  var import_three20 = require("three");
11015
11246
 
11016
11247
  // src/tile/face-layout.ts
@@ -11106,8 +11337,19 @@ function makeTileAddress(face, level, faceSize, tileSize, col, row) {
11106
11337
  function tileKey(address) {
11107
11338
  return `${address.level}/${address.face}/${address.row}/${address.col}`;
11108
11339
  }
11109
- function getPreferredLevel(levels, viewportHeight, pixelRatio, verticalFov, currentLevel) {
11110
- const requiredFaceSize = viewportHeight * pixelRatio * (90 / Math.max(verticalFov, 1));
11340
+ function tanHalfFov2(verticalFov) {
11341
+ const fov = Math.min(179, Math.max(1, verticalFov));
11342
+ return Math.tan(import_three19.MathUtils.degToRad(fov) * 0.5);
11343
+ }
11344
+ function getPreferredLevel(levels, viewportHeight, pixelRatio, verticalFov, currentLevel, viewportWidth = viewportHeight) {
11345
+ const height = Math.max(1, viewportHeight) * pixelRatio;
11346
+ const width = Math.max(1, viewportWidth) * pixelRatio;
11347
+ const tanHalfV = tanHalfFov2(verticalFov);
11348
+ const requiredFaceSize = Math.max(
11349
+ height / tanHalfV,
11350
+ height * tanHalfV,
11351
+ width * width * tanHalfV / height
11352
+ );
11111
11353
  let preferredLevel = levels.length;
11112
11354
  for (let index = 0; index < levels.length; index += 1) {
11113
11355
  if (levels[index] >= requiredFaceSize) {
@@ -11163,7 +11405,7 @@ function PreviewFace({
11163
11405
  texture,
11164
11406
  faceOrder
11165
11407
  }) {
11166
- const geometry = (0, import_react39.useMemo)(() => {
11408
+ const geometry = (0, import_react40.useMemo)(() => {
11167
11409
  const nextGeometry = new import_three20.PlaneGeometry(
11168
11410
  PREVIEW_RADIUS * 2,
11169
11411
  PREVIEW_RADIUS * 2
@@ -11178,7 +11420,7 @@ function PreviewFace({
11178
11420
  uv.needsUpdate = true;
11179
11421
  return nextGeometry;
11180
11422
  }, [face, faceOrder]);
11181
- (0, import_react39.useEffect)(
11423
+ (0, import_react40.useEffect)(
11182
11424
  () => () => {
11183
11425
  geometry.dispose();
11184
11426
  },
@@ -11218,7 +11460,7 @@ function PreviewCube({
11218
11460
  face
11219
11461
  ));
11220
11462
  }
11221
- var TileMesh = (0, import_react39.memo)(function TileMesh2({
11463
+ var TileMesh = (0, import_react40.memo)(function TileMesh2({
11222
11464
  address,
11223
11465
  manager,
11224
11466
  renderOrder,
@@ -11251,7 +11493,7 @@ var TileMesh = (0, import_react39.memo)(function TileMesh2({
11251
11493
  }
11252
11494
  ) });
11253
11495
  });
11254
- var TileLayerMeshes = (0, import_react39.memo)(function TileLayerMeshes2({
11496
+ var TileLayerMeshes = (0, import_react40.memo)(function TileLayerMeshes2({
11255
11497
  layer,
11256
11498
  manager,
11257
11499
  resolveUrl
@@ -11423,10 +11665,10 @@ function Tile({
11423
11665
  onLevelChange
11424
11666
  }) {
11425
11667
  const { camera, gl, size } = (0, import_fiber17.useThree)();
11426
- const multiresConfig = (0, import_react39.useMemo)(() => parseMultires(multires), [multires]);
11668
+ const multiresConfig = (0, import_react40.useMemo)(() => parseMultires(multires), [multires]);
11427
11669
  const { levels, tileSize } = multiresConfig;
11428
11670
  const multiresToken = `${tileSize}:${levels.join(",")}`;
11429
- const candidatesByLevel = (0, import_react39.useMemo)(
11671
+ const candidatesByLevel = (0, import_react40.useMemo)(
11430
11672
  () => buildTileCandidates(levels, tileSize),
11431
11673
  [levels, tileSize]
11432
11674
  );
@@ -11438,13 +11680,13 @@ function Tile({
11438
11680
  onPreviewError
11439
11681
  );
11440
11682
  const sharedManager = useSharedTileTextureManager();
11441
- const progressRef = (0, import_react39.useRef)(onLoadProgress);
11442
- const errorRef = (0, import_react39.useRef)(onTileError);
11443
- const levelChangeRef = (0, import_react39.useRef)(onLevelChange);
11683
+ const progressRef = (0, import_react40.useRef)(onLoadProgress);
11684
+ const errorRef = (0, import_react40.useRef)(onTileError);
11685
+ const levelChangeRef = (0, import_react40.useRef)(onLevelChange);
11444
11686
  progressRef.current = onLoadProgress;
11445
11687
  errorRef.current = onTileError;
11446
11688
  levelChangeRef.current = onLevelChange;
11447
- const ownedManager = (0, import_react39.useMemo)(
11689
+ const ownedManager = (0, import_react40.useMemo)(
11448
11690
  () => new TileTextureManager({
11449
11691
  anisotropy,
11450
11692
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -11465,7 +11707,7 @@ function Tile({
11465
11707
  ]
11466
11708
  );
11467
11709
  const manager = sharedManager ?? ownedManager;
11468
- (0, import_react39.useEffect)(() => {
11710
+ (0, import_react40.useEffect)(() => {
11469
11711
  manager.resume();
11470
11712
  return () => {
11471
11713
  if (manager === ownedManager) {
@@ -11475,11 +11717,11 @@ function Tile({
11475
11717
  queueMicrotask(() => manager.releaseUnused());
11476
11718
  };
11477
11719
  }, [manager, ownedManager]);
11478
- const readyRef = (0, import_react39.useRef)(false);
11479
- (0, import_react39.useEffect)(() => {
11720
+ const readyRef = (0, import_react40.useRef)(false);
11721
+ (0, import_react40.useEffect)(() => {
11480
11722
  readyRef.current = false;
11481
11723
  }, [effectivePreviewUrl]);
11482
- (0, import_react39.useEffect)(() => {
11724
+ (0, import_react40.useEffect)(() => {
11483
11725
  if (!previewTexture || readyRef.current) {
11484
11726
  return;
11485
11727
  }
@@ -11487,37 +11729,37 @@ function Tile({
11487
11729
  onReady?.();
11488
11730
  }, [onReady, previewTexture]);
11489
11731
  const template = urlTemplate ?? buildDefaultTileUrlTemplate();
11490
- const resolveUrl = (0, import_react39.useMemo)(
11732
+ const resolveUrl = (0, import_react40.useMemo)(
11491
11733
  () => (address) => {
11492
11734
  const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
11493
11735
  return resolveRelativeTileUrl(baseUrl, relativePath);
11494
11736
  },
11495
11737
  [baseUrl, resolveTileUrl, template]
11496
11738
  );
11497
- const [layers, setLayers] = (0, import_react39.useState)([]);
11498
- const layerTokenRef = (0, import_react39.useRef)("");
11499
- const currentLevelRef = (0, import_react39.useRef)(1);
11500
- const lastUpdateAtRef = (0, import_react39.useRef)(-Infinity);
11501
- const visibilityDirtyRef = (0, import_react39.useRef)(true);
11502
- const lastVisibilityQuaternionRef = (0, import_react39.useRef)(camera.quaternion.clone());
11503
- const lastVisibilityFovRef = (0, import_react39.useRef)(Number.NaN);
11504
- const lastViewportRef = (0, import_react39.useRef)({ width: 0, height: 0, dpr: 0 });
11505
- const frustumRef = (0, import_react39.useRef)(new import_three20.Frustum());
11506
- const projectionMatrixRef = (0, import_react39.useRef)(new import_three20.Matrix4());
11507
- const visibilityCameraRef = (0, import_react39.useRef)(new import_three20.PerspectiveCamera());
11508
- const trackedLoadModeRef = (0, import_react39.useRef)(loadMode);
11509
- const targetKeysRef = (0, import_react39.useRef)([]);
11510
- const detailTokenRef = (0, import_react39.useRef)("");
11739
+ const [layers, setLayers] = (0, import_react40.useState)([]);
11740
+ const layerTokenRef = (0, import_react40.useRef)("");
11741
+ const currentLevelRef = (0, import_react40.useRef)(1);
11742
+ const lastUpdateAtRef = (0, import_react40.useRef)(-Infinity);
11743
+ const visibilityDirtyRef = (0, import_react40.useRef)(true);
11744
+ const lastVisibilityQuaternionRef = (0, import_react40.useRef)(camera.quaternion.clone());
11745
+ const lastVisibilityFovRef = (0, import_react40.useRef)(Number.NaN);
11746
+ const lastViewportRef = (0, import_react40.useRef)({ width: 0, height: 0, dpr: 0 });
11747
+ const frustumRef = (0, import_react40.useRef)(new import_three20.Frustum());
11748
+ const projectionMatrixRef = (0, import_react40.useRef)(new import_three20.Matrix4());
11749
+ const visibilityCameraRef = (0, import_react40.useRef)(new import_three20.PerspectiveCamera());
11750
+ const trackedLoadModeRef = (0, import_react40.useRef)(loadMode);
11751
+ const targetKeysRef = (0, import_react40.useRef)([]);
11752
+ const detailTokenRef = (0, import_react40.useRef)("");
11511
11753
  const detailReporter = useSceneDetailReporter();
11512
- const detailReporterRef = (0, import_react39.useRef)(detailReporter);
11754
+ const detailReporterRef = (0, import_react40.useRef)(detailReporter);
11513
11755
  detailReporterRef.current = detailReporter;
11514
- (0, import_react39.useEffect)(() => {
11756
+ (0, import_react40.useEffect)(() => {
11515
11757
  currentLevelRef.current = 1;
11516
11758
  layerTokenRef.current = "";
11517
11759
  visibilityDirtyRef.current = true;
11518
11760
  setLayers([]);
11519
11761
  }, [manager, multiresToken]);
11520
- (0, import_react39.useEffect)(() => {
11762
+ (0, import_react40.useEffect)(() => {
11521
11763
  if (loadMode !== "preview") {
11522
11764
  return;
11523
11765
  }
@@ -11567,7 +11809,8 @@ function Tile({
11567
11809
  size.height,
11568
11810
  pixelRatio,
11569
11811
  camera.fov,
11570
- currentLevelRef.current
11812
+ currentLevelRef.current,
11813
+ size.width
11571
11814
  );
11572
11815
  if (targetLevel !== currentLevelRef.current) {
11573
11816
  currentLevelRef.current = targetLevel;
@@ -11693,9 +11936,9 @@ function SnapshotCapture({
11693
11936
  onCaptured
11694
11937
  }) {
11695
11938
  const { camera, gl, scene, size } = (0, import_fiber18.useThree)();
11696
- const capturedRequestRef = (0, import_react40.useRef)(0);
11697
- const renderTargetRef = (0, import_react40.useRef)(null);
11698
- (0, import_react40.useEffect)(
11939
+ const capturedRequestRef = (0, import_react41.useRef)(0);
11940
+ const renderTargetRef = (0, import_react41.useRef)(null);
11941
+ (0, import_react41.useEffect)(
11699
11942
  () => () => {
11700
11943
  renderTargetRef.current?.dispose();
11701
11944
  renderTargetRef.current = null;
@@ -11753,43 +11996,43 @@ function ScenesController({
11753
11996
  transitionWaitForDetailMs,
11754
11997
  phaseRef: externalPhaseRef
11755
11998
  }) {
11756
- const controlsRef = (0, import_react40.useContext)(PanoramaViewContext);
11999
+ const controlsRef = (0, import_react41.useContext)(PanoramaViewContext);
11757
12000
  const manager = useSharedTileTextureManager();
11758
- const transition = (0, import_react40.useMemo)(
12001
+ const transition = (0, import_react41.useMemo)(
11759
12002
  () => resolveTransition(transitionInput),
11760
12003
  [transitionInput]
11761
12004
  );
11762
- const sceneMap = (0, import_react40.useMemo)(
12005
+ const sceneMap = (0, import_react41.useMemo)(
11763
12006
  () => new Map(scenes.map((scene) => [scene.id, scene])),
11764
12007
  [scenes]
11765
12008
  );
11766
12009
  const initialScene = sceneMap.get(activeSceneId) ?? null;
11767
- const [slots, setSlots] = (0, import_react40.useState)([
12010
+ const [slots, setSlots] = (0, import_react41.useState)([
11768
12011
  initialScene,
11769
12012
  null
11770
12013
  ]);
11771
- const [activeSlot, setActiveSlot] = (0, import_react40.useState)(0);
11772
- const [liveSlot, setLiveSlot] = (0, import_react40.useState)(0);
11773
- const [pendingSlot, setPendingSlot] = (0, import_react40.useState)(null);
11774
- const [phase, setPhase] = (0, import_react40.useState)(initialScene ? "idle" : "preloading");
11775
- const [captureRequestId, setCaptureRequestId] = (0, import_react40.useState)(0);
11776
- const [transitionRunId, setTransitionRunId] = (0, import_react40.useState)(0);
11777
- const [snapshot, setSnapshot] = (0, import_react40.useState)(null);
11778
- const [recoveryKey, setRecoveryKey] = (0, import_react40.useState)(0);
11779
- const requestedSceneIdRef = (0, import_react40.useRef)(activeSceneId);
11780
- const captureReasonRef = (0, import_react40.useRef)("start");
11781
- const interactionReleaseRef = (0, import_react40.useRef)(null);
11782
- const slotsRef = (0, import_react40.useRef)(slots);
11783
- const phaseRef = (0, import_react40.useRef)(phase);
11784
- const liveSlotRef = (0, import_react40.useRef)(liveSlot);
11785
- const transitionFromRef = (0, import_react40.useRef)(initialScene);
11786
- const readySceneRef = (0, import_react40.useRef)(null);
11787
- const waitRef = (0, import_react40.useRef)(null);
11788
- const waitTimerRef = (0, import_react40.useRef)(null);
11789
- const detailGenerationRef = (0, import_react40.useRef)(0);
11790
- const [detailGeneration, setDetailGeneration] = (0, import_react40.useState)(0);
11791
- const mountedRef = (0, import_react40.useRef)(true);
11792
- const waitForDetailMsRef = (0, import_react40.useRef)(0);
12014
+ const [activeSlot, setActiveSlot] = (0, import_react41.useState)(0);
12015
+ const [liveSlot, setLiveSlot] = (0, import_react41.useState)(0);
12016
+ const [pendingSlot, setPendingSlot] = (0, import_react41.useState)(null);
12017
+ const [phase, setPhase] = (0, import_react41.useState)(initialScene ? "idle" : "preloading");
12018
+ const [captureRequestId, setCaptureRequestId] = (0, import_react41.useState)(0);
12019
+ const [transitionRunId, setTransitionRunId] = (0, import_react41.useState)(0);
12020
+ const [snapshot, setSnapshot] = (0, import_react41.useState)(null);
12021
+ const [recoveryKey, setRecoveryKey] = (0, import_react41.useState)(0);
12022
+ const requestedSceneIdRef = (0, import_react41.useRef)(activeSceneId);
12023
+ const captureReasonRef = (0, import_react41.useRef)("start");
12024
+ const interactionReleaseRef = (0, import_react41.useRef)(null);
12025
+ const slotsRef = (0, import_react41.useRef)(slots);
12026
+ const phaseRef = (0, import_react41.useRef)(phase);
12027
+ const liveSlotRef = (0, import_react41.useRef)(liveSlot);
12028
+ const transitionFromRef = (0, import_react41.useRef)(initialScene);
12029
+ const readySceneRef = (0, import_react41.useRef)(null);
12030
+ const waitRef = (0, import_react41.useRef)(null);
12031
+ const waitTimerRef = (0, import_react41.useRef)(null);
12032
+ const detailGenerationRef = (0, import_react41.useRef)(0);
12033
+ const [detailGeneration, setDetailGeneration] = (0, import_react41.useState)(0);
12034
+ const mountedRef = (0, import_react41.useRef)(true);
12035
+ const waitForDetailMsRef = (0, import_react41.useRef)(0);
11793
12036
  waitForDetailMsRef.current = normalizeTransitionWaitMs(transitionWaitForDetailMs);
11794
12037
  slotsRef.current = slots;
11795
12038
  phaseRef.current = phase;
@@ -11797,21 +12040,21 @@ function ScenesController({
11797
12040
  externalPhaseRef.current = phase;
11798
12041
  }
11799
12042
  liveSlotRef.current = liveSlot;
11800
- (0, import_react40.useEffect)(() => {
12043
+ (0, import_react41.useEffect)(() => {
11801
12044
  const ready = readySceneRef.current;
11802
12045
  if (ready) controlsRef?.current?.setSceneViewLimits(sceneMap.get(ready.id)?.viewLimits);
11803
12046
  }, [controlsRef, sceneMap]);
11804
- (0, import_react40.useEffect)(() => () => {
12047
+ (0, import_react41.useEffect)(() => () => {
11805
12048
  controlsRef?.current?.setSceneViewLimits(void 0);
11806
12049
  }, [controlsRef]);
11807
- const releaseInteraction = (0, import_react40.useCallback)(() => {
12050
+ const releaseInteraction = (0, import_react41.useCallback)(() => {
11808
12051
  interactionReleaseRef.current?.();
11809
12052
  interactionReleaseRef.current = null;
11810
12053
  }, []);
11811
- const reportError = (0, import_react40.useCallback)((sceneId, error) => {
12054
+ const reportError = (0, import_react41.useCallback)((sceneId, error) => {
11812
12055
  onTransitionError?.({ sceneId, error });
11813
12056
  }, [onTransitionError]);
11814
- const clearDetailWait = (0, import_react40.useCallback)((bumpGeneration) => {
12057
+ const clearDetailWait = (0, import_react41.useCallback)((bumpGeneration) => {
11815
12058
  if (waitTimerRef.current !== null) {
11816
12059
  clearTimeout(waitTimerRef.current);
11817
12060
  waitTimerRef.current = null;
@@ -11832,14 +12075,14 @@ function ScenesController({
11832
12075
  detailGenerationRef.current = nextGeneration;
11833
12076
  setDetailGeneration(nextGeneration);
11834
12077
  }, []);
11835
- (0, import_react40.useEffect)(() => {
12078
+ (0, import_react41.useEffect)(() => {
11836
12079
  mountedRef.current = true;
11837
12080
  return () => {
11838
12081
  mountedRef.current = false;
11839
12082
  clearDetailWait(false);
11840
12083
  };
11841
12084
  }, [clearDetailWait]);
11842
- const startPreload = (0, import_react40.useCallback)((scene) => {
12085
+ const startPreload = (0, import_react41.useCallback)((scene) => {
11843
12086
  clearDetailWait(true);
11844
12087
  const nextSlot = 1 - liveSlotRef.current;
11845
12088
  requestedSceneIdRef.current = scene.id;
@@ -11851,7 +12094,7 @@ function ScenesController({
11851
12094
  setPendingSlot(nextSlot);
11852
12095
  setPhase("preloading");
11853
12096
  }, [clearDetailWait]);
11854
- (0, import_react40.useEffect)(() => {
12097
+ (0, import_react41.useEffect)(() => {
11855
12098
  const requested = sceneMap.get(activeSceneId);
11856
12099
  if (!requested) {
11857
12100
  reportError(activeSceneId, new Error(`Unknown panorama scene: ${activeSceneId}`));
@@ -11877,13 +12120,13 @@ function ScenesController({
11877
12120
  }
11878
12121
  startPreload(requested);
11879
12122
  }, [activeSceneId, clearDetailWait, pendingSlot, reportError, sceneMap, startPreload]);
11880
- const notifySceneReady = (0, import_react40.useCallback)((scene) => {
12123
+ const notifySceneReady = (0, import_react41.useCallback)((scene) => {
11881
12124
  if (!scene || readySceneRef.current === scene) return;
11882
12125
  readySceneRef.current = scene;
11883
12126
  controlsRef?.current?.setSceneViewLimits(sceneMap.get(scene.id)?.viewLimits);
11884
12127
  onSceneReady?.(scene.id);
11885
12128
  }, [controlsRef, onSceneReady, sceneMap]);
11886
- const handleReady = (0, import_react40.useCallback)((slot) => {
12129
+ const handleReady = (0, import_react41.useCallback)((slot) => {
11887
12130
  if (slot === liveSlotRef.current && pendingSlot === null && phaseRef.current === "idle") {
11888
12131
  notifySceneReady(slotsRef.current[slot]);
11889
12132
  return;
@@ -11919,7 +12162,7 @@ function ScenesController({
11919
12162
  setPhase("capturing");
11920
12163
  setCaptureRequestId((current) => current + 1);
11921
12164
  }, [controlsRef, notifySceneReady, onTransitionEnd, pendingSlot, transition.preset]);
11922
- const finishTransition = (0, import_react40.useCallback)(() => {
12165
+ const finishTransition = (0, import_react41.useCallback)(() => {
11923
12166
  const nextSlot = pendingSlot;
11924
12167
  const previousScene = transitionFromRef.current;
11925
12168
  const nextScene = nextSlot === null ? null : slotsRef.current[nextSlot];
@@ -11944,11 +12187,11 @@ function ScenesController({
11944
12187
  });
11945
12188
  }
11946
12189
  }, [onTransitionEnd, pendingSlot, releaseInteraction, snapshot, transition.preset]);
11947
- const finishTransitionRef = (0, import_react40.useRef)(finishTransition);
12190
+ const finishTransitionRef = (0, import_react41.useRef)(finishTransition);
11948
12191
  finishTransitionRef.current = finishTransition;
11949
- const transitionRef = (0, import_react40.useRef)(transition);
12192
+ const transitionRef = (0, import_react41.useRef)(transition);
11950
12193
  transitionRef.current = transition;
11951
- const consumeDetailSignal = (0, import_react40.useCallback)((signal) => {
12194
+ const consumeDetailSignal = (0, import_react41.useCallback)((signal) => {
11952
12195
  const decision = acceptDetailWait(waitRef.current, signal);
11953
12196
  waitRef.current = decision.wait;
11954
12197
  if (!decision.start) {
@@ -11968,7 +12211,7 @@ function ScenesController({
11968
12211
  setPhase("transitioning");
11969
12212
  setTransitionRunId((current) => current + 1);
11970
12213
  }, []);
11971
- const handleCaptured = (0, import_react40.useCallback)((nextSnapshot) => {
12214
+ const handleCaptured = (0, import_react41.useCallback)((nextSnapshot) => {
11972
12215
  if (captureReasonRef.current === "interrupt") {
11973
12216
  setSnapshot((current) => {
11974
12217
  current?.dispose();
@@ -12066,15 +12309,15 @@ function ScenesController({
12066
12309
  startPreload,
12067
12310
  transition.preset
12068
12311
  ]);
12069
- const snapshotRef = (0, import_react40.useRef)(snapshot);
12312
+ const snapshotRef = (0, import_react41.useRef)(snapshot);
12070
12313
  snapshotRef.current = snapshot;
12071
- (0, import_react40.useEffect)(() => () => {
12314
+ (0, import_react41.useEffect)(() => () => {
12072
12315
  releaseInteraction();
12073
12316
  snapshotRef.current?.dispose();
12074
12317
  snapshotRef.current = null;
12075
12318
  }, [releaseInteraction]);
12076
12319
  const { gl } = (0, import_fiber18.useThree)();
12077
- (0, import_react40.useEffect)(() => {
12320
+ (0, import_react41.useEffect)(() => {
12078
12321
  const canvas = gl.domElement;
12079
12322
  const onContextLost = (event) => {
12080
12323
  event.preventDefault();
@@ -12102,7 +12345,7 @@ function ScenesController({
12102
12345
  const snapshotVisible = snapshot !== null && (phase === "transitioning" || phase === "recapturing" || phase === "preloading");
12103
12346
  const slotSceneKey = `${slots[0]?.id ?? ""}
12104
12347
  ${slots[1]?.id ?? ""}`;
12105
- const detailReporters = (0, import_react40.useMemo)(() => {
12348
+ const detailReporters = (0, import_react41.useMemo)(() => {
12106
12349
  const [firstSceneId = "", secondSceneId = ""] = slotSceneKey.split("\n");
12107
12350
  const sceneIds = [firstSceneId, secondSceneId];
12108
12351
  return sceneIds.map((sceneId) => ({
@@ -12119,7 +12362,7 @@ ${slots[1]?.id ?? ""}`;
12119
12362
  }
12120
12363
  }));
12121
12364
  }, [consumeDetailSignal, detailGeneration, slotSceneKey]);
12122
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react40.Fragment, { children: [
12365
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react41.Fragment, { children: [
12123
12366
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12124
12367
  SnapshotCapture,
12125
12368
  {
@@ -12178,11 +12421,11 @@ function Scenes({
12178
12421
  ...rest
12179
12422
  }) {
12180
12423
  const isControlled = controlledId !== void 0;
12181
- const [internalId, setInternalId] = (0, import_react40.useState)(
12424
+ const [internalId, setInternalId] = (0, import_react41.useState)(
12182
12425
  () => controlledId ?? defaultActiveSceneId ?? scenes[0]?.id ?? ""
12183
12426
  );
12184
12427
  const resolvedId = isControlled ? controlledId : internalId;
12185
- const requestSceneChange = (0, import_react40.useCallback)(
12428
+ const requestSceneChange = (0, import_react41.useCallback)(
12186
12429
  (id) => {
12187
12430
  if (isControlled) {
12188
12431
  onActiveSceneIdChange?.(id);
@@ -12192,13 +12435,13 @@ function Scenes({
12192
12435
  },
12193
12436
  [isControlled, onActiveSceneIdChange]
12194
12437
  );
12195
- const scenesRef = (0, import_react40.useRef)(scenes);
12438
+ const scenesRef = (0, import_react41.useRef)(scenes);
12196
12439
  scenesRef.current = scenes;
12197
- const resolvedIdRef = (0, import_react40.useRef)(resolvedId);
12440
+ const resolvedIdRef = (0, import_react41.useRef)(resolvedId);
12198
12441
  resolvedIdRef.current = resolvedId;
12199
- const host = (0, import_react40.useContext)(ScenesHostContext);
12200
- const phaseRef = (0, import_react40.useRef)("idle");
12201
- (0, import_react40.useEffect)(() => {
12442
+ const host = (0, import_react41.useContext)(ScenesHostContext);
12443
+ const phaseRef = (0, import_react41.useRef)("idle");
12444
+ (0, import_react41.useEffect)(() => {
12202
12445
  if (!host) {
12203
12446
  return;
12204
12447
  }
@@ -12268,12 +12511,12 @@ function Scenes({
12268
12511
 
12269
12512
  // src/hotspot/image-hotspot.tsx
12270
12513
  var import_fiber22 = require("@react-three/fiber");
12271
- var import_react45 = require("react");
12514
+ var import_react46 = require("react");
12272
12515
  var import_three27 = require("three");
12273
12516
 
12274
12517
  // src/hotspot/hotspot-anchor.tsx
12275
12518
  var import_fiber21 = require("@react-three/fiber");
12276
- var import_react44 = require("react");
12519
+ var import_react45 = require("react");
12277
12520
  var import_three25 = require("three");
12278
12521
 
12279
12522
  // src/hotspot/drag-session.ts
@@ -12365,18 +12608,18 @@ function suppressFollowingClick(target) {
12365
12608
  }
12366
12609
 
12367
12610
  // src/hotspot/hotspot-tooltip.tsx
12368
- var import_react42 = require("react");
12611
+ var import_react43 = require("react");
12369
12612
 
12370
12613
  // src/webvr/stereo-html.tsx
12371
12614
  var import_fiber19 = require("@react-three/fiber");
12372
- var import_react41 = require("react");
12615
+ var import_react42 = require("react");
12373
12616
  var import_client = require("react-dom/client");
12374
12617
  var import_three22 = require("three");
12375
12618
  var import_jsx_runtime24 = require("react/jsx-runtime");
12376
12619
  function useFallbackStereoView() {
12377
- const runtime = (0, import_react41.useContext)(WebVRRuntimeContext);
12620
+ const runtime = (0, import_react42.useContext)(WebVRRuntimeContext);
12378
12621
  const host = runtime?.host ?? null;
12379
- const fallback = (0, import_react41.useSyncExternalStore)(
12622
+ const fallback = (0, import_react42.useSyncExternalStore)(
12380
12623
  (onStoreChange) => host ? subscribeWebVRHost(host, onStoreChange) : () => void 0,
12381
12624
  () => isFallbackStereoMode(host?.snapshot.mode ?? null),
12382
12625
  () => false
@@ -12392,9 +12635,9 @@ function ProjectedHtml({
12392
12635
  eye
12393
12636
  }) {
12394
12637
  const { camera, gl, events, size } = (0, import_fiber19.useThree)();
12395
- const group = (0, import_react41.useRef)(null);
12396
- const overlay = (0, import_react41.useRef)(null);
12397
- const scratch = (0, import_react41.useRef)(new import_three22.Vector3());
12638
+ const group = (0, import_react42.useRef)(null);
12639
+ const overlay = (0, import_react42.useRef)(null);
12640
+ const scratch = (0, import_react42.useRef)(new import_three22.Vector3());
12398
12641
  const target = events.connected || gl.domElement.parentElement;
12399
12642
  const updatePosition = () => {
12400
12643
  const anchor = group.current;
@@ -12422,7 +12665,7 @@ function ProjectedHtml({
12422
12665
  )
12423
12666
  );
12424
12667
  };
12425
- (0, import_react41.useLayoutEffect)(() => {
12668
+ (0, import_react42.useLayoutEffect)(() => {
12426
12669
  if (!target) return;
12427
12670
  const element = document.createElement("div");
12428
12671
  Object.assign(element.style, {
@@ -12440,7 +12683,7 @@ function ProjectedHtml({
12440
12683
  queueMicrotask(() => root.unmount());
12441
12684
  };
12442
12685
  }, [target]);
12443
- (0, import_react41.useLayoutEffect)(() => {
12686
+ (0, import_react42.useLayoutEffect)(() => {
12444
12687
  overlay.current?.root.render(
12445
12688
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12446
12689
  "div",
@@ -12457,9 +12700,9 @@ function ProjectedHtml({
12457
12700
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("group", { ref: group });
12458
12701
  }
12459
12702
  function PanoHtml(props) {
12460
- const runtime = (0, import_react41.useContext)(WebVRRuntimeContext);
12703
+ const runtime = (0, import_react42.useContext)(WebVRRuntimeContext);
12461
12704
  const host = runtime?.host ?? null;
12462
- const mode = (0, import_react41.useSyncExternalStore)(
12705
+ const mode = (0, import_react42.useSyncExternalStore)(
12463
12706
  (onStoreChange) => host ? subscribeWebVRHost(host, onStoreChange) : () => void 0,
12464
12707
  () => host?.snapshot.mode ?? null,
12465
12708
  () => null
@@ -12548,16 +12791,16 @@ function HotspotTooltip({
12548
12791
  offset = DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
12549
12792
  appearance
12550
12793
  }) {
12551
- const [imageFailed, setImageFailed] = (0, import_react42.useState)(false);
12552
- (0, import_react42.useEffect)(() => {
12794
+ const [imageFailed, setImageFailed] = (0, import_react43.useState)(false);
12795
+ (0, import_react43.useEffect)(() => {
12553
12796
  setImageFailed(false);
12554
12797
  }, [content.image]);
12555
12798
  const showImage = Boolean(content.image) && !imageFailed;
12556
- const resolvedAppearance = (0, import_react42.useMemo)(
12799
+ const resolvedAppearance = (0, import_react43.useMemo)(
12557
12800
  () => resolveHotspotTooltipAppearance(appearance),
12558
12801
  [appearance]
12559
12802
  );
12560
- const bubbleStyle = (0, import_react42.useMemo)(
12803
+ const bubbleStyle = (0, import_react43.useMemo)(
12561
12804
  () => ({
12562
12805
  background: resolvedAppearance.background,
12563
12806
  border: resolvedAppearance.border,
@@ -12653,7 +12896,7 @@ function selectionFrameScale(worldScaleX, worldScaleY, cameraSpaceDepth, fovDegr
12653
12896
  // src/hotspot/hotspot-stroke-line.tsx
12654
12897
  var import_drei = require("@react-three/drei");
12655
12898
  var import_fiber20 = require("@react-three/fiber");
12656
- var import_react43 = require("react");
12899
+ var import_react44 = require("react");
12657
12900
  var import_three24 = require("three");
12658
12901
  var import_jsx_runtime26 = require("react/jsx-runtime");
12659
12902
  var LINE_WORLD_POSITION = new import_three24.Vector3();
@@ -12686,10 +12929,10 @@ function HotspotStrokeLine({
12686
12929
  dashSize,
12687
12930
  gapSize
12688
12931
  }) {
12689
- const lineRef = (0, import_react43.useRef)(null);
12932
+ const lineRef = (0, import_react44.useRef)(null);
12690
12933
  const { camera, size } = (0, import_fiber20.useThree)();
12691
12934
  const dash = resolveDash(dashSize, gapSize);
12692
- (0, import_react43.useLayoutEffect)(() => {
12935
+ (0, import_react44.useLayoutEffect)(() => {
12693
12936
  if (!dash.dashed) {
12694
12937
  return;
12695
12938
  }
@@ -13099,27 +13342,27 @@ function HotspotAnchor({
13099
13342
  onPositionChange,
13100
13343
  onDragEnd
13101
13344
  }) {
13102
- const controlsRef = (0, import_react44.useContext)(PanoramaViewContext);
13103
- const hotspotTargetRegistry = (0, import_react44.useContext)(HotspotTargetRegistryContext);
13104
- const eventBus = (0, import_react44.useContext)(PanoEventBusContext);
13345
+ const controlsRef = (0, import_react45.useContext)(PanoramaViewContext);
13346
+ const hotspotTargetRegistry = (0, import_react45.useContext)(HotspotTargetRegistryContext);
13347
+ const eventBus = (0, import_react45.useContext)(PanoEventBusContext);
13105
13348
  const cursorApi = usePanoCursor();
13106
13349
  const { camera, gl, size } = (0, import_fiber21.useThree)();
13107
- const groupRef = (0, import_react44.useRef)(null);
13108
- const selectionFrameRef = (0, import_react44.useRef)(null);
13109
- const tooltipAnchorRef = (0, import_react44.useRef)(null);
13110
- const dragStateRef = (0, import_react44.useRef)(null);
13111
- const updateDragRef = (0, import_react44.useRef)(() => void 0);
13112
- const endDragRef = (0, import_react44.useRef)(() => void 0);
13113
- const suppressNextClickRef = (0, import_react44.useRef)(false);
13114
- const [hovered, setHovered] = (0, import_react44.useState)(false);
13115
- const [dragging, setDragging] = (0, import_react44.useState)(false);
13116
- const [pinned, setPinned] = (0, import_react44.useState)(false);
13117
- const tooltipContent = (0, import_react44.useMemo)(
13350
+ const groupRef = (0, import_react45.useRef)(null);
13351
+ const selectionFrameRef = (0, import_react45.useRef)(null);
13352
+ const tooltipAnchorRef = (0, import_react45.useRef)(null);
13353
+ const dragStateRef = (0, import_react45.useRef)(null);
13354
+ const updateDragRef = (0, import_react45.useRef)(() => void 0);
13355
+ const endDragRef = (0, import_react45.useRef)(() => void 0);
13356
+ const suppressNextClickRef = (0, import_react45.useRef)(false);
13357
+ const [hovered, setHovered] = (0, import_react45.useState)(false);
13358
+ const [dragging, setDragging] = (0, import_react45.useState)(false);
13359
+ const [pinned, setPinned] = (0, import_react45.useState)(false);
13360
+ const tooltipContent = (0, import_react45.useMemo)(
13118
13361
  () => resolveHotspotTooltipContent(tooltip),
13119
13362
  [tooltip]
13120
13363
  );
13121
13364
  const normalizedPosition = normalizePanoPosition(position);
13122
- (0, import_react44.useEffect)(() => {
13365
+ (0, import_react45.useEffect)(() => {
13123
13366
  if (!hotspotTargetRegistry) {
13124
13367
  return;
13125
13368
  }
@@ -13136,24 +13379,24 @@ function HotspotAnchor({
13136
13379
  scaledWidth,
13137
13380
  scaledHeight
13138
13381
  );
13139
- const worldPosition = (0, import_react44.useMemo)(
13382
+ const worldPosition = (0, import_react45.useMemo)(
13140
13383
  () => panoPositionToVector3(normalizedPosition, resolvedDistance),
13141
13384
  [normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
13142
13385
  );
13143
- const surfaceQuaternion3 = (0, import_react44.useMemo)(
13386
+ const surfaceQuaternion3 = (0, import_react45.useMemo)(
13144
13387
  () => createSurfaceQuaternion(normalizedPosition),
13145
13388
  [normalizedPosition.pitch, normalizedPosition.yaw]
13146
13389
  );
13147
13390
  const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
13148
13391
  const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
13149
- const focusGeometry = (0, import_react44.useMemo)(() => {
13392
+ const focusGeometry = (0, import_react45.useMemo)(() => {
13150
13393
  const planeGeometry = new import_three25.PlaneGeometry(1, 1);
13151
13394
  const geometry = new import_three25.EdgesGeometry(planeGeometry);
13152
13395
  planeGeometry.dispose();
13153
13396
  return geometry;
13154
13397
  }, []);
13155
- const eventHovered = (0, import_react44.useRef)(false);
13156
- (0, import_react44.useEffect)(() => {
13398
+ const eventHovered = (0, import_react45.useRef)(false);
13399
+ (0, import_react45.useEffect)(() => {
13157
13400
  if (!visible || !interactive || pointerEvents === "none") eventHovered.current = false;
13158
13401
  }, [visible, interactive, pointerEvents]);
13159
13402
  const acceptsPointer = acceptsHotspotPointerEvents(interactive, pointerEvents);
@@ -13177,7 +13420,7 @@ function HotspotAnchor({
13177
13420
  });
13178
13421
  const tooltipOpen = visible && tooltipContent !== null && isTooltipOpen(tooltipTrigger, hovered, focused, pinned);
13179
13422
  const cursorClaimId = hotspotCursorClaimId(id);
13180
- (0, import_react44.useEffect)(
13423
+ (0, import_react45.useEffect)(
13181
13424
  () => () => {
13182
13425
  const dragState = dragStateRef.current;
13183
13426
  dragState?.stopTracking();
@@ -13186,7 +13429,7 @@ function HotspotAnchor({
13186
13429
  },
13187
13430
  []
13188
13431
  );
13189
- (0, import_react44.useEffect)(() => {
13432
+ (0, import_react45.useEffect)(() => {
13190
13433
  if (!cursorApi) {
13191
13434
  return;
13192
13435
  }
@@ -13208,14 +13451,14 @@ function HotspotAnchor({
13208
13451
  hovered,
13209
13452
  visible
13210
13453
  ]);
13211
- (0, import_react44.useEffect)(
13454
+ (0, import_react45.useEffect)(
13212
13455
  () => () => {
13213
13456
  cursorApi?.release(cursorClaimId);
13214
13457
  },
13215
13458
  [cursorApi, cursorClaimId]
13216
13459
  );
13217
- (0, import_react44.useEffect)(() => () => focusGeometry.dispose(), [focusGeometry]);
13218
- (0, import_react44.useEffect)(() => {
13460
+ (0, import_react45.useEffect)(() => () => focusGeometry.dispose(), [focusGeometry]);
13461
+ (0, import_react45.useEffect)(() => {
13219
13462
  if (tooltipTrigger !== "click") {
13220
13463
  setPinned(false);
13221
13464
  return;
@@ -13224,12 +13467,12 @@ function HotspotAnchor({
13224
13467
  setPinned(false);
13225
13468
  });
13226
13469
  }, [eventBus, tooltipTrigger]);
13227
- (0, import_react44.useEffect)(() => {
13470
+ (0, import_react45.useEffect)(() => {
13228
13471
  if (tooltipTrigger !== "hover") {
13229
13472
  setHovered(false);
13230
13473
  }
13231
13474
  }, [tooltipTrigger]);
13232
- (0, import_react44.useEffect)(() => {
13475
+ (0, import_react45.useEffect)(() => {
13233
13476
  if (acceptsPointer) {
13234
13477
  return;
13235
13478
  }
@@ -13243,7 +13486,7 @@ function HotspotAnchor({
13243
13486
  dragStateRef.current = null;
13244
13487
  setDragging(false);
13245
13488
  }, [acceptsPointer]);
13246
- (0, import_react44.useLayoutEffect)(() => {
13489
+ (0, import_react45.useLayoutEffect)(() => {
13247
13490
  const group = groupRef.current;
13248
13491
  if (!group) {
13249
13492
  return;
@@ -13664,12 +13907,12 @@ function HotspotPlane({
13664
13907
  var import_jsx_runtime29 = require("react/jsx-runtime");
13665
13908
  function useHotspotImageTexture(src, onLoad, onError) {
13666
13909
  const gl = (0, import_fiber22.useThree)((state) => state.gl);
13667
- const [texture, setTexture] = (0, import_react45.useState)(null);
13668
- const onLoadRef = (0, import_react45.useRef)(onLoad);
13669
- const onErrorRef = (0, import_react45.useRef)(onError);
13910
+ const [texture, setTexture] = (0, import_react46.useState)(null);
13911
+ const onLoadRef = (0, import_react46.useRef)(onLoad);
13912
+ const onErrorRef = (0, import_react46.useRef)(onError);
13670
13913
  onLoadRef.current = onLoad;
13671
13914
  onErrorRef.current = onError;
13672
- (0, import_react45.useEffect)(() => {
13915
+ (0, import_react46.useEffect)(() => {
13673
13916
  let active = true;
13674
13917
  setTexture(null);
13675
13918
  if (!src) {
@@ -13707,7 +13950,7 @@ function useHotspotImageTexture(src, onLoad, onError) {
13707
13950
  active = false;
13708
13951
  };
13709
13952
  }, [gl, src]);
13710
- (0, import_react45.useEffect)(
13953
+ (0, import_react46.useEffect)(
13711
13954
  () => () => {
13712
13955
  texture?.dispose();
13713
13956
  },
@@ -13729,7 +13972,7 @@ function ImageHotspot({
13729
13972
  }
13730
13973
 
13731
13974
  // src/hotspot/graphic-hotspot.tsx
13732
- var import_react46 = require("react");
13975
+ var import_react47 = require("react");
13733
13976
  var import_three28 = require("three");
13734
13977
  var import_jsx_runtime30 = require("react/jsx-runtime");
13735
13978
  var GRAPHIC_TEXTURE_SIZE = 512;
@@ -13920,12 +14163,12 @@ function createGraphicTexture(graphic, aspectWidth, aspectHeight) {
13920
14163
  return texture;
13921
14164
  }
13922
14165
  function useGraphicTexture(graphic, aspectWidth, aspectHeight, onLoad, onError) {
13923
- const [texture, setTexture] = (0, import_react46.useState)(null);
13924
- const onLoadRef = (0, import_react46.useRef)(onLoad);
13925
- const onErrorRef = (0, import_react46.useRef)(onError);
14166
+ const [texture, setTexture] = (0, import_react47.useState)(null);
14167
+ const onLoadRef = (0, import_react47.useRef)(onLoad);
14168
+ const onErrorRef = (0, import_react47.useRef)(onError);
13926
14169
  onLoadRef.current = onLoad;
13927
14170
  onErrorRef.current = onError;
13928
- (0, import_react46.useEffect)(() => {
14171
+ (0, import_react47.useEffect)(() => {
13929
14172
  let nextTexture = null;
13930
14173
  try {
13931
14174
  nextTexture = createGraphicTexture(graphic, aspectWidth, aspectHeight);
@@ -13964,7 +14207,7 @@ function GraphicHotspot(props) {
13964
14207
 
13965
14208
  // src/hotspot/sequence-hotspot.tsx
13966
14209
  var import_fiber23 = require("@react-three/fiber");
13967
- var import_react47 = require("react");
14210
+ var import_react48 = require("react");
13968
14211
  var import_three29 = require("three");
13969
14212
  var import_jsx_runtime31 = require("react/jsx-runtime");
13970
14213
  var DEFAULT_SEQUENCE_FPS = 12;
@@ -13981,12 +14224,12 @@ function useSpriteTexture({
13981
14224
  onLoadProgress
13982
14225
  }) {
13983
14226
  const gl = (0, import_fiber23.useThree)((state) => state.gl);
13984
- const [texture, setTexture] = (0, import_react47.useState)(null);
13985
- const onErrorRef = (0, import_react47.useRef)(onError);
13986
- const onLoadProgressRef = (0, import_react47.useRef)(onLoadProgress);
14227
+ const [texture, setTexture] = (0, import_react48.useState)(null);
14228
+ const onErrorRef = (0, import_react48.useRef)(onError);
14229
+ const onLoadProgressRef = (0, import_react48.useRef)(onLoadProgress);
13987
14230
  onErrorRef.current = onError;
13988
14231
  onLoadProgressRef.current = onLoadProgress;
13989
- (0, import_react47.useEffect)(() => {
14232
+ (0, import_react48.useEffect)(() => {
13990
14233
  let active = true;
13991
14234
  setTexture(null);
13992
14235
  if (!src) {
@@ -14027,7 +14270,7 @@ function useSpriteTexture({
14027
14270
  active = false;
14028
14271
  };
14029
14272
  }, [gl, id, src]);
14030
- (0, import_react47.useEffect)(
14273
+ (0, import_react48.useEffect)(
14031
14274
  () => () => {
14032
14275
  texture?.dispose();
14033
14276
  },
@@ -14056,24 +14299,24 @@ function SequenceHotspot({
14056
14299
  const texture = useSpriteTexture({ id, src, onError, onLoadProgress });
14057
14300
  const frameCount = resolveFrameCount(requestedFrameCount);
14058
14301
  const safeFps = resolveFps(fps);
14059
- const [frameIndex, setFrameIndex] = (0, import_react47.useState)(0);
14060
- const elapsedRef = (0, import_react47.useRef)(0);
14061
- const frameIndexRef = (0, import_react47.useRef)(0);
14062
- const endedRef = (0, import_react47.useRef)(false);
14063
- const lastPlaybackStateRef = (0, import_react47.useRef)(null);
14064
- const onFrameChangeRef = (0, import_react47.useRef)(onFrameChange);
14065
- const onPlaybackStateChangeRef = (0, import_react47.useRef)(onPlaybackStateChange);
14066
- const onEndedRef = (0, import_react47.useRef)(onEnded);
14302
+ const [frameIndex, setFrameIndex] = (0, import_react48.useState)(0);
14303
+ const elapsedRef = (0, import_react48.useRef)(0);
14304
+ const frameIndexRef = (0, import_react48.useRef)(0);
14305
+ const endedRef = (0, import_react48.useRef)(false);
14306
+ const lastPlaybackStateRef = (0, import_react48.useRef)(null);
14307
+ const onFrameChangeRef = (0, import_react48.useRef)(onFrameChange);
14308
+ const onPlaybackStateChangeRef = (0, import_react48.useRef)(onPlaybackStateChange);
14309
+ const onEndedRef = (0, import_react48.useRef)(onEnded);
14067
14310
  onFrameChangeRef.current = onFrameChange;
14068
14311
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14069
14312
  onEndedRef.current = onEnded;
14070
- (0, import_react47.useEffect)(() => {
14313
+ (0, import_react48.useEffect)(() => {
14071
14314
  elapsedRef.current = 0;
14072
14315
  frameIndexRef.current = 0;
14073
14316
  endedRef.current = false;
14074
14317
  setFrameIndex(0);
14075
14318
  }, [frameCount, frameDirection, src]);
14076
- (0, import_react47.useEffect)(() => {
14319
+ (0, import_react48.useEffect)(() => {
14077
14320
  if (!texture) {
14078
14321
  return;
14079
14322
  }
@@ -14087,13 +14330,13 @@ function SequenceHotspot({
14087
14330
  );
14088
14331
  texture.needsUpdate = true;
14089
14332
  }, [frameCount, frameDirection, frameIndex, texture]);
14090
- (0, import_react47.useEffect)(() => {
14333
+ (0, import_react48.useEffect)(() => {
14091
14334
  if (!texture) {
14092
14335
  return;
14093
14336
  }
14094
14337
  onFrameChangeRef.current?.({ id, index: 0, src });
14095
14338
  }, [id, src, texture]);
14096
- (0, import_react47.useEffect)(() => {
14339
+ (0, import_react48.useEffect)(() => {
14097
14340
  if (!texture) {
14098
14341
  return;
14099
14342
  }
@@ -14147,7 +14390,7 @@ function SequenceHotspot({
14147
14390
 
14148
14391
  // src/hotspot/video-hotspot.tsx
14149
14392
  var import_fiber24 = require("@react-three/fiber");
14150
- var import_react48 = require("react");
14393
+ var import_react49 = require("react");
14151
14394
  var import_three30 = require("three");
14152
14395
 
14153
14396
  // src/video/inline-playback.ts
@@ -14210,10 +14453,10 @@ function clampVolume2(volume) {
14210
14453
  }
14211
14454
  function usePosterTexture(id, poster, onError) {
14212
14455
  const gl = (0, import_fiber24.useThree)((state) => state.gl);
14213
- const [texture, setTexture] = (0, import_react48.useState)(null);
14214
- const onErrorRef = (0, import_react48.useRef)(onError);
14456
+ const [texture, setTexture] = (0, import_react49.useState)(null);
14457
+ const onErrorRef = (0, import_react49.useRef)(onError);
14215
14458
  onErrorRef.current = onError;
14216
- (0, import_react48.useEffect)(() => {
14459
+ (0, import_react49.useEffect)(() => {
14217
14460
  let active = true;
14218
14461
  setTexture(null);
14219
14462
  if (!poster) {
@@ -14250,7 +14493,7 @@ function usePosterTexture(id, poster, onError) {
14250
14493
  active = false;
14251
14494
  };
14252
14495
  }, [gl, id, poster]);
14253
- (0, import_react48.useEffect)(
14496
+ (0, import_react49.useEffect)(
14254
14497
  () => () => {
14255
14498
  texture?.dispose();
14256
14499
  },
@@ -14269,14 +14512,14 @@ function useVideoResource({
14269
14512
  src
14270
14513
  }) {
14271
14514
  const gl = (0, import_fiber24.useThree)((state) => state.gl);
14272
- const [resource, setResource] = (0, import_react48.useState)(null);
14273
- const onEndedRef = (0, import_react48.useRef)(onEnded);
14274
- const onErrorRef = (0, import_react48.useRef)(onError);
14275
- const onPlaybackStateChangeRef = (0, import_react48.useRef)(onPlaybackStateChange);
14515
+ const [resource, setResource] = (0, import_react49.useState)(null);
14516
+ const onEndedRef = (0, import_react49.useRef)(onEnded);
14517
+ const onErrorRef = (0, import_react49.useRef)(onError);
14518
+ const onPlaybackStateChangeRef = (0, import_react49.useRef)(onPlaybackStateChange);
14276
14519
  onEndedRef.current = onEnded;
14277
14520
  onErrorRef.current = onError;
14278
14521
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14279
- (0, import_react48.useEffect)(() => {
14522
+ (0, import_react49.useEffect)(() => {
14280
14523
  if (!src) {
14281
14524
  setResource(null);
14282
14525
  return;
@@ -14346,7 +14589,7 @@ function useVideoResource({
14346
14589
  texture.dispose();
14347
14590
  };
14348
14591
  }, [crossOrigin, gl, id, playsInline, poster, src]);
14349
- (0, import_react48.useLayoutEffect)(() => {
14592
+ (0, import_react49.useLayoutEffect)(() => {
14350
14593
  if (!resource) {
14351
14594
  return;
14352
14595
  }
@@ -14389,11 +14632,11 @@ function VideoHotspot({
14389
14632
  src
14390
14633
  });
14391
14634
  const posterTexture = usePosterTexture(id, poster, onError);
14392
- const onPlaybackErrorRef = (0, import_react48.useRef)(onPlaybackError);
14393
- const onPlaybackStateChangeRef = (0, import_react48.useRef)(onPlaybackStateChange);
14635
+ const onPlaybackErrorRef = (0, import_react49.useRef)(onPlaybackError);
14636
+ const onPlaybackStateChangeRef = (0, import_react49.useRef)(onPlaybackStateChange);
14394
14637
  onPlaybackErrorRef.current = onPlaybackError;
14395
14638
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14396
- (0, import_react48.useLayoutEffect)(() => {
14639
+ (0, import_react49.useLayoutEffect)(() => {
14397
14640
  if (!resource) {
14398
14641
  return;
14399
14642
  }
@@ -14403,7 +14646,7 @@ function VideoHotspot({
14403
14646
  applyInlineVideoPlayback(resource.video, playsInline);
14404
14647
  resource.video.preload = preload;
14405
14648
  }, [loop, muted, playsInline, preload, resource, volume]);
14406
- (0, import_react48.useEffect)(() => {
14649
+ (0, import_react49.useEffect)(() => {
14407
14650
  if (!resource) {
14408
14651
  return;
14409
14652
  }
@@ -14431,7 +14674,7 @@ function VideoHotspot({
14431
14674
 
14432
14675
  // src/hotspot/audio-hotspot.tsx
14433
14676
  var import_fiber25 = require("@react-three/fiber");
14434
- var import_react49 = require("react");
14677
+ var import_react50 = require("react");
14435
14678
  var import_three32 = require("three");
14436
14679
 
14437
14680
  // src/hotspot/audio-spatial.ts
@@ -14543,8 +14786,8 @@ function createSpeakerTexture(state) {
14543
14786
  return texture;
14544
14787
  }
14545
14788
  function useSpeakerTextures(enabled) {
14546
- const [textures, setTextures] = (0, import_react49.useState)({ playing: null, stopped: null });
14547
- (0, import_react49.useEffect)(() => {
14789
+ const [textures, setTextures] = (0, import_react50.useState)({ playing: null, stopped: null });
14790
+ (0, import_react50.useEffect)(() => {
14548
14791
  if (!enabled) {
14549
14792
  setTextures({ playing: null, stopped: null });
14550
14793
  return;
@@ -14561,10 +14804,10 @@ function useSpeakerTextures(enabled) {
14561
14804
  }
14562
14805
  function useIconTexture(icon, enabled, onError, id) {
14563
14806
  const gl = (0, import_fiber25.useThree)((state) => state.gl);
14564
- const [texture, setTexture] = (0, import_react49.useState)(null);
14565
- const onErrorRef = (0, import_react49.useRef)(onError);
14807
+ const [texture, setTexture] = (0, import_react50.useState)(null);
14808
+ const onErrorRef = (0, import_react50.useRef)(onError);
14566
14809
  onErrorRef.current = onError;
14567
- (0, import_react49.useEffect)(() => {
14810
+ (0, import_react50.useEffect)(() => {
14568
14811
  let active = true;
14569
14812
  setTexture(null);
14570
14813
  if (!enabled || !icon) {
@@ -14601,7 +14844,7 @@ function useIconTexture(icon, enabled, onError, id) {
14601
14844
  active = false;
14602
14845
  };
14603
14846
  }, [enabled, gl, icon, id]);
14604
- (0, import_react49.useEffect)(
14847
+ (0, import_react50.useEffect)(
14605
14848
  () => () => {
14606
14849
  texture?.dispose();
14607
14850
  },
@@ -14621,13 +14864,13 @@ function useAudioHowl({
14621
14864
  src,
14622
14865
  format
14623
14866
  }) {
14624
- const [howl, setHowl] = (0, import_react49.useState)(null);
14625
- const playingRef = (0, import_react49.useRef)(playing);
14626
- const loopRef = (0, import_react49.useRef)(loop ?? false);
14627
- const onEndedRef = (0, import_react49.useRef)(onEnded);
14628
- const onErrorRef = (0, import_react49.useRef)(onError);
14629
- const onPlaybackErrorRef = (0, import_react49.useRef)(onPlaybackError);
14630
- const onPlaybackStateChangeRef = (0, import_react49.useRef)(onPlaybackStateChange);
14867
+ const [howl, setHowl] = (0, import_react50.useState)(null);
14868
+ const playingRef = (0, import_react50.useRef)(playing);
14869
+ const loopRef = (0, import_react50.useRef)(loop ?? false);
14870
+ const onEndedRef = (0, import_react50.useRef)(onEnded);
14871
+ const onErrorRef = (0, import_react50.useRef)(onError);
14872
+ const onPlaybackErrorRef = (0, import_react50.useRef)(onPlaybackError);
14873
+ const onPlaybackStateChangeRef = (0, import_react50.useRef)(onPlaybackStateChange);
14631
14874
  playingRef.current = playing;
14632
14875
  loopRef.current = loop ?? false;
14633
14876
  onEndedRef.current = onEnded;
@@ -14636,7 +14879,7 @@ function useAudioHowl({
14636
14879
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14637
14880
  const sourcesKey = sourceList(src).join("\0");
14638
14881
  const formatsKey = sourceList(format).join("\0");
14639
- (0, import_react49.useEffect)(() => {
14882
+ (0, import_react50.useEffect)(() => {
14640
14883
  const sources = sourcesKey.length > 0 ? sourcesKey.split("\0") : [];
14641
14884
  if (sources.length === 0) {
14642
14885
  setHowl(null);
@@ -14674,7 +14917,7 @@ function useAudioHowl({
14674
14917
  handle.dispose();
14675
14918
  };
14676
14919
  }, [crossOrigin, id, sourcesKey, formatsKey]);
14677
- (0, import_react49.useEffect)(() => {
14920
+ (0, import_react50.useEffect)(() => {
14678
14921
  howl?.loop(loop ?? false);
14679
14922
  }, [howl, loop]);
14680
14923
  return howl;
@@ -14720,15 +14963,15 @@ function AudioHotspot({
14720
14963
  const stoppedIconTexture = useIconTexture(icon, marker, onError, id);
14721
14964
  const playingIconTexture = useIconTexture(playingIcon, marker, onError, id);
14722
14965
  const markerTexture = playing ? playingIconTexture ?? stoppedIconTexture ?? speakerTextures.playing : stoppedIconTexture ?? speakerTextures.stopped;
14723
- const playingRef = (0, import_react49.useRef)(playing);
14966
+ const playingRef = (0, import_react50.useRef)(playing);
14724
14967
  playingRef.current = playing;
14725
- const mutedRef = (0, import_react49.useRef)(muted);
14726
- const volumeRef = (0, import_react49.useRef)(volume);
14727
- const rangeRef = (0, import_react49.useRef)(range);
14968
+ const mutedRef = (0, import_react50.useRef)(muted);
14969
+ const volumeRef = (0, import_react50.useRef)(volume);
14970
+ const rangeRef = (0, import_react50.useRef)(range);
14728
14971
  mutedRef.current = muted;
14729
14972
  volumeRef.current = volume;
14730
14973
  rangeRef.current = range;
14731
- (0, import_react49.useEffect)(() => {
14974
+ (0, import_react50.useEffect)(() => {
14732
14975
  if (!howl) {
14733
14976
  return;
14734
14977
  }
@@ -14738,7 +14981,7 @@ function AudioHotspot({
14738
14981
  }
14739
14982
  howl.play();
14740
14983
  }, [howl, playing]);
14741
- (0, import_react49.useEffect)(() => {
14984
+ (0, import_react50.useEffect)(() => {
14742
14985
  if (!howl || !pauseWhenHidden) {
14743
14986
  return;
14744
14987
  }
@@ -14773,7 +15016,7 @@ function AudioHotspot({
14773
15016
  }
14774
15017
 
14775
15018
  // src/hotspot/text-hotspot.tsx
14776
- var import_react50 = require("react");
15019
+ var import_react51 = require("react");
14777
15020
  var import_fiber26 = require("@react-three/fiber");
14778
15021
  var import_three33 = require("three");
14779
15022
  var import_jsx_runtime34 = require("react/jsx-runtime");
@@ -15072,7 +15315,7 @@ function useRasterLongSide(width, height, scale, scaleMode, referenceFov) {
15072
15315
  Math.max(layout.canvasWidth, layout.canvasHeight)
15073
15316
  );
15074
15317
  };
15075
- const [rasterLong, setRasterLong] = (0, import_react50.useState)(measure);
15318
+ const [rasterLong, setRasterLong] = (0, import_react51.useState)(measure);
15076
15319
  const measured = measure();
15077
15320
  if (measured !== rasterLong) {
15078
15321
  setRasterLong(measured);
@@ -15085,12 +15328,12 @@ function useRasterLongSide(width, height, scale, scaleMode, referenceFov) {
15085
15328
  }
15086
15329
  function useTextTexture(text, width, height, style, scale, scaleMode, referenceFov, onLoad, onError) {
15087
15330
  const rasterLong = useRasterLongSide(width, height, scale, scaleMode, referenceFov);
15088
- const [texture, setTexture] = (0, import_react50.useState)(null);
15089
- const onLoadRef = (0, import_react50.useRef)(onLoad);
15090
- const onErrorRef = (0, import_react50.useRef)(onError);
15331
+ const [texture, setTexture] = (0, import_react51.useState)(null);
15332
+ const onLoadRef = (0, import_react51.useRef)(onLoad);
15333
+ const onErrorRef = (0, import_react51.useRef)(onError);
15091
15334
  onLoadRef.current = onLoad;
15092
15335
  onErrorRef.current = onError;
15093
- (0, import_react50.useEffect)(() => {
15336
+ (0, import_react51.useEffect)(() => {
15094
15337
  let cancelled = false;
15095
15338
  let nextTexture = null;
15096
15339
  const run = async () => {
@@ -15316,7 +15559,7 @@ function IframeHotspot({
15316
15559
  }
15317
15560
 
15318
15561
  // src/hotspot/polygon-hotspot.tsx
15319
- var import_react51 = require("react");
15562
+ var import_react52 = require("react");
15320
15563
  var import_three35 = require("three");
15321
15564
  var import_jsx_runtime36 = require("react/jsx-runtime");
15322
15565
  var POLYGON_RADIUS = 990;
@@ -15574,24 +15817,24 @@ function PolygonHotspot({
15574
15817
  onInvalid,
15575
15818
  ...anchorProps
15576
15819
  }) {
15577
- const issues = (0, import_react51.useMemo)(() => validatePolygonVertices(vertices), [vertices]);
15820
+ const issues = (0, import_react52.useMemo)(() => validatePolygonVertices(vertices), [vertices]);
15578
15821
  const valid = issues.length === 0;
15579
- const center = (0, import_react51.useMemo)(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
15580
- const fillGeometry = (0, import_react51.useMemo)(
15822
+ const center = (0, import_react52.useMemo)(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
15823
+ const fillGeometry = (0, import_react52.useMemo)(
15581
15824
  () => valid && center ? makeFillGeometry(vertices, center) : null,
15582
15825
  [center, valid, vertices]
15583
15826
  );
15584
- const strokePoints = (0, import_react51.useMemo)(
15827
+ const strokePoints = (0, import_react52.useMemo)(
15585
15828
  () => valid && center ? makeStrokePositions(vertices, center) : null,
15586
15829
  [center, valid, vertices]
15587
15830
  );
15588
15831
  const resolvedStrokeOpacity = Math.max(0, Math.min(strokeOpacity, 1));
15589
- const startVerticesRef = (0, import_react51.useRef)(null);
15832
+ const startVerticesRef = (0, import_react52.useRef)(null);
15590
15833
  const issuesKey = issues.map((issue) => issue.code).join(",");
15591
- (0, import_react51.useEffect)(() => {
15834
+ (0, import_react52.useEffect)(() => {
15592
15835
  if (!valid) onInvalid?.(issues);
15593
15836
  }, [issues, issuesKey, onInvalid, valid]);
15594
- (0, import_react51.useEffect)(() => () => fillGeometry?.dispose(), [fillGeometry]);
15837
+ (0, import_react52.useEffect)(() => () => fillGeometry?.dispose(), [fillGeometry]);
15595
15838
  if (!valid || !center || !fillGeometry || !strokePoints || strokePoints.length < 2) {
15596
15839
  return null;
15597
15840
  }
@@ -15671,7 +15914,7 @@ function PolygonHotspot({
15671
15914
  }
15672
15915
 
15673
15916
  // src/hotspot/polyline-hotspot.tsx
15674
- var import_react52 = require("react");
15917
+ var import_react53 = require("react");
15675
15918
  var import_three36 = require("three");
15676
15919
  var import_jsx_runtime37 = require("react/jsx-runtime");
15677
15920
  var POLYLINE_RADIUS = 990;
@@ -15790,16 +16033,16 @@ function PolylineHotspot({
15790
16033
  onInvalid,
15791
16034
  ...anchorProps
15792
16035
  }) {
15793
- const issues = (0, import_react52.useMemo)(() => validatePolylineVertices(vertices), [vertices]);
16036
+ const issues = (0, import_react53.useMemo)(() => validatePolylineVertices(vertices), [vertices]);
15794
16037
  const valid = issues.length === 0;
15795
- const center = (0, import_react52.useMemo)(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
15796
- const linePoints = (0, import_react52.useMemo)(
16038
+ const center = (0, import_react53.useMemo)(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
16039
+ const linePoints = (0, import_react53.useMemo)(
15797
16040
  () => valid && center ? makeLinePositions(vertices, center) : null,
15798
16041
  [center, valid, vertices]
15799
16042
  );
15800
16043
  const opacity = Math.max(0, Math.min(strokeOpacity, 1));
15801
- const startVerticesRef = (0, import_react52.useRef)(null);
15802
- (0, import_react52.useEffect)(() => {
16044
+ const startVerticesRef = (0, import_react53.useRef)(null);
16045
+ (0, import_react53.useEffect)(() => {
15803
16046
  if (!valid) onInvalid?.(issues);
15804
16047
  }, [issues, onInvalid, valid]);
15805
16048
  if (!valid || !center || !linePoints || linePoints.length < 2) return null;
@@ -15992,7 +16235,7 @@ function ProjectHotspot({
15992
16235
  renderHotspot,
15993
16236
  execute
15994
16237
  }) {
15995
- const [playing, setPlaying] = (0, import_react53.useState)(
16238
+ const [playing, setPlaying] = (0, import_react54.useState)(
15996
16239
  "playing" in hotspot ? hotspot.playing ?? false : false
15997
16240
  );
15998
16241
  const togglePlayback = () => setPlaying((value) => !value);
@@ -16048,21 +16291,21 @@ function ProjectScenes({
16048
16291
  renderHotspot,
16049
16292
  ...options
16050
16293
  }) {
16051
- const controls = (0, import_react53.useContext)(PanoramaViewContext);
16294
+ const controls = (0, import_react54.useContext)(PanoramaViewContext);
16052
16295
  if (!controls)
16053
16296
  throw new Error("<ProjectScenes> must be rendered inside <PanoViewer>.");
16054
- const [selection, setSelection] = (0, import_react53.useState)({
16297
+ const [selection, setSelection] = (0, import_react54.useState)({
16055
16298
  project,
16056
16299
  id: project.initialSceneId
16057
16300
  });
16058
16301
  if (selection.project !== project)
16059
16302
  setSelection({ project, id: project.initialSceneId });
16060
16303
  const currentId = activeSceneId ?? (selection.project === project ? selection.id : project.initialSceneId);
16061
- const entries = (0, import_react53.useMemo)(
16304
+ const entries = (0, import_react54.useMemo)(
16062
16305
  () => new Map(project.scenes.map((entry) => [entry.scene.id, entry])),
16063
16306
  [project]
16064
16307
  );
16065
- const scenes = (0, import_react53.useMemo)(
16308
+ const scenes = (0, import_react54.useMemo)(
16066
16309
  () => project.scenes.map((entry) => entry.scene),
16067
16310
  [project]
16068
16311
  );
@@ -16115,7 +16358,7 @@ function ProjectScenes({
16115
16358
  }
16116
16359
 
16117
16360
  // src/background-audio.tsx
16118
- var import_react54 = require("react");
16361
+ var import_react55 = require("react");
16119
16362
  var DEFAULT_FADE_MS = 400;
16120
16363
  function resolveFadeMs(value) {
16121
16364
  if (!Number.isFinite(value)) {
@@ -16157,31 +16400,31 @@ function BackgroundAudio({
16157
16400
  onEnded,
16158
16401
  onError
16159
16402
  }) {
16160
- const controlsRef = (0, import_react54.useContext)(PanoramaViewContext);
16403
+ const controlsRef = (0, import_react55.useContext)(PanoramaViewContext);
16161
16404
  if (!controlsRef) {
16162
16405
  throw new Error("<BackgroundAudio> must be rendered inside <PanoViewer>.");
16163
16406
  }
16164
16407
  const isControlled = controlledPlaying !== void 0;
16165
- const [internalPlaying, setInternalPlaying] = (0, import_react54.useState)(defaultPlaying);
16408
+ const [internalPlaying, setInternalPlaying] = (0, import_react55.useState)(defaultPlaying);
16166
16409
  const playing = isControlled ? controlledPlaying : internalPlaying;
16167
16410
  const urls = resolveBackgroundAudioUrls(src, sources, sceneId);
16168
16411
  usePanoMediaActivationIntent(playing && urls.length > 0);
16169
16412
  const trackKey = urls.join("\0");
16170
- const currentRef = (0, import_react54.useRef)(null);
16171
- const outgoingRef = (0, import_react54.useRef)([]);
16172
- const fadingInRef = (0, import_react54.useRef)(false);
16173
- const isFirstTrackRef = (0, import_react54.useRef)(true);
16174
- const playingRef = (0, import_react54.useRef)(playing);
16175
- const loopRef = (0, import_react54.useRef)(loop);
16176
- const mutedRef = (0, import_react54.useRef)(muted);
16177
- const volumeRef = (0, import_react54.useRef)(volume);
16178
- const mutedPropRef = (0, import_react54.useRef)(muted);
16179
- const volumePropRef = (0, import_react54.useRef)(volume);
16180
- const fadeMsRef = (0, import_react54.useRef)(resolveFadeMs(fadeMs));
16181
- const onPlaybackStateChangeRef = (0, import_react54.useRef)(onPlaybackStateChange);
16182
- const onPlaybackErrorRef = (0, import_react54.useRef)(onPlaybackError);
16183
- const onEndedRef = (0, import_react54.useRef)(onEnded);
16184
- const onErrorRef = (0, import_react54.useRef)(onError);
16413
+ const currentRef = (0, import_react55.useRef)(null);
16414
+ const outgoingRef = (0, import_react55.useRef)([]);
16415
+ const fadingInRef = (0, import_react55.useRef)(false);
16416
+ const isFirstTrackRef = (0, import_react55.useRef)(true);
16417
+ const playingRef = (0, import_react55.useRef)(playing);
16418
+ const loopRef = (0, import_react55.useRef)(loop);
16419
+ const mutedRef = (0, import_react55.useRef)(muted);
16420
+ const volumeRef = (0, import_react55.useRef)(volume);
16421
+ const mutedPropRef = (0, import_react55.useRef)(muted);
16422
+ const volumePropRef = (0, import_react55.useRef)(volume);
16423
+ const fadeMsRef = (0, import_react55.useRef)(resolveFadeMs(fadeMs));
16424
+ const onPlaybackStateChangeRef = (0, import_react55.useRef)(onPlaybackStateChange);
16425
+ const onPlaybackErrorRef = (0, import_react55.useRef)(onPlaybackError);
16426
+ const onEndedRef = (0, import_react55.useRef)(onEnded);
16427
+ const onErrorRef = (0, import_react55.useRef)(onError);
16185
16428
  playingRef.current = playing;
16186
16429
  loopRef.current = loop;
16187
16430
  fadeMsRef.current = resolveFadeMs(fadeMs);
@@ -16197,7 +16440,7 @@ function BackgroundAudio({
16197
16440
  onPlaybackErrorRef.current = onPlaybackError;
16198
16441
  onEndedRef.current = onEnded;
16199
16442
  onErrorRef.current = onError;
16200
- const snapshotRef = (0, import_react54.useRef)({
16443
+ const snapshotRef = (0, import_react55.useRef)({
16201
16444
  ready: false,
16202
16445
  playing: false,
16203
16446
  blocked: false,
@@ -16205,7 +16448,7 @@ function BackgroundAudio({
16205
16448
  muted,
16206
16449
  volume
16207
16450
  });
16208
- const snapshotListenersRef = (0, import_react54.useRef)(/* @__PURE__ */ new Set());
16451
+ const snapshotListenersRef = (0, import_react55.useRef)(/* @__PURE__ */ new Set());
16209
16452
  const notifySnapshot = (patch) => {
16210
16453
  const prev = snapshotRef.current;
16211
16454
  snapshotRef.current = { ...prev, ...patch };
@@ -16213,9 +16456,9 @@ function BackgroundAudio({
16213
16456
  listener();
16214
16457
  }
16215
16458
  };
16216
- const isControlledRef = (0, import_react54.useRef)(isControlled);
16459
+ const isControlledRef = (0, import_react55.useRef)(isControlled);
16217
16460
  isControlledRef.current = isControlled;
16218
- const controller = (0, import_react54.useMemo)(
16461
+ const controller = (0, import_react55.useMemo)(
16219
16462
  () => ({
16220
16463
  subscribe: (onStoreChange) => {
16221
16464
  snapshotListenersRef.current.add(onStoreChange);
@@ -16273,8 +16516,8 @@ function BackgroundAudio({
16273
16516
  }),
16274
16517
  []
16275
16518
  );
16276
- const bgmHost = (0, import_react54.useContext)(BackgroundAudioHostContext);
16277
- (0, import_react54.useEffect)(() => {
16519
+ const bgmHost = (0, import_react55.useContext)(BackgroundAudioHostContext);
16520
+ (0, import_react55.useEffect)(() => {
16278
16521
  if (!bgmHost) {
16279
16522
  return;
16280
16523
  }
@@ -16287,7 +16530,7 @@ function BackgroundAudio({
16287
16530
  }
16288
16531
  };
16289
16532
  }, [bgmHost, controller]);
16290
- (0, import_react54.useEffect)(() => {
16533
+ (0, import_react55.useEffect)(() => {
16291
16534
  return () => {
16292
16535
  fadingInRef.current = false;
16293
16536
  isFirstTrackRef.current = true;
@@ -16299,7 +16542,7 @@ function BackgroundAudio({
16299
16542
  outgoingRef.current = [];
16300
16543
  };
16301
16544
  }, []);
16302
- (0, import_react54.useEffect)(() => {
16545
+ (0, import_react55.useEffect)(() => {
16303
16546
  const previous = currentRef.current;
16304
16547
  if (previous?.key === trackKey && previous.crossOrigin === crossOrigin) {
16305
16548
  return;
@@ -16402,7 +16645,7 @@ function BackgroundAudio({
16402
16645
  retire(previous);
16403
16646
  }
16404
16647
  }, [crossOrigin, trackKey]);
16405
- (0, import_react54.useEffect)(() => {
16648
+ (0, import_react55.useEffect)(() => {
16406
16649
  const howl = currentRef.current?.handle.howl;
16407
16650
  if (!howl) {
16408
16651
  return;
@@ -16422,7 +16665,7 @@ function BackgroundAudio({
16422
16665
  howl.play();
16423
16666
  }
16424
16667
  }, [playing, trackKey]);
16425
- (0, import_react54.useEffect)(() => {
16668
+ (0, import_react55.useEffect)(() => {
16426
16669
  const slot = currentRef.current;
16427
16670
  if (!slot) {
16428
16671
  return;
@@ -16437,7 +16680,7 @@ function BackgroundAudio({
16437
16680
  volume: clampAudioVolume(volumeRef.current)
16438
16681
  });
16439
16682
  }, [loop, muted, trackKey, volume]);
16440
- (0, import_react54.useEffect)(() => {
16683
+ (0, import_react55.useEffect)(() => {
16441
16684
  if (!pauseWhenHidden) {
16442
16685
  return;
16443
16686
  }
@@ -16463,7 +16706,7 @@ function BackgroundAudio({
16463
16706
  }
16464
16707
 
16465
16708
  // src/pano-filter/pano-filter.tsx
16466
- var import_react55 = require("react");
16709
+ var import_react56 = require("react");
16467
16710
  function PanoFilter({
16468
16711
  preset = "none",
16469
16712
  intensity,
@@ -16471,11 +16714,11 @@ function PanoFilter({
16471
16714
  colorMatrix,
16472
16715
  colorMatrixScope
16473
16716
  }) {
16474
- const host = (0, import_react55.useContext)(PanoFilterHostContext);
16717
+ const host = (0, import_react56.useContext)(PanoFilterHostContext);
16475
16718
  if (!host) {
16476
16719
  throw new Error("<PanoFilter> must be rendered inside <PanoViewer>.");
16477
16720
  }
16478
- const snapshot = (0, import_react55.useMemo)(
16721
+ const snapshot = (0, import_react56.useMemo)(
16479
16722
  () => resolvePanoFilterSnapshot({
16480
16723
  preset,
16481
16724
  intensity,
@@ -16485,13 +16728,13 @@ function PanoFilter({
16485
16728
  }),
16486
16729
  [enabled, intensity, preset, colorMatrix, colorMatrixScope]
16487
16730
  );
16488
- (0, import_react55.useEffect)(() => {
16731
+ (0, import_react56.useEffect)(() => {
16489
16732
  claimPanoFilter(host);
16490
16733
  return () => {
16491
16734
  releasePanoFilter(host);
16492
16735
  };
16493
16736
  }, [host]);
16494
- (0, import_react55.useEffect)(() => {
16737
+ (0, import_react56.useEffect)(() => {
16495
16738
  setPanoFilterSnapshot(host, snapshot);
16496
16739
  }, [host, snapshot]);
16497
16740
  return null;
@@ -16499,7 +16742,7 @@ function PanoFilter({
16499
16742
 
16500
16743
  // src/gyro.tsx
16501
16744
  var import_fiber27 = require("@react-three/fiber");
16502
- var import_react56 = require("react");
16745
+ var import_react57 = require("react");
16503
16746
  var import_three37 = require("three");
16504
16747
  var deviceOrientationPermissionState = "unknown";
16505
16748
  function isMobileDevice() {
@@ -16517,7 +16760,7 @@ function resolveFriction(value) {
16517
16760
  function resolveSoftstart(value) {
16518
16761
  return Math.max(finiteOr(value, 0.5), 0);
16519
16762
  }
16520
- var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16763
+ var Gyro = (0, import_react57.forwardRef)(function Gyro2({
16521
16764
  enabled = false,
16522
16765
  camroll = true,
16523
16766
  absolute = false,
@@ -16532,35 +16775,35 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16532
16775
  onDisable,
16533
16776
  onDenied
16534
16777
  }, ref) {
16535
- const controlsRef = (0, import_react56.useContext)(PanoramaViewContext);
16536
- const eventBus = (0, import_react56.useContext)(PanoEventBusContext);
16778
+ const controlsRef = (0, import_react57.useContext)(PanoramaViewContext);
16779
+ const eventBus = (0, import_react57.useContext)(PanoEventBusContext);
16537
16780
  const { gl } = (0, import_fiber27.useThree)();
16538
- const [internallyDisabled, setInternallyDisabled] = (0, import_react56.useState)(false);
16539
- const [available, setAvailableState] = (0, import_react56.useState)(false);
16540
- const availableRef = (0, import_react56.useRef)(false);
16541
- const availabilityReportedRef = (0, import_react56.useRef)(null);
16542
- const activeRef = (0, import_react56.useRef)(false);
16543
- const permissionRef = (0, import_react56.useRef)(deviceOrientationPermissionState);
16544
- const absoluteEventSourceRef = (0, import_react56.useRef)(null);
16545
- const rawQuaternionRef = (0, import_react56.useRef)(new import_three37.Quaternion());
16546
- const hasRawQuaternionRef = (0, import_react56.useRef)(false);
16547
- const calibrationRef = (0, import_react56.useRef)(new import_three37.Quaternion());
16548
- const hasCalibrationRef = (0, import_react56.useRef)(false);
16549
- const calibratedQuaternionRef = (0, import_react56.useRef)(new import_three37.Quaternion());
16550
- const absoluteOffsetRef = (0, import_react56.useRef)({ yaw: 0, pitch: 0 });
16551
- const screenAngleRef = (0, import_react56.useRef)(getScreenOrientationAngle());
16552
- const startPoseRef = (0, import_react56.useRef)(null);
16553
- const enabledAtRef = (0, import_react56.useRef)(0);
16554
- const filteredPoseRef = (0, import_react56.useRef)(null);
16555
- const lastAppliedPoseRef = (0, import_react56.useRef)(null);
16556
- const lastSamplePoseRef = (0, import_react56.useRef)(null);
16557
- const lastSampleAtRef = (0, import_react56.useRef)(null);
16558
- const previousYawRef = (0, import_react56.useRef)(void 0);
16559
- const absoluteRef = (0, import_react56.useRef)(absolute);
16560
- const northRef = (0, import_react56.useRef)(north);
16781
+ const [internallyDisabled, setInternallyDisabled] = (0, import_react57.useState)(false);
16782
+ const [available, setAvailableState] = (0, import_react57.useState)(false);
16783
+ const availableRef = (0, import_react57.useRef)(false);
16784
+ const availabilityReportedRef = (0, import_react57.useRef)(null);
16785
+ const activeRef = (0, import_react57.useRef)(false);
16786
+ const permissionRef = (0, import_react57.useRef)(deviceOrientationPermissionState);
16787
+ const absoluteEventSourceRef = (0, import_react57.useRef)(null);
16788
+ const rawQuaternionRef = (0, import_react57.useRef)(new import_three37.Quaternion());
16789
+ const hasRawQuaternionRef = (0, import_react57.useRef)(false);
16790
+ const calibrationRef = (0, import_react57.useRef)(new import_three37.Quaternion());
16791
+ const hasCalibrationRef = (0, import_react57.useRef)(false);
16792
+ const calibratedQuaternionRef = (0, import_react57.useRef)(new import_three37.Quaternion());
16793
+ const absoluteOffsetRef = (0, import_react57.useRef)({ yaw: 0, pitch: 0 });
16794
+ const screenAngleRef = (0, import_react57.useRef)(getScreenOrientationAngle());
16795
+ const startPoseRef = (0, import_react57.useRef)(null);
16796
+ const enabledAtRef = (0, import_react57.useRef)(0);
16797
+ const filteredPoseRef = (0, import_react57.useRef)(null);
16798
+ const lastAppliedPoseRef = (0, import_react57.useRef)(null);
16799
+ const lastSamplePoseRef = (0, import_react57.useRef)(null);
16800
+ const lastSampleAtRef = (0, import_react57.useRef)(null);
16801
+ const previousYawRef = (0, import_react57.useRef)(void 0);
16802
+ const absoluteRef = (0, import_react57.useRef)(absolute);
16803
+ const northRef = (0, import_react57.useRef)(north);
16561
16804
  absoluteRef.current = absolute;
16562
16805
  northRef.current = north;
16563
- const callbacksRef = (0, import_react56.useRef)({
16806
+ const callbacksRef = (0, import_react57.useRef)({
16564
16807
  onAvailable,
16565
16808
  onUnavailable,
16566
16809
  onEnable,
@@ -16577,7 +16820,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16577
16820
  if (!controlsRef) {
16578
16821
  throw new Error("<Gyro> must be rendered inside <PanoViewer>.");
16579
16822
  }
16580
- const emit = (0, import_react56.useCallback)(
16823
+ const emit = (0, import_react57.useCallback)(
16581
16824
  (type) => {
16582
16825
  eventBus?.emit(type, void 0);
16583
16826
  switch (type) {
@@ -16604,7 +16847,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16604
16847
  },
16605
16848
  [eventBus]
16606
16849
  );
16607
- const setAvailable = (0, import_react56.useCallback)(
16850
+ const setAvailable = (0, import_react57.useCallback)(
16608
16851
  (nextAvailable) => {
16609
16852
  availableRef.current = nextAvailable;
16610
16853
  setAvailableState(nextAvailable);
@@ -16616,7 +16859,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16616
16859
  },
16617
16860
  [emit]
16618
16861
  );
16619
- const requestPermission = (0, import_react56.useCallback)(async () => {
16862
+ const requestPermission = (0, import_react57.useCallback)(async () => {
16620
16863
  if (!availableRef.current) {
16621
16864
  return false;
16622
16865
  }
@@ -16649,7 +16892,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16649
16892
  emit("gyrodenied");
16650
16893
  return false;
16651
16894
  }, [emit, setAvailable]);
16652
- const deactivate = (0, import_react56.useCallback)(() => {
16895
+ const deactivate = (0, import_react57.useCallback)(() => {
16653
16896
  if (activeRef.current) {
16654
16897
  activeRef.current = false;
16655
16898
  controlsRef.current?.setGyroActive(false);
@@ -16664,7 +16907,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16664
16907
  lastSampleAtRef.current = null;
16665
16908
  previousYawRef.current = void 0;
16666
16909
  }, [controlsRef, emit]);
16667
- const anchorToView = (0, import_react56.useCallback)(
16910
+ const anchorToView = (0, import_react57.useCallback)(
16668
16911
  (yaw, pitch) => {
16669
16912
  if (!hasRawQuaternionRef.current) {
16670
16913
  return;
@@ -16694,7 +16937,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16694
16937
  },
16695
16938
  []
16696
16939
  );
16697
- const resetSensor = (0, import_react56.useCallback)(
16940
+ const resetSensor = (0, import_react57.useCallback)(
16698
16941
  (yaw, pitch) => {
16699
16942
  if (!hasRawQuaternionRef.current) {
16700
16943
  return;
@@ -16715,7 +16958,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16715
16958
  },
16716
16959
  [anchorToView, controlsRef]
16717
16960
  );
16718
- (0, import_react56.useImperativeHandle)(
16961
+ (0, import_react57.useImperativeHandle)(
16719
16962
  ref,
16720
16963
  () => ({
16721
16964
  resetSensor,
@@ -16725,19 +16968,19 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16725
16968
  }),
16726
16969
  [requestPermission, resetSensor]
16727
16970
  );
16728
- (0, import_react56.useEffect)(() => {
16971
+ (0, import_react57.useEffect)(() => {
16729
16972
  if (!enabled) {
16730
16973
  setInternallyDisabled(false);
16731
16974
  }
16732
16975
  }, [enabled]);
16733
- (0, import_react56.useEffect)(() => {
16976
+ (0, import_react57.useEffect)(() => {
16734
16977
  const supported = typeof window !== "undefined" && window.isSecureContext && typeof DeviceOrientationEvent !== "undefined" && (desktopSupport || isMobileDevice());
16735
16978
  setAvailable(supported);
16736
16979
  }, [desktopSupport, setAvailable]);
16737
- (0, import_react56.useEffect)(() => {
16980
+ (0, import_react57.useEffect)(() => {
16738
16981
  controlsRef.current?.setGyroTouchMode(touchMode);
16739
16982
  }, [controlsRef, touchMode]);
16740
- (0, import_react56.useEffect)(() => {
16983
+ (0, import_react57.useEffect)(() => {
16741
16984
  if (!enabled || internallyDisabled || touchMode !== "disablegyro") {
16742
16985
  return;
16743
16986
  }
@@ -16751,7 +16994,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16751
16994
  gl.domElement.removeEventListener("pointerdown", onPointerDown);
16752
16995
  };
16753
16996
  }, [enabled, gl, internallyDisabled, touchMode]);
16754
- (0, import_react56.useEffect)(() => {
16997
+ (0, import_react57.useEffect)(() => {
16755
16998
  if (!enabled || internallyDisabled || !available) {
16756
16999
  deactivate();
16757
17000
  return;
@@ -16974,7 +17217,7 @@ var Gyro = (0, import_react56.forwardRef)(function Gyro2({
16974
17217
 
16975
17218
  // src/webvr/webvr.tsx
16976
17219
  var import_fiber30 = require("@react-three/fiber");
16977
- var import_react61 = require("react");
17220
+ var import_react62 = require("react");
16978
17221
 
16979
17222
  // src/webvr/detect.ts
16980
17223
  function isMobileVRDevice() {
@@ -16995,6 +17238,184 @@ async function detectWebVRCapabilities() {
16995
17238
  return { webxr, mobile, sensor };
16996
17239
  }
16997
17240
 
17241
+ // src/webvr/dpdb.ts
17242
+ var DEFAULT_WEBVR_DATABASE_URL = "https://storage.googleapis.com/cardboard-dpdb/dpdb.json";
17243
+ var SCREENSIZE_MIN = 3;
17244
+ var SCREENSIZE_MAX = 20;
17245
+ var cache = /* @__PURE__ */ new Map();
17246
+ function normalizeDatabaseUrl(value) {
17247
+ if (value === null || value === "") {
17248
+ return null;
17249
+ }
17250
+ if (value === void 0) {
17251
+ return DEFAULT_WEBVR_DATABASE_URL;
17252
+ }
17253
+ const trimmed = value.trim();
17254
+ return trimmed.length > 0 ? trimmed : null;
17255
+ }
17256
+ function physicalResolution(screenWidth, screenHeight, devicePixelRatio) {
17257
+ const ratio = Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;
17258
+ return [
17259
+ Math.round(Math.max(0, screenWidth) * ratio),
17260
+ Math.round(Math.max(0, screenHeight) * ratio)
17261
+ ];
17262
+ }
17263
+ function resolutionsMatch(rule, physical) {
17264
+ const [rw, rh] = rule;
17265
+ const [pw, ph] = physical;
17266
+ return rw === pw && rh === ph || rw === ph && rh === pw;
17267
+ }
17268
+ function ruleMatches(rule, input, physical) {
17269
+ if (typeof rule.ua === "string") {
17270
+ return input.userAgent.includes(rule.ua);
17271
+ }
17272
+ if (Array.isArray(rule.res) && rule.res.length === 2 && typeof rule.res[0] === "number" && typeof rule.res[1] === "number") {
17273
+ return resolutionsMatch(
17274
+ [rule.res[0], rule.res[1]],
17275
+ physical
17276
+ );
17277
+ }
17278
+ return false;
17279
+ }
17280
+ function deviceMatches(device, input) {
17281
+ const rules = device.rules;
17282
+ if (!Array.isArray(rules) || rules.length === 0) {
17283
+ return false;
17284
+ }
17285
+ const physical = physicalResolution(
17286
+ input.screenWidth,
17287
+ input.screenHeight,
17288
+ input.devicePixelRatio
17289
+ );
17290
+ return rules.some(
17291
+ (rule) => rule !== null && typeof rule === "object" && ruleMatches(rule, input, physical)
17292
+ );
17293
+ }
17294
+ function diagonalInchesFromDpi(dpi, physicalWidth, physicalHeight) {
17295
+ const shortPx = Math.min(physicalWidth, physicalHeight);
17296
+ const longPx = Math.max(physicalWidth, physicalHeight);
17297
+ if (!(shortPx > 0 && longPx > 0)) {
17298
+ return null;
17299
+ }
17300
+ let dpiX;
17301
+ let dpiY;
17302
+ if (typeof dpi === "number") {
17303
+ if (!(Number.isFinite(dpi) && dpi > 0)) {
17304
+ return null;
17305
+ }
17306
+ dpiX = dpi;
17307
+ dpiY = dpi;
17308
+ } else if (Array.isArray(dpi) && dpi.length >= 1) {
17309
+ const first = dpi[0];
17310
+ const second = dpi.length >= 2 ? dpi[1] : first;
17311
+ if (typeof first !== "number" || typeof second !== "number" || !(Number.isFinite(first) && first > 0) || !(Number.isFinite(second) && second > 0)) {
17312
+ return null;
17313
+ }
17314
+ dpiX = first;
17315
+ dpiY = second;
17316
+ } else {
17317
+ return null;
17318
+ }
17319
+ const widthIn = shortPx / dpiX;
17320
+ const heightIn = longPx / dpiY;
17321
+ const diagonal = Math.sqrt(widthIn * widthIn + heightIn * heightIn);
17322
+ if (!Number.isFinite(diagonal) || diagonal < SCREENSIZE_MIN || diagonal > SCREENSIZE_MAX) {
17323
+ return null;
17324
+ }
17325
+ return Math.round(diagonal * 10) / 10;
17326
+ }
17327
+ function matchDpdbDevice(document2, input) {
17328
+ const devices = document2.devices;
17329
+ if (!Array.isArray(devices)) {
17330
+ return null;
17331
+ }
17332
+ const physical = physicalResolution(
17333
+ input.screenWidth,
17334
+ input.screenHeight,
17335
+ input.devicePixelRatio
17336
+ );
17337
+ for (const device of devices) {
17338
+ if (!deviceMatches(device, input)) {
17339
+ continue;
17340
+ }
17341
+ if (device.dpi === void 0) {
17342
+ continue;
17343
+ }
17344
+ const screensizeInches = diagonalInchesFromDpi(
17345
+ device.dpi,
17346
+ physical[0],
17347
+ physical[1]
17348
+ );
17349
+ if (screensizeInches === null) {
17350
+ continue;
17351
+ }
17352
+ return { device, screensizeInches };
17353
+ }
17354
+ return null;
17355
+ }
17356
+ function readDpdbMatchInput(globalObject = globalThis) {
17357
+ const navigatorObject = globalObject.navigator;
17358
+ const screenObject = globalObject.screen;
17359
+ if (!navigatorObject || !screenObject) {
17360
+ return null;
17361
+ }
17362
+ const width = screenObject.width;
17363
+ const height = screenObject.height;
17364
+ if (typeof width !== "number" || typeof height !== "number" || !(width > 0 && height > 0)) {
17365
+ return null;
17366
+ }
17367
+ const dpr = typeof globalObject.devicePixelRatio === "number" ? globalObject.devicePixelRatio : 1;
17368
+ return {
17369
+ userAgent: navigatorObject.userAgent ?? "",
17370
+ screenWidth: width,
17371
+ screenHeight: height,
17372
+ devicePixelRatio: dpr
17373
+ };
17374
+ }
17375
+ async function fetchDpdbDocument(url) {
17376
+ try {
17377
+ const response = await fetch(url, {
17378
+ credentials: "omit",
17379
+ mode: "cors"
17380
+ });
17381
+ if (!response.ok) {
17382
+ return null;
17383
+ }
17384
+ const data = await response.json();
17385
+ if (data === null || typeof data !== "object" || !("devices" in data) || !Array.isArray(data.devices)) {
17386
+ return null;
17387
+ }
17388
+ return data;
17389
+ } catch {
17390
+ return null;
17391
+ }
17392
+ }
17393
+ function loadDpdbDocument(url) {
17394
+ const existing = cache.get(url);
17395
+ if (existing) {
17396
+ return existing;
17397
+ }
17398
+ const pending = fetchDpdbDocument(url).then((document2) => {
17399
+ if (!document2) {
17400
+ cache.delete(url);
17401
+ }
17402
+ return document2;
17403
+ });
17404
+ cache.set(url, pending);
17405
+ return pending;
17406
+ }
17407
+ async function resolveScreensizeFromDpdb(databaseUrl, input = readDpdbMatchInput()) {
17408
+ const url = normalizeDatabaseUrl(databaseUrl);
17409
+ if (!url || !input) {
17410
+ return null;
17411
+ }
17412
+ const document2 = await loadDpdbDocument(url);
17413
+ if (!document2) {
17414
+ return null;
17415
+ }
17416
+ return matchDpdbDevice(document2, input)?.screensizeInches ?? null;
17417
+ }
17418
+
16998
17419
  // src/webvr/presentation.ts
16999
17420
  var PRESENTATION_PROPERTIES = [
17000
17421
  "background",
@@ -17152,7 +17573,7 @@ function subscribeWebVRPresentationExit(element, onExit) {
17152
17573
 
17153
17574
  // src/webvr/reticle.tsx
17154
17575
  var import_fiber28 = require("@react-three/fiber");
17155
- var import_react57 = require("react");
17576
+ var import_react58 = require("react");
17156
17577
  var import_three38 = require("three");
17157
17578
  var import_jsx_runtime39 = require("react/jsx-runtime");
17158
17579
  var CENTER_NDC = new import_three38.Vector2(0, 0);
@@ -17240,13 +17661,13 @@ function WebVRReticle({
17240
17661
  scale = RETICLE_SCALE,
17241
17662
  visible
17242
17663
  }) {
17243
- const groupRef = (0, import_react57.useRef)(null);
17244
- const ringMeshRef = (0, import_react57.useRef)(null);
17245
- const directionRef = (0, import_react57.useRef)(new import_three38.Vector3());
17246
- const raycasterRef = (0, import_react57.useRef)(new import_three38.Raycaster());
17247
- const hoveredRef = (0, import_react57.useRef)(null);
17248
- const dwellRef = (0, import_react57.useRef)(0);
17249
- const clickedRef = (0, import_react57.useRef)(false);
17664
+ const groupRef = (0, import_react58.useRef)(null);
17665
+ const ringMeshRef = (0, import_react58.useRef)(null);
17666
+ const directionRef = (0, import_react58.useRef)(new import_three38.Vector3());
17667
+ const raycasterRef = (0, import_react58.useRef)(new import_three38.Raycaster());
17668
+ const hoveredRef = (0, import_react58.useRef)(null);
17669
+ const dwellRef = (0, import_react58.useRef)(0);
17670
+ const clickedRef = (0, import_react58.useRef)(false);
17250
17671
  const { camera, gl, scene } = (0, import_fiber28.useThree)();
17251
17672
  (0, import_fiber28.useFrame)((_, deltaSeconds) => {
17252
17673
  const group = groupRef.current;
@@ -17409,9 +17830,43 @@ function WebVRReticle({
17409
17830
 
17410
17831
  // src/webvr/stereo.tsx
17411
17832
  var import_fiber29 = require("@react-three/fiber");
17412
- var import_react58 = require("react");
17833
+ var import_react59 = require("react");
17413
17834
  var import_three39 = require("three");
17414
17835
 
17836
+ // src/webvr/lens-center.ts
17837
+ function physicalScreenFromDiagonal(screensizeInches, displayAspect) {
17838
+ if (!(Number.isFinite(screensizeInches) && screensizeInches > 0 && Number.isFinite(displayAspect) && displayAspect > 0)) {
17839
+ return null;
17840
+ }
17841
+ const diagonalMm = screensizeInches * 25.4;
17842
+ const heightMm = diagonalMm / Math.sqrt(displayAspect * displayAspect + 1);
17843
+ const widthMm = heightMm * displayAspect;
17844
+ if (!(heightMm > 0 && widthMm > 0)) {
17845
+ return null;
17846
+ }
17847
+ return { widthMm, heightMm };
17848
+ }
17849
+ function computeStereoLensCenters(profile, screensize, displayAspect) {
17850
+ const zero = { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } };
17851
+ if (screensize === "auto") {
17852
+ return zero;
17853
+ }
17854
+ const screen = physicalScreenFromDiagonal(screensize, displayAspect);
17855
+ if (!screen) {
17856
+ return zero;
17857
+ }
17858
+ const halfEyeWidthMm = screen.widthMm / 4;
17859
+ const ipdMm = Number.isFinite(profile.ipdMm) ? profile.ipdMm : 60;
17860
+ const x = halfEyeWidthMm > 0 ? (halfEyeWidthMm - ipdMm / 2) / halfEyeWidthMm : 0;
17861
+ const trayDistanceMm = typeof profile.trayDistanceMm === "number" && Number.isFinite(profile.trayDistanceMm) && profile.trayDistanceMm > 0 ? profile.trayDistanceMm : 0;
17862
+ const y = trayDistanceMm > 0 && screen.heightMm > 0 ? 2 * trayDistanceMm / screen.heightMm - 1 : 0;
17863
+ const clampUnit = (value) => Math.max(-1, Math.min(1, Number.isFinite(value) ? value : 0));
17864
+ return {
17865
+ left: { x: clampUnit(x), y: clampUnit(y) },
17866
+ right: { x: clampUnit(-x), y: clampUnit(y) }
17867
+ };
17868
+ }
17869
+
17415
17870
  // src/webvr/stereo-disparity.ts
17416
17871
  var WEBVR_PANORAMA_VIRTUAL_DISTANCE_M = 2;
17417
17872
  function panoramaStereoHalfAngle(ipdMm, virtualDistanceM = WEBVR_PANORAMA_VIRTUAL_DISTANCE_M) {
@@ -17427,19 +17882,32 @@ function panoramaStereoToeInYaw(eye, halfAngleRad) {
17427
17882
  }
17428
17883
 
17429
17884
  // src/webvr/stereo-layout.ts
17430
- function computeStereoLayout(logicalWidth, logicalHeight, pixelRatio) {
17885
+ var DEFAULT_WEBVR_OVERSAMPLING = 1;
17886
+ var MIN_WEBVR_OVERSAMPLING = 0.5;
17887
+ var MAX_WEBVR_OVERSAMPLING = 2;
17888
+ function clampWebVROversampling(value) {
17889
+ if (typeof value !== "number" || !Number.isFinite(value)) {
17890
+ return DEFAULT_WEBVR_OVERSAMPLING;
17891
+ }
17892
+ return Math.max(
17893
+ MIN_WEBVR_OVERSAMPLING,
17894
+ Math.min(MAX_WEBVR_OVERSAMPLING, value)
17895
+ );
17896
+ }
17897
+ function computeStereoLayout(logicalWidth, logicalHeight, pixelRatio, oversampling = DEFAULT_WEBVR_OVERSAMPLING) {
17431
17898
  const fullWidth = Math.max(2, Math.floor(logicalWidth));
17432
17899
  const fullHeight = Math.max(1, Math.floor(logicalHeight));
17433
17900
  const leftWidth = Math.max(1, Math.floor(fullWidth / 2));
17434
17901
  const rightWidth = Math.max(1, fullWidth - leftWidth);
17435
17902
  const ratio = Number.isFinite(pixelRatio) && pixelRatio > 0 ? pixelRatio : 1;
17903
+ const scale = clampWebVROversampling(oversampling);
17436
17904
  return {
17437
17905
  fullWidth,
17438
17906
  fullHeight,
17439
17907
  left: { width: leftWidth, height: fullHeight, offsetX: 0 },
17440
17908
  right: { width: rightWidth, height: fullHeight, offsetX: leftWidth },
17441
- renderTargetWidth: Math.max(1, Math.floor(leftWidth * ratio)),
17442
- renderTargetHeight: Math.max(1, Math.floor(fullHeight * ratio)),
17909
+ renderTargetWidth: Math.max(1, Math.floor(leftWidth * ratio * scale)),
17910
+ renderTargetHeight: Math.max(1, Math.floor(fullHeight * ratio * scale)),
17443
17911
  stereoAspect: leftWidth / fullWidth
17444
17912
  };
17445
17913
  }
@@ -17456,12 +17924,13 @@ void main() {
17456
17924
  var fragmentShader = `
17457
17925
  uniform sampler2D sourceTexture;
17458
17926
  uniform vec2 distortion;
17927
+ uniform vec2 lensCenter;
17459
17928
  varying vec2 vUv;
17460
17929
  void main() {
17461
- vec2 centered = vUv * 2.0 - 1.0;
17930
+ vec2 centered = vUv * 2.0 - 1.0 - lensCenter;
17462
17931
  float radius2 = dot(centered, centered);
17463
17932
  float scale = 1.0 + distortion.x * radius2 + distortion.y * radius2 * radius2;
17464
- vec2 sourceUv = centered * scale * 0.5 + 0.5;
17933
+ vec2 sourceUv = (centered * scale + lensCenter) * 0.5 + 0.5;
17465
17934
  if (sourceUv.x < 0.0 || sourceUv.x > 1.0 || sourceUv.y < 0.0 || sourceUv.y > 1.0) {
17466
17935
  gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
17467
17936
  } else {
@@ -17481,6 +17950,7 @@ function createDistortionResources() {
17481
17950
  fragmentShader,
17482
17951
  uniforms: {
17483
17952
  distortion: { value: new import_three39.Vector2() },
17953
+ lensCenter: { value: new import_three39.Vector2() },
17484
17954
  sourceTexture: { value: target.texture }
17485
17955
  },
17486
17956
  vertexShader
@@ -17497,12 +17967,27 @@ function createDistortionResources() {
17497
17967
  target
17498
17968
  };
17499
17969
  }
17500
- function syncStereoCameras(stereo, camera, profile, screensize, size, gl) {
17970
+ function applyEyeViewOffset(eyeCamera, eye, center) {
17971
+ const fullWidth = eye.width;
17972
+ const fullHeight = eye.height;
17973
+ const offsetX = ((center.x + 1) / 2 - 0.5) * fullWidth;
17974
+ const offsetY = (0.5 - (center.y + 1) / 2) * fullHeight;
17975
+ eyeCamera.setViewOffset(
17976
+ fullWidth,
17977
+ fullHeight,
17978
+ offsetX,
17979
+ offsetY,
17980
+ fullWidth,
17981
+ fullHeight
17982
+ );
17983
+ }
17984
+ function syncStereoCameras(stereo, camera, profile, screensize, oversampling, size, gl) {
17501
17985
  gl.getSize(size);
17502
17986
  const layout = computeStereoLayout(
17503
17987
  size.x,
17504
17988
  size.y,
17505
- gl.getPixelRatio()
17989
+ gl.getPixelRatio(),
17990
+ oversampling
17506
17991
  );
17507
17992
  const screenToLensMm = profile.screenToLensMm ?? 42;
17508
17993
  const displayAspect = layout.fullWidth / layout.fullHeight;
@@ -17521,35 +18006,41 @@ function syncStereoCameras(stereo, camera, profile, screensize, size, gl) {
17521
18006
  TOE_IN.makeRotationY(panoramaStereoToeInYaw("right", halfAngle));
17522
18007
  stereo.cameraR.matrix.multiply(TOE_IN);
17523
18008
  stereo.cameraR.matrixWorldNeedsUpdate = true;
18009
+ const centers = computeStereoLensCenters(profile, screensize, displayAspect);
18010
+ applyEyeViewOffset(stereo.cameraL, layout.left, centers.left);
18011
+ applyEyeViewOffset(stereo.cameraR, layout.right, centers.right);
17524
18012
  stereo.cameraL.updateMatrixWorld(true);
17525
18013
  stereo.cameraR.updateMatrixWorld(true);
17526
- return layout;
18014
+ return { layout, centers };
17527
18015
  }
17528
18016
  function WebVRStereoRenderer({
17529
18017
  mode,
18018
+ oversampling = 1,
17530
18019
  profile,
17531
18020
  screensize
17532
18021
  }) {
17533
18022
  const { camera, gl } = (0, import_fiber29.useThree)();
17534
- const runtime = (0, import_react58.useContext)(WebVRRuntimeContext);
17535
- const fallbackStereoRef = (0, import_react58.useRef)(null);
18023
+ const runtime = (0, import_react59.useContext)(WebVRRuntimeContext);
18024
+ const fallbackStereoRef = (0, import_react59.useRef)(null);
17536
18025
  if (!fallbackStereoRef.current) {
17537
18026
  fallbackStereoRef.current = new import_three39.StereoCamera();
17538
18027
  }
17539
18028
  const stereo = runtime?.stereoView.stereo ?? fallbackStereoRef.current;
17540
- const stereoRef = (0, import_react58.useRef)(stereo);
18029
+ const stereoRef = (0, import_react59.useRef)(stereo);
17541
18030
  stereoRef.current = stereo;
17542
- const resources = (0, import_react58.useMemo)(createDistortionResources, []);
17543
- const sizeRef = (0, import_react58.useRef)(new import_three39.Vector2());
17544
- const viewportRef = (0, import_react58.useRef)(new import_three39.Vector4());
17545
- const scissorRef = (0, import_react58.useRef)(new import_three39.Vector4());
17546
- const originalFovRef = (0, import_react58.useRef)(null);
17547
- const stereoActiveRef = (0, import_react58.useRef)(false);
17548
- const profileRef = (0, import_react58.useRef)(profile);
17549
- const screensizeRef = (0, import_react58.useRef)(screensize);
18031
+ const resources = (0, import_react59.useMemo)(createDistortionResources, []);
18032
+ const sizeRef = (0, import_react59.useRef)(new import_three39.Vector2());
18033
+ const viewportRef = (0, import_react59.useRef)(new import_three39.Vector4());
18034
+ const scissorRef = (0, import_react59.useRef)(new import_three39.Vector4());
18035
+ const originalFovRef = (0, import_react59.useRef)(null);
18036
+ const stereoActiveRef = (0, import_react59.useRef)(false);
18037
+ const profileRef = (0, import_react59.useRef)(profile);
18038
+ const screensizeRef = (0, import_react59.useRef)(screensize);
18039
+ const oversamplingRef = (0, import_react59.useRef)(clampWebVROversampling(oversampling));
17550
18040
  profileRef.current = profile;
17551
18041
  screensizeRef.current = screensize;
17552
- (0, import_react58.useEffect)(
18042
+ oversamplingRef.current = clampWebVROversampling(oversampling);
18043
+ (0, import_react59.useEffect)(
17553
18044
  () => () => {
17554
18045
  resources.geometry.dispose();
17555
18046
  resources.material.dispose();
@@ -17557,7 +18048,7 @@ function WebVRStereoRenderer({
17557
18048
  },
17558
18049
  [resources]
17559
18050
  );
17560
- (0, import_react58.useEffect)(() => {
18051
+ (0, import_react59.useEffect)(() => {
17561
18052
  const originalRender = gl.render.bind(gl);
17562
18053
  gl.render = (sceneToRender, cameraToRender) => {
17563
18054
  if (!stereoActiveRef.current || !(cameraToRender instanceof import_three39.PerspectiveCamera)) {
@@ -17569,11 +18060,12 @@ function WebVRStereoRenderer({
17569
18060
  if (originalFovRef.current === null) {
17570
18061
  originalFovRef.current = cameraToRender.fov;
17571
18062
  }
17572
- const layout = syncStereoCameras(
18063
+ const { layout, centers } = syncStereoCameras(
17573
18064
  stereoRef.current,
17574
18065
  cameraToRender,
17575
18066
  currentProfile,
17576
18067
  currentScreensize,
18068
+ oversamplingRef.current,
17577
18069
  sizeRef.current,
17578
18070
  gl
17579
18071
  );
@@ -17585,7 +18077,7 @@ function WebVRStereoRenderer({
17585
18077
  gl.getScissor(scissorRef.current);
17586
18078
  gl.autoClear = true;
17587
18079
  gl.setScissorTest(true);
17588
- const renderEye = (eyeCamera, eye) => {
18080
+ const renderEye = (eyeCamera, eye, center) => {
17589
18081
  if (hasDistortion) {
17590
18082
  if (resources.target.width !== layout.renderTargetWidth || resources.target.height !== layout.renderTargetHeight) {
17591
18083
  resources.target.setSize(
@@ -17602,6 +18094,7 @@ function WebVRStereoRenderer({
17602
18094
  currentProfile.k1,
17603
18095
  currentProfile.k2
17604
18096
  );
18097
+ resources.material.uniforms.lensCenter?.value.set(center.x, center.y);
17605
18098
  originalRender(resources.scene, resources.camera);
17606
18099
  return;
17607
18100
  }
@@ -17610,8 +18103,8 @@ function WebVRStereoRenderer({
17610
18103
  gl.setScissor(eye.offsetX, 0, eye.width, eye.height);
17611
18104
  originalRender(sceneToRender, eyeCamera);
17612
18105
  };
17613
- renderEye(stereoRef.current.cameraL, layout.left);
17614
- renderEye(stereoRef.current.cameraR, layout.right);
18106
+ renderEye(stereoRef.current.cameraL, layout.left, centers.left);
18107
+ renderEye(stereoRef.current.cameraR, layout.right, centers.right);
17615
18108
  gl.setRenderTarget(previousTarget);
17616
18109
  gl.setViewport(viewportRef.current);
17617
18110
  gl.setScissor(scissorRef.current);
@@ -17622,7 +18115,7 @@ function WebVRStereoRenderer({
17622
18115
  gl.render = originalRender;
17623
18116
  };
17624
18117
  }, [gl, resources]);
17625
- (0, import_react58.useEffect)(
18118
+ (0, import_react59.useEffect)(
17626
18119
  () => () => {
17627
18120
  if (runtime) {
17628
18121
  runtime.stereoView.active = false;
@@ -17642,6 +18135,7 @@ function WebVRStereoRenderer({
17642
18135
  camera,
17643
18136
  profileRef.current,
17644
18137
  screensizeRef.current,
18138
+ oversamplingRef.current,
17645
18139
  sizeRef.current,
17646
18140
  gl
17647
18141
  );
@@ -17657,7 +18151,7 @@ function WebVRStereoRenderer({
17657
18151
  }
17658
18152
 
17659
18153
  // src/webvr/pointerlock.ts
17660
- var import_react59 = require("react");
18154
+ var import_react60 = require("react");
17661
18155
  var POINTER_LOCK_CURSOR_ID = "webvr:pointerlock";
17662
18156
  async function requestWebVRPointerLock(element) {
17663
18157
  if (typeof document === "undefined") {
@@ -17690,11 +18184,11 @@ function exitWebVRPointerLock(element) {
17690
18184
  }
17691
18185
  function useWebVRPointerLock(enabled, element) {
17692
18186
  const cursor = usePanoCursor();
17693
- const cursorRef = (0, import_react59.useRef)(cursor);
17694
- const enabledRef = (0, import_react59.useRef)(enabled);
18187
+ const cursorRef = (0, import_react60.useRef)(cursor);
18188
+ const enabledRef = (0, import_react60.useRef)(enabled);
17695
18189
  cursorRef.current = cursor;
17696
18190
  enabledRef.current = enabled;
17697
- (0, import_react59.useEffect)(() => {
18191
+ (0, import_react60.useEffect)(() => {
17698
18192
  if (!enabled || !element) {
17699
18193
  return;
17700
18194
  }
@@ -17724,7 +18218,7 @@ function useWebVRPointerLock(enabled, element) {
17724
18218
  }
17725
18219
 
17726
18220
  // src/webvr/wakelock.ts
17727
- var import_react60 = require("react");
18221
+ var import_react61 = require("react");
17728
18222
 
17729
18223
  // src/webvr/wakelock-fallback.ts
17730
18224
  var WEBVR_WAKELOCK_FALLBACK_VIDEO_BASE64 = "AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAbBbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAD4AAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAxt0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAggAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAABAAAAAQAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAIIAAAEAAABAAAAAAKTbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAyAAAAGgBVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAACPm1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAf5zdGJsAAAAvnN0c2QAAAAAAAAAAQAAAK5hdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAABAAEABIAAAASAAAAAAAAAABFUxhdmM2Mi4xMS4xMDAgbGlieDI2NAAAAAAAAAAAAAAAGP//AAAANGF2Y0MBZAAK/+EAF2dkAAqs2V7ARAAAAwAEAAADAMg8SJZYAQAGaOvjyyLA/fj4AAAAABBwYXNwAAAAAQAAAAEAAAAUYnRydAAAAAAAADQ5AAAAAAAAABhzdHRzAAAAAAAAAAEAAAANAAACAAAAABRzdHNzAAAAAAAAAAEAAAABAAAAeGN0dHMAAAAAAAAADQAAAAEAAAQAAAAAAQAACgAAAAABAAAEAAAAAAEAAAAAAAAAAQAAAgAAAAABAAAKAAAAAAEAAAQAAAAAAQAAAAAAAAABAAACAAAAAAEAAAoAAAAAAQAABAAAAAABAAAAAAAAAAEAAAIAAAAAKHN0c2MAAAAAAAAAAgAAAAEAAAADAAAAAQAAAAUAAAABAAAAAQAAAEhzdHN6AAAAAAAAAAAAAAANAAACxQAAAAwAAAAMAAAADAAAAAwAAAASAAAADgAAAAwAAAAMAAAAEgAAAA4AAAAMAAAADAAAACRzdGNvAAAAAAAAAAUAAAcGAAAJ5wAAChUAAAo/AAAKbwAAAtF0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAACAAAAAAAAD4AAAAAAAAAAAAAAAAEBAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAA+AAAAEAAABAAAAAAJJbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAfQAAAgABVxAAAAAAALWhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAABTb3VuZEhhbmRsZXIAAAAB9G1pbmYAAAAQc21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABuHN0YmwAAAB+c3RzZAAAAAAAAAABAAAAbm1wNGEAAAAAAAAAAQAAAAAAAAAAAAEAEAAAAAAfQAAAAAAANmVzZHMAAAAAA4CAgCUAAgAEgICAF0AVAAAAAAAfQAAAARsFgICABRWIVuUABoCAgAECAAAAFGJ0cnQAAAAAAAAfQAAAARsAAAAYc3R0cwAAAAAAAAABAAAAIAAABAAAAAAoc3RzYwAAAAAAAAACAAAAAQAAAAEAAAABAAAABgAAABsAAAABAAAAlHN0c3oAAAAAAAAAAAAAACAAAAAVAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAChzdGNvAAAAAAAAAAYAAAbxAAAJ4wAAChEAAAo7AAAKawAACnsAAAAac2dwZAEAAAByb2xsAAAAAgAAAAH//wAAABxzYmdwAAAAAHJvbGwAAAABAAAAIAAAAAEAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYyLjMuMTAwAAAACGZyZWUAAAP+bWRhdN4CAExhdmM2Mi4xMS4xMDAAAjBADgAAAq4GBf//qtxF6b3m2Ui3lizYINkj7u94MjY0IC0gY29yZSAxNjUgcjMyMjIgYjM1NjA1YSAtIEguMjY0L01QRUctNCBBVkMgY29kZWMgLSBDb3B5bGVmdCAyMDAzLTIwMjUgLSBodHRwOi8vd3d3LnZpZGVvbGFuLm9yZy94MjY0Lmh0bWwgLSBvcHRpb25zOiBjYWJhYz0xIHJlZj0zIGRlYmxvY2s9MTowOjAgYW5hbHlzZT0weDM6MHgxMTMgbWU9aGV4IHN1Ym1lPTcgcHN5PTEgcHN5X3JkPTEuMDA6MC4wMCBtaXhlZF9yZWY9MSBtZV9yYW5nZT0xNiBjaHJvbWFfbWU9MSB0cmVsbGlzPTEgOHg4ZGN0PTEgY3FtPTAgZGVhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz0xIGxvb2thaGVhZF90aHJlYWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MyBiX3B5cmFtaWQ9MiBiX2FkYXB0PTEgYl9iaWFzPTAgZGlyZWN0PTEgd2VpZ2h0Yj0xIG9wZW5fZ29wPTAgd2VpZ2h0cD0yIGtleWludD0yNTAga2V5aW50X21pbj0yNSBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmNfbG9va2FoZWFkPTQwIHJjPWNyZiBtYnRyZWU9MSBjcmY9MjMuMCBxY29tcD0wLjYwIHFwbWluPTAgcXBtYXg9NjkgcXBzdGVwPTQgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAAA9liIQAO//+906/AptUwmEAAAAIQZokbEN//uAAAAAIQZ5CeIX/wYEBGCAHAAAACAGeYXRCv8SAAAAACAGeY2pCv8SBAAAADkGaaEmoQWiZTAhn//7hARggBwAAAApBnoZFESwv/8GBAAAACAGepXRCv8SBAAAACAGep2pCv8SAARggBwAAAA5BmqxJqEFsmUwIV//+wAAAAApBnspFFSwv/8GBAAAACAGe6XRCv8SAARggBwAAAAgBnutqQr/EgAEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBw==";
@@ -17853,7 +18347,7 @@ function releaseWebVRWakeLock() {
17853
18347
  stopFallbackVideo();
17854
18348
  }
17855
18349
  function useWebVRWakeLock(enabled) {
17856
- (0, import_react60.useEffect)(() => {
18350
+ (0, import_react61.useEffect)(() => {
17857
18351
  if (!enabled) {
17858
18352
  return;
17859
18353
  }
@@ -17871,6 +18365,42 @@ function useWebVRWakeLock(enabled) {
17871
18365
  }, [enabled]);
17872
18366
  }
17873
18367
 
18368
+ // src/webvr/xr-quality.ts
18369
+ function clampWebVRFoveation(value) {
18370
+ if (typeof value !== "number" || !Number.isFinite(value)) {
18371
+ return void 0;
18372
+ }
18373
+ return Math.max(0, Math.min(1, value));
18374
+ }
18375
+ function resolveWebVRFrameRateOption(frameRate) {
18376
+ if (frameRate === void 0) {
18377
+ return void 0;
18378
+ }
18379
+ if (frameRate === "low" || frameRate === "mid" || frameRate === "high") {
18380
+ return frameRate;
18381
+ }
18382
+ if (typeof frameRate !== "number" || !Number.isFinite(frameRate)) {
18383
+ return void 0;
18384
+ }
18385
+ const target = frameRate;
18386
+ return (rates) => {
18387
+ if (rates.length === 0) {
18388
+ return false;
18389
+ }
18390
+ let best = rates[0];
18391
+ let bestDelta = Math.abs(best - target);
18392
+ for (let i = 1; i < rates.length; i += 1) {
18393
+ const candidate = rates[i];
18394
+ const delta = Math.abs(candidate - target);
18395
+ if (delta < bestDelta) {
18396
+ best = candidate;
18397
+ bestDelta = delta;
18398
+ }
18399
+ }
18400
+ return best;
18401
+ };
18402
+ }
18403
+
17874
18404
  // src/webvr/webvr.tsx
17875
18405
  var import_jsx_runtime40 = require("react/jsx-runtime");
17876
18406
  function initializeSettings(screensize, profile) {
@@ -17883,13 +18413,21 @@ function initializeSettings(screensize, profile) {
17883
18413
  function getViewerElement(canvas) {
17884
18414
  return canvas.closest("[data-pano-viewer]") ?? canvas.parentElement ?? canvas;
17885
18415
  }
17886
- var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18416
+ var WebVR = (0, import_react62.forwardRef)(function WebVR2({
17887
18417
  chrome = true,
17888
18418
  mobileVr = true,
17889
18419
  desktopSupport = false,
17890
18420
  fakeSupport = true,
17891
18421
  screensize,
18422
+ databaseUrl,
17892
18423
  profile,
18424
+ absolute = false,
18425
+ north = 0,
18426
+ friction = 0,
18427
+ touchSupport = false,
18428
+ oversampling = 1,
18429
+ foveation,
18430
+ frameRate,
17893
18431
  cursor = true,
17894
18432
  cursorDwellMs = DEFAULT_WEBVR_CURSOR_DWELL_MS,
17895
18433
  wakelock = true,
@@ -17901,24 +18439,40 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
17901
18439
  onDenied,
17902
18440
  onUnknownDevice
17903
18441
  }, ref) {
17904
- const runtime = (0, import_react61.useContext)(WebVRRuntimeContext);
17905
- const controlsRef = (0, import_react61.useContext)(PanoramaViewContext);
17906
- const eventBus = (0, import_react61.useContext)(PanoEventBusContext);
18442
+ const runtime = (0, import_react62.useContext)(WebVRRuntimeContext);
18443
+ const controlsRef = (0, import_react62.useContext)(PanoramaViewContext);
18444
+ const eventBus = (0, import_react62.useContext)(PanoEventBusContext);
17907
18445
  const gl = (0, import_fiber30.useThree)((state) => state.gl);
17908
- const gyroRef = (0, import_react61.useRef)(null);
17909
- const capabilitiesRef = (0, import_react61.useRef)({
18446
+ const gyroRef = (0, import_react62.useRef)(null);
18447
+ const capabilitiesRef = (0, import_react62.useRef)({
17910
18448
  mobile: false,
17911
18449
  sensor: false,
17912
18450
  webxr: false
17913
18451
  });
17914
- const availableRef = (0, import_react61.useRef)(false);
17915
- const modeRef = (0, import_react61.useRef)(null);
17916
- const webXRSessionRef = (0, import_react61.useRef)(null);
17917
- const [mode, setMode] = (0, import_react61.useState)(null);
17918
- const [settings, setSettings] = (0, import_react61.useState)(
18452
+ const availableRef = (0, import_react62.useRef)(false);
18453
+ const modeRef = (0, import_react62.useRef)(null);
18454
+ const webXRSessionRef = (0, import_react62.useRef)(null);
18455
+ const dpdbRequestIdRef = (0, import_react62.useRef)(0);
18456
+ const databaseUrlRef = (0, import_react62.useRef)(databaseUrl);
18457
+ databaseUrlRef.current = databaseUrl;
18458
+ const oversamplingRef = (0, import_react62.useRef)(clampWebVROversampling(oversampling));
18459
+ oversamplingRef.current = clampWebVROversampling(oversampling);
18460
+ const foveationRef = (0, import_react62.useRef)(clampWebVRFoveation(foveation));
18461
+ foveationRef.current = clampWebVRFoveation(foveation);
18462
+ const frameRateRef = (0, import_react62.useRef)(frameRate);
18463
+ frameRateRef.current = frameRate;
18464
+ const screensizePropRef = (0, import_react62.useRef)(screensize);
18465
+ screensizePropRef.current = screensize;
18466
+ const profilePropRef = (0, import_react62.useRef)(profile);
18467
+ profilePropRef.current = profile;
18468
+ const [mode, setMode] = (0, import_react62.useState)(null);
18469
+ const [settings, setSettings] = (0, import_react62.useState)(
17919
18470
  () => initializeSettings(screensize, profile)
17920
18471
  );
17921
- const callbacksRef = (0, import_react61.useRef)({
18472
+ const [detectedScreensize, setDetectedScreensize] = (0, import_react62.useState)(
18473
+ null
18474
+ );
18475
+ const callbacksRef = (0, import_react62.useRef)({
17922
18476
  onAvailable,
17923
18477
  onDenied,
17924
18478
  onEnterVR,
@@ -17938,22 +18492,48 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
17938
18492
  throw new Error("<WebVR> must be rendered inside <PanoViewer>.");
17939
18493
  }
17940
18494
  const { host, xrStore } = runtime;
17941
- const resolvedProfile = (0, import_react61.useMemo)(
18495
+ const resolvedProfile = (0, import_react62.useMemo)(
17942
18496
  () => resolveWebVRProfile(profile, settings.profileId),
17943
18497
  [profile, settings.profileId]
17944
18498
  );
17945
- const reportDenied = (0, import_react61.useCallback)(
18499
+ const effectiveScreensize = settings.screensize === "auto" ? detectedScreensize ?? "auto" : settings.screensize;
18500
+ const resolvedOversampling = clampWebVROversampling(oversampling);
18501
+ const reportDenied = (0, import_react62.useCallback)(
17946
18502
  (error) => {
17947
18503
  eventBus.emit("vrdenied", { error });
17948
18504
  callbacksRef.current.onDenied?.(error);
17949
18505
  },
17950
18506
  [eventBus]
17951
18507
  );
17952
- const reportUnknownDevice = (0, import_react61.useCallback)(() => {
18508
+ const reportUnknownDevice = (0, import_react62.useCallback)(() => {
17953
18509
  eventBus.emit("vrunknowndevice", void 0);
17954
18510
  callbacksRef.current.onUnknownDevice?.();
17955
18511
  }, [eventBus]);
17956
- const activate = (0, import_react61.useCallback)(
18512
+ const clearDetectedScreensize = (0, import_react62.useCallback)(() => {
18513
+ dpdbRequestIdRef.current += 1;
18514
+ setDetectedScreensize(null);
18515
+ updateWebVRHost(host, { detectedScreensize: null });
18516
+ }, [host]);
18517
+ const resolveAutoScreensize = (0, import_react62.useCallback)(() => {
18518
+ const requestId = ++dpdbRequestIdRef.current;
18519
+ const url = normalizeDatabaseUrl(databaseUrlRef.current);
18520
+ if (!url) {
18521
+ reportUnknownDevice();
18522
+ return;
18523
+ }
18524
+ void resolveScreensizeFromDpdb(url).then((inches) => {
18525
+ if (requestId !== dpdbRequestIdRef.current) {
18526
+ return;
18527
+ }
18528
+ if (inches === null) {
18529
+ reportUnknownDevice();
18530
+ return;
18531
+ }
18532
+ setDetectedScreensize(inches);
18533
+ updateWebVRHost(host, { detectedScreensize: inches });
18534
+ });
18535
+ }, [host, reportUnknownDevice]);
18536
+ const activate = (0, import_react62.useCallback)(
17957
18537
  (nextMode) => {
17958
18538
  if (modeRef.current === nextMode) {
17959
18539
  return;
@@ -17968,7 +18548,7 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
17968
18548
  },
17969
18549
  [controlsRef, eventBus]
17970
18550
  );
17971
- const deactivate = (0, import_react61.useCallback)(
18551
+ const deactivate = (0, import_react62.useCallback)(
17972
18552
  (expectedMode) => {
17973
18553
  const previousMode = modeRef.current;
17974
18554
  if (!previousMode || expectedMode && previousMode !== expectedMode) {
@@ -17976,6 +18556,7 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
17976
18556
  }
17977
18557
  modeRef.current = null;
17978
18558
  setMode(null);
18559
+ clearDetectedScreensize();
17979
18560
  if (previousMode === "fake") {
17980
18561
  controlsRef.current?.setGyroActive(false);
17981
18562
  }
@@ -17983,24 +18564,42 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
17983
18564
  eventBus.emit("vrexit", { mode: previousMode });
17984
18565
  callbacksRef.current.onExitVR?.(previousMode);
17985
18566
  },
17986
- [controlsRef, eventBus, gl.domElement]
18567
+ [clearDetectedScreensize, controlsRef, eventBus, gl.domElement]
17987
18568
  );
17988
- const requestPermission = (0, import_react61.useCallback)(async () => {
18569
+ const requestPermission = (0, import_react62.useCallback)(async () => {
17989
18570
  return await gyroRef.current?.requestPermission() ?? false;
17990
18571
  }, []);
17991
- const enterVR = (0, import_react61.useCallback)(async () => {
18572
+ const applyWebXRQuality = (0, import_react62.useCallback)(() => {
18573
+ const scale = oversamplingRef.current;
18574
+ if (typeof gl.xr.setFramebufferScaleFactor === "function") {
18575
+ gl.xr.setFramebufferScaleFactor(scale);
18576
+ }
18577
+ const nextFoveation = foveationRef.current;
18578
+ if (nextFoveation !== void 0 && typeof gl.xr.setFoveation === "function") {
18579
+ gl.xr.setFoveation(nextFoveation);
18580
+ }
18581
+ const frameRateOption = resolveWebVRFrameRateOption(frameRateRef.current);
18582
+ if (frameRateOption !== void 0) {
18583
+ xrStore.setFrameRate(frameRateOption);
18584
+ }
18585
+ }, [gl.xr, xrStore]);
18586
+ const enterVR = (0, import_react62.useCallback)(async () => {
17992
18587
  if (modeRef.current) {
17993
18588
  return true;
17994
18589
  }
17995
18590
  const capabilities = capabilitiesRef.current;
17996
18591
  if (capabilities.webxr) {
17997
18592
  try {
18593
+ if (typeof gl.xr.setFramebufferScaleFactor === "function") {
18594
+ gl.xr.setFramebufferScaleFactor(oversamplingRef.current);
18595
+ }
17998
18596
  await xrStore.enterVR();
17999
18597
  const session2 = xrStore.getState().session;
18000
18598
  if (!session2) {
18001
18599
  throw new Error("The WebXR session did not start.");
18002
18600
  }
18003
18601
  webXRSessionRef.current = session2;
18602
+ applyWebXRQuality();
18004
18603
  session2.addEventListener(
18005
18604
  "end",
18006
18605
  () => {
@@ -18030,10 +18629,10 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18030
18629
  reportDenied();
18031
18630
  return false;
18032
18631
  }
18632
+ activate("mobilevr");
18033
18633
  if (settings.screensize === "auto") {
18034
- reportUnknownDevice();
18634
+ resolveAutoScreensize();
18035
18635
  }
18036
- activate("mobilevr");
18037
18636
  return true;
18038
18637
  }
18039
18638
  if (fakeSupport) {
@@ -18052,21 +18651,23 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18052
18651
  return false;
18053
18652
  }, [
18054
18653
  activate,
18654
+ applyWebXRQuality,
18055
18655
  deactivate,
18056
18656
  desktopSupport,
18057
18657
  eventBus,
18058
18658
  fakeSupport,
18059
18659
  gl.domElement,
18660
+ gl.xr,
18060
18661
  mobileVr,
18061
18662
  mousePointerLock,
18062
18663
  reportDenied,
18063
- reportUnknownDevice,
18064
18664
  requestPermission,
18665
+ resolveAutoScreensize,
18065
18666
  settings.screensize,
18066
18667
  wakelock,
18067
18668
  xrStore
18068
18669
  ]);
18069
- const exitVR = (0, import_react61.useCallback)(async () => {
18670
+ const exitVR = (0, import_react62.useCallback)(async () => {
18070
18671
  if (modeRef.current === "webxr") {
18071
18672
  const session2 = webXRSessionRef.current ?? xrStore.getState().session ?? null;
18072
18673
  if (session2) {
@@ -18079,47 +18680,90 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18079
18680
  deactivate();
18080
18681
  leaveWebVRPresentation();
18081
18682
  }, [deactivate, xrStore]);
18082
- const toggleVR = (0, import_react61.useCallback)(async () => {
18683
+ const toggleVR = (0, import_react62.useCallback)(async () => {
18083
18684
  if (modeRef.current) {
18084
18685
  await exitVR();
18085
18686
  return false;
18086
18687
  }
18087
18688
  return enterVR();
18088
18689
  }, [enterVR, exitVR]);
18089
- const updateSettings = (0, import_react61.useCallback)((next) => {
18090
- const normalized = {
18091
- profileId: next.profileId,
18092
- screensize: next.screensize === "auto" ? "auto" : Math.round(Math.max(3, Math.min(20, next.screensize)) * 10) / 10
18093
- };
18094
- setSettings(normalized);
18095
- saveWebVRSettings(normalized);
18096
- }, []);
18097
- const controller = (0, import_react61.useMemo)(
18690
+ const updateSettings = (0, import_react62.useCallback)(
18691
+ (next) => {
18692
+ const normalized = {
18693
+ profileId: next.profileId,
18694
+ screensize: next.screensize === "auto" ? "auto" : Math.round(Math.max(3, Math.min(20, next.screensize)) * 10) / 10
18695
+ };
18696
+ setSettings(normalized);
18697
+ saveWebVRSettings(normalized);
18698
+ if (normalized.screensize === "auto") {
18699
+ if (modeRef.current === "mobilevr") {
18700
+ setDetectedScreensize(null);
18701
+ updateWebVRHost(host, { detectedScreensize: null });
18702
+ resolveAutoScreensize();
18703
+ }
18704
+ } else {
18705
+ clearDetectedScreensize();
18706
+ }
18707
+ },
18708
+ [clearDetectedScreensize, host, resolveAutoScreensize]
18709
+ );
18710
+ const resetSettings = (0, import_react62.useCallback)(() => {
18711
+ clearWebVRSettings();
18712
+ const next = resolveInitialWebVRSettings(
18713
+ screensizePropRef.current,
18714
+ profilePropRef.current
18715
+ );
18716
+ setSettings(next);
18717
+ if (next.screensize === "auto") {
18718
+ if (modeRef.current === "mobilevr") {
18719
+ setDetectedScreensize(null);
18720
+ updateWebVRHost(host, { detectedScreensize: null });
18721
+ resolveAutoScreensize();
18722
+ } else {
18723
+ clearDetectedScreensize();
18724
+ }
18725
+ } else {
18726
+ clearDetectedScreensize();
18727
+ }
18728
+ }, [clearDetectedScreensize, host, resolveAutoScreensize]);
18729
+ const controller = (0, import_react62.useMemo)(
18098
18730
  () => ({
18099
18731
  closeSetup: () => updateWebVRHost(host, { setupOpen: false }),
18100
18732
  enterVR,
18101
18733
  exitVR,
18102
18734
  openSetup: () => updateWebVRHost(host, { setupOpen: true }),
18103
18735
  requestPermission,
18736
+ resetSettings,
18104
18737
  toggleVR,
18105
18738
  updateSettings
18106
18739
  }),
18107
- [enterVR, exitVR, host, requestPermission, toggleVR, updateSettings]
18740
+ [
18741
+ enterVR,
18742
+ exitVR,
18743
+ host,
18744
+ requestPermission,
18745
+ resetSettings,
18746
+ toggleVR,
18747
+ updateSettings
18748
+ ]
18108
18749
  );
18109
- (0, import_react61.useImperativeHandle)(
18750
+ (0, import_react62.useImperativeHandle)(
18110
18751
  ref,
18111
18752
  () => ({
18112
18753
  enterVR,
18113
18754
  exitVR,
18114
18755
  getMode: () => modeRef.current,
18756
+ getState: () => getWebVRState(host),
18115
18757
  isAvailable: () => availableRef.current,
18116
18758
  isEnabled: () => modeRef.current !== null,
18117
18759
  requestPermission,
18760
+ resetSettings,
18761
+ subscribe: (onStoreChange) => subscribeWebVRHost(host, onStoreChange),
18118
18762
  toggleVR
18119
18763
  }),
18120
- [enterVR, exitVR, requestPermission, toggleVR]
18764
+ [enterVR, exitVR, host, requestPermission, resetSettings, toggleVR]
18121
18765
  );
18122
- (0, import_react61.useEffect)(() => {
18766
+ (0, import_react62.useEffect)(() => {
18123
18767
  let active = true;
18124
18768
  void detectWebVRCapabilities().then((capabilities) => {
18125
18769
  if (!active) {
@@ -18141,19 +18785,30 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18141
18785
  active = false;
18142
18786
  };
18143
18787
  }, [desktopSupport, eventBus, fakeSupport, host, mobileVr]);
18144
- (0, import_react61.useEffect)(() => {
18788
+ (0, import_react62.useEffect)(() => {
18145
18789
  host.controller = controller;
18146
18790
  return () => {
18147
18791
  if (host.controller === controller) {
18148
18792
  host.controller = null;
18149
- updateWebVRHost(host, { mode: null, setupOpen: false });
18793
+ dpdbRequestIdRef.current += 1;
18794
+ setDetectedScreensize(null);
18795
+ updateWebVRHost(host, {
18796
+ detectedScreensize: null,
18797
+ mode: null,
18798
+ setupOpen: false
18799
+ });
18150
18800
  }
18151
18801
  };
18152
18802
  }, [controller, host]);
18153
- (0, import_react61.useEffect)(() => {
18154
- updateWebVRHost(host, { chrome, mode, settings });
18155
- }, [chrome, host, mode, settings]);
18156
- (0, import_react61.useEffect)(() => {
18803
+ (0, import_react62.useEffect)(() => {
18804
+ updateWebVRHost(host, {
18805
+ chrome,
18806
+ detectedScreensize,
18807
+ mode,
18808
+ settings
18809
+ });
18810
+ }, [chrome, detectedScreensize, host, mode, settings]);
18811
+ (0, import_react62.useEffect)(() => {
18157
18812
  if (mode !== "fake") {
18158
18813
  return;
18159
18814
  }
@@ -18177,7 +18832,7 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18177
18832
  document.removeEventListener("mousemove", onMouseMove);
18178
18833
  };
18179
18834
  }, [controlsRef, gl.domElement, mode, mousePointerLock]);
18180
- (0, import_react61.useEffect)(
18835
+ (0, import_react62.useEffect)(
18181
18836
  () => () => {
18182
18837
  const session2 = webXRSessionRef.current;
18183
18838
  if (session2) {
@@ -18192,7 +18847,7 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18192
18847
  },
18193
18848
  [controlsRef]
18194
18849
  );
18195
- (0, import_react61.useEffect)(() => {
18850
+ (0, import_react62.useEffect)(() => {
18196
18851
  if (mode !== "mobilevr" && mode !== "fake") {
18197
18852
  return;
18198
18853
  }
@@ -18201,6 +18856,23 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18201
18856
  void exitVR();
18202
18857
  });
18203
18858
  }, [exitVR, gl.domElement, mode]);
18859
+ (0, import_react62.useEffect)(() => {
18860
+ if (mode !== "webxr") {
18861
+ return;
18862
+ }
18863
+ const session2 = webXRSessionRef.current ?? xrStore.getState().session ?? null;
18864
+ if (!session2) {
18865
+ return;
18866
+ }
18867
+ const nextFoveation = foveationRef.current;
18868
+ if (nextFoveation !== void 0 && typeof gl.xr.setFoveation === "function") {
18869
+ gl.xr.setFoveation(nextFoveation);
18870
+ }
18871
+ const frameRateOption = resolveWebVRFrameRateOption(frameRateRef.current);
18872
+ if (frameRateOption !== void 0) {
18873
+ xrStore.setFrameRate(frameRateOption);
18874
+ }
18875
+ }, [foveation, frameRate, gl.xr, mode, xrStore]);
18204
18876
  useWebVRPointerLock(
18205
18877
  mousePointerLock && mode === "fake",
18206
18878
  gl.domElement
@@ -18213,22 +18885,26 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18213
18885
  Gyro,
18214
18886
  {
18215
18887
  ref: gyroRef,
18888
+ absolute,
18216
18889
  desktopSupport,
18217
18890
  enabled: mode === "mobilevr",
18891
+ friction,
18892
+ north,
18218
18893
  onDenied: () => {
18219
18894
  reportDenied();
18220
18895
  deactivate("mobilevr");
18221
18896
  },
18222
18897
  softstart: 0,
18223
- touchMode: "off"
18898
+ touchMode: touchSupport ? "horizontaloffset" : "off"
18224
18899
  }
18225
18900
  ),
18226
18901
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
18227
18902
  WebVRStereoRenderer,
18228
18903
  {
18229
18904
  mode,
18905
+ oversampling: resolvedOversampling,
18230
18906
  profile: resolvedProfile,
18231
- screensize: settings.screensize
18907
+ screensize: effectiveScreensize
18232
18908
  }
18233
18909
  ),
18234
18910
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
@@ -18243,7 +18919,7 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18243
18919
  });
18244
18920
 
18245
18921
  // src/pano-scene-menu.tsx
18246
- var import_react62 = require("react");
18922
+ var import_react63 = require("react");
18247
18923
 
18248
18924
  // src/drag-motion.ts
18249
18925
  var DRAG_INERTIA_DAMPING = 8;
@@ -18627,10 +19303,10 @@ function PanoSceneMenu({
18627
19303
  expandLabel = "Show scenes",
18628
19304
  collapseLabel = "Hide scenes"
18629
19305
  }) {
18630
- const listId = (0, import_react62.useId)();
18631
- const listRef = (0, import_react62.useRef)(null);
18632
- const trackRef = (0, import_react62.useRef)(null);
18633
- const itemRefs = (0, import_react62.useRef)(/* @__PURE__ */ new Map());
19306
+ const listId = (0, import_react63.useId)();
19307
+ const listRef = (0, import_react63.useRef)(null);
19308
+ const trackRef = (0, import_react63.useRef)(null);
19309
+ const itemRefs = (0, import_react63.useRef)(/* @__PURE__ */ new Map());
18634
19310
  const resolved = resolveAppearance2(appearance);
18635
19311
  const resolvedInset = resolveInset(inset);
18636
19312
  const horizontal = isHorizontal(placement);
@@ -18639,34 +19315,34 @@ function PanoSceneMenu({
18639
19315
  dragMotionProp === false ? false : dragMotionProp
18640
19316
  );
18641
19317
  const isExpandedControlled = expandedProp !== void 0;
18642
- const [internalExpanded, setInternalExpanded] = (0, import_react62.useState)(defaultExpanded);
19318
+ const [internalExpanded, setInternalExpanded] = (0, import_react63.useState)(defaultExpanded);
18643
19319
  const expanded = collapsible ? isExpandedControlled ? expandedProp : internalExpanded : true;
18644
- const [offset, setOffset] = (0, import_react62.useState)(0);
18645
- const [viewportSize, setViewportSize] = (0, import_react62.useState)(0);
18646
- const [contentSize, setContentSize] = (0, import_react62.useState)(0);
18647
- const offsetRef = (0, import_react62.useRef)(0);
18648
- const dragRef = (0, import_react62.useRef)(null);
18649
- const animRef = (0, import_react62.useRef)(null);
19320
+ const [offset, setOffset] = (0, import_react63.useState)(0);
19321
+ const [viewportSize, setViewportSize] = (0, import_react63.useState)(0);
19322
+ const [contentSize, setContentSize] = (0, import_react63.useState)(0);
19323
+ const offsetRef = (0, import_react63.useRef)(0);
19324
+ const dragRef = (0, import_react63.useRef)(null);
19325
+ const animRef = (0, import_react63.useRef)(null);
18650
19326
  const maxOffset = Math.max(0, contentSize - viewportSize);
18651
19327
  offsetRef.current = offset;
18652
- const activeIndex = (0, import_react62.useMemo)(() => {
19328
+ const activeIndex = (0, import_react63.useMemo)(() => {
18653
19329
  if (activeId == null) {
18654
19330
  return -1;
18655
19331
  }
18656
19332
  return items.findIndex((item) => item.id === activeId);
18657
19333
  }, [activeId, items]);
18658
- const cancelScrollAnimation = (0, import_react62.useCallback)(() => {
19334
+ const cancelScrollAnimation = (0, import_react63.useCallback)(() => {
18659
19335
  const anim = animRef.current;
18660
19336
  if (anim?.raf != null) {
18661
19337
  cancelAnimationFrame(anim.raf);
18662
19338
  }
18663
19339
  animRef.current = null;
18664
19340
  }, []);
18665
- const clampOffset = (0, import_react62.useCallback)(
19341
+ const clampOffset = (0, import_react63.useCallback)(
18666
19342
  (value) => Math.min(maxOffset, Math.max(0, value)),
18667
19343
  [maxOffset]
18668
19344
  );
18669
- const startScrollAnimation = (0, import_react62.useCallback)(
19345
+ const startScrollAnimation = (0, import_react63.useCallback)(
18670
19346
  (velocity) => {
18671
19347
  cancelScrollAnimation();
18672
19348
  const current = offsetRef.current;
@@ -18721,8 +19397,8 @@ function PanoSceneMenu({
18721
19397
  maxOffset
18722
19398
  ]
18723
19399
  );
18724
- (0, import_react62.useEffect)(() => () => cancelScrollAnimation(), [cancelScrollAnimation]);
18725
- (0, import_react62.useLayoutEffect)(() => {
19400
+ (0, import_react63.useEffect)(() => () => cancelScrollAnimation(), [cancelScrollAnimation]);
19401
+ (0, import_react63.useLayoutEffect)(() => {
18726
19402
  if (!customDrag || !expanded) return;
18727
19403
  const list = listRef.current;
18728
19404
  const track = trackRef.current;
@@ -18743,12 +19419,12 @@ function PanoSceneMenu({
18743
19419
  observer.observe(track);
18744
19420
  return () => observer.disconnect();
18745
19421
  }, [customDrag, expanded, horizontal, items]);
18746
- (0, import_react62.useEffect)(() => {
19422
+ (0, import_react63.useEffect)(() => {
18747
19423
  if (!customDrag) return;
18748
19424
  if (dragRef.current || animRef.current) return;
18749
19425
  setOffset((previous) => Math.min(maxOffset, Math.max(0, previous)));
18750
19426
  }, [customDrag, maxOffset]);
18751
- (0, import_react62.useEffect)(() => {
19427
+ (0, import_react63.useEffect)(() => {
18752
19428
  if (!expanded || activeId == null) {
18753
19429
  return;
18754
19430
  }
@@ -18791,7 +19467,7 @@ function PanoSceneMenu({
18791
19467
  expanded,
18792
19468
  horizontal
18793
19469
  ]);
18794
- const rootStyle = (0, import_react62.useMemo)(() => {
19470
+ const rootStyle = (0, import_react63.useMemo)(() => {
18795
19471
  const vars = {
18796
19472
  "--pano-scene-menu-background": resolved.background,
18797
19473
  "--pano-scene-menu-padding": cssLength(resolved.padding),
@@ -18933,7 +19609,7 @@ function PanoSceneMenu({
18933
19609
  setOffset((previous) => clampOffset(previous + event.deltaX));
18934
19610
  };
18935
19611
  const axisPoint = (event) => horizontal ? event.clientX : event.clientY;
18936
- const endSuppressClickRef = (0, import_react62.useRef)(false);
19612
+ const endSuppressClickRef = (0, import_react63.useRef)(false);
18937
19613
  const beginListDrag = (event, captureTarget) => {
18938
19614
  if (!customDrag || event.button !== 0) return;
18939
19615
  cancelScrollAnimation();
@@ -19143,7 +19819,7 @@ function PanoSceneMenu({
19143
19819
  }
19144
19820
 
19145
19821
  // src/use-pano-viewer.ts
19146
- var import_react63 = require("react");
19822
+ var import_react64 = require("react");
19147
19823
  function createPanoViewerHandle(ref) {
19148
19824
  return {
19149
19825
  getView: () => ref.current?.getView() ?? { yaw: 0, pitch: 0, fov: 75 },
@@ -19174,6 +19850,36 @@ function createPanoViewerHandle(ref) {
19174
19850
  isVRAvailable: () => ref.current?.isVRAvailable() ?? false,
19175
19851
  isVREnabled: () => ref.current?.isVREnabled() ?? false,
19176
19852
  getVRMode: () => ref.current?.getVRMode() ?? null,
19853
+ getVRState: () => ref.current?.getVRState() ?? EMPTY_WEBVR_STATE,
19854
+ subscribeVR: (onStoreChange) => {
19855
+ let disposed = false;
19856
+ let unsubscribe;
19857
+ const timers = [];
19858
+ const attach = () => {
19859
+ if (disposed || unsubscribe) {
19860
+ return;
19861
+ }
19862
+ const handle = ref.current;
19863
+ if (!handle) {
19864
+ return;
19865
+ }
19866
+ unsubscribe = handle.subscribeVR(onStoreChange);
19867
+ onStoreChange();
19868
+ };
19869
+ attach();
19870
+ if (typeof window !== "undefined") {
19871
+ for (const delay of [0, 16, 50, 100, 250]) {
19872
+ timers.push(window.setTimeout(attach, delay));
19873
+ }
19874
+ }
19875
+ return () => {
19876
+ disposed = true;
19877
+ for (const id of timers) {
19878
+ window.clearTimeout(id);
19879
+ }
19880
+ unsubscribe?.();
19881
+ };
19882
+ },
19177
19883
  requestVRPermission: () => ref.current?.requestVRPermission() ?? Promise.resolve(false),
19178
19884
  getVideo: () => ref.current?.getVideo() ?? null,
19179
19885
  subscribeVideo: (onStoreChange) => ref.current?.subscribeVideo(onStoreChange) ?? (() => {
@@ -19225,14 +19931,14 @@ function createPanoViewerHandle(ref) {
19225
19931
  };
19226
19932
  }
19227
19933
  function usePanoViewer() {
19228
- const ref = (0, import_react63.useRef)(null);
19229
- const handle = (0, import_react63.useMemo)(() => createPanoViewerHandle(ref), []);
19230
- return (0, import_react63.useMemo)(() => ({ ref, ...handle }), [handle]);
19934
+ const ref = (0, import_react64.useRef)(null);
19935
+ const handle = (0, import_react64.useMemo)(() => createPanoViewerHandle(ref), []);
19936
+ return (0, import_react64.useMemo)(() => ({ ref, ...handle }), [handle]);
19231
19937
  }
19232
19938
 
19233
19939
  // src/video/pano-video.tsx
19234
19940
  var import_fiber31 = require("@react-three/fiber");
19235
- var import_react64 = require("react");
19941
+ var import_react65 = require("react");
19236
19942
  var import_three40 = require("three");
19237
19943
  var import_jsx_runtime42 = require("react/jsx-runtime");
19238
19944
  var EMPTY_TRACKS = [];
@@ -19319,10 +20025,10 @@ function applyEquirectangularLayout(texture, anisotropy) {
19319
20025
  }
19320
20026
  function usePosterTexture2(poster, onError) {
19321
20027
  const gl = (0, import_fiber31.useThree)((state) => state.gl);
19322
- const [texture, setTexture] = (0, import_react64.useState)(null);
19323
- const onErrorRef = (0, import_react64.useRef)(onError);
20028
+ const [texture, setTexture] = (0, import_react65.useState)(null);
20029
+ const onErrorRef = (0, import_react65.useRef)(onError);
19324
20030
  onErrorRef.current = onError;
19325
- (0, import_react64.useEffect)(() => {
20031
+ (0, import_react65.useEffect)(() => {
19326
20032
  let active = true;
19327
20033
  setTexture(null);
19328
20034
  if (!poster) {
@@ -19359,7 +20065,7 @@ function usePosterTexture2(poster, onError) {
19359
20065
  active = false;
19360
20066
  };
19361
20067
  }, [gl, poster]);
19362
- (0, import_react64.useEffect)(
20068
+ (0, import_react65.useEffect)(
19363
20069
  () => () => {
19364
20070
  texture?.dispose();
19365
20071
  },
@@ -19418,37 +20124,37 @@ function PanoVideo({
19418
20124
  }) {
19419
20125
  usePanoMediaActivationIntent(autoPlay);
19420
20126
  const gl = (0, import_fiber31.useThree)((state) => state.gl);
19421
- const host = (0, import_react64.useContext)(PanoVideoHostContext);
19422
- const variants = (0, import_react64.useMemo)(
20127
+ const host = (0, import_react65.useContext)(PanoVideoHostContext);
20128
+ const variants = (0, import_react65.useMemo)(
19423
20129
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
19424
20130
  [poster, src, variantsProp]
19425
20131
  );
19426
20132
  const tracks = tracksProp ?? EMPTY_TRACKS;
19427
20133
  const captionsEnabled = captions !== false;
19428
- const captionAppearance = (0, import_react64.useMemo)(
20134
+ const captionAppearance = (0, import_react65.useMemo)(
19429
20135
  () => resolvePanoVideoCaptionAppearance(
19430
20136
  typeof captions === "object" ? captions : void 0
19431
20137
  ),
19432
20138
  [captions]
19433
20139
  );
19434
- const [variantId, setVariantId] = (0, import_react64.useState)(
20140
+ const [variantId, setVariantId] = (0, import_react65.useState)(
19435
20141
  () => resolvePanoVideoVariantId(variants, defaultVariantId)
19436
20142
  );
19437
- const [trackId, setTrackId] = (0, import_react64.useState)(
20143
+ const [trackId, setTrackId] = (0, import_react65.useState)(
19438
20144
  () => captionsEnabled ? defaultPanoVideoTrackId(tracks) : null
19439
20145
  );
19440
- const [ready, setReady] = (0, import_react64.useState)(false);
19441
- const autoPlayRef = (0, import_react64.useRef)(autoPlay);
20146
+ const [ready, setReady] = (0, import_react65.useState)(false);
20147
+ const autoPlayRef = (0, import_react65.useRef)(autoPlay);
19442
20148
  autoPlayRef.current = autoPlay;
19443
- const blockedRef = (0, import_react64.useRef)(false);
19444
- const captionAppearanceOverrideRef = (0, import_react64.useRef)(
20149
+ const blockedRef = (0, import_react65.useRef)(false);
20150
+ const captionAppearanceOverrideRef = (0, import_react65.useRef)(
19445
20151
  {}
19446
20152
  );
19447
- const mediaCapabilitiesRef = (0, import_react64.useRef)({
20153
+ const mediaCapabilitiesRef = (0, import_react65.useRef)({
19448
20154
  volumeAdjustable: true,
19449
20155
  playbackRates: [...DEFAULT_PANO_VIDEO_PLAYBACK_RATES]
19450
20156
  });
19451
- const storeRef = (0, import_react64.useRef)(null);
20157
+ const storeRef = (0, import_react65.useRef)(null);
19452
20158
  if (!storeRef.current) {
19453
20159
  storeRef.current = {
19454
20160
  snapshot: {
@@ -19458,7 +20164,7 @@ function PanoVideo({
19458
20164
  listeners: /* @__PURE__ */ new Set()
19459
20165
  };
19460
20166
  }
19461
- const actionsRef = (0, import_react64.useRef)({
20167
+ const actionsRef = (0, import_react65.useRef)({
19462
20168
  play: async () => {
19463
20169
  },
19464
20170
  pause: () => {
@@ -19478,19 +20184,19 @@ function PanoVideo({
19478
20184
  setCaptionAppearance: (_appearance) => {
19479
20185
  }
19480
20186
  });
19481
- const onLoadRef = (0, import_react64.useRef)(onLoad);
19482
- const onErrorRef = (0, import_react64.useRef)(onError);
19483
- const onPlaybackStateChangeRef = (0, import_react64.useRef)(onPlaybackStateChange);
19484
- const onEndedRef = (0, import_react64.useRef)(onEnded);
19485
- const onVariantChangeRef = (0, import_react64.useRef)(onVariantChange);
19486
- const onTrackChangeRef = (0, import_react64.useRef)(onTrackChange);
20187
+ const onLoadRef = (0, import_react65.useRef)(onLoad);
20188
+ const onErrorRef = (0, import_react65.useRef)(onError);
20189
+ const onPlaybackStateChangeRef = (0, import_react65.useRef)(onPlaybackStateChange);
20190
+ const onEndedRef = (0, import_react65.useRef)(onEnded);
20191
+ const onVariantChangeRef = (0, import_react65.useRef)(onVariantChange);
20192
+ const onTrackChangeRef = (0, import_react65.useRef)(onTrackChange);
19487
20193
  onLoadRef.current = onLoad;
19488
20194
  onErrorRef.current = onError;
19489
20195
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
19490
20196
  onEndedRef.current = onEnded;
19491
20197
  onVariantChangeRef.current = onVariantChange;
19492
20198
  onTrackChangeRef.current = onTrackChange;
19493
- const resource = (0, import_react64.useMemo)(() => {
20199
+ const resource = (0, import_react65.useMemo)(() => {
19494
20200
  if (typeof document === "undefined") {
19495
20201
  return null;
19496
20202
  }
@@ -19505,7 +20211,7 @@ function PanoVideo({
19505
20211
  texture.generateMipmaps = false;
19506
20212
  return { video, texture };
19507
20213
  }, []);
19508
- (0, import_react64.useEffect)(
20214
+ (0, import_react65.useEffect)(
19509
20215
  () => () => {
19510
20216
  if (!resource) {
19511
20217
  return;
@@ -19520,13 +20226,13 @@ function PanoVideo({
19520
20226
  },
19521
20227
  [resource]
19522
20228
  );
19523
- (0, import_react64.useEffect)(() => {
20229
+ (0, import_react65.useEffect)(() => {
19524
20230
  if (!resource) {
19525
20231
  return;
19526
20232
  }
19527
20233
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
19528
20234
  }, [gl, resource]);
19529
- (0, import_react64.useLayoutEffect)(() => {
20235
+ (0, import_react65.useLayoutEffect)(() => {
19530
20236
  if (!resource) {
19531
20237
  return;
19532
20238
  }
@@ -19542,7 +20248,7 @@ function PanoVideo({
19542
20248
  activeVariant?.poster ?? poster,
19543
20249
  onError
19544
20250
  );
19545
- const emitSnapshot = (0, import_react64.useCallback)(
20251
+ const emitSnapshot = (0, import_react65.useCallback)(
19546
20252
  (patch) => {
19547
20253
  const store = storeRef.current;
19548
20254
  const video = resource?.video;
@@ -19594,7 +20300,7 @@ function PanoVideo({
19594
20300
  captionAppearance
19595
20301
  ]
19596
20302
  );
19597
- const controller = (0, import_react64.useMemo)(
20303
+ const controller = (0, import_react65.useMemo)(
19598
20304
  () => ({
19599
20305
  subscribe: (onStoreChange) => {
19600
20306
  const store = storeRef.current;
@@ -19646,7 +20352,7 @@ function PanoVideo({
19646
20352
  }),
19647
20353
  []
19648
20354
  );
19649
- (0, import_react64.useLayoutEffect)(() => {
20355
+ (0, import_react65.useLayoutEffect)(() => {
19650
20356
  if (!host || variants.length === 0) {
19651
20357
  return;
19652
20358
  }
@@ -19659,7 +20365,7 @@ function PanoVideo({
19659
20365
  notifyPanoVideoHost(host);
19660
20366
  };
19661
20367
  }, [captionsEnabled, controller, controls, host, variants.length]);
19662
- (0, import_react64.useLayoutEffect)(() => {
20368
+ (0, import_react65.useLayoutEffect)(() => {
19663
20369
  if (!resource) {
19664
20370
  return;
19665
20371
  }
@@ -19672,10 +20378,10 @@ function PanoVideo({
19672
20378
  };
19673
20379
  emitSnapshot();
19674
20380
  }, [emitSnapshot, playbackRates, resource]);
19675
- (0, import_react64.useEffect)(() => {
20381
+ (0, import_react65.useEffect)(() => {
19676
20382
  emitSnapshot();
19677
20383
  }, [emitSnapshot]);
19678
- (0, import_react64.useEffect)(() => {
20384
+ (0, import_react65.useEffect)(() => {
19679
20385
  if (!resource || !activeVariant || !mediaKey) {
19680
20386
  return;
19681
20387
  }
@@ -19732,7 +20438,7 @@ function PanoVideo({
19732
20438
  video.removeEventListener("error", handleError);
19733
20439
  };
19734
20440
  }, [mediaKey, resource]);
19735
- (0, import_react64.useEffect)(() => {
20441
+ (0, import_react65.useEffect)(() => {
19736
20442
  if (!resource) {
19737
20443
  return;
19738
20444
  }
@@ -19824,7 +20530,7 @@ function PanoVideo({
19824
20530
  }
19825
20531
  };
19826
20532
  }, [emitSnapshot, resource]);
19827
- (0, import_react64.useLayoutEffect)(() => {
20533
+ (0, import_react65.useLayoutEffect)(() => {
19828
20534
  if (!resource) {
19829
20535
  return;
19830
20536
  }
@@ -19837,7 +20543,7 @@ function PanoVideo({
19837
20543
  applyInlineVideoPlayback(video, playsInline);
19838
20544
  video.preload = preload;
19839
20545
  }, [loop, muted, playsInline, preload, resource, volume]);
19840
- (0, import_react64.useEffect)(() => {
20546
+ (0, import_react65.useEffect)(() => {
19841
20547
  if (!resource) {
19842
20548
  return;
19843
20549
  }
@@ -19887,7 +20593,7 @@ function PanoVideo({
19887
20593
  video.removeEventListener("ratechange", handleVolume);
19888
20594
  };
19889
20595
  }, [emitSnapshot, resource]);
19890
- (0, import_react64.useEffect)(() => {
20596
+ (0, import_react65.useEffect)(() => {
19891
20597
  if (!resource) {
19892
20598
  return;
19893
20599
  }
@@ -19922,7 +20628,7 @@ function PanoVideo({
19922
20628
  video.textTracks.removeEventListener("change", applyModes);
19923
20629
  };
19924
20630
  }, [emitSnapshot, resource, trackId, tracks]);
19925
- (0, import_react64.useEffect)(() => {
20631
+ (0, import_react65.useEffect)(() => {
19926
20632
  if (!captionsEnabled) {
19927
20633
  setTrackId(null);
19928
20634
  return;
@@ -19957,7 +20663,7 @@ function PanoVideo({
19957
20663
  }
19958
20664
 
19959
20665
  // src/map/pano-map.tsx
19960
- var import_react66 = require("react");
20666
+ var import_react67 = require("react");
19961
20667
 
19962
20668
  // src/map/geometry.ts
19963
20669
  function sectorRotation(heading, yaw) {
@@ -20017,14 +20723,14 @@ function normalizeDegrees(value) {
20017
20723
  }
20018
20724
 
20019
20725
  // src/map/use-map-view.ts
20020
- var import_react65 = require("react");
20726
+ var import_react66 = require("react");
20021
20727
  var DEFAULT_VIEW2 = { yaw: 0, pitch: 0, fov: 75 };
20022
20728
  function useMapView(controlled) {
20023
- const eventBus = (0, import_react65.useContext)(PanoEventBusContext);
20024
- const [view, setView] = (0, import_react65.useState)(controlled ?? DEFAULT_VIEW2);
20025
- const controlledRef = (0, import_react65.useRef)(controlled);
20729
+ const eventBus = (0, import_react66.useContext)(PanoEventBusContext);
20730
+ const [view, setView] = (0, import_react66.useState)(controlled ?? DEFAULT_VIEW2);
20731
+ const controlledRef = (0, import_react66.useRef)(controlled);
20026
20732
  controlledRef.current = controlled;
20027
- (0, import_react65.useEffect)(() => {
20733
+ (0, import_react66.useEffect)(() => {
20028
20734
  if (controlled !== void 0) {
20029
20735
  setView(controlled);
20030
20736
  return;
@@ -20174,22 +20880,22 @@ function PanoMap({
20174
20880
  renderChrome,
20175
20881
  initialScale
20176
20882
  }) {
20177
- const reactId = (0, import_react66.useId)().replace(/:/g, "");
20883
+ const reactId = (0, import_react67.useId)().replace(/:/g, "");
20178
20884
  const view = useMapView(controlledView);
20179
- const rootRef = (0, import_react66.useRef)(null);
20180
- const containerRef = (0, import_react66.useRef)(null);
20181
- const sectorNodeRef = (0, import_react66.useRef)(null);
20182
- const yawRef = (0, import_react66.useRef)(view.yaw);
20183
- const [size, setSize] = (0, import_react66.useState)({ width: 0, height: 0 });
20184
- const [imageSize2, setImageSize] = (0, import_react66.useState)({ width: 0, height: 0 });
20185
- const [scale, setScale] = (0, import_react66.useState)(1);
20186
- const [pan, setPan] = (0, import_react66.useState)({ x: 0, y: 0 });
20187
- const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react66.useState)(false);
20188
- const dragRef = (0, import_react66.useRef)(null);
20189
- const panAnimRef = (0, import_react66.useRef)(null);
20190
- const panRef = (0, import_react66.useRef)(pan);
20191
- const scaleRef = (0, import_react66.useRef)(scale);
20192
- const initialViewKeyRef = (0, import_react66.useRef)(null);
20885
+ const rootRef = (0, import_react67.useRef)(null);
20886
+ const containerRef = (0, import_react67.useRef)(null);
20887
+ const sectorNodeRef = (0, import_react67.useRef)(null);
20888
+ const yawRef = (0, import_react67.useRef)(view.yaw);
20889
+ const [size, setSize] = (0, import_react67.useState)({ width: 0, height: 0 });
20890
+ const [imageSize2, setImageSize] = (0, import_react67.useState)({ width: 0, height: 0 });
20891
+ const [scale, setScale] = (0, import_react67.useState)(1);
20892
+ const [pan, setPan] = (0, import_react67.useState)({ x: 0, y: 0 });
20893
+ const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react67.useState)(false);
20894
+ const dragRef = (0, import_react67.useRef)(null);
20895
+ const panAnimRef = (0, import_react67.useRef)(null);
20896
+ const panRef = (0, import_react67.useRef)(pan);
20897
+ const scaleRef = (0, import_react67.useRef)(scale);
20898
+ const initialViewKeyRef = (0, import_react67.useRef)(null);
20193
20899
  const spots = map.spots;
20194
20900
  const iconSize = map.iconSize ?? DEFAULT_ICON_SIZE2;
20195
20901
  const interaction = map.interaction ?? "fixed";
@@ -20207,15 +20913,15 @@ function PanoMap({
20207
20913
  const collapseControl = showCollapseControl ?? map.controls?.collapse === true;
20208
20914
  const collapsed = controlledCollapsed ?? uncontrolledCollapsed;
20209
20915
  const editing = mode === "edit";
20210
- const fit = (0, import_react66.useMemo)(
20916
+ const fit = (0, import_react67.useMemo)(
20211
20917
  () => containRect(size.width, size.height, imageSize2.width, imageSize2.height),
20212
20918
  [size, imageSize2]
20213
20919
  );
20214
- const viewport = (0, import_react66.useMemo)(
20920
+ const viewport = (0, import_react67.useMemo)(
20215
20921
  () => ({ fit, scale, panX: pan.x, panY: pan.y }),
20216
20922
  [fit, scale, pan]
20217
20923
  );
20218
- (0, import_react66.useLayoutEffect)(() => {
20924
+ (0, import_react67.useLayoutEffect)(() => {
20219
20925
  if (collapsed) return;
20220
20926
  const node = containerRef.current;
20221
20927
  if (!node) return;
@@ -20232,7 +20938,7 @@ function PanoMap({
20232
20938
  observer.observe(node);
20233
20939
  return () => observer.disconnect();
20234
20940
  }, [collapsed]);
20235
- (0, import_react66.useEffect)(() => {
20941
+ (0, import_react67.useEffect)(() => {
20236
20942
  yawRef.current = view.yaw;
20237
20943
  const node = sectorNodeRef.current;
20238
20944
  if (!node) return;
@@ -20249,7 +20955,7 @@ function PanoMap({
20249
20955
  };
20250
20956
  panRef.current = pan;
20251
20957
  scaleRef.current = scale;
20252
- const panBoundsFor = (0, import_react66.useCallback)(
20958
+ const panBoundsFor = (0, import_react67.useCallback)(
20253
20959
  (nextScale) => {
20254
20960
  const width = fit.width * nextScale;
20255
20961
  const height = fit.height * nextScale;
@@ -20265,7 +20971,7 @@ function PanoMap({
20265
20971
  },
20266
20972
  [fit, size]
20267
20973
  );
20268
- const clampPan = (0, import_react66.useCallback)(
20974
+ const clampPan = (0, import_react67.useCallback)(
20269
20975
  (nextScale, nextPan) => {
20270
20976
  const bounds = panBoundsFor(nextScale);
20271
20977
  return {
@@ -20275,14 +20981,14 @@ function PanoMap({
20275
20981
  },
20276
20982
  [panBoundsFor]
20277
20983
  );
20278
- const cancelPanAnimation = (0, import_react66.useCallback)(() => {
20984
+ const cancelPanAnimation = (0, import_react67.useCallback)(() => {
20279
20985
  const anim = panAnimRef.current;
20280
20986
  if (anim?.raf != null) {
20281
20987
  cancelAnimationFrame(anim.raf);
20282
20988
  }
20283
20989
  panAnimRef.current = null;
20284
20990
  }, []);
20285
- const startPanAnimation = (0, import_react66.useCallback)(
20991
+ const startPanAnimation = (0, import_react67.useCallback)(
20286
20992
  (velocity) => {
20287
20993
  cancelPanAnimation();
20288
20994
  const current = panRef.current;
@@ -20342,11 +21048,11 @@ function PanoMap({
20342
21048
  },
20343
21049
  [cancelPanAnimation, clampPan, dragMotion.bounce, dragMotion.inertia, panBoundsFor]
20344
21050
  );
20345
- (0, import_react66.useEffect)(() => () => cancelPanAnimation(), [cancelPanAnimation]);
20346
- (0, import_react66.useEffect)(() => {
21051
+ (0, import_react67.useEffect)(() => () => cancelPanAnimation(), [cancelPanAnimation]);
21052
+ (0, import_react67.useEffect)(() => {
20347
21053
  if (collapsed) cancelPanAnimation();
20348
21054
  }, [cancelPanAnimation, collapsed]);
20349
- (0, import_react66.useEffect)(() => {
21055
+ (0, import_react67.useEffect)(() => {
20350
21056
  if (!gestureZoom) {
20351
21057
  initialViewKeyRef.current = null;
20352
21058
  setScale(1);
@@ -20361,7 +21067,7 @@ function PanoMap({
20361
21067
  return next;
20362
21068
  });
20363
21069
  }, [clampPan, gestureZoom, maxScale]);
20364
- (0, import_react66.useEffect)(() => {
21070
+ (0, import_react67.useEffect)(() => {
20365
21071
  if (!gestureZoom) return;
20366
21072
  if (!(fit.width > 0) || !(fit.height > 0)) return;
20367
21073
  if (!(size.width > 0) || !(size.height > 0)) return;
@@ -20388,7 +21094,7 @@ function PanoMap({
20388
21094
  size.height,
20389
21095
  size.width
20390
21096
  ]);
20391
- const applyZoom = (0, import_react66.useCallback)(
21097
+ const applyZoom = (0, import_react67.useCallback)(
20392
21098
  (nextScale, origin) => {
20393
21099
  if (!gestureZoom) return;
20394
21100
  cancelPanAnimation();
@@ -20416,7 +21122,7 @@ function PanoMap({
20416
21122
  size.width
20417
21123
  ]
20418
21124
  );
20419
- (0, import_react66.useEffect)(() => {
21125
+ (0, import_react67.useEffect)(() => {
20420
21126
  const root = rootRef.current;
20421
21127
  if (!root) return;
20422
21128
  const onWheel = (event) => {
@@ -20838,7 +21544,7 @@ function PanoMap({
20838
21544
  }
20839
21545
 
20840
21546
  // src/map/pano-map-overlay.tsx
20841
- var import_react67 = require("react");
21547
+ var import_react68 = require("react");
20842
21548
  var import_jsx_runtime44 = require("react/jsx-runtime");
20843
21549
  var DEFAULT_DIALOG_WIDTH = 720;
20844
21550
  var DEFAULT_DIALOG_HEIGHT = 520;
@@ -20858,10 +21564,10 @@ function PanoMapDialogShell({
20858
21564
  }) {
20859
21565
  usePanoModal(open);
20860
21566
  const { size } = usePanoOverlay();
20861
- const titleId = (0, import_react67.useId)();
20862
- const panelRef = (0, import_react67.useRef)(null);
20863
- const previouslyFocused = (0, import_react67.useRef)(null);
20864
- (0, import_react67.useEffect)(() => {
21567
+ const titleId = (0, import_react68.useId)();
21568
+ const panelRef = (0, import_react68.useRef)(null);
21569
+ const previouslyFocused = (0, import_react68.useRef)(null);
21570
+ (0, import_react68.useEffect)(() => {
20865
21571
  if (!open) return;
20866
21572
  const active = panelRef.current?.ownerDocument.activeElement;
20867
21573
  previouslyFocused.current = active instanceof HTMLElement ? active : null;
@@ -20979,8 +21685,8 @@ function PanoMapOverlay({
20979
21685
  editorSurface = false
20980
21686
  }) {
20981
21687
  const { size } = usePanoOverlay();
20982
- const [open, setOpen] = (0, import_react67.useState)(false);
20983
- const [collapsed, setCollapsed] = (0, import_react67.useState)(false);
21688
+ const [open, setOpen] = (0, import_react68.useState)(false);
21689
+ const [collapsed, setCollapsed] = (0, import_react68.useState)(false);
20984
21690
  const basePresentation = presentationOverride ?? map.presentation ?? {
20985
21691
  mode: "docked",
20986
21692
  corner: "bottom-left",
@@ -20995,7 +21701,7 @@ function PanoMapOverlay({
20995
21701
  const offsetY = offsetYProp ?? basePresentation.offsetY ?? 16;
20996
21702
  const width = widthProp ?? basePresentation.width ?? (presentationMode === "dialog" ? 48 : 220);
20997
21703
  const height = heightProp ?? basePresentation.height ?? (presentationMode === "dialog" ? 48 : 160);
20998
- const resolvedMap = (0, import_react67.useMemo)(() => {
21704
+ const resolvedMap = (0, import_react68.useMemo)(() => {
20999
21705
  if (interaction === void 0 && maxScale === void 0 && iconSize === void 0) {
21000
21706
  return map;
21001
21707
  }
@@ -21023,7 +21729,7 @@ function PanoMapOverlay({
21023
21729
  if (presentationMode === "dialog") setOpen(false);
21024
21730
  }
21025
21731
  };
21026
- const editorBox = (0, import_react67.useMemo)(() => {
21732
+ const editorBox = (0, import_react68.useMemo)(() => {
21027
21733
  const panelWidth2 = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
21028
21734
  const panelHeight2 = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
21029
21735
  return {
@@ -21124,6 +21830,7 @@ function PanoMapOverlay({
21124
21830
  DEFAULT_PANO_VIDEO_PLAYBACK_RATES,
21125
21831
  DEFAULT_WEBVR_CHROME_APPEARANCE,
21126
21832
  DEFAULT_WEBVR_CURSOR_DWELL_MS,
21833
+ EMPTY_WEBVR_STATE,
21127
21834
  GraphicHotspot,
21128
21835
  Gyro,
21129
21836
  IframeHotspot,
@@ -21138,6 +21845,7 @@ function PanoMapOverlay({
21138
21845
  PanoFilter,
21139
21846
  PanoMap,
21140
21847
  PanoMapOverlay,
21848
+ PanoOverlay,
21141
21849
  PanoOverlayPortalProvider,
21142
21850
  PanoSceneMenu,
21143
21851
  PanoVideo,