@ericchen1990/pano-view 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2252,6 +2252,29 @@ import {
2252
2252
  useState as useState5
2253
2253
  } from "react";
2254
2254
 
2255
+ // src/background-audio-host.ts
2256
+ import { createContext } from "react";
2257
+ var BackgroundAudioHostContext = createContext(null);
2258
+ function createBackgroundAudioHost() {
2259
+ return {
2260
+ controller: null,
2261
+ revision: 0,
2262
+ listeners: /* @__PURE__ */ new Set()
2263
+ };
2264
+ }
2265
+ function notifyBackgroundAudioHost(host) {
2266
+ host.revision += 1;
2267
+ for (const listener of host.listeners) {
2268
+ listener();
2269
+ }
2270
+ }
2271
+ function subscribeBackgroundAudioHost(host, listener) {
2272
+ host.listeners.add(listener);
2273
+ return () => {
2274
+ host.listeners.delete(listener);
2275
+ };
2276
+ }
2277
+
2255
2278
  // src/cn.ts
2256
2279
  function cn(...values) {
2257
2280
  return values.filter(Boolean).join(" ");
@@ -2262,7 +2285,7 @@ import { useFrame as useFrame2 } from "@react-three/fiber";
2262
2285
  import { useContext, useEffect as useEffect2, useRef as useRef2 } from "react";
2263
2286
 
2264
2287
  // src/pano-event-bus.ts
2265
- import { createContext } from "react";
2288
+ import { createContext as createContext2 } from "react";
2266
2289
  function createPanoEventBus() {
2267
2290
  const listeners = /* @__PURE__ */ new Map();
2268
2291
  return {
@@ -2291,13 +2314,13 @@ function createPanoEventBus() {
2291
2314
  }
2292
2315
  };
2293
2316
  }
2294
- var PanoEventBusContext = createContext(null);
2317
+ var PanoEventBusContext = createContext2(null);
2295
2318
 
2296
2319
  // src/panorama-view-runtime.tsx
2297
2320
  import { useFrame, useThree } from "@react-three/fiber";
2298
2321
  import { useXR } from "@react-three/xr";
2299
2322
  import {
2300
- createContext as createContext2,
2323
+ createContext as createContext3,
2301
2324
  forwardRef,
2302
2325
  useEffect,
2303
2326
  useImperativeHandle,
@@ -2313,7 +2336,7 @@ var DEFAULT_ROTATE_DAMPING = 14;
2313
2336
  var DEFAULT_ZOOM_DAMPING = 16;
2314
2337
  var DEFAULT_FRICTION_STOP = 0.01;
2315
2338
  var VIEW_SETTLE_EPSILON = 1e-3;
2316
- var PanoramaViewContext = createContext2(null);
2339
+ var PanoramaViewContext = createContext3(null);
2317
2340
  function clamp(value, min, max) {
2318
2341
  return Math.max(min, Math.min(max, value));
2319
2342
  }
@@ -2790,9 +2813,9 @@ function AutoRotate({
2790
2813
  }
2791
2814
 
2792
2815
  // src/control-claims.ts
2793
- import { createContext as createContext3, useContext as useContext2 } from "react";
2794
- var ControlClaimsContext = createContext3(null);
2795
- var DefaultControlChannelContext = createContext3(false);
2816
+ import { createContext as createContext4, useContext as useContext2 } from "react";
2817
+ var ControlClaimsContext = createContext4(null);
2818
+ var DefaultControlChannelContext = createContext4(false);
2796
2819
  var CHANNEL_COMPONENT = {
2797
2820
  mouse: "MouseControls",
2798
2821
  touch: "TouchControls",
@@ -2874,7 +2897,7 @@ function subscribeFullscreenChange(onChange) {
2874
2897
 
2875
2898
  // src/hotspot/accessibility.tsx
2876
2899
  import {
2877
- createContext as createContext4,
2900
+ createContext as createContext5,
2878
2901
  useCallback,
2879
2902
  useContext as useContext3,
2880
2903
  useEffect as useEffect3,
@@ -2883,7 +2906,7 @@ import {
2883
2906
  useState
2884
2907
  } from "react";
2885
2908
  import { jsx } from "react/jsx-runtime";
2886
- var HotspotAccessibilityContext = createContext4(null);
2909
+ var HotspotAccessibilityContext = createContext5(null);
2887
2910
  function warnAboutMissingLabel(id) {
2888
2911
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
2889
2912
  if (nodeEnv !== "production") {
@@ -3393,7 +3416,7 @@ function MouseControls({
3393
3416
  }
3394
3417
 
3395
3418
  // src/pano-filter/host.ts
3396
- import { createContext as createContext5, useContext as useContext6, useSyncExternalStore } from "react";
3419
+ import { createContext as createContext6, useContext as useContext6, useSyncExternalStore } from "react";
3397
3420
 
3398
3421
  // src/pano-filter/presets.ts
3399
3422
  var PANO_FILTER_PRESETS = [
@@ -3434,7 +3457,7 @@ function isPanoFilterActive(snapshot) {
3434
3457
  }
3435
3458
 
3436
3459
  // src/pano-filter/host.ts
3437
- var PanoFilterHostContext = createContext5(null);
3460
+ var PanoFilterHostContext = createContext6(null);
3438
3461
  function createPanoFilterHost() {
3439
3462
  return {
3440
3463
  snapshot: { ...DEFAULT_PANO_FILTER_SNAPSHOT },
@@ -3504,7 +3527,7 @@ function isProduction2() {
3504
3527
 
3505
3528
  // src/pano-context-menu.tsx
3506
3529
  import {
3507
- createContext as createContext6,
3530
+ createContext as createContext7,
3508
3531
  useCallback as useCallback2,
3509
3532
  useContext as useContext7,
3510
3533
  useEffect as useEffect6,
@@ -3518,7 +3541,7 @@ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
3518
3541
  var DEFAULT_ICON_SIZE = 16;
3519
3542
  var MENU_EDGE_PADDING = 8;
3520
3543
  var MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY = 0.4;
3521
- var PanoContextMenuOverlayContext = createContext6(null);
3544
+ var PanoContextMenuOverlayContext = createContext7(null);
3522
3545
  var DEFAULT_APPEARANCE = {
3523
3546
  background: "#161616",
3524
3547
  color: "#f4f4f4",
@@ -4183,12 +4206,12 @@ function composePanoContextMenuItems(props, presets) {
4183
4206
  ...resolvePanoContextMenuEntries(props.append ?? [], presets)
4184
4207
  ];
4185
4208
  }
4186
- var PanoContextMenuActionsContext = createContext6(null);
4209
+ var PanoContextMenuActionsContext = createContext7(null);
4187
4210
 
4188
4211
  // src/pano-cursor.tsx
4189
4212
  import { useThree as useThree4 } from "@react-three/fiber";
4190
4213
  import {
4191
- createContext as createContext7,
4214
+ createContext as createContext8,
4192
4215
  useCallback as useCallback3,
4193
4216
  useContext as useContext8,
4194
4217
  useEffect as useEffect7,
@@ -4209,7 +4232,7 @@ var LAYER_PRIORITY = {
4209
4232
  hotspotDragging: 3,
4210
4233
  hidden: 4
4211
4234
  };
4212
- var PanoCursorContext = createContext7(null);
4235
+ var PanoCursorContext = createContext8(null);
4213
4236
  function usePanoCursor() {
4214
4237
  return useContext8(PanoCursorContext);
4215
4238
  }
@@ -4783,6 +4806,23 @@ function PanoramaEventSurface() {
4783
4806
  );
4784
4807
  }
4785
4808
 
4809
+ // src/scenes-host.ts
4810
+ import { createContext as createContext9 } from "react";
4811
+ var ScenesHostContext = createContext9(null);
4812
+ function createScenesHost() {
4813
+ return {
4814
+ controller: null,
4815
+ revision: 0,
4816
+ listeners: /* @__PURE__ */ new Set()
4817
+ };
4818
+ }
4819
+ function notifyScenesHost(host) {
4820
+ host.revision += 1;
4821
+ for (const listener of host.listeners) {
4822
+ listener();
4823
+ }
4824
+ }
4825
+
4786
4826
  // src/touch-controls.tsx
4787
4827
  import { useThree as useThree7 } from "@react-three/fiber";
4788
4828
  import { useContext as useContext11, useEffect as useEffect10, useRef as useRef9 } from "react";
@@ -4931,9 +4971,9 @@ function TouchControls({
4931
4971
  }
4932
4972
 
4933
4973
  // src/video/chrome-overlay.tsx
4934
- import { createContext as createContext8, useCallback as useCallback4, useMemo as useMemo5, useState as useState3 } from "react";
4974
+ import { createContext as createContext10, useCallback as useCallback4, useMemo as useMemo5, useState as useState3 } from "react";
4935
4975
  import { jsx as jsx5 } from "react/jsx-runtime";
4936
- var PanoChromeOverlayContext = createContext8(null);
4976
+ var PanoChromeOverlayContext = createContext10(null);
4937
4977
  function usePanoChromeOverlay() {
4938
4978
  const [overlayElement, setOverlayElement] = useState3(
4939
4979
  null
@@ -4957,9 +4997,9 @@ function usePanoChromeOverlay() {
4957
4997
  }
4958
4998
 
4959
4999
  // src/video/host.ts
4960
- import { createContext as createContext9, useContext as useContext12 } from "react";
4961
- var PanoVideoHostContext = createContext9(null);
4962
- var DefaultPanoVideoControlsContext = createContext9(false);
5000
+ import { createContext as createContext11, useContext as useContext12 } from "react";
5001
+ var PanoVideoHostContext = createContext11(null);
5002
+ var DefaultPanoVideoControlsContext = createContext11(false);
4963
5003
  function createPanoVideoHost() {
4964
5004
  return {
4965
5005
  controller: null,
@@ -6216,8 +6256,8 @@ function PanoVideoChromeBridge({
6216
6256
  import { useSyncExternalStore as useSyncExternalStore5 } from "react";
6217
6257
 
6218
6258
  // src/webvr/host.ts
6219
- import { createContext as createContext10 } from "react";
6220
- var WebVRRuntimeContext = createContext10(null);
6259
+ import { createContext as createContext12 } from "react";
6260
+ var WebVRRuntimeContext = createContext12(null);
6221
6261
  function createWebVRHost() {
6222
6262
  return {
6223
6263
  controller: null,
@@ -6698,6 +6738,8 @@ var PanoViewer = forwardRef2(
6698
6738
  const videoHost = useMemo7(() => createPanoVideoHost(), []);
6699
6739
  const filterHost = useMemo7(() => createPanoFilterHost(), []);
6700
6740
  const webVRHost = useMemo7(() => createWebVRHost(), []);
6741
+ const scenesHost = useMemo7(() => createScenesHost(), []);
6742
+ const bgmHost = useMemo7(() => createBackgroundAudioHost(), []);
6701
6743
  const stereoView = useMemo7(() => createWebVRStereoView(), []);
6702
6744
  const xrStore = useMemo7(
6703
6745
  () => createXRStore({
@@ -6781,6 +6823,28 @@ var PanoViewer = forwardRef2(
6781
6823
  }),
6782
6824
  [controlOptions.keyboard]
6783
6825
  );
6826
+ const enterFullscreen = useCallback6(async () => {
6827
+ if (typeof document === "undefined") {
6828
+ return;
6829
+ }
6830
+ if (getFullscreenElement()) {
6831
+ return;
6832
+ }
6833
+ const root = rootRef.current;
6834
+ if (!root) {
6835
+ return;
6836
+ }
6837
+ await requestElementFullscreen(root);
6838
+ }, []);
6839
+ const exitFullscreen = useCallback6(async () => {
6840
+ if (typeof document === "undefined") {
6841
+ return;
6842
+ }
6843
+ if (!getFullscreenElement()) {
6844
+ return;
6845
+ }
6846
+ await exitElementFullscreen();
6847
+ }, []);
6784
6848
  const toggleFullscreen = useCallback6(async () => {
6785
6849
  if (typeof document === "undefined") {
6786
6850
  return;
@@ -6854,9 +6918,12 @@ var PanoViewer = forwardRef2(
6854
6918
  useEffect12(() => {
6855
6919
  setLegacyAutoRotate(legacyAutoRotateOptions.autoRotate ?? false);
6856
6920
  }, [legacyAutoRotateOptions.autoRotate]);
6921
+ const isFullscreenRef = useRef12(false);
6922
+ isFullscreenRef.current = isViewerFullscreen;
6857
6923
  useImperativeHandle2(
6858
6924
  ref,
6859
6925
  () => ({
6926
+ // -- View --
6860
6927
  getView: () => controlsRef.current?.getView() ?? { ...fallbackViewRef.current },
6861
6928
  setView: (view, options) => {
6862
6929
  controlsRef.current?.setView(view, options);
@@ -6864,15 +6931,65 @@ var PanoViewer = forwardRef2(
6864
6931
  reset: () => {
6865
6932
  controlsRef.current?.reset();
6866
6933
  },
6934
+ // -- Fullscreen --
6935
+ enterFullscreen,
6936
+ exitFullscreen,
6937
+ toggleFullscreen,
6938
+ isFullscreen: () => isFullscreenRef.current,
6939
+ // -- Scenes --
6940
+ setScene: (id) => scenesHost.controller?.setScene(id) ?? false,
6941
+ nextScene: () => scenesHost.controller?.nextScene() ?? false,
6942
+ previousScene: () => scenesHost.controller?.previousScene() ?? false,
6943
+ getActiveSceneId: () => scenesHost.controller?.getActiveSceneId() ?? null,
6944
+ getSceneIds: () => scenesHost.controller?.getSceneIds() ?? [],
6945
+ isSceneTransitioning: () => scenesHost.controller?.isTransitioning() ?? false,
6946
+ // -- WebVR --
6947
+ enterVR: () => webVRHost.controller?.enterVR() ?? Promise.resolve(false),
6948
+ exitVR: () => webVRHost.controller?.exitVR() ?? Promise.resolve(),
6949
+ toggleVR: () => webVRHost.controller?.toggleVR() ?? Promise.resolve(false),
6950
+ isVRAvailable: () => webVRHost.snapshot.available,
6951
+ isVREnabled: () => webVRHost.snapshot.mode !== null,
6952
+ getVRMode: () => webVRHost.snapshot.mode,
6953
+ requestVRPermission: () => webVRHost.controller?.requestPermission() ?? Promise.resolve(false),
6954
+ // -- Video --
6955
+ getVideo: () => videoHost.controller,
6956
+ subscribeVideo: (onStoreChange) => subscribePanoVideoHost(videoHost, onStoreChange),
6957
+ playVideo: () => videoHost.controller?.play() ?? Promise.resolve(),
6958
+ pauseVideo: () => videoHost.controller?.pause(),
6959
+ toggleVideo: () => videoHost.controller?.togglePlay(),
6960
+ seekVideo: (time) => videoHost.controller?.seek(time),
6961
+ setVideoVolume: (vol) => videoHost.controller?.setVolume(vol),
6962
+ setVideoMuted: (m) => videoHost.controller?.setMuted(m),
6963
+ toggleVideoMuted: () => videoHost.controller?.toggleMuted(),
6964
+ // -- Background Audio --
6965
+ getBackgroundAudio: () => bgmHost.controller,
6966
+ subscribeBackgroundAudio: (onStoreChange) => subscribeBackgroundAudioHost(bgmHost, onStoreChange),
6967
+ playBackgroundAudio: () => {
6968
+ bgmHost.controller?.play();
6969
+ return Promise.resolve();
6970
+ },
6971
+ pauseBackgroundAudio: () => bgmHost.controller?.pause(),
6972
+ toggleBackgroundAudio: () => bgmHost.controller?.togglePlay(),
6973
+ setBackgroundAudioVolume: (vol) => bgmHost.controller?.setVolume(vol),
6974
+ setBackgroundAudioMuted: (m) => bgmHost.controller?.setMuted(m),
6975
+ toggleBackgroundAudioMuted: () => bgmHost.controller?.toggleMuted(),
6976
+ // -- Deprecated --
6867
6977
  startAutoRotate: () => {
6868
6978
  setLegacyAutoRotate(true);
6869
6979
  },
6870
6980
  stopAutoRotate: () => {
6871
6981
  setLegacyAutoRotate(false);
6872
- },
6873
- toggleFullscreen
6982
+ }
6874
6983
  }),
6875
- [toggleFullscreen]
6984
+ [
6985
+ bgmHost,
6986
+ enterFullscreen,
6987
+ exitFullscreen,
6988
+ scenesHost,
6989
+ toggleFullscreen,
6990
+ videoHost,
6991
+ webVRHost
6992
+ ]
6876
6993
  );
6877
6994
  const rootStyle = useMemo7(
6878
6995
  () => ({
@@ -6924,11 +7041,11 @@ var PanoViewer = forwardRef2(
6924
7041
  PanoContextMenuOverlayContext.Provider,
6925
7042
  {
6926
7043
  value: contextMenuOverlayApi,
6927
- children: /* @__PURE__ */ jsx11(PanoChromeOverlayContext.Provider, { value: chromeOverlayApi, children: /* @__PURE__ */ jsx11(PanoVideoHostContext.Provider, { value: videoHost, children: /* @__PURE__ */ jsx11(PanoFilterHostContext.Provider, { value: filterHost, children: /* @__PURE__ */ jsxs6(
7044
+ children: /* @__PURE__ */ jsx11(PanoChromeOverlayContext.Provider, { value: chromeOverlayApi, children: /* @__PURE__ */ jsx11(PanoVideoHostContext.Provider, { value: videoHost, children: /* @__PURE__ */ jsx11(PanoFilterHostContext.Provider, { value: filterHost, children: /* @__PURE__ */ jsx11(
6928
7045
  PanoContextMenuActionsContext.Provider,
6929
7046
  {
6930
7047
  value: contextMenuActions,
6931
- children: [
7048
+ children: /* @__PURE__ */ jsx11(ScenesHostContext.Provider, { value: scenesHost, children: /* @__PURE__ */ jsxs6(BackgroundAudioHostContext.Provider, { value: bgmHost, children: [
6932
7049
  /* @__PURE__ */ jsx11(PanoVideoHostReset, {}),
6933
7050
  /* @__PURE__ */ jsx11(
6934
7051
  PanoramaViewRuntime,
@@ -6972,7 +7089,7 @@ var PanoViewer = forwardRef2(
6972
7089
  }
6973
7090
  )
6974
7091
  ] })
6975
- ]
7092
+ ] }) })
6976
7093
  }
6977
7094
  ) }) }) })
6978
7095
  }
@@ -7010,7 +7127,7 @@ var PanoViewer = forwardRef2(
7010
7127
  );
7011
7128
 
7012
7129
  // src/background-audio.tsx
7013
- import { useContext as useContext15, useEffect as useEffect13, useRef as useRef13 } from "react";
7130
+ import { useContext as useContext15, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef13, useState as useState6 } from "react";
7014
7131
 
7015
7132
  // src/audio/howl.ts
7016
7133
  var import_howler = __toESM(require_howler());
@@ -7180,7 +7297,8 @@ function BackgroundAudio({
7180
7297
  src,
7181
7298
  sources,
7182
7299
  sceneId,
7183
- playing,
7300
+ playing: controlledPlaying,
7301
+ defaultPlaying = false,
7184
7302
  loop = true,
7185
7303
  muted = false,
7186
7304
  volume = 1,
@@ -7196,6 +7314,9 @@ function BackgroundAudio({
7196
7314
  if (!controlsRef) {
7197
7315
  throw new Error("<BackgroundAudio> must be rendered inside <PanoViewer>.");
7198
7316
  }
7317
+ const isControlled = controlledPlaying !== void 0;
7318
+ const [internalPlaying, setInternalPlaying] = useState6(defaultPlaying);
7319
+ const playing = isControlled ? controlledPlaying : internalPlaying;
7199
7320
  const urls = resolveBackgroundAudioUrls(src, sources, sceneId);
7200
7321
  const trackKey = urls.join("\0");
7201
7322
  const currentRef = useRef13(null);
@@ -7220,6 +7341,99 @@ function BackgroundAudio({
7220
7341
  onPlaybackErrorRef.current = onPlaybackError;
7221
7342
  onEndedRef.current = onEnded;
7222
7343
  onErrorRef.current = onError;
7344
+ const snapshotRef = useRef13({
7345
+ ready: false,
7346
+ playing: false,
7347
+ blocked: false,
7348
+ ended: false,
7349
+ muted,
7350
+ volume
7351
+ });
7352
+ const snapshotListenersRef = useRef13(/* @__PURE__ */ new Set());
7353
+ const notifySnapshot = (patch) => {
7354
+ const prev = snapshotRef.current;
7355
+ snapshotRef.current = { ...prev, ...patch };
7356
+ for (const listener of snapshotListenersRef.current) {
7357
+ listener();
7358
+ }
7359
+ };
7360
+ const isControlledRef = useRef13(isControlled);
7361
+ isControlledRef.current = isControlled;
7362
+ const controller = useMemo8(
7363
+ () => ({
7364
+ subscribe: (onStoreChange) => {
7365
+ snapshotListenersRef.current.add(onStoreChange);
7366
+ return () => {
7367
+ snapshotListenersRef.current.delete(onStoreChange);
7368
+ };
7369
+ },
7370
+ getSnapshot: () => snapshotRef.current,
7371
+ play: () => {
7372
+ if (isControlledRef.current) {
7373
+ return;
7374
+ }
7375
+ setInternalPlaying(true);
7376
+ },
7377
+ pause: () => {
7378
+ if (isControlledRef.current) {
7379
+ return;
7380
+ }
7381
+ setInternalPlaying(false);
7382
+ },
7383
+ togglePlay: () => {
7384
+ if (isControlledRef.current) {
7385
+ return;
7386
+ }
7387
+ setInternalPlaying((prev) => !prev);
7388
+ },
7389
+ setVolume: (vol) => {
7390
+ if (isControlledRef.current) {
7391
+ return;
7392
+ }
7393
+ const howl = currentRef.current?.handle.howl;
7394
+ if (howl) {
7395
+ howl.volume(clampAudioVolume(vol));
7396
+ }
7397
+ notifySnapshot({ volume: clampAudioVolume(vol) });
7398
+ },
7399
+ setMuted: (m) => {
7400
+ if (isControlledRef.current) {
7401
+ return;
7402
+ }
7403
+ const howl = currentRef.current?.handle.howl;
7404
+ if (howl) {
7405
+ howl.mute(m);
7406
+ }
7407
+ notifySnapshot({ muted: m });
7408
+ },
7409
+ toggleMuted: () => {
7410
+ if (isControlledRef.current) {
7411
+ return;
7412
+ }
7413
+ const next = !snapshotRef.current.muted;
7414
+ const howl = currentRef.current?.handle.howl;
7415
+ if (howl) {
7416
+ howl.mute(next);
7417
+ }
7418
+ notifySnapshot({ muted: next });
7419
+ }
7420
+ }),
7421
+ []
7422
+ );
7423
+ const bgmHost = useContext15(BackgroundAudioHostContext);
7424
+ useEffect13(() => {
7425
+ if (!bgmHost) {
7426
+ return;
7427
+ }
7428
+ bgmHost.controller = controller;
7429
+ notifyBackgroundAudioHost(bgmHost);
7430
+ return () => {
7431
+ if (bgmHost.controller === controller) {
7432
+ bgmHost.controller = null;
7433
+ notifyBackgroundAudioHost(bgmHost);
7434
+ }
7435
+ };
7436
+ }, [bgmHost, controller]);
7223
7437
  useEffect13(() => {
7224
7438
  return () => {
7225
7439
  fadingInRef.current = false;
@@ -7266,6 +7480,7 @@ function BackgroundAudio({
7266
7480
  isActive: () => currentRef.current?.key === trackKey,
7267
7481
  shouldRetryPlay: () => currentRef.current?.key === trackKey && playingRef.current,
7268
7482
  onEnded: () => {
7483
+ notifySnapshot({ playing: false, ended: true });
7269
7484
  onPlaybackStateChangeRef.current?.("ended");
7270
7485
  onEndedRef.current?.();
7271
7486
  },
@@ -7277,15 +7492,18 @@ function BackgroundAudio({
7277
7492
  durationMs
7278
7493
  );
7279
7494
  }
7495
+ notifySnapshot({ ready: true, playing: true, blocked: false, ended: false });
7280
7496
  onPlaybackStateChangeRef.current?.("playing");
7281
7497
  },
7282
7498
  onPause: () => {
7499
+ notifySnapshot({ playing: false });
7283
7500
  onPlaybackStateChangeRef.current?.("paused");
7284
7501
  },
7285
7502
  onLoadError: (error) => {
7286
7503
  onErrorRef.current?.({ error });
7287
7504
  },
7288
7505
  onPlayError: (error) => {
7506
+ notifySnapshot({ blocked: true });
7289
7507
  onPlaybackStateChangeRef.current?.("blocked");
7290
7508
  onPlaybackErrorRef.current?.(error);
7291
7509
  }
@@ -7354,6 +7572,7 @@ function BackgroundAudio({
7354
7572
  if (!fadingInRef.current) {
7355
7573
  slot.handle.howl.volume(clampAudioVolume(volume));
7356
7574
  }
7575
+ notifySnapshot({ muted, volume: clampAudioVolume(volume) });
7357
7576
  }, [loop, muted, trackKey, volume]);
7358
7577
  useEffect13(() => {
7359
7578
  if (!pauseWhenHidden) {
@@ -7381,7 +7600,7 @@ function BackgroundAudio({
7381
7600
  }
7382
7601
 
7383
7602
  // src/pano-filter/pano-filter.tsx
7384
- import { useContext as useContext16, useEffect as useEffect14, useMemo as useMemo8 } from "react";
7603
+ import { useContext as useContext16, useEffect as useEffect14, useMemo as useMemo9 } from "react";
7385
7604
  function PanoFilter({
7386
7605
  preset = "none",
7387
7606
  intensity,
@@ -7391,7 +7610,7 @@ function PanoFilter({
7391
7610
  if (!host) {
7392
7611
  throw new Error("<PanoFilter> must be rendered inside <PanoViewer>.");
7393
7612
  }
7394
- const snapshot = useMemo8(
7613
+ const snapshot = useMemo9(
7395
7614
  () => resolvePanoFilterSnapshot({ preset, intensity, enabled }),
7396
7615
  [enabled, intensity, preset]
7397
7616
  );
@@ -7416,7 +7635,7 @@ import {
7416
7635
  useEffect as useEffect15,
7417
7636
  useImperativeHandle as useImperativeHandle3,
7418
7637
  useRef as useRef14,
7419
- useState as useState6
7638
+ useState as useState7
7420
7639
  } from "react";
7421
7640
 
7422
7641
  // src/gyro-orientation.ts
@@ -7520,7 +7739,7 @@ var Gyro = forwardRef3(function Gyro2({
7520
7739
  const controlsRef = useContext17(PanoramaViewContext);
7521
7740
  const eventBus = useContext17(PanoEventBusContext);
7522
7741
  const { gl } = useThree9();
7523
- const [internallyDisabled, setInternallyDisabled] = useState6(false);
7742
+ const [internallyDisabled, setInternallyDisabled] = useState7(false);
7524
7743
  const availableRef = useRef14(false);
7525
7744
  const availabilityReportedRef = useRef14(null);
7526
7745
  const activeRef = useRef14(false);
@@ -7876,9 +8095,9 @@ import {
7876
8095
  useContext as useContext19,
7877
8096
  useEffect as useEffect19,
7878
8097
  useImperativeHandle as useImperativeHandle4,
7879
- useMemo as useMemo10,
8098
+ useMemo as useMemo11,
7880
8099
  useRef as useRef18,
7881
- useState as useState7
8100
+ useState as useState8
7882
8101
  } from "react";
7883
8102
 
7884
8103
  // src/webvr/detect.ts
@@ -8162,7 +8381,7 @@ function WebVRReticle({
8162
8381
 
8163
8382
  // src/webvr/stereo.tsx
8164
8383
  import { useFrame as useFrame8, useThree as useThree11 } from "@react-three/fiber";
8165
- import { useContext as useContext18, useEffect as useEffect16, useMemo as useMemo9, useRef as useRef16 } from "react";
8384
+ import { useContext as useContext18, useEffect as useEffect16, useMemo as useMemo10, useRef as useRef16 } from "react";
8166
8385
  import {
8167
8386
  Mesh as Mesh2,
8168
8387
  OrthographicCamera,
@@ -8256,7 +8475,7 @@ function WebVRStereoRenderer({
8256
8475
  const stereo = runtime?.stereoView.stereo ?? fallbackStereoRef.current;
8257
8476
  const stereoRef = useRef16(stereo);
8258
8477
  stereoRef.current = stereo;
8259
- const resources = useMemo9(createDistortionResources, []);
8478
+ const resources = useMemo10(createDistortionResources, []);
8260
8479
  const drawingSizeRef = useRef16(new Vector23());
8261
8480
  const viewportRef = useRef16(new Vector4());
8262
8481
  const scissorRef = useRef16(new Vector4());
@@ -8635,8 +8854,8 @@ var WebVR = forwardRef4(function WebVR2({
8635
8854
  const availableRef = useRef18(false);
8636
8855
  const modeRef = useRef18(null);
8637
8856
  const webXRSessionRef = useRef18(null);
8638
- const [mode, setMode] = useState7(null);
8639
- const [settings, setSettings] = useState7(
8857
+ const [mode, setMode] = useState8(null);
8858
+ const [settings, setSettings] = useState8(
8640
8859
  () => initializeSettings(screensize, profile)
8641
8860
  );
8642
8861
  const callbacksRef = useRef18({
@@ -8659,7 +8878,7 @@ var WebVR = forwardRef4(function WebVR2({
8659
8878
  throw new Error("<WebVR> must be rendered inside <PanoViewer>.");
8660
8879
  }
8661
8880
  const { host, xrStore } = runtime;
8662
- const resolvedProfile = useMemo10(
8881
+ const resolvedProfile = useMemo11(
8663
8882
  () => resolveWebVRProfile(profile, settings.profileId),
8664
8883
  [profile, settings.profileId]
8665
8884
  );
@@ -8824,16 +9043,17 @@ var WebVR = forwardRef4(function WebVR2({
8824
9043
  setSettings(normalized);
8825
9044
  saveWebVRSettings(normalized);
8826
9045
  }, []);
8827
- const controller = useMemo10(
9046
+ const controller = useMemo11(
8828
9047
  () => ({
8829
9048
  closeSetup: () => updateWebVRHost(host, { setupOpen: false }),
8830
9049
  enterVR,
8831
9050
  exitVR,
8832
9051
  openSetup: () => updateWebVRHost(host, { setupOpen: true }),
9052
+ requestPermission,
8833
9053
  toggleVR,
8834
9054
  updateSettings
8835
9055
  }),
8836
- [enterVR, exitVR, host, toggleVR, updateSettings]
9056
+ [enterVR, exitVR, host, requestPermission, toggleVR, updateSettings]
8837
9057
  );
8838
9058
  useImperativeHandle4(
8839
9059
  ref,
@@ -8958,9 +9178,92 @@ var WebVR = forwardRef4(function WebVR2({
8958
9178
  ] });
8959
9179
  });
8960
9180
 
9181
+ // src/use-pano-viewer.ts
9182
+ import { useMemo as useMemo12, useRef as useRef19 } from "react";
9183
+ function createPanoViewerHandle(ref) {
9184
+ return {
9185
+ getView: () => ref.current?.getView() ?? { yaw: 0, pitch: 0, fov: 75 },
9186
+ setView: (view, options) => {
9187
+ ref.current?.setView(view, options);
9188
+ },
9189
+ reset: () => {
9190
+ ref.current?.reset();
9191
+ },
9192
+ enterFullscreen: () => ref.current?.enterFullscreen() ?? Promise.resolve(),
9193
+ exitFullscreen: () => ref.current?.exitFullscreen() ?? Promise.resolve(),
9194
+ toggleFullscreen: () => ref.current?.toggleFullscreen() ?? Promise.resolve(),
9195
+ isFullscreen: () => ref.current?.isFullscreen() ?? false,
9196
+ setScene: (id) => ref.current?.setScene(id) ?? false,
9197
+ nextScene: () => ref.current?.nextScene() ?? false,
9198
+ previousScene: () => ref.current?.previousScene() ?? false,
9199
+ getActiveSceneId: () => ref.current?.getActiveSceneId() ?? null,
9200
+ getSceneIds: () => ref.current?.getSceneIds() ?? [],
9201
+ isSceneTransitioning: () => ref.current?.isSceneTransitioning() ?? false,
9202
+ enterVR: () => ref.current?.enterVR() ?? Promise.resolve(false),
9203
+ exitVR: () => ref.current?.exitVR() ?? Promise.resolve(),
9204
+ toggleVR: () => ref.current?.toggleVR() ?? Promise.resolve(false),
9205
+ isVRAvailable: () => ref.current?.isVRAvailable() ?? false,
9206
+ isVREnabled: () => ref.current?.isVREnabled() ?? false,
9207
+ getVRMode: () => ref.current?.getVRMode() ?? null,
9208
+ requestVRPermission: () => ref.current?.requestVRPermission() ?? Promise.resolve(false),
9209
+ getVideo: () => ref.current?.getVideo() ?? null,
9210
+ subscribeVideo: (onStoreChange) => ref.current?.subscribeVideo(onStoreChange) ?? (() => {
9211
+ }),
9212
+ playVideo: () => ref.current?.playVideo() ?? Promise.resolve(),
9213
+ pauseVideo: () => {
9214
+ ref.current?.pauseVideo();
9215
+ },
9216
+ toggleVideo: () => {
9217
+ ref.current?.toggleVideo();
9218
+ },
9219
+ seekVideo: (time) => {
9220
+ ref.current?.seekVideo(time);
9221
+ },
9222
+ setVideoVolume: (volume) => {
9223
+ ref.current?.setVideoVolume(volume);
9224
+ },
9225
+ setVideoMuted: (muted) => {
9226
+ ref.current?.setVideoMuted(muted);
9227
+ },
9228
+ toggleVideoMuted: () => {
9229
+ ref.current?.toggleVideoMuted();
9230
+ },
9231
+ getBackgroundAudio: () => ref.current?.getBackgroundAudio() ?? null,
9232
+ subscribeBackgroundAudio: (onStoreChange) => ref.current?.subscribeBackgroundAudio(onStoreChange) ?? (() => {
9233
+ }),
9234
+ playBackgroundAudio: () => ref.current?.playBackgroundAudio() ?? Promise.resolve(),
9235
+ pauseBackgroundAudio: () => {
9236
+ ref.current?.pauseBackgroundAudio();
9237
+ },
9238
+ toggleBackgroundAudio: () => {
9239
+ ref.current?.toggleBackgroundAudio();
9240
+ },
9241
+ setBackgroundAudioVolume: (volume) => {
9242
+ ref.current?.setBackgroundAudioVolume(volume);
9243
+ },
9244
+ setBackgroundAudioMuted: (muted) => {
9245
+ ref.current?.setBackgroundAudioMuted(muted);
9246
+ },
9247
+ toggleBackgroundAudioMuted: () => {
9248
+ ref.current?.toggleBackgroundAudioMuted();
9249
+ },
9250
+ startAutoRotate: () => {
9251
+ ref.current?.startAutoRotate();
9252
+ },
9253
+ stopAutoRotate: () => {
9254
+ ref.current?.stopAutoRotate();
9255
+ }
9256
+ };
9257
+ }
9258
+ function usePanoViewer() {
9259
+ const ref = useRef19(null);
9260
+ const handle = useMemo12(() => createPanoViewerHandle(ref), []);
9261
+ return useMemo12(() => ({ ref, ...handle }), [handle]);
9262
+ }
9263
+
8961
9264
  // src/sphere.tsx
8962
9265
  import { useThree as useThree13 } from "@react-three/fiber";
8963
- import { useEffect as useEffect20, useMemo as useMemo12, useRef as useRef20, useState as useState8 } from "react";
9266
+ import { useEffect as useEffect20, useMemo as useMemo14, useRef as useRef21, useState as useState9 } from "react";
8964
9267
  import {
8965
9268
  BackSide as BackSide2,
8966
9269
  LinearFilter,
@@ -8993,7 +9296,7 @@ function resolveUrlAgainstFile(fileUrl, path) {
8993
9296
  }
8994
9297
 
8995
9298
  // src/pano-filter/pano-basic-material.tsx
8996
- import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo11, useRef as useRef19 } from "react";
9299
+ import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo13, useRef as useRef20 } from "react";
8997
9300
 
8998
9301
  // src/pano-filter/shaders.ts
8999
9302
  var GLSL_FILTER_COMMON = `
@@ -9153,13 +9456,13 @@ function PanoBasicMaterial({
9153
9456
  toneMapped = false
9154
9457
  }) {
9155
9458
  const snapshot = usePanoFilterSnapshot();
9156
- const materialRef = useRef19(null);
9157
- const intensityRef = useRef19(snapshot.intensity);
9158
- const intensityUniformRef = useRef19(null);
9459
+ const materialRef = useRef20(null);
9460
+ const intensityRef = useRef20(snapshot.intensity);
9461
+ const intensityUniformRef = useRef20(null);
9159
9462
  intensityRef.current = snapshot.intensity;
9160
9463
  const active = isPanoFilterActive(snapshot);
9161
9464
  const preset = snapshot.preset;
9162
- const onBeforeCompile = useMemo11(() => {
9465
+ const onBeforeCompile = useMemo13(() => {
9163
9466
  if (!active || preset === "none") {
9164
9467
  return void 0;
9165
9468
  }
@@ -9209,9 +9512,9 @@ function PanoBasicMaterial({
9209
9512
  // src/sphere.tsx
9210
9513
  import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
9211
9514
  function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
9212
- const [texture, setTexture] = useState8(null);
9213
- const onLoadRef = useRef20(onLoad);
9214
- const onErrorRef = useRef20(onError);
9515
+ const [texture, setTexture] = useState9(null);
9516
+ const onLoadRef = useRef21(onLoad);
9517
+ const onErrorRef = useRef21(onError);
9215
9518
  onLoadRef.current = onLoad;
9216
9519
  onErrorRef.current = onError;
9217
9520
  useEffect20(() => {
@@ -9273,7 +9576,7 @@ function Sphere({
9273
9576
  }) {
9274
9577
  const gl = useThree13((state) => state.gl);
9275
9578
  const anisotropy = gl.capabilities.getMaxAnisotropy();
9276
- const resolvedPreviewUrl = useMemo12(
9579
+ const resolvedPreviewUrl = useMemo14(
9277
9580
  () => resolveUrlAgainstFile(src, previewUrl),
9278
9581
  [previewUrl, src]
9279
9582
  );
@@ -9292,7 +9595,7 @@ function Sphere({
9292
9595
  onPreviewError
9293
9596
  );
9294
9597
  const displayTexture = texture ?? previewTexture;
9295
- const readyRef = useRef20(false);
9598
+ const readyRef = useRef21(false);
9296
9599
  useEffect20(() => {
9297
9600
  readyRef.current = false;
9298
9601
  }, [resolvedPreviewUrl, src]);
@@ -9333,9 +9636,9 @@ import {
9333
9636
  useContext as useContext20,
9334
9637
  useEffect as useEffect21,
9335
9638
  useLayoutEffect as useLayoutEffect3,
9336
- useMemo as useMemo13,
9337
- useRef as useRef21,
9338
- useState as useState9
9639
+ useMemo as useMemo15,
9640
+ useRef as useRef22,
9641
+ useState as useState10
9339
9642
  } from "react";
9340
9643
  import {
9341
9644
  BackSide as BackSide3,
@@ -9432,8 +9735,8 @@ function applyEquirectangularLayout(texture, anisotropy) {
9432
9735
  }
9433
9736
  function usePosterTexture(poster, onError) {
9434
9737
  const gl = useThree14((state) => state.gl);
9435
- const [texture, setTexture] = useState9(null);
9436
- const onErrorRef = useRef21(onError);
9738
+ const [texture, setTexture] = useState10(null);
9739
+ const onErrorRef = useRef22(onError);
9437
9740
  onErrorRef.current = onError;
9438
9741
  useEffect21(() => {
9439
9742
  let active = true;
@@ -9532,36 +9835,36 @@ function PanoVideo({
9532
9835
  const gl = useThree14((state) => state.gl);
9533
9836
  const host = useContext20(PanoVideoHostContext);
9534
9837
  const overlay = useContext20(PanoChromeOverlayContext);
9535
- const variants = useMemo13(
9838
+ const variants = useMemo15(
9536
9839
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
9537
9840
  [poster, src, variantsProp]
9538
9841
  );
9539
9842
  const tracks = tracksProp ?? EMPTY_TRACKS;
9540
9843
  const captionsEnabled = captions !== false;
9541
- const captionAppearance = useMemo13(
9844
+ const captionAppearance = useMemo15(
9542
9845
  () => resolvePanoVideoCaptionAppearance(
9543
9846
  typeof captions === "object" ? captions : void 0
9544
9847
  ),
9545
9848
  [captions]
9546
9849
  );
9547
- const [variantId, setVariantId] = useState9(
9850
+ const [variantId, setVariantId] = useState10(
9548
9851
  () => resolvePanoVideoVariantId(variants, defaultVariantId)
9549
9852
  );
9550
- const [trackId, setTrackId] = useState9(
9853
+ const [trackId, setTrackId] = useState10(
9551
9854
  () => captionsEnabled ? defaultPanoVideoTrackId(tracks) : null
9552
9855
  );
9553
- const [ready, setReady] = useState9(false);
9554
- const autoPlayRef = useRef21(autoPlay);
9856
+ const [ready, setReady] = useState10(false);
9857
+ const autoPlayRef = useRef22(autoPlay);
9555
9858
  autoPlayRef.current = autoPlay;
9556
- const blockedRef = useRef21(false);
9557
- const captionAppearanceOverrideRef = useRef21(
9859
+ const blockedRef = useRef22(false);
9860
+ const captionAppearanceOverrideRef = useRef22(
9558
9861
  {}
9559
9862
  );
9560
- const mediaCapabilitiesRef = useRef21({
9863
+ const mediaCapabilitiesRef = useRef22({
9561
9864
  volumeAdjustable: true,
9562
9865
  playbackRates: [...DEFAULT_PANO_VIDEO_PLAYBACK_RATES]
9563
9866
  });
9564
- const storeRef = useRef21(null);
9867
+ const storeRef = useRef22(null);
9565
9868
  if (!storeRef.current) {
9566
9869
  storeRef.current = {
9567
9870
  snapshot: {
@@ -9571,7 +9874,7 @@ function PanoVideo({
9571
9874
  listeners: /* @__PURE__ */ new Set()
9572
9875
  };
9573
9876
  }
9574
- const actionsRef = useRef21({
9877
+ const actionsRef = useRef22({
9575
9878
  play: async () => {
9576
9879
  },
9577
9880
  pause: () => {
@@ -9591,19 +9894,19 @@ function PanoVideo({
9591
9894
  setCaptionAppearance: (_appearance) => {
9592
9895
  }
9593
9896
  });
9594
- const onLoadRef = useRef21(onLoad);
9595
- const onErrorRef = useRef21(onError);
9596
- const onPlaybackStateChangeRef = useRef21(onPlaybackStateChange);
9597
- const onEndedRef = useRef21(onEnded);
9598
- const onVariantChangeRef = useRef21(onVariantChange);
9599
- const onTrackChangeRef = useRef21(onTrackChange);
9897
+ const onLoadRef = useRef22(onLoad);
9898
+ const onErrorRef = useRef22(onError);
9899
+ const onPlaybackStateChangeRef = useRef22(onPlaybackStateChange);
9900
+ const onEndedRef = useRef22(onEnded);
9901
+ const onVariantChangeRef = useRef22(onVariantChange);
9902
+ const onTrackChangeRef = useRef22(onTrackChange);
9600
9903
  onLoadRef.current = onLoad;
9601
9904
  onErrorRef.current = onError;
9602
9905
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
9603
9906
  onEndedRef.current = onEnded;
9604
9907
  onVariantChangeRef.current = onVariantChange;
9605
9908
  onTrackChangeRef.current = onTrackChange;
9606
- const resource = useMemo13(() => {
9909
+ const resource = useMemo15(() => {
9607
9910
  if (typeof document === "undefined") {
9608
9911
  return null;
9609
9912
  }
@@ -9716,7 +10019,7 @@ function PanoVideo({
9716
10019
  captionAppearance
9717
10020
  ]
9718
10021
  );
9719
- const controller = useMemo13(
10022
+ const controller = useMemo15(
9720
10023
  () => ({
9721
10024
  subscribe: (onStoreChange) => {
9722
10025
  const store = storeRef.current;
@@ -10083,9 +10386,9 @@ import {
10083
10386
  useCallback as useCallback10,
10084
10387
  useContext as useContext22,
10085
10388
  useEffect as useEffect26,
10086
- useMemo as useMemo18,
10087
- useRef as useRef25,
10088
- useState as useState11
10389
+ useMemo as useMemo20,
10390
+ useRef as useRef26,
10391
+ useState as useState12
10089
10392
  } from "react";
10090
10393
  import {
10091
10394
  LinearSRGBColorSpace,
@@ -10141,7 +10444,7 @@ function isSnapshotTransition(preset) {
10141
10444
 
10142
10445
  // src/scene-transitions/particle-overlay.tsx
10143
10446
  import { useFrame as useFrame9, useThree as useThree15 } from "@react-three/fiber";
10144
- import { useEffect as useEffect22, useMemo as useMemo14, useRef as useRef22 } from "react";
10447
+ import { useEffect as useEffect22, useMemo as useMemo16, useRef as useRef23 } from "react";
10145
10448
  import {
10146
10449
  DoubleSide as DoubleSide2,
10147
10450
  InstancedBufferAttribute,
@@ -10281,12 +10584,12 @@ function ParticleOverlay({
10281
10584
  onFinish
10282
10585
  }) {
10283
10586
  const { camera } = useThree15();
10284
- const meshRef = useRef22(null);
10285
- const startRef = useRef22(null);
10286
- const finishedRef = useRef22(false);
10287
- const directionRef = useRef22(new Vector39());
10288
- const geometry = useMemo14(() => createParticleGeometry(), []);
10289
- const material = useMemo14(
10587
+ const meshRef = useRef23(null);
10588
+ const startRef = useRef23(null);
10589
+ const finishedRef = useRef23(false);
10590
+ const directionRef = useRef23(new Vector39());
10591
+ const geometry = useMemo16(() => createParticleGeometry(), []);
10592
+ const material = useMemo16(
10290
10593
  () => new ShaderMaterial2({
10291
10594
  transparent: true,
10292
10595
  depthTest: false,
@@ -10340,7 +10643,7 @@ function ParticleOverlay({
10340
10643
 
10341
10644
  // src/scene-transitions/snapshot-overlay.tsx
10342
10645
  import { useFrame as useFrame10, useThree as useThree16 } from "@react-three/fiber";
10343
- import { useEffect as useEffect23, useMemo as useMemo15, useRef as useRef23 } from "react";
10646
+ import { useEffect as useEffect23, useMemo as useMemo17, useRef as useRef24 } from "react";
10344
10647
  import { Vector3 as Vector310 } from "three";
10345
10648
 
10346
10649
  // src/scene-transitions/snapshot-shaders.ts
@@ -10629,11 +10932,11 @@ function SnapshotOverlay({
10629
10932
  onFinish
10630
10933
  }) {
10631
10934
  const { camera, gl } = useThree16();
10632
- const meshRef = useRef23(null);
10633
- const startRef = useRef23(null);
10634
- const finishedRef = useRef23(false);
10635
- const directionRef = useRef23(new Vector310());
10636
- const material = useMemo15(() => {
10935
+ const meshRef = useRef24(null);
10936
+ const startRef = useRef24(null);
10937
+ const finishedRef = useRef24(false);
10938
+ const directionRef = useRef24(new Vector310());
10939
+ const material = useMemo17(() => {
10637
10940
  if (!isSnapshotTransition(transition.preset)) {
10638
10941
  return null;
10639
10942
  }
@@ -10723,10 +11026,10 @@ function TransitionOverlay(props) {
10723
11026
  // src/tile/texture-manager-context.tsx
10724
11027
  import { useThree as useThree17 } from "@react-three/fiber";
10725
11028
  import {
10726
- createContext as createContext11,
11029
+ createContext as createContext13,
10727
11030
  useContext as useContext21,
10728
11031
  useEffect as useEffect24,
10729
- useMemo as useMemo16
11032
+ useMemo as useMemo18
10730
11033
  } from "react";
10731
11034
 
10732
11035
  // src/tile/texture-manager.ts
@@ -10957,7 +11260,7 @@ var TileTextureManager = class {
10957
11260
 
10958
11261
  // src/tile/texture-manager-context.tsx
10959
11262
  import { jsx as jsx20 } from "react/jsx-runtime";
10960
- var TileTextureManagerContext = createContext11(
11263
+ var TileTextureManagerContext = createContext13(
10961
11264
  null
10962
11265
  );
10963
11266
  function TileTextureManagerProvider({
@@ -10967,7 +11270,7 @@ function TileTextureManagerProvider({
10967
11270
  }) {
10968
11271
  const gl = useThree17((state) => state.gl);
10969
11272
  const anisotropy = gl.capabilities.getMaxAnisotropy();
10970
- const manager = useMemo16(
11273
+ const manager = useMemo18(
10971
11274
  () => new TileTextureManager({
10972
11275
  anisotropy,
10973
11276
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -10991,9 +11294,9 @@ import { useFrame as useFrame11, useThree as useThree18 } from "@react-three/fib
10991
11294
  import {
10992
11295
  memo,
10993
11296
  useEffect as useEffect25,
10994
- useMemo as useMemo17,
10995
- useRef as useRef24,
10996
- useState as useState10
11297
+ useMemo as useMemo19,
11298
+ useRef as useRef25,
11299
+ useState as useState11
10997
11300
  } from "react";
10998
11301
  import {
10999
11302
  ClampToEdgeWrapping as ClampToEdgeWrapping2,
@@ -11198,8 +11501,8 @@ var VISIBILITY_FOV_PADDING_DEGREES = 8;
11198
11501
  var PREVIEW_RADIUS = CUBE_RADIUS + 0.05;
11199
11502
  var SHARED_TILE_GEOMETRY = new PlaneGeometry4(1, 1);
11200
11503
  function usePreviewTexture(url, anisotropy, onError) {
11201
- const [texture, setTexture] = useState10(null);
11202
- const onErrorRef = useRef24(onError);
11504
+ const [texture, setTexture] = useState11(null);
11505
+ const onErrorRef = useRef25(onError);
11203
11506
  onErrorRef.current = onError;
11204
11507
  useEffect25(() => {
11205
11508
  if (!url) {
@@ -11251,7 +11554,7 @@ function PreviewFace({
11251
11554
  texture,
11252
11555
  faceOrder
11253
11556
  }) {
11254
- const geometry = useMemo17(() => {
11557
+ const geometry = useMemo19(() => {
11255
11558
  const nextGeometry = new PlaneGeometry4(
11256
11559
  PREVIEW_RADIUS * 2,
11257
11560
  PREVIEW_RADIUS * 2
@@ -11310,7 +11613,7 @@ function PreviewCube({
11310
11613
  ));
11311
11614
  }
11312
11615
  function useManagedTexture(manager, key, url, address) {
11313
- const [managedState, setManagedState] = useState10(() => ({
11616
+ const [managedState, setManagedState] = useState11(() => ({
11314
11617
  manager,
11315
11618
  key,
11316
11619
  url,
@@ -11537,10 +11840,10 @@ function Tile({
11537
11840
  onLevelChange
11538
11841
  }) {
11539
11842
  const { camera, gl, size } = useThree18();
11540
- const multiresConfig = useMemo17(() => parseMultires(multires), [multires]);
11843
+ const multiresConfig = useMemo19(() => parseMultires(multires), [multires]);
11541
11844
  const { levels, tileSize } = multiresConfig;
11542
11845
  const multiresToken = `${tileSize}:${levels.join(",")}`;
11543
- const candidatesByLevel = useMemo17(
11846
+ const candidatesByLevel = useMemo19(
11544
11847
  () => buildTileCandidates(levels, tileSize),
11545
11848
  [levels, tileSize]
11546
11849
  );
@@ -11552,13 +11855,13 @@ function Tile({
11552
11855
  onPreviewError
11553
11856
  );
11554
11857
  const sharedManager = useSharedTileTextureManager();
11555
- const progressRef = useRef24(onLoadProgress);
11556
- const errorRef = useRef24(onTileError);
11557
- const levelChangeRef = useRef24(onLevelChange);
11858
+ const progressRef = useRef25(onLoadProgress);
11859
+ const errorRef = useRef25(onTileError);
11860
+ const levelChangeRef = useRef25(onLevelChange);
11558
11861
  progressRef.current = onLoadProgress;
11559
11862
  errorRef.current = onTileError;
11560
11863
  levelChangeRef.current = onLevelChange;
11561
- const ownedManager = useMemo17(
11864
+ const ownedManager = useMemo19(
11562
11865
  () => new TileTextureManager({
11563
11866
  anisotropy,
11564
11867
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -11589,7 +11892,7 @@ function Tile({
11589
11892
  queueMicrotask(() => manager.releaseUnused());
11590
11893
  };
11591
11894
  }, [manager, ownedManager]);
11592
- const readyRef = useRef24(false);
11895
+ const readyRef = useRef25(false);
11593
11896
  useEffect25(() => {
11594
11897
  readyRef.current = false;
11595
11898
  }, [effectivePreviewUrl]);
@@ -11601,24 +11904,24 @@ function Tile({
11601
11904
  onReady?.();
11602
11905
  }, [onReady, previewTexture]);
11603
11906
  const template = urlTemplate ?? buildDefaultTileUrlTemplate();
11604
- const resolveUrl = useMemo17(
11907
+ const resolveUrl = useMemo19(
11605
11908
  () => (address) => {
11606
11909
  const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
11607
11910
  return resolveRelativeTileUrl(baseUrl, relativePath);
11608
11911
  },
11609
11912
  [baseUrl, resolveTileUrl, template]
11610
11913
  );
11611
- const [layers, setLayers] = useState10([]);
11612
- const layerTokenRef = useRef24("");
11613
- const currentLevelRef = useRef24(1);
11614
- const lastUpdateAtRef = useRef24(-Infinity);
11615
- const visibilityDirtyRef = useRef24(true);
11616
- const lastVisibilityQuaternionRef = useRef24(camera.quaternion.clone());
11617
- const lastVisibilityFovRef = useRef24(Number.NaN);
11618
- const lastViewportRef = useRef24({ width: 0, height: 0, dpr: 0 });
11619
- const frustumRef = useRef24(new Frustum());
11620
- const projectionMatrixRef = useRef24(new Matrix4());
11621
- const visibilityCameraRef = useRef24(new PerspectiveCamera4());
11914
+ const [layers, setLayers] = useState11([]);
11915
+ const layerTokenRef = useRef25("");
11916
+ const currentLevelRef = useRef25(1);
11917
+ const lastUpdateAtRef = useRef25(-Infinity);
11918
+ const visibilityDirtyRef = useRef25(true);
11919
+ const lastVisibilityQuaternionRef = useRef25(camera.quaternion.clone());
11920
+ const lastVisibilityFovRef = useRef25(Number.NaN);
11921
+ const lastViewportRef = useRef25({ width: 0, height: 0, dpr: 0 });
11922
+ const frustumRef = useRef25(new Frustum());
11923
+ const projectionMatrixRef = useRef25(new Matrix4());
11924
+ const visibilityCameraRef = useRef25(new PerspectiveCamera4());
11622
11925
  useEffect25(() => {
11623
11926
  currentLevelRef.current = 1;
11624
11927
  layerTokenRef.current = "";
@@ -11776,8 +12079,8 @@ function SnapshotCapture({
11776
12079
  onCaptured
11777
12080
  }) {
11778
12081
  const { camera, gl, scene, size } = useThree19();
11779
- const capturedRequestRef = useRef25(0);
11780
- const renderTargetRef = useRef25(null);
12082
+ const capturedRequestRef = useRef26(0);
12083
+ const renderTargetRef = useRef26(null);
11781
12084
  useEffect26(
11782
12085
  () => () => {
11783
12086
  renderTargetRef.current?.dispose();
@@ -11831,40 +12134,44 @@ function ScenesController({
11831
12134
  renderHotspots,
11832
12135
  onTransitionEnd,
11833
12136
  onTransitionError,
11834
- snapshotMaxPixels = 3686400
12137
+ snapshotMaxPixels = 3686400,
12138
+ phaseRef: externalPhaseRef
11835
12139
  }) {
11836
12140
  const controlsRef = useContext22(PanoramaViewContext);
11837
12141
  const manager = useSharedTileTextureManager();
11838
- const transition = useMemo18(
12142
+ const transition = useMemo20(
11839
12143
  () => resolveTransition(transitionInput),
11840
12144
  [transitionInput]
11841
12145
  );
11842
- const sceneMap = useMemo18(
12146
+ const sceneMap = useMemo20(
11843
12147
  () => new Map(scenes.map((scene) => [scene.id, scene])),
11844
12148
  [scenes]
11845
12149
  );
11846
12150
  const initialScene = sceneMap.get(activeSceneId) ?? null;
11847
- const [slots, setSlots] = useState11([
12151
+ const [slots, setSlots] = useState12([
11848
12152
  initialScene,
11849
12153
  null
11850
12154
  ]);
11851
- const [activeSlot, setActiveSlot] = useState11(0);
11852
- const [liveSlot, setLiveSlot] = useState11(0);
11853
- const [pendingSlot, setPendingSlot] = useState11(null);
11854
- const [phase, setPhase] = useState11(initialScene ? "idle" : "preloading");
11855
- const [captureRequestId, setCaptureRequestId] = useState11(0);
11856
- const [transitionRunId, setTransitionRunId] = useState11(0);
11857
- const [snapshot, setSnapshot] = useState11(null);
11858
- const [recoveryKey, setRecoveryKey] = useState11(0);
11859
- const requestedSceneIdRef = useRef25(activeSceneId);
11860
- const captureReasonRef = useRef25("start");
11861
- const interactionReleaseRef = useRef25(null);
11862
- const slotsRef = useRef25(slots);
11863
- const phaseRef = useRef25(phase);
11864
- const liveSlotRef = useRef25(liveSlot);
11865
- const transitionFromRef = useRef25(initialScene);
12155
+ const [activeSlot, setActiveSlot] = useState12(0);
12156
+ const [liveSlot, setLiveSlot] = useState12(0);
12157
+ const [pendingSlot, setPendingSlot] = useState12(null);
12158
+ const [phase, setPhase] = useState12(initialScene ? "idle" : "preloading");
12159
+ const [captureRequestId, setCaptureRequestId] = useState12(0);
12160
+ const [transitionRunId, setTransitionRunId] = useState12(0);
12161
+ const [snapshot, setSnapshot] = useState12(null);
12162
+ const [recoveryKey, setRecoveryKey] = useState12(0);
12163
+ const requestedSceneIdRef = useRef26(activeSceneId);
12164
+ const captureReasonRef = useRef26("start");
12165
+ const interactionReleaseRef = useRef26(null);
12166
+ const slotsRef = useRef26(slots);
12167
+ const phaseRef = useRef26(phase);
12168
+ const liveSlotRef = useRef26(liveSlot);
12169
+ const transitionFromRef = useRef26(initialScene);
11866
12170
  slotsRef.current = slots;
11867
12171
  phaseRef.current = phase;
12172
+ if (externalPhaseRef) {
12173
+ externalPhaseRef.current = phase;
12174
+ }
11868
12175
  liveSlotRef.current = liveSlot;
11869
12176
  const releaseInteraction = useCallback10(() => {
11870
12177
  interactionReleaseRef.current?.();
@@ -12060,21 +12367,104 @@ function ScenesController({
12060
12367
  function Scenes({
12061
12368
  maxTextureMemoryMb,
12062
12369
  maxConcurrentTileLoads,
12063
- ...props
12370
+ activeSceneId: controlledId,
12371
+ defaultActiveSceneId,
12372
+ onActiveSceneIdChange,
12373
+ scenes,
12374
+ ...rest
12064
12375
  }) {
12376
+ const isControlled = controlledId !== void 0;
12377
+ const [internalId, setInternalId] = useState12(
12378
+ () => controlledId ?? defaultActiveSceneId ?? scenes[0]?.id ?? ""
12379
+ );
12380
+ const resolvedId = isControlled ? controlledId : internalId;
12381
+ const requestSceneChange = useCallback10(
12382
+ (id) => {
12383
+ if (isControlled) {
12384
+ onActiveSceneIdChange?.(id);
12385
+ } else {
12386
+ setInternalId(id);
12387
+ }
12388
+ },
12389
+ [isControlled, onActiveSceneIdChange]
12390
+ );
12391
+ const scenesRef = useRef26(scenes);
12392
+ scenesRef.current = scenes;
12393
+ const resolvedIdRef = useRef26(resolvedId);
12394
+ resolvedIdRef.current = resolvedId;
12395
+ const host = useContext22(ScenesHostContext);
12396
+ const phaseRef = useRef26("idle");
12397
+ useEffect26(() => {
12398
+ if (!host) {
12399
+ return;
12400
+ }
12401
+ const controller = {
12402
+ setScene: (id) => {
12403
+ const match = scenesRef.current.find((s) => s.id === id);
12404
+ if (!match) {
12405
+ return false;
12406
+ }
12407
+ requestSceneChange(id);
12408
+ return true;
12409
+ },
12410
+ nextScene: () => {
12411
+ const next = cycleSceneId(
12412
+ scenesRef.current,
12413
+ resolvedIdRef.current,
12414
+ 1
12415
+ );
12416
+ if (next === null) {
12417
+ return false;
12418
+ }
12419
+ requestSceneChange(next);
12420
+ return true;
12421
+ },
12422
+ previousScene: () => {
12423
+ const next = cycleSceneId(
12424
+ scenesRef.current,
12425
+ resolvedIdRef.current,
12426
+ -1
12427
+ );
12428
+ if (next === null) {
12429
+ return false;
12430
+ }
12431
+ requestSceneChange(next);
12432
+ return true;
12433
+ },
12434
+ getActiveSceneId: () => resolvedIdRef.current,
12435
+ getSceneIds: () => scenesRef.current.map((s) => s.id),
12436
+ isTransitioning: () => phaseRef.current !== "idle"
12437
+ };
12438
+ host.controller = controller;
12439
+ notifyScenesHost(host);
12440
+ return () => {
12441
+ if (host.controller === controller) {
12442
+ host.controller = null;
12443
+ notifyScenesHost(host);
12444
+ }
12445
+ };
12446
+ }, [host, requestSceneChange]);
12065
12447
  return /* @__PURE__ */ jsx22(
12066
12448
  TileTextureManagerProvider,
12067
12449
  {
12068
12450
  maxTextureMemoryMb,
12069
12451
  maxConcurrentLoads: maxConcurrentTileLoads,
12070
- children: /* @__PURE__ */ jsx22(ScenesController, { ...props })
12452
+ children: /* @__PURE__ */ jsx22(
12453
+ ScenesController,
12454
+ {
12455
+ ...rest,
12456
+ scenes,
12457
+ activeSceneId: resolvedId,
12458
+ phaseRef
12459
+ }
12460
+ )
12071
12461
  }
12072
12462
  );
12073
12463
  }
12074
12464
 
12075
12465
  // src/hotspot/image-hotspot.tsx
12076
12466
  import { useThree as useThree21 } from "@react-three/fiber";
12077
- import { useEffect as useEffect29, useRef as useRef27, useState as useState14 } from "react";
12467
+ import { useEffect as useEffect29, useRef as useRef28, useState as useState15 } from "react";
12078
12468
  import {
12079
12469
  ClampToEdgeWrapping as ClampToEdgeWrapping3,
12080
12470
  LinearFilter as LinearFilter6,
@@ -12089,9 +12479,9 @@ import {
12089
12479
  useContext as useContext24,
12090
12480
  useEffect as useEffect28,
12091
12481
  useLayoutEffect as useLayoutEffect4,
12092
- useMemo as useMemo20,
12093
- useRef as useRef26,
12094
- useState as useState13
12482
+ useMemo as useMemo22,
12483
+ useRef as useRef27,
12484
+ useState as useState14
12095
12485
  } from "react";
12096
12486
  import {
12097
12487
  Box3 as Box33,
@@ -12107,7 +12497,7 @@ import {
12107
12497
  } from "three";
12108
12498
 
12109
12499
  // src/hotspot/hotspot-tooltip.tsx
12110
- import { useEffect as useEffect27, useMemo as useMemo19, useState as useState12 } from "react";
12500
+ import { useEffect as useEffect27, useMemo as useMemo21, useState as useState13 } from "react";
12111
12501
 
12112
12502
  // src/webvr/stereo-html.tsx
12113
12503
  import { Html } from "@react-three/drei";
@@ -12230,16 +12620,16 @@ function HotspotTooltip({
12230
12620
  offset = DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
12231
12621
  appearance
12232
12622
  }) {
12233
- const [imageFailed, setImageFailed] = useState12(false);
12623
+ const [imageFailed, setImageFailed] = useState13(false);
12234
12624
  useEffect27(() => {
12235
12625
  setImageFailed(false);
12236
12626
  }, [content.image]);
12237
12627
  const showImage = Boolean(content.image) && !imageFailed;
12238
- const resolvedAppearance = useMemo19(
12628
+ const resolvedAppearance = useMemo21(
12239
12629
  () => resolveHotspotTooltipAppearance(appearance),
12240
12630
  [appearance]
12241
12631
  );
12242
- const bubbleStyle = useMemo19(
12632
+ const bubbleStyle = useMemo21(
12243
12633
  () => ({
12244
12634
  background: resolvedAppearance.background,
12245
12635
  border: resolvedAppearance.border,
@@ -12622,14 +13012,14 @@ function HotspotAnchor({
12622
13012
  const eventBus = useContext24(PanoEventBusContext);
12623
13013
  const cursorApi = usePanoCursor();
12624
13014
  const { camera, size } = useThree20();
12625
- const groupRef = useRef26(null);
12626
- const tooltipAnchorRef = useRef26(null);
12627
- const dragStateRef = useRef26(null);
12628
- const suppressNextClickRef = useRef26(false);
12629
- const [hovered, setHovered] = useState13(false);
12630
- const [dragging, setDragging] = useState13(false);
12631
- const [pinned, setPinned] = useState13(false);
12632
- const tooltipContent = useMemo20(
13015
+ const groupRef = useRef27(null);
13016
+ const tooltipAnchorRef = useRef27(null);
13017
+ const dragStateRef = useRef27(null);
13018
+ const suppressNextClickRef = useRef27(false);
13019
+ const [hovered, setHovered] = useState14(false);
13020
+ const [dragging, setDragging] = useState14(false);
13021
+ const [pinned, setPinned] = useState14(false);
13022
+ const tooltipContent = useMemo22(
12633
13023
  () => resolveHotspotTooltipContent(tooltip),
12634
13024
  [tooltip]
12635
13025
  );
@@ -12645,17 +13035,17 @@ function HotspotAnchor({
12645
13035
  scaledWidth,
12646
13036
  scaledHeight
12647
13037
  );
12648
- const worldPosition = useMemo20(
13038
+ const worldPosition = useMemo22(
12649
13039
  () => panoPositionToVector3(normalizedPosition, resolvedDistance),
12650
13040
  [normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
12651
13041
  );
12652
- const surfaceQuaternion3 = useMemo20(
13042
+ const surfaceQuaternion3 = useMemo22(
12653
13043
  () => createSurfaceQuaternion(normalizedPosition),
12654
13044
  [normalizedPosition.pitch, normalizedPosition.yaw]
12655
13045
  );
12656
13046
  const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
12657
13047
  const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
12658
- const focusGeometry = useMemo20(() => {
13048
+ const focusGeometry = useMemo22(() => {
12659
13049
  const planeGeometry = new PlaneGeometry5(1, 1);
12660
13050
  const geometry = new EdgesGeometry(planeGeometry);
12661
13051
  planeGeometry.dispose();
@@ -13076,9 +13466,9 @@ function HotspotPlane({
13076
13466
  import { jsx as jsx27 } from "react/jsx-runtime";
13077
13467
  function useHotspotImageTexture(src, onLoad, onError) {
13078
13468
  const gl = useThree21((state) => state.gl);
13079
- const [texture, setTexture] = useState14(null);
13080
- const onLoadRef = useRef27(onLoad);
13081
- const onErrorRef = useRef27(onError);
13469
+ const [texture, setTexture] = useState15(null);
13470
+ const onLoadRef = useRef28(onLoad);
13471
+ const onErrorRef = useRef28(onError);
13082
13472
  onLoadRef.current = onLoad;
13083
13473
  onErrorRef.current = onError;
13084
13474
  useEffect29(() => {
@@ -13141,7 +13531,7 @@ function ImageHotspot({
13141
13531
  }
13142
13532
 
13143
13533
  // src/hotspot/polygon-hotspot.tsx
13144
- import { useEffect as useEffect30, useMemo as useMemo21, useRef as useRef29 } from "react";
13534
+ import { useEffect as useEffect30, useMemo as useMemo23, useRef as useRef30 } from "react";
13145
13535
  import {
13146
13536
  BufferGeometry,
13147
13537
  DoubleSide as DoubleSide5,
@@ -13156,7 +13546,7 @@ import {
13156
13546
  // src/hotspot/hotspot-stroke-line.tsx
13157
13547
  import { Line } from "@react-three/drei";
13158
13548
  import { useFrame as useFrame14, useThree as useThree22 } from "@react-three/fiber";
13159
- import { useLayoutEffect as useLayoutEffect5, useRef as useRef28 } from "react";
13549
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef29 } from "react";
13160
13550
  import {
13161
13551
  MathUtils as MathUtils8,
13162
13552
  PerspectiveCamera as PerspectiveCamera6,
@@ -13193,7 +13583,7 @@ function HotspotStrokeLine({
13193
13583
  dashSize,
13194
13584
  gapSize
13195
13585
  }) {
13196
- const lineRef = useRef28(null);
13586
+ const lineRef = useRef29(null);
13197
13587
  const { camera, size } = useThree22();
13198
13588
  const dash = resolveDash(dashSize, gapSize);
13199
13589
  useLayoutEffect5(() => {
@@ -13486,19 +13876,19 @@ function PolygonHotspot({
13486
13876
  onInvalid,
13487
13877
  ...anchorProps
13488
13878
  }) {
13489
- const issues = useMemo21(() => validatePolygonVertices(vertices), [vertices]);
13879
+ const issues = useMemo23(() => validatePolygonVertices(vertices), [vertices]);
13490
13880
  const valid = issues.length === 0;
13491
- const center = useMemo21(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
13492
- const fillGeometry = useMemo21(
13881
+ const center = useMemo23(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
13882
+ const fillGeometry = useMemo23(
13493
13883
  () => valid && center ? makeFillGeometry(vertices, center) : null,
13494
13884
  [center, valid, vertices]
13495
13885
  );
13496
- const strokePoints = useMemo21(
13886
+ const strokePoints = useMemo23(
13497
13887
  () => valid && center ? makeStrokePositions(vertices, center) : null,
13498
13888
  [center, valid, vertices]
13499
13889
  );
13500
13890
  const resolvedStrokeOpacity = Math.max(0, Math.min(strokeOpacity, 1));
13501
- const startVerticesRef = useRef29(null);
13891
+ const startVerticesRef = useRef30(null);
13502
13892
  const issuesKey = issues.map((issue) => issue.code).join(",");
13503
13893
  useEffect30(() => {
13504
13894
  if (!valid) onInvalid?.(issues);
@@ -13583,7 +13973,7 @@ function PolygonHotspot({
13583
13973
  }
13584
13974
 
13585
13975
  // src/hotspot/polyline-hotspot.tsx
13586
- import { useEffect as useEffect31, useMemo as useMemo22, useRef as useRef30 } from "react";
13976
+ import { useEffect as useEffect31, useMemo as useMemo24, useRef as useRef31 } from "react";
13587
13977
  import {
13588
13978
  MathUtils as MathUtils9,
13589
13979
  Matrix4 as Matrix44,
@@ -13707,15 +14097,15 @@ function PolylineHotspot({
13707
14097
  onInvalid,
13708
14098
  ...anchorProps
13709
14099
  }) {
13710
- const issues = useMemo22(() => validatePolylineVertices(vertices), [vertices]);
14100
+ const issues = useMemo24(() => validatePolylineVertices(vertices), [vertices]);
13711
14101
  const valid = issues.length === 0;
13712
- const center = useMemo22(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
13713
- const linePoints = useMemo22(
14102
+ const center = useMemo24(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
14103
+ const linePoints = useMemo24(
13714
14104
  () => valid && center ? makeLinePositions(vertices, center) : null,
13715
14105
  [center, valid, vertices]
13716
14106
  );
13717
14107
  const opacity = Math.max(0, Math.min(strokeOpacity, 1));
13718
- const startVerticesRef = useRef30(null);
14108
+ const startVerticesRef = useRef31(null);
13719
14109
  useEffect31(() => {
13720
14110
  if (!valid) onInvalid?.(issues);
13721
14111
  }, [issues, onInvalid, valid]);
@@ -13782,7 +14172,7 @@ function PolylineHotspot({
13782
14172
  }
13783
14173
 
13784
14174
  // src/hotspot/graphic-hotspot.tsx
13785
- import { useEffect as useEffect32, useRef as useRef31, useState as useState15 } from "react";
14175
+ import { useEffect as useEffect32, useRef as useRef32, useState as useState16 } from "react";
13786
14176
  import {
13787
14177
  CanvasTexture,
13788
14178
  ClampToEdgeWrapping as ClampToEdgeWrapping4,
@@ -13979,9 +14369,9 @@ function createGraphicTexture(graphic, aspectWidth, aspectHeight) {
13979
14369
  return texture;
13980
14370
  }
13981
14371
  function useGraphicTexture(graphic, aspectWidth, aspectHeight, onLoad, onError) {
13982
- const [texture, setTexture] = useState15(null);
13983
- const onLoadRef = useRef31(onLoad);
13984
- const onErrorRef = useRef31(onError);
14372
+ const [texture, setTexture] = useState16(null);
14373
+ const onLoadRef = useRef32(onLoad);
14374
+ const onErrorRef = useRef32(onError);
13985
14375
  onLoadRef.current = onLoad;
13986
14376
  onErrorRef.current = onError;
13987
14377
  useEffect32(() => {
@@ -14023,7 +14413,7 @@ function GraphicHotspot(props) {
14023
14413
 
14024
14414
  // src/hotspot/sequence-hotspot.tsx
14025
14415
  import { useFrame as useFrame15, useThree as useThree23 } from "@react-three/fiber";
14026
- import { useEffect as useEffect33, useRef as useRef32, useState as useState16 } from "react";
14416
+ import { useEffect as useEffect33, useRef as useRef33, useState as useState17 } from "react";
14027
14417
  import {
14028
14418
  ClampToEdgeWrapping as ClampToEdgeWrapping5,
14029
14419
  LinearFilter as LinearFilter8,
@@ -14046,9 +14436,9 @@ function useSpriteTexture({
14046
14436
  onLoadProgress
14047
14437
  }) {
14048
14438
  const gl = useThree23((state) => state.gl);
14049
- const [texture, setTexture] = useState16(null);
14050
- const onErrorRef = useRef32(onError);
14051
- const onLoadProgressRef = useRef32(onLoadProgress);
14439
+ const [texture, setTexture] = useState17(null);
14440
+ const onErrorRef = useRef33(onError);
14441
+ const onLoadProgressRef = useRef33(onLoadProgress);
14052
14442
  onErrorRef.current = onError;
14053
14443
  onLoadProgressRef.current = onLoadProgress;
14054
14444
  useEffect33(() => {
@@ -14121,14 +14511,14 @@ function SequenceHotspot({
14121
14511
  const texture = useSpriteTexture({ id, src, onError, onLoadProgress });
14122
14512
  const frameCount = resolveFrameCount(requestedFrameCount);
14123
14513
  const safeFps = resolveFps(fps);
14124
- const [frameIndex, setFrameIndex] = useState16(0);
14125
- const elapsedRef = useRef32(0);
14126
- const frameIndexRef = useRef32(0);
14127
- const endedRef = useRef32(false);
14128
- const lastPlaybackStateRef = useRef32(null);
14129
- const onFrameChangeRef = useRef32(onFrameChange);
14130
- const onPlaybackStateChangeRef = useRef32(onPlaybackStateChange);
14131
- const onEndedRef = useRef32(onEnded);
14514
+ const [frameIndex, setFrameIndex] = useState17(0);
14515
+ const elapsedRef = useRef33(0);
14516
+ const frameIndexRef = useRef33(0);
14517
+ const endedRef = useRef33(false);
14518
+ const lastPlaybackStateRef = useRef33(null);
14519
+ const onFrameChangeRef = useRef33(onFrameChange);
14520
+ const onPlaybackStateChangeRef = useRef33(onPlaybackStateChange);
14521
+ const onEndedRef = useRef33(onEnded);
14132
14522
  onFrameChangeRef.current = onFrameChange;
14133
14523
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14134
14524
  onEndedRef.current = onEnded;
@@ -14212,7 +14602,7 @@ function SequenceHotspot({
14212
14602
 
14213
14603
  // src/hotspot/video-hotspot.tsx
14214
14604
  import { useThree as useThree24 } from "@react-three/fiber";
14215
- import { useEffect as useEffect34, useRef as useRef33, useState as useState17 } from "react";
14605
+ import { useEffect as useEffect34, useRef as useRef34, useState as useState18 } from "react";
14216
14606
  import {
14217
14607
  ClampToEdgeWrapping as ClampToEdgeWrapping6,
14218
14608
  LinearFilter as LinearFilter9,
@@ -14230,8 +14620,8 @@ function clampVolume2(volume) {
14230
14620
  }
14231
14621
  function usePosterTexture2(id, poster, onError) {
14232
14622
  const gl = useThree24((state) => state.gl);
14233
- const [texture, setTexture] = useState17(null);
14234
- const onErrorRef = useRef33(onError);
14623
+ const [texture, setTexture] = useState18(null);
14624
+ const onErrorRef = useRef34(onError);
14235
14625
  onErrorRef.current = onError;
14236
14626
  useEffect34(() => {
14237
14627
  let active = true;
@@ -14288,10 +14678,10 @@ function useVideoResource({
14288
14678
  poster,
14289
14679
  src
14290
14680
  }) {
14291
- const [resource, setResource] = useState17(null);
14292
- const onEndedRef = useRef33(onEnded);
14293
- const onErrorRef = useRef33(onError);
14294
- const onPlaybackStateChangeRef = useRef33(onPlaybackStateChange);
14681
+ const [resource, setResource] = useState18(null);
14682
+ const onEndedRef = useRef34(onEnded);
14683
+ const onErrorRef = useRef34(onError);
14684
+ const onPlaybackStateChangeRef = useRef34(onPlaybackStateChange);
14295
14685
  onEndedRef.current = onEnded;
14296
14686
  onErrorRef.current = onError;
14297
14687
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
@@ -14396,8 +14786,8 @@ function VideoHotspot({
14396
14786
  src
14397
14787
  });
14398
14788
  const posterTexture = usePosterTexture2(id, poster, onError);
14399
- const onPlaybackErrorRef = useRef33(onPlaybackError);
14400
- const onPlaybackStateChangeRef = useRef33(onPlaybackStateChange);
14789
+ const onPlaybackErrorRef = useRef34(onPlaybackError);
14790
+ const onPlaybackStateChangeRef = useRef34(onPlaybackStateChange);
14401
14791
  onPlaybackErrorRef.current = onPlaybackError;
14402
14792
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
14403
14793
  useEffect34(() => {
@@ -14438,7 +14828,7 @@ function VideoHotspot({
14438
14828
 
14439
14829
  // src/hotspot/audio-hotspot.tsx
14440
14830
  import { useFrame as useFrame16, useThree as useThree25 } from "@react-three/fiber";
14441
- import { useEffect as useEffect35, useRef as useRef34, useState as useState18 } from "react";
14831
+ import { useEffect as useEffect35, useRef as useRef35, useState as useState19 } from "react";
14442
14832
  import {
14443
14833
  CanvasTexture as CanvasTexture2,
14444
14834
  ClampToEdgeWrapping as ClampToEdgeWrapping7,
@@ -14511,7 +14901,7 @@ function createSpeakerTexture(state) {
14511
14901
  return texture;
14512
14902
  }
14513
14903
  function useSpeakerTextures(enabled) {
14514
- const [textures, setTextures] = useState18({ playing: null, stopped: null });
14904
+ const [textures, setTextures] = useState19({ playing: null, stopped: null });
14515
14905
  useEffect35(() => {
14516
14906
  if (!enabled) {
14517
14907
  setTextures({ playing: null, stopped: null });
@@ -14529,8 +14919,8 @@ function useSpeakerTextures(enabled) {
14529
14919
  }
14530
14920
  function useIconTexture(icon, enabled, onError, id) {
14531
14921
  const gl = useThree25((state) => state.gl);
14532
- const [texture, setTexture] = useState18(null);
14533
- const onErrorRef = useRef34(onError);
14922
+ const [texture, setTexture] = useState19(null);
14923
+ const onErrorRef = useRef35(onError);
14534
14924
  onErrorRef.current = onError;
14535
14925
  useEffect35(() => {
14536
14926
  let active = true;
@@ -14588,13 +14978,13 @@ function useAudioHowl({
14588
14978
  playing,
14589
14979
  src
14590
14980
  }) {
14591
- const [howl, setHowl] = useState18(null);
14592
- const playingRef = useRef34(playing);
14593
- const loopRef = useRef34(loop ?? false);
14594
- const onEndedRef = useRef34(onEnded);
14595
- const onErrorRef = useRef34(onError);
14596
- const onPlaybackErrorRef = useRef34(onPlaybackError);
14597
- const onPlaybackStateChangeRef = useRef34(onPlaybackStateChange);
14981
+ const [howl, setHowl] = useState19(null);
14982
+ const playingRef = useRef35(playing);
14983
+ const loopRef = useRef35(loop ?? false);
14984
+ const onEndedRef = useRef35(onEnded);
14985
+ const onErrorRef = useRef35(onError);
14986
+ const onPlaybackErrorRef = useRef35(onPlaybackError);
14987
+ const onPlaybackStateChangeRef = useRef35(onPlaybackStateChange);
14598
14988
  playingRef.current = playing;
14599
14989
  loopRef.current = loop ?? false;
14600
14990
  onEndedRef.current = onEnded;
@@ -14682,11 +15072,11 @@ function AudioHotspot({
14682
15072
  const stoppedIconTexture = useIconTexture(icon, marker, onError, id);
14683
15073
  const playingIconTexture = useIconTexture(playingIcon, marker, onError, id);
14684
15074
  const markerTexture = playing ? playingIconTexture ?? stoppedIconTexture ?? speakerTextures.playing : stoppedIconTexture ?? speakerTextures.stopped;
14685
- const playingRef = useRef34(playing);
15075
+ const playingRef = useRef35(playing);
14686
15076
  playingRef.current = playing;
14687
- const mutedRef = useRef34(muted);
14688
- const volumeRef = useRef34(volume);
14689
- const rangeRef = useRef34(range);
15077
+ const mutedRef = useRef35(muted);
15078
+ const volumeRef = useRef35(volume);
15079
+ const rangeRef = useRef35(range);
14690
15080
  mutedRef.current = muted;
14691
15081
  volumeRef.current = volume;
14692
15082
  rangeRef.current = range;
@@ -14735,7 +15125,7 @@ function AudioHotspot({
14735
15125
  }
14736
15126
 
14737
15127
  // src/hotspot/text-hotspot.tsx
14738
- import { useEffect as useEffect36, useRef as useRef35, useState as useState19 } from "react";
15128
+ import { useEffect as useEffect36, useRef as useRef36, useState as useState20 } from "react";
14739
15129
  import {
14740
15130
  CanvasTexture as CanvasTexture3,
14741
15131
  ClampToEdgeWrapping as ClampToEdgeWrapping8,
@@ -14963,9 +15353,9 @@ function createTextTexture(text, width, height, style) {
14963
15353
  return texture;
14964
15354
  }
14965
15355
  function useTextTexture(text, width, height, style, onLoad, onError) {
14966
- const [texture, setTexture] = useState19(null);
14967
- const onLoadRef = useRef35(onLoad);
14968
- const onErrorRef = useRef35(onError);
15356
+ const [texture, setTexture] = useState20(null);
15357
+ const onLoadRef = useRef36(onLoad);
15358
+ const onErrorRef = useRef36(onError);
14969
15359
  onLoadRef.current = onLoad;
14970
15360
  onErrorRef.current = onError;
14971
15361
  useEffect36(() => {
@@ -15231,6 +15621,7 @@ export {
15231
15621
  resolvePanoContextMenuEntries,
15232
15622
  unwrapPolygonVertices,
15233
15623
  usePanoEvents,
15624
+ usePanoViewer,
15234
15625
  validatePolygonVertices,
15235
15626
  validatePolylineVertices,
15236
15627
  vector3ToPanoPosition