@ericchen1990/pano-view 2.0.0 → 2.2.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
@@ -718,7 +718,7 @@ function useHotspotAccessibilityLayer() {
718
718
  "button",
719
719
  {
720
720
  "aria-label": entry.label,
721
- className: "sr-only",
721
+ className: "pano-sr-only",
722
722
  onBlur: () => entry.onFocusChange(false),
723
723
  onClick: (event) => entry.activate(event.nativeEvent),
724
724
  onFocus: () => entry.onFocusChange(true),
@@ -1437,9 +1437,6 @@ function DefaultContextMenuPanel({
1437
1437
  fontSize: resolved.fontSize,
1438
1438
  minWidth: resolved.minWidth,
1439
1439
  padding: resolved.padding,
1440
- pointerEvents: "auto",
1441
- position: "relative",
1442
- userSelect: "none",
1443
1440
  ...style
1444
1441
  };
1445
1442
  return /* @__PURE__ */ jsxs(
@@ -1447,7 +1444,7 @@ function DefaultContextMenuPanel({
1447
1444
  {
1448
1445
  ref: menuRef,
1449
1446
  "aria-label": "Panorama context menu",
1450
- className: cn("relative select-none", className),
1447
+ className: cn("pano-context-menu", className),
1451
1448
  onKeyDown: handleKeyDown,
1452
1449
  role: "menu",
1453
1450
  style: panelStyle,
@@ -1457,7 +1454,7 @@ function DefaultContextMenuPanel({
1457
1454
  "div",
1458
1455
  {
1459
1456
  "aria-hidden": true,
1460
- className: "pointer-events-none absolute inset-0 z-0",
1457
+ className: "pano-context-menu-backdrop",
1461
1458
  style: {
1462
1459
  background: resolved.background,
1463
1460
  borderRadius: resolved.borderRadius,
@@ -1465,7 +1462,7 @@ function DefaultContextMenuPanel({
1465
1462
  }
1466
1463
  }
1467
1464
  ),
1468
- /* @__PURE__ */ jsx2("div", { className: "relative z-[1]", children: visibleItems.map((item, index) => {
1465
+ /* @__PURE__ */ jsx2("div", { className: "pano-context-menu-items", children: visibleItems.map((item, index) => {
1469
1466
  if (!isActionItem(item)) {
1470
1467
  return /* @__PURE__ */ jsx2(
1471
1468
  "div",
@@ -1503,7 +1500,7 @@ function DefaultContextMenuPanel({
1503
1500
  {
1504
1501
  "aria-disabled": disabled || void 0,
1505
1502
  className: cn(
1506
- "flex w-full items-center gap-2 rounded text-left",
1503
+ "pano-context-menu-item",
1507
1504
  itemClassName,
1508
1505
  item.className
1509
1506
  ),
@@ -1538,7 +1535,7 @@ function DefaultContextMenuPanel({
1538
1535
  "span",
1539
1536
  {
1540
1537
  "aria-hidden": true,
1541
- className: "inline-flex shrink-0 items-center justify-center",
1538
+ className: "pano-context-menu-item-icon",
1542
1539
  style: {
1543
1540
  height: iconSize,
1544
1541
  width: iconSize
@@ -1589,7 +1586,7 @@ function PositionedMenuShell({
1589
1586
  return /* @__PURE__ */ jsx2(
1590
1587
  "div",
1591
1588
  {
1592
- className: "absolute z-20",
1589
+ className: "pano-context-menu-shell",
1593
1590
  ref: shellRef,
1594
1591
  style: {
1595
1592
  left: offset.left,
@@ -2713,7 +2710,7 @@ function usePanoChromeOverlay() {
2713
2710
  const overlay = /* @__PURE__ */ jsx5(
2714
2711
  "div",
2715
2712
  {
2716
- className: "pointer-events-none absolute inset-0 z-[15]",
2713
+ className: "pano-chrome-overlay",
2717
2714
  "data-pano-chrome-overlay": "",
2718
2715
  ref: setOverlayNode
2719
2716
  }
@@ -2805,14 +2802,7 @@ function PanoVideoCaptionsOverlay({
2805
2802
  return null;
2806
2803
  }
2807
2804
  const wrapperStyle = {
2808
- bottom: snapshot.captionAppearance.bottom,
2809
- display: "flex",
2810
- justifyContent: "center",
2811
- left: 0,
2812
- pointerEvents: "none",
2813
- position: "absolute",
2814
- right: 0,
2815
- zIndex: 1
2805
+ bottom: snapshot.captionAppearance.bottom
2816
2806
  };
2817
2807
  const textStyle = {
2818
2808
  background: snapshot.captionAppearance.background,
@@ -2831,7 +2821,7 @@ function PanoVideoCaptionsOverlay({
2831
2821
  return /* @__PURE__ */ jsx6(
2832
2822
  "div",
2833
2823
  {
2834
- className: "pointer-events-none absolute inset-x-0 z-1 flex justify-center",
2824
+ className: "pano-video-captions",
2835
2825
  "data-pano-video-captions": "",
2836
2826
  style: wrapperStyle,
2837
2827
  children: /* @__PURE__ */ jsx6("div", { style: textStyle, children: snapshot.captionText })
@@ -3075,7 +3065,7 @@ function ChromeButton({
3075
3065
  "button",
3076
3066
  {
3077
3067
  "aria-label": label,
3078
- className: "inline-flex min-h-7 min-w-7 items-center justify-center rounded",
3068
+ className: "pano-video-chrome-button",
3079
3069
  disabled,
3080
3070
  id,
3081
3071
  onClick,
@@ -3083,18 +3073,7 @@ function ChromeButton({
3083
3073
  event.stopPropagation();
3084
3074
  },
3085
3075
  style: {
3086
- alignItems: "center",
3087
- background: "transparent",
3088
- border: "none",
3089
- borderRadius: 4,
3090
- color: appearance.color,
3091
- cursor: disabled ? "default" : "pointer",
3092
- display: "inline-flex",
3093
- height: 28,
3094
- justifyContent: "center",
3095
- minWidth: 28,
3096
- opacity: disabled ? 0.45 : 1,
3097
- padding: 4
3076
+ color: appearance.color
3098
3077
  },
3099
3078
  title: label,
3100
3079
  type: "button",
@@ -3139,7 +3118,7 @@ function ChromeMenuSurface({
3139
3118
  "div",
3140
3119
  {
3141
3120
  "aria-labelledby": labelledBy,
3142
- className: "absolute right-0 flex min-w-28 flex-col p-1",
3121
+ className: "pano-video-chrome-menu",
3143
3122
  id,
3144
3123
  role,
3145
3124
  style: {
@@ -3163,7 +3142,7 @@ function ChromeMenuRadioItem({
3163
3142
  "button",
3164
3143
  {
3165
3144
  "aria-checked": active,
3166
- className: "rounded px-2.5 py-1.5 text-left whitespace-nowrap",
3145
+ className: "pano-video-chrome-menu-item",
3167
3146
  onClick,
3168
3147
  role: "menuitemradio",
3169
3148
  style: {
@@ -3187,7 +3166,7 @@ function ChromeMenuActionItem({
3187
3166
  return /* @__PURE__ */ jsxs3(
3188
3167
  "button",
3189
3168
  {
3190
- className: "flex items-center justify-between gap-3 rounded px-2.5 py-1.5 text-left whitespace-nowrap",
3169
+ className: "pano-video-chrome-menu-action",
3191
3170
  onClick,
3192
3171
  role: "menuitem",
3193
3172
  style: {
@@ -3208,17 +3187,10 @@ function ChromeMenuActionItem({
3208
3187
  function ChromeMenuSectionLabel({
3209
3188
  children
3210
3189
  }) {
3211
- return /* @__PURE__ */ jsx7(
3212
- "div",
3213
- {
3214
- className: "px-2.5 pt-2 pb-1 uppercase opacity-70",
3215
- style: { fontSize: 10, letterSpacing: "0.08em" },
3216
- children
3217
- }
3218
- );
3190
+ return /* @__PURE__ */ jsx7("div", { className: "pano-video-chrome-menu-label", children });
3219
3191
  }
3220
3192
  function ChromeMenuDivider() {
3221
- return /* @__PURE__ */ jsx7("div", { className: "my-1 h-px bg-white/10" });
3193
+ return /* @__PURE__ */ jsx7("div", { className: "pano-video-chrome-menu-divider" });
3222
3194
  }
3223
3195
  function CaptionsMenu({
3224
3196
  appearance,
@@ -3239,7 +3211,7 @@ function CaptionsMenu({
3239
3211
  labelledBy,
3240
3212
  role: "menu",
3241
3213
  children: panel === "tracks" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
3242
- /* @__PURE__ */ jsxs3("div", { className: "flex max-h-48 flex-col overflow-y-auto", children: [
3214
+ /* @__PURE__ */ jsxs3("div", { className: "pano-video-chrome-menu-list", children: [
3243
3215
  /* @__PURE__ */ jsx7(
3244
3216
  ChromeMenuRadioItem,
3245
3217
  {
@@ -3347,7 +3319,7 @@ function VerticalVolumeSlider({
3347
3319
  return /* @__PURE__ */ jsxs3(
3348
3320
  "div",
3349
3321
  {
3350
- className: "absolute left-1/2 flex min-w-11 -translate-x-1/2 flex-col items-center rounded-[14px]",
3322
+ className: "pano-video-volume-popover",
3351
3323
  style: {
3352
3324
  background: "rgba(12, 12, 12, 0.92)",
3353
3325
  bottom: `calc(100% + ${CHROME_POPOVER_GAP}px)`,
@@ -3362,7 +3334,7 @@ function VerticalVolumeSlider({
3362
3334
  "span",
3363
3335
  {
3364
3336
  "aria-hidden": true,
3365
- className: "mb-3.5 font-bold leading-none [font-variant-numeric:tabular-nums]",
3337
+ className: "pano-video-volume-percent",
3366
3338
  style: {
3367
3339
  color: appearance.color,
3368
3340
  fontSize: VOLUME_POP_FONT_SIZE
@@ -3401,7 +3373,7 @@ function VerticalVolumeSlider({
3401
3373
  },
3402
3374
  ref: trackRef,
3403
3375
  role: "slider",
3404
- className: "relative cursor-pointer rounded-[999px]",
3376
+ className: "pano-video-volume-track",
3405
3377
  style: {
3406
3378
  background: "rgba(255, 255, 255, 0.14)",
3407
3379
  borderRadius: 999,
@@ -3498,7 +3470,7 @@ function VolumeControl({
3498
3470
  "div",
3499
3471
  {
3500
3472
  "data-pano-video-volume": "",
3501
- className: "relative",
3473
+ className: "pano-video-volume",
3502
3474
  onBlur: (event) => {
3503
3475
  if (event.currentTarget.contains(event.relatedTarget)) {
3504
3476
  return;
@@ -3829,7 +3801,7 @@ function PanoVideoControlsHud({
3829
3801
  transition: "opacity 160ms ease",
3830
3802
  zIndex: 2
3831
3803
  };
3832
- const rateButton = /* @__PURE__ */ jsxs3("div", { className: "relative", "data-pano-video-menu": "rate", children: [
3804
+ const rateButton = /* @__PURE__ */ jsxs3("div", { className: "pano-video-menu", "data-pano-video-menu": "rate", children: [
3833
3805
  !compact && menu === "rate" ? menuNode : null,
3834
3806
  /* @__PURE__ */ jsx7(
3835
3807
  ChromeButton,
@@ -3839,11 +3811,11 @@ function PanoVideoControlsHud({
3839
3811
  onClick: () => {
3840
3812
  toggleMenu("rate");
3841
3813
  },
3842
- children: /* @__PURE__ */ jsx7("span", { className: "px-0.5 text-[11px]", id: `${barId}-rate-btn`, children: snapshot.playbackRate === 1 ? "1x" : `${snapshot.playbackRate}x` })
3814
+ children: /* @__PURE__ */ jsx7("span", { className: "pano-video-chrome-label", id: `${barId}-rate-btn`, children: snapshot.playbackRate === 1 ? "1x" : `${snapshot.playbackRate}x` })
3843
3815
  }
3844
3816
  )
3845
3817
  ] });
3846
- const variantButton = canSwitchVariant ? /* @__PURE__ */ jsxs3("div", { className: "relative", "data-pano-video-menu": "variant", children: [
3818
+ const variantButton = canSwitchVariant ? /* @__PURE__ */ jsxs3("div", { className: "pano-video-menu", "data-pano-video-menu": "variant", children: [
3847
3819
  !compact && menu === "variant" ? menuNode : null,
3848
3820
  /* @__PURE__ */ jsx7(
3849
3821
  ChromeButton,
@@ -3853,11 +3825,11 @@ function PanoVideoControlsHud({
3853
3825
  onClick: () => {
3854
3826
  toggleMenu("variant");
3855
3827
  },
3856
- children: /* @__PURE__ */ jsx7("span", { className: "px-0.5 text-[11px]", id: `${barId}-variant-btn`, children: snapshot.variants.find((variant) => variant.id === snapshot.variantId)?.label ?? "Quality" })
3828
+ children: /* @__PURE__ */ jsx7("span", { className: "pano-video-chrome-label", id: `${barId}-variant-btn`, children: snapshot.variants.find((variant) => variant.id === snapshot.variantId)?.label ?? "Quality" })
3857
3829
  }
3858
3830
  )
3859
3831
  ] }) : null;
3860
- const captionsButton = showCaptionsMenu ? /* @__PURE__ */ jsxs3("div", { className: "relative", "data-pano-video-menu": "captions", children: [
3832
+ const captionsButton = showCaptionsMenu ? /* @__PURE__ */ jsxs3("div", { className: "pano-video-menu", "data-pano-video-menu": "captions", children: [
3861
3833
  !compact && menu === "captions" ? menuNode : null,
3862
3834
  /* @__PURE__ */ jsx7(
3863
3835
  ChromeButton,
@@ -3874,7 +3846,7 @@ function PanoVideoControlsHud({
3874
3846
  return /* @__PURE__ */ jsxs3(
3875
3847
  "div",
3876
3848
  {
3877
- className: "absolute right-2.5 bottom-2.5 left-2.5 z-2 flex items-center gap-2 transition-opacity",
3849
+ className: "pano-video-controls",
3878
3850
  "data-pano-video-controls": "",
3879
3851
  style: barStyle,
3880
3852
  children: [
@@ -3889,7 +3861,7 @@ function PanoVideoControlsHud({
3889
3861
  children: snapshot.playing ? /* @__PURE__ */ jsx7(Icon, { path: "M5 3.5h2.2v9H5zM8.8 3.5H11v9H8.8z" }) : /* @__PURE__ */ jsx7(Icon, { path: "M5 3.2v9.6L13 8z" })
3890
3862
  }
3891
3863
  ),
3892
- hideTime ? null : /* @__PURE__ */ jsx7("span", { className: "min-w-8.5 opacity-[0.85] [font-variant-numeric:tabular-nums]", children: formatMediaTime(snapshot.currentTime) }),
3864
+ hideTime ? null : /* @__PURE__ */ jsx7("span", { className: "pano-video-time", children: formatMediaTime(snapshot.currentTime) }),
3893
3865
  /* @__PURE__ */ jsx7(
3894
3866
  "input",
3895
3867
  {
@@ -3912,7 +3884,7 @@ function PanoVideoControlsHud({
3912
3884
  value: duration > 0 ? snapshot.currentTime : 0
3913
3885
  }
3914
3886
  ),
3915
- hideTime ? null : /* @__PURE__ */ jsx7("span", { className: "min-w-8.5 opacity-[0.85]", children: formatMediaTime(duration) }),
3887
+ hideTime ? null : /* @__PURE__ */ jsx7("span", { className: "pano-video-time", children: formatMediaTime(duration) }),
3916
3888
  /* @__PURE__ */ jsx7(
3917
3889
  VolumeControl,
3918
3890
  {
@@ -3929,7 +3901,7 @@ function PanoVideoControlsHud({
3929
3901
  volumeAdjustable: snapshot.volumeAdjustable
3930
3902
  }
3931
3903
  ),
3932
- compact ? /* @__PURE__ */ jsxs3("div", { className: "relative", "data-pano-video-menu": "more", children: [
3904
+ compact ? /* @__PURE__ */ jsxs3("div", { className: "pano-video-menu", "data-pano-video-menu": "more", children: [
3933
3905
  menu !== null ? menuNode : null,
3934
3906
  /* @__PURE__ */ jsx7(
3935
3907
  ChromeButton,
@@ -4166,7 +4138,7 @@ function ChromeButton2({
4166
4138
  return /* @__PURE__ */ jsx9(
4167
4139
  "button",
4168
4140
  {
4169
- className: "pointer-events-auto border border-white/20 px-3 py-2 font-semibold shadow-lg backdrop-blur-sm",
4141
+ className: "pano-webvr-button",
4170
4142
  onClick,
4171
4143
  style: {
4172
4144
  background: appearance.background,
@@ -4204,19 +4176,19 @@ function SetupPanel({
4204
4176
  "div",
4205
4177
  {
4206
4178
  "aria-label": "Mobile VR setup",
4207
- className: "pointer-events-auto absolute top-1/2 left-1/2 w-[min(22rem,calc(100%-2rem))] -translate-x-1/2 -translate-y-1/2 border border-white/20 p-5 text-center shadow-2xl backdrop-blur-md",
4179
+ className: "pano-webvr-setup",
4208
4180
  role: "dialog",
4209
4181
  style: panelStyle,
4210
4182
  children: [
4211
- /* @__PURE__ */ jsx9("h2", { className: "m-0 mb-5 text-2xl font-extrabold", children: appearance.setupTitle ?? "MobileVR Setup" }),
4212
- /* @__PURE__ */ jsxs5("label", { className: "mb-5 block font-semibold", children: [
4183
+ /* @__PURE__ */ jsx9("h2", { className: "pano-webvr-setup-title", children: appearance.setupTitle ?? "MobileVR Setup" }),
4184
+ /* @__PURE__ */ jsxs5("label", { className: "pano-webvr-setup-field", children: [
4213
4185
  "Screen size (inch)",
4214
- /* @__PURE__ */ jsxs5("div", { className: "mt-2 flex items-center justify-center gap-3", children: [
4186
+ /* @__PURE__ */ jsxs5("div", { className: "pano-webvr-setup-row", children: [
4215
4187
  /* @__PURE__ */ jsx9(
4216
4188
  "button",
4217
4189
  {
4218
4190
  "aria-label": "Decrease screen size",
4219
- className: "text-2xl",
4191
+ className: "pano-webvr-setup-stepper",
4220
4192
  onClick: () => setScreensize(
4221
4193
  settings.screensize === "auto" ? 5 : Math.max(3, settings.screensize - 0.1)
4222
4194
  ),
@@ -4224,12 +4196,12 @@ function SetupPanel({
4224
4196
  children: "\u2039"
4225
4197
  }
4226
4198
  ),
4227
- /* @__PURE__ */ jsx9("strong", { className: "min-w-16 text-xl", children: settings.screensize === "auto" ? "Auto" : settings.screensize.toFixed(1) }),
4199
+ /* @__PURE__ */ jsx9("strong", { className: "pano-webvr-setup-value", children: settings.screensize === "auto" ? "Auto" : settings.screensize.toFixed(1) }),
4228
4200
  /* @__PURE__ */ jsx9(
4229
4201
  "button",
4230
4202
  {
4231
4203
  "aria-label": "Increase screen size",
4232
- className: "text-2xl",
4204
+ className: "pano-webvr-setup-stepper",
4233
4205
  onClick: () => setScreensize(
4234
4206
  settings.screensize === "auto" ? 5 : Math.min(20, settings.screensize + 0.1)
4235
4207
  ),
@@ -4239,25 +4211,25 @@ function SetupPanel({
4239
4211
  )
4240
4212
  ] })
4241
4213
  ] }),
4242
- /* @__PURE__ */ jsxs5("fieldset", { className: "m-0 mb-5 border-0 p-0", children: [
4243
- /* @__PURE__ */ jsx9("legend", { className: "mb-2 font-semibold", children: "VR headset preset" }),
4244
- /* @__PURE__ */ jsxs5("div", { className: "mt-2 flex items-center justify-center gap-3", children: [
4214
+ /* @__PURE__ */ jsxs5("fieldset", { className: "pano-webvr-setup-fieldset", children: [
4215
+ /* @__PURE__ */ jsx9("legend", { className: "pano-webvr-setup-legend", children: "VR headset preset" }),
4216
+ /* @__PURE__ */ jsxs5("div", { className: "pano-webvr-setup-row", children: [
4245
4217
  /* @__PURE__ */ jsx9(
4246
4218
  "button",
4247
4219
  {
4248
4220
  "aria-label": "Previous headset preset",
4249
- className: "text-2xl",
4221
+ className: "pano-webvr-setup-stepper",
4250
4222
  onClick: () => cycleProfile(-1),
4251
4223
  type: "button",
4252
4224
  children: "\u2039"
4253
4225
  }
4254
4226
  ),
4255
- /* @__PURE__ */ jsx9("strong", { className: "min-w-32 text-xl", children: WEBVR_PROFILES[settings.profileId].label }),
4227
+ /* @__PURE__ */ jsx9("strong", { className: "pano-webvr-setup-value-wide", children: WEBVR_PROFILES[settings.profileId].label }),
4256
4228
  /* @__PURE__ */ jsx9(
4257
4229
  "button",
4258
4230
  {
4259
4231
  "aria-label": "Next headset preset",
4260
- className: "text-2xl",
4232
+ className: "pano-webvr-setup-stepper",
4261
4233
  onClick: () => cycleProfile(1),
4262
4234
  type: "button",
4263
4235
  children: "\u203A"
@@ -4289,7 +4261,7 @@ function WebVRChromeBridge({ host }) {
4289
4261
  }
4290
4262
  const appearance = resolveAppearance(chrome);
4291
4263
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4292
- /* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute inset-x-0 top-4 flex justify-center", children: mode ? /* @__PURE__ */ jsxs5("div", { className: "flex gap-2", children: [
4264
+ /* @__PURE__ */ jsx9("div", { className: "pano-webvr-toolbar", children: mode ? /* @__PURE__ */ jsxs5("div", { className: "pano-webvr-toolbar-actions", children: [
4293
4265
  mode !== "webxr" ? /* @__PURE__ */ jsx9(
4294
4266
  ChromeButton2,
4295
4267
  {
@@ -4317,7 +4289,7 @@ function WebVRChromeBridge({ host }) {
4317
4289
  mode === "fake" ? /* @__PURE__ */ jsx9(
4318
4290
  "p",
4319
4291
  {
4320
- className: "pointer-events-none absolute inset-x-0 bottom-5 m-0 text-center text-xs font-semibold text-white drop-shadow",
4292
+ className: "pano-webvr-hint",
4321
4293
  style: { color: appearance.color },
4322
4294
  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."
4323
4295
  }
@@ -4663,7 +4635,7 @@ var PanoViewer = forwardRef2(
4663
4635
  "div",
4664
4636
  {
4665
4637
  ...divProps,
4666
- className: cn("relative overflow-hidden", divProps.className),
4638
+ className: cn("pano-viewer", divProps.className),
4667
4639
  "data-pano-viewer": "",
4668
4640
  ref: rootRef,
4669
4641
  "aria-label": ariaLabel,
@@ -4762,12 +4734,9 @@ var PanoViewer = forwardRef2(
4762
4734
  /* @__PURE__ */ jsxs6(
4763
4735
  "div",
4764
4736
  {
4765
- className: "pointer-events-none absolute inset-0 z-15",
4737
+ className: "pano-chrome-overlay",
4766
4738
  "data-pano-chrome-overlay": "",
4767
4739
  ref: setOverlayNode,
4768
- style: {
4769
- inset: 0
4770
- },
4771
4740
  children: [
4772
4741
  /* @__PURE__ */ jsx11(
4773
4742
  PanoVideoChromeBridge,
@@ -6369,7 +6338,7 @@ var WebVR = forwardRef4(function WebVR2({
6369
6338
 
6370
6339
  // src/sphere.tsx
6371
6340
  import { useThree as useThree13 } from "@react-three/fiber";
6372
- import { useEffect as useEffect19, useRef as useRef19, useState as useState8 } from "react";
6341
+ import { useEffect as useEffect19, useMemo as useMemo12, useRef as useRef19, useState as useState8 } from "react";
6373
6342
  import {
6374
6343
  BackSide as BackSide2,
6375
6344
  LinearFilter,
@@ -6380,6 +6349,27 @@ import {
6380
6349
  TextureLoader
6381
6350
  } from "three";
6382
6351
 
6352
+ // src/asset-url.ts
6353
+ function isAbsoluteAssetUrl(url) {
6354
+ return url.startsWith("/") || url.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(url);
6355
+ }
6356
+ function resolveRelativeAssetUrl(baseUrl, relativePath) {
6357
+ const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
6358
+ const normalizedPath = relativePath.replace(/^\/+/, "");
6359
+ return normalizedBaseUrl ? `${normalizedBaseUrl}/${normalizedPath}` : normalizedPath;
6360
+ }
6361
+ function resolveAssetUrl(baseUrl, path) {
6362
+ return isAbsoluteAssetUrl(path) ? path : resolveRelativeAssetUrl(baseUrl, path);
6363
+ }
6364
+ function resolveUrlAgainstFile(fileUrl, path) {
6365
+ if (isAbsoluteAssetUrl(path)) {
6366
+ return path;
6367
+ }
6368
+ const slash = fileUrl.lastIndexOf("/");
6369
+ const directory = slash === -1 ? "" : fileUrl.slice(0, slash);
6370
+ return resolveRelativeAssetUrl(directory, path);
6371
+ }
6372
+
6383
6373
  // src/pano-filter/pano-basic-material.tsx
6384
6374
  import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo11, useRef as useRef18 } from "react";
6385
6375
 
@@ -6596,25 +6586,22 @@ function PanoBasicMaterial({
6596
6586
 
6597
6587
  // src/sphere.tsx
6598
6588
  import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
6599
- function Sphere({
6600
- src,
6601
- yawOffset = 0,
6602
- onLoad,
6603
- onError,
6604
- visible = true
6605
- }) {
6606
- const gl = useThree13((state) => state.gl);
6589
+ function useEquirectTexture(url, anisotropy, generateMipmaps, onLoad, onError) {
6607
6590
  const [texture, setTexture] = useState8(null);
6608
6591
  const onLoadRef = useRef19(onLoad);
6609
6592
  const onErrorRef = useRef19(onError);
6610
6593
  onLoadRef.current = onLoad;
6611
6594
  onErrorRef.current = onError;
6612
6595
  useEffect19(() => {
6596
+ if (!url) {
6597
+ setTexture(null);
6598
+ return;
6599
+ }
6613
6600
  let active = true;
6614
6601
  const loader = new TextureLoader();
6615
6602
  setTexture(null);
6616
6603
  loader.load(
6617
- src,
6604
+ url,
6618
6605
  (loadedTexture) => {
6619
6606
  if (!active) {
6620
6607
  loadedTexture.dispose();
@@ -6624,10 +6611,10 @@ function Sphere({
6624
6611
  loadedTexture.wrapS = RepeatWrapping;
6625
6612
  loadedTexture.repeat.x = -1;
6626
6613
  loadedTexture.offset.x = 1;
6627
- loadedTexture.anisotropy = gl.capabilities.getMaxAnisotropy();
6614
+ loadedTexture.anisotropy = anisotropy;
6628
6615
  loadedTexture.magFilter = LinearFilter;
6629
- loadedTexture.minFilter = LinearMipmapLinearFilter;
6630
- loadedTexture.generateMipmaps = true;
6616
+ loadedTexture.minFilter = generateMipmaps ? LinearMipmapLinearFilter : LinearFilter;
6617
+ loadedTexture.generateMipmaps = generateMipmaps;
6631
6618
  loadedTexture.needsUpdate = true;
6632
6619
  setTexture(loadedTexture);
6633
6620
  onLoadRef.current?.(loadedTexture);
@@ -6635,6 +6622,7 @@ function Sphere({
6635
6622
  void 0,
6636
6623
  (error) => {
6637
6624
  if (active) {
6625
+ setTexture(null);
6638
6626
  onErrorRef.current?.(error);
6639
6627
  }
6640
6628
  }
@@ -6642,14 +6630,58 @@ function Sphere({
6642
6630
  return () => {
6643
6631
  active = false;
6644
6632
  };
6645
- }, [gl, src]);
6633
+ }, [anisotropy, generateMipmaps, url]);
6646
6634
  useEffect19(
6647
6635
  () => () => {
6648
6636
  texture?.dispose();
6649
6637
  },
6650
6638
  [texture]
6651
6639
  );
6652
- if (!texture) {
6640
+ return texture;
6641
+ }
6642
+ function Sphere({
6643
+ src,
6644
+ previewUrl,
6645
+ yawOffset = 0,
6646
+ onReady,
6647
+ onLoad,
6648
+ onError,
6649
+ onPreviewError,
6650
+ visible = true
6651
+ }) {
6652
+ const gl = useThree13((state) => state.gl);
6653
+ const anisotropy = gl.capabilities.getMaxAnisotropy();
6654
+ const resolvedPreviewUrl = useMemo12(
6655
+ () => resolveUrlAgainstFile(src, previewUrl),
6656
+ [previewUrl, src]
6657
+ );
6658
+ const texture = useEquirectTexture(
6659
+ src,
6660
+ anisotropy,
6661
+ true,
6662
+ onLoad,
6663
+ onError
6664
+ );
6665
+ const previewTexture = useEquirectTexture(
6666
+ texture ? null : resolvedPreviewUrl,
6667
+ anisotropy,
6668
+ false,
6669
+ void 0,
6670
+ onPreviewError
6671
+ );
6672
+ const displayTexture = texture ?? previewTexture;
6673
+ const readyRef = useRef19(false);
6674
+ useEffect19(() => {
6675
+ readyRef.current = false;
6676
+ }, [resolvedPreviewUrl, src]);
6677
+ useEffect19(() => {
6678
+ if (!displayTexture || readyRef.current) {
6679
+ return;
6680
+ }
6681
+ readyRef.current = true;
6682
+ onReady?.();
6683
+ }, [displayTexture, onReady]);
6684
+ if (!displayTexture) {
6653
6685
  return null;
6654
6686
  }
6655
6687
  return /* @__PURE__ */ jsxs9(
@@ -6662,7 +6694,7 @@ function Sphere({
6662
6694
  /* @__PURE__ */ jsx15(
6663
6695
  PanoBasicMaterial,
6664
6696
  {
6665
- map: texture,
6697
+ map: displayTexture,
6666
6698
  side: BackSide2,
6667
6699
  toneMapped: false
6668
6700
  }
@@ -6679,7 +6711,7 @@ import {
6679
6711
  useContext as useContext19,
6680
6712
  useEffect as useEffect20,
6681
6713
  useLayoutEffect as useLayoutEffect3,
6682
- useMemo as useMemo12,
6714
+ useMemo as useMemo13,
6683
6715
  useRef as useRef20,
6684
6716
  useState as useState9
6685
6717
  } from "react";
@@ -6878,13 +6910,13 @@ function PanoVideo({
6878
6910
  const gl = useThree14((state) => state.gl);
6879
6911
  const host = useContext19(PanoVideoHostContext);
6880
6912
  const overlay = useContext19(PanoChromeOverlayContext);
6881
- const variants = useMemo12(
6913
+ const variants = useMemo13(
6882
6914
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
6883
6915
  [poster, src, variantsProp]
6884
6916
  );
6885
6917
  const tracks = tracksProp ?? EMPTY_TRACKS;
6886
6918
  const captionsEnabled = captions !== false;
6887
- const captionAppearance = useMemo12(
6919
+ const captionAppearance = useMemo13(
6888
6920
  () => resolvePanoVideoCaptionAppearance(
6889
6921
  typeof captions === "object" ? captions : void 0
6890
6922
  ),
@@ -6949,7 +6981,7 @@ function PanoVideo({
6949
6981
  onEndedRef.current = onEnded;
6950
6982
  onVariantChangeRef.current = onVariantChange;
6951
6983
  onTrackChangeRef.current = onTrackChange;
6952
- const resource = useMemo12(() => {
6984
+ const resource = useMemo13(() => {
6953
6985
  if (typeof document === "undefined") {
6954
6986
  return null;
6955
6987
  }
@@ -7062,7 +7094,7 @@ function PanoVideo({
7062
7094
  captionAppearance
7063
7095
  ]
7064
7096
  );
7065
- const controller = useMemo12(
7097
+ const controller = useMemo13(
7066
7098
  () => ({
7067
7099
  subscribe: (onStoreChange) => {
7068
7100
  const store = storeRef.current;
@@ -7429,7 +7461,7 @@ import {
7429
7461
  useCallback as useCallback10,
7430
7462
  useContext as useContext21,
7431
7463
  useEffect as useEffect25,
7432
- useMemo as useMemo17,
7464
+ useMemo as useMemo18,
7433
7465
  useRef as useRef24,
7434
7466
  useState as useState11
7435
7467
  } from "react";
@@ -7487,7 +7519,7 @@ function isSnapshotTransition(preset) {
7487
7519
 
7488
7520
  // src/scene-transitions/particle-overlay.tsx
7489
7521
  import { useFrame as useFrame9, useThree as useThree15 } from "@react-three/fiber";
7490
- import { useEffect as useEffect21, useMemo as useMemo13, useRef as useRef21 } from "react";
7522
+ import { useEffect as useEffect21, useMemo as useMemo14, useRef as useRef21 } from "react";
7491
7523
  import {
7492
7524
  DoubleSide as DoubleSide2,
7493
7525
  InstancedBufferAttribute,
@@ -7631,8 +7663,8 @@ function ParticleOverlay({
7631
7663
  const startRef = useRef21(null);
7632
7664
  const finishedRef = useRef21(false);
7633
7665
  const directionRef = useRef21(new Vector38());
7634
- const geometry = useMemo13(() => createParticleGeometry(), []);
7635
- const material = useMemo13(
7666
+ const geometry = useMemo14(() => createParticleGeometry(), []);
7667
+ const material = useMemo14(
7636
7668
  () => new ShaderMaterial2({
7637
7669
  transparent: true,
7638
7670
  depthTest: false,
@@ -7686,7 +7718,7 @@ function ParticleOverlay({
7686
7718
 
7687
7719
  // src/scene-transitions/snapshot-overlay.tsx
7688
7720
  import { useFrame as useFrame10, useThree as useThree16 } from "@react-three/fiber";
7689
- import { useEffect as useEffect22, useMemo as useMemo14, useRef as useRef22 } from "react";
7721
+ import { useEffect as useEffect22, useMemo as useMemo15, useRef as useRef22 } from "react";
7690
7722
  import { Vector3 as Vector39 } from "three";
7691
7723
 
7692
7724
  // src/scene-transitions/snapshot-shaders.ts
@@ -7979,7 +8011,7 @@ function SnapshotOverlay({
7979
8011
  const startRef = useRef22(null);
7980
8012
  const finishedRef = useRef22(false);
7981
8013
  const directionRef = useRef22(new Vector39());
7982
- const material = useMemo14(() => {
8014
+ const material = useMemo15(() => {
7983
8015
  if (!isSnapshotTransition(transition.preset)) {
7984
8016
  return null;
7985
8017
  }
@@ -8072,7 +8104,7 @@ import {
8072
8104
  createContext as createContext11,
8073
8105
  useContext as useContext20,
8074
8106
  useEffect as useEffect23,
8075
- useMemo as useMemo15
8107
+ useMemo as useMemo16
8076
8108
  } from "react";
8077
8109
 
8078
8110
  // src/tile/texture-manager.ts
@@ -8313,7 +8345,7 @@ function TileTextureManagerProvider({
8313
8345
  }) {
8314
8346
  const gl = useThree17((state) => state.gl);
8315
8347
  const anisotropy = gl.capabilities.getMaxAnisotropy();
8316
- const manager = useMemo15(
8348
+ const manager = useMemo16(
8317
8349
  () => new TileTextureManager({
8318
8350
  anisotropy,
8319
8351
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -8337,7 +8369,7 @@ import { useFrame as useFrame11, useThree as useThree18 } from "@react-three/fib
8337
8369
  import {
8338
8370
  memo,
8339
8371
  useEffect as useEffect24,
8340
- useMemo as useMemo16,
8372
+ useMemo as useMemo17,
8341
8373
  useRef as useRef23,
8342
8374
  useState as useState10
8343
8375
  } from "react";
@@ -8520,13 +8552,12 @@ function parseMultires(multires) {
8520
8552
  );
8521
8553
  return { tileSize, levels };
8522
8554
  }
8555
+ var DEFAULT_TILE_URL_TEMPLATE = "tiles/%s/l%l/%v/l%l_%s_%h_%v.webp";
8523
8556
  function buildDefaultTileUrlTemplate() {
8524
- return "tiles/%s/l%l/%v/l%l_%s_%h_%v.webp";
8557
+ return DEFAULT_TILE_URL_TEMPLATE;
8525
8558
  }
8526
8559
  function resolveRelativeTileUrl(baseUrl, relativePath) {
8527
- const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
8528
- const normalizedPath = relativePath.replace(/^\/+/, "");
8529
- return normalizedBaseUrl ? `${normalizedBaseUrl}/${normalizedPath}` : normalizedPath;
8560
+ return resolveRelativeAssetUrl(baseUrl, relativePath);
8530
8561
  }
8531
8562
  function resolveTemplateUrl(template, address) {
8532
8563
  return template.replace(/%s/g, address.face).replace(/%l/g, String(address.level)).replace(/%(0*)([hxucvywr])/g, (_match, zeroes, placeholder) => {
@@ -8598,7 +8629,7 @@ function PreviewFace({
8598
8629
  texture,
8599
8630
  faceOrder
8600
8631
  }) {
8601
- const geometry = useMemo16(() => {
8632
+ const geometry = useMemo17(() => {
8602
8633
  const nextGeometry = new PlaneGeometry4(
8603
8634
  PREVIEW_RADIUS * 2,
8604
8635
  PREVIEW_RADIUS * 2
@@ -8884,15 +8915,15 @@ function Tile({
8884
8915
  onLevelChange
8885
8916
  }) {
8886
8917
  const { camera, gl, size } = useThree18();
8887
- const multiresConfig = useMemo16(() => parseMultires(multires), [multires]);
8918
+ const multiresConfig = useMemo17(() => parseMultires(multires), [multires]);
8888
8919
  const { levels, tileSize } = multiresConfig;
8889
8920
  const multiresToken = `${tileSize}:${levels.join(",")}`;
8890
- const candidatesByLevel = useMemo16(
8921
+ const candidatesByLevel = useMemo17(
8891
8922
  () => buildTileCandidates(levels, tileSize),
8892
8923
  [levels, tileSize]
8893
8924
  );
8894
8925
  const anisotropy = gl.capabilities.getMaxAnisotropy();
8895
- const effectivePreviewUrl = previewUrl === void 0 ? `${baseUrl.replace(/\/$/, "")}/previews/cube-vertical.webp` : previewUrl;
8926
+ const effectivePreviewUrl = resolveAssetUrl(baseUrl, previewUrl);
8896
8927
  const previewTexture = usePreviewTexture(
8897
8928
  effectivePreviewUrl,
8898
8929
  anisotropy,
@@ -8905,7 +8936,7 @@ function Tile({
8905
8936
  progressRef.current = onLoadProgress;
8906
8937
  errorRef.current = onTileError;
8907
8938
  levelChangeRef.current = onLevelChange;
8908
- const ownedManager = useMemo16(
8939
+ const ownedManager = useMemo17(
8909
8940
  () => new TileTextureManager({
8910
8941
  anisotropy,
8911
8942
  maxBytes: Math.max(1, maxTextureMemoryMb) * 1024 * 1024,
@@ -8948,7 +8979,7 @@ function Tile({
8948
8979
  onReady?.();
8949
8980
  }, [onReady, previewTexture]);
8950
8981
  const template = urlTemplate ?? buildDefaultTileUrlTemplate();
8951
- const resolveUrl = useMemo16(
8982
+ const resolveUrl = useMemo17(
8952
8983
  () => (address) => {
8953
8984
  const relativePath = resolveTileUrl ? resolveTileUrl(address) : resolveTemplateUrl(template, address);
8954
8985
  return resolveRelativeTileUrl(baseUrl, relativePath);
@@ -9090,9 +9121,10 @@ function SceneSource({
9090
9121
  Sphere,
9091
9122
  {
9092
9123
  src: scene.src,
9124
+ previewUrl: scene.previewUrl,
9093
9125
  yawOffset: scene.yawOffset,
9094
9126
  visible,
9095
- onLoad: onReady,
9127
+ onReady,
9096
9128
  onError
9097
9129
  }
9098
9130
  );
@@ -9181,11 +9213,11 @@ function ScenesController({
9181
9213
  }) {
9182
9214
  const controlsRef = useContext21(PanoramaViewContext);
9183
9215
  const manager = useSharedTileTextureManager();
9184
- const transition = useMemo17(
9216
+ const transition = useMemo18(
9185
9217
  () => resolveTransition(transitionInput),
9186
9218
  [transitionInput]
9187
9219
  );
9188
- const sceneMap = useMemo17(
9220
+ const sceneMap = useMemo18(
9189
9221
  () => new Map(scenes.map((scene) => [scene.id, scene])),
9190
9222
  [scenes]
9191
9223
  );
@@ -9435,7 +9467,7 @@ import {
9435
9467
  useContext as useContext23,
9436
9468
  useEffect as useEffect27,
9437
9469
  useLayoutEffect as useLayoutEffect4,
9438
- useMemo as useMemo19,
9470
+ useMemo as useMemo20,
9439
9471
  useRef as useRef25,
9440
9472
  useState as useState13
9441
9473
  } from "react";
@@ -9453,7 +9485,7 @@ import {
9453
9485
  } from "three";
9454
9486
 
9455
9487
  // src/hotspot/hotspot-tooltip.tsx
9456
- import { useEffect as useEffect26, useMemo as useMemo18, useState as useState12 } from "react";
9488
+ import { useEffect as useEffect26, useMemo as useMemo19, useState as useState12 } from "react";
9457
9489
 
9458
9490
  // src/webvr/stereo-html.tsx
9459
9491
  import { Html } from "@react-three/drei";
@@ -9581,11 +9613,11 @@ function HotspotTooltip({
9581
9613
  setImageFailed(false);
9582
9614
  }, [content.image]);
9583
9615
  const showImage = Boolean(content.image) && !imageFailed;
9584
- const resolvedAppearance = useMemo18(
9616
+ const resolvedAppearance = useMemo19(
9585
9617
  () => resolveHotspotTooltipAppearance(appearance),
9586
9618
  [appearance]
9587
9619
  );
9588
- const bubbleStyle = useMemo18(
9620
+ const bubbleStyle = useMemo19(
9589
9621
  () => ({
9590
9622
  background: resolvedAppearance.background,
9591
9623
  border: resolvedAppearance.border,
@@ -9607,7 +9639,7 @@ function HotspotTooltip({
9607
9639
  return /* @__PURE__ */ jsx24(PanoHtml, { pointerEvents: "none", style: WRAPPER_STYLE, zIndexRange: [20, 10], children: /* @__PURE__ */ jsxs15(
9608
9640
  "div",
9609
9641
  {
9610
- className: "pointer-events-none flex gap-2 whitespace-nowrap",
9642
+ className: "pano-hotspot-tooltip",
9611
9643
  role: "tooltip",
9612
9644
  style: bubbleStyle,
9613
9645
  children: [
@@ -9615,12 +9647,12 @@ function HotspotTooltip({
9615
9647
  "img",
9616
9648
  {
9617
9649
  alt: content.imageAlt ?? content.text ?? "",
9618
- className: "block h-auto max-h-[120px] max-w-[220px] rounded object-contain",
9650
+ className: "pano-hotspot-tooltip-image",
9619
9651
  onError: () => setImageFailed(true),
9620
9652
  src: content.image
9621
9653
  }
9622
9654
  ) : null,
9623
- content.text ? /* @__PURE__ */ jsx24("p", { className: "m-0 overflow-visible whitespace-nowrap leading-[1.4]", children: content.text }) : null
9655
+ content.text ? /* @__PURE__ */ jsx24("p", { className: "pano-hotspot-tooltip-text", children: content.text }) : null
9624
9656
  ]
9625
9657
  }
9626
9658
  ) });
@@ -9975,7 +10007,7 @@ function HotspotAnchor({
9975
10007
  const [hovered, setHovered] = useState13(false);
9976
10008
  const [dragging, setDragging] = useState13(false);
9977
10009
  const [pinned, setPinned] = useState13(false);
9978
- const tooltipContent = useMemo19(
10010
+ const tooltipContent = useMemo20(
9979
10011
  () => resolveHotspotTooltipContent(tooltip),
9980
10012
  [tooltip]
9981
10013
  );
@@ -9991,17 +10023,17 @@ function HotspotAnchor({
9991
10023
  scaledWidth,
9992
10024
  scaledHeight
9993
10025
  );
9994
- const worldPosition = useMemo19(
10026
+ const worldPosition = useMemo20(
9995
10027
  () => panoPositionToVector3(normalizedPosition, resolvedDistance),
9996
10028
  [normalizedPosition.pitch, normalizedPosition.yaw, resolvedDistance]
9997
10029
  );
9998
- const surfaceQuaternion3 = useMemo19(
10030
+ const surfaceQuaternion3 = useMemo20(
9999
10031
  () => createSurfaceQuaternion(normalizedPosition),
10000
10032
  [normalizedPosition.pitch, normalizedPosition.yaw]
10001
10033
  );
10002
10034
  const worldWidth = angularSizeToWorldSize(scaledWidth, resolvedDistance);
10003
10035
  const worldHeight = angularSizeToWorldSize(scaledHeight, resolvedDistance);
10004
- const focusGeometry = useMemo19(() => {
10036
+ const focusGeometry = useMemo20(() => {
10005
10037
  const planeGeometry = new PlaneGeometry5(1, 1);
10006
10038
  const geometry = new EdgesGeometry(planeGeometry);
10007
10039
  planeGeometry.dispose();
@@ -10487,7 +10519,7 @@ function ImageHotspot({
10487
10519
  }
10488
10520
 
10489
10521
  // src/hotspot/polygon-hotspot.tsx
10490
- import { useEffect as useEffect29, useMemo as useMemo20, useRef as useRef28 } from "react";
10522
+ import { useEffect as useEffect29, useMemo as useMemo21, useRef as useRef28 } from "react";
10491
10523
  import {
10492
10524
  BufferGeometry,
10493
10525
  DoubleSide as DoubleSide5,
@@ -10832,14 +10864,14 @@ function PolygonHotspot({
10832
10864
  onInvalid,
10833
10865
  ...anchorProps
10834
10866
  }) {
10835
- const issues = useMemo20(() => validatePolygonVertices(vertices), [vertices]);
10867
+ const issues = useMemo21(() => validatePolygonVertices(vertices), [vertices]);
10836
10868
  const valid = issues.length === 0;
10837
- const center = useMemo20(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
10838
- const fillGeometry = useMemo20(
10869
+ const center = useMemo21(() => valid ? polygonCenter(vertices) : null, [valid, vertices]);
10870
+ const fillGeometry = useMemo21(
10839
10871
  () => valid && center ? makeFillGeometry(vertices, center) : null,
10840
10872
  [center, valid, vertices]
10841
10873
  );
10842
- const strokePoints = useMemo20(
10874
+ const strokePoints = useMemo21(
10843
10875
  () => valid && center ? makeStrokePositions(vertices, center) : null,
10844
10876
  [center, valid, vertices]
10845
10877
  );
@@ -10929,7 +10961,7 @@ function PolygonHotspot({
10929
10961
  }
10930
10962
 
10931
10963
  // src/hotspot/polyline-hotspot.tsx
10932
- import { useEffect as useEffect30, useMemo as useMemo21, useRef as useRef29 } from "react";
10964
+ import { useEffect as useEffect30, useMemo as useMemo22, useRef as useRef29 } from "react";
10933
10965
  import {
10934
10966
  MathUtils as MathUtils8,
10935
10967
  Matrix4 as Matrix44,
@@ -11053,10 +11085,10 @@ function PolylineHotspot({
11053
11085
  onInvalid,
11054
11086
  ...anchorProps
11055
11087
  }) {
11056
- const issues = useMemo21(() => validatePolylineVertices(vertices), [vertices]);
11088
+ const issues = useMemo22(() => validatePolylineVertices(vertices), [vertices]);
11057
11089
  const valid = issues.length === 0;
11058
- const center = useMemo21(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
11059
- const linePoints = useMemo21(
11090
+ const center = useMemo22(() => valid ? polylineCenter(vertices) : null, [valid, vertices]);
11091
+ const linePoints = useMemo22(
11060
11092
  () => valid && center ? makeLinePositions(vertices, center) : null,
11061
11093
  [center, valid, vertices]
11062
11094
  );
@@ -12213,7 +12245,7 @@ function IframeHotspot({
12213
12245
  "iframe",
12214
12246
  {
12215
12247
  allow,
12216
- className: "block h-full w-full border-0",
12248
+ className: "pano-iframe-hotspot",
12217
12249
  loading,
12218
12250
  onError: (event) => onError?.(event.nativeEvent),
12219
12251
  onLoad,