@ericchen1990/pano-view 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +293 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +272 -117
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +4 -3
- package/docs/dialogs.md +4 -0
- package/docs/dialogs.zh-CN.md +4 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2409,6 +2409,19 @@ function createSpatialHost() {
|
|
|
2409
2409
|
}
|
|
2410
2410
|
var SpatialHostContext = createContext(null);
|
|
2411
2411
|
|
|
2412
|
+
// src/spatial/model-controls.ts
|
|
2413
|
+
function resolveSurfaceModelAdapter(surface, controls, hovered, previous) {
|
|
2414
|
+
let first = null;
|
|
2415
|
+
let retained = null;
|
|
2416
|
+
for (const [node, adapter] of controls) {
|
|
2417
|
+
if (!surface.contains(node)) continue;
|
|
2418
|
+
first ?? (first = adapter);
|
|
2419
|
+
if (adapter === hovered) return adapter;
|
|
2420
|
+
if (adapter === previous) retained = adapter;
|
|
2421
|
+
}
|
|
2422
|
+
return retained ?? first;
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2412
2425
|
// src/controls/actions.ts
|
|
2413
2426
|
var CONTROL_PATHS = {
|
|
2414
2427
|
play: "M8 5 L19 12 L8 19 Z",
|
|
@@ -2432,6 +2445,7 @@ var CONTROL_PATHS = {
|
|
|
2432
2445
|
function mediaActions(controller, seekSpeed = 10, volumeSpeed = 0.25) {
|
|
2433
2446
|
if (!controller) return [];
|
|
2434
2447
|
const snapshot = () => controller.getSnapshot();
|
|
2448
|
+
const safeVolumeSpeed = Number.isFinite(volumeSpeed) && volumeSpeed >= 0 ? volumeSpeed : 0.25;
|
|
2435
2449
|
return [
|
|
2436
2450
|
{
|
|
2437
2451
|
id: snapshot().playing ? "pause" : "play",
|
|
@@ -2456,22 +2470,25 @@ function mediaActions(controller, seekSpeed = 10, volumeSpeed = 0.25) {
|
|
|
2456
2470
|
)
|
|
2457
2471
|
)
|
|
2458
2472
|
})),
|
|
2459
|
-
...(snapshot().volumeAdjustable ? [-1, 1] : []).map((direction) => ({
|
|
2473
|
+
...(snapshot().hasAudio !== false && snapshot().volumeAdjustable ? [-1, 1] : []).map((direction) => ({
|
|
2460
2474
|
id: direction < 0 ? "vol-" : "vol+",
|
|
2461
2475
|
label: direction < 0 ? "Volume down" : "Volume up",
|
|
2462
|
-
enabled: () => snapshot().volumeAdjustable && (direction < 0 ? snapshot().volume > 0 : snapshot().volume < 1),
|
|
2463
|
-
update: (dt) =>
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
Math.min(1, snapshot().volume + direction * dt *
|
|
2467
|
-
)
|
|
2468
|
-
|
|
2476
|
+
enabled: () => snapshot().hasAudio !== false && snapshot().volumeAdjustable && (direction < 0 ? snapshot().volume > 0 : snapshot().volume < 1),
|
|
2477
|
+
update: (dt) => {
|
|
2478
|
+
if (!Number.isFinite(dt) || dt <= 0 || snapshot().hasAudio === false || !snapshot().volumeAdjustable) return;
|
|
2479
|
+
controller.setVolume(
|
|
2480
|
+
Math.max(0, Math.min(1, snapshot().volume + direction * dt * safeVolumeSpeed))
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2469
2483
|
})),
|
|
2470
|
-
{
|
|
2484
|
+
...snapshot().hasAudio === false ? [] : [{
|
|
2471
2485
|
id: snapshot().muted ? "unmute" : "mute",
|
|
2472
2486
|
label: snapshot().muted ? "Unmute" : "Mute",
|
|
2473
|
-
|
|
2474
|
-
|
|
2487
|
+
enabled: () => snapshot().hasAudio !== false,
|
|
2488
|
+
activate: () => {
|
|
2489
|
+
if (snapshot().hasAudio !== false) controller.setMuted(!snapshot().muted);
|
|
2490
|
+
}
|
|
2491
|
+
}]
|
|
2475
2492
|
];
|
|
2476
2493
|
}
|
|
2477
2494
|
function modelActions(adapter, rotateSpeed = 30, zoomSpeed = 1.5) {
|
|
@@ -2610,6 +2627,35 @@ function scrollViewportBounds(root, target, maxHeight) {
|
|
|
2610
2627
|
};
|
|
2611
2628
|
}
|
|
2612
2629
|
|
|
2630
|
+
// src/controls/media-audio.ts
|
|
2631
|
+
function probeMediaHasAudio(media) {
|
|
2632
|
+
if (media.readyState < 1) return void 0;
|
|
2633
|
+
const metadata = media;
|
|
2634
|
+
if ((metadata.audioTracks?.length ?? 0) > 0 || metadata.mozHasAudio === true || (metadata.webkitAudioDecodedByteCount ?? 0) > 0) return true;
|
|
2635
|
+
if (metadata.audioTracks?.length === 0 || metadata.mozHasAudio === false) return false;
|
|
2636
|
+
return void 0;
|
|
2637
|
+
}
|
|
2638
|
+
function subscribeMediaAudio(media, notify2) {
|
|
2639
|
+
let tracks;
|
|
2640
|
+
const trackEvents = ["addtrack", "removetrack", "change"];
|
|
2641
|
+
const sync = () => {
|
|
2642
|
+
const next = media.audioTracks;
|
|
2643
|
+
if (next !== tracks) {
|
|
2644
|
+
trackEvents.forEach((event) => tracks?.removeEventListener(event, notify2));
|
|
2645
|
+
tracks = next;
|
|
2646
|
+
trackEvents.forEach((event) => tracks?.addEventListener(event, notify2));
|
|
2647
|
+
}
|
|
2648
|
+
notify2();
|
|
2649
|
+
};
|
|
2650
|
+
const events = ["loadstart", "emptied", "loadedmetadata", "loadeddata", "playing", "timeupdate"];
|
|
2651
|
+
events.forEach((event) => media.addEventListener(event, sync));
|
|
2652
|
+
sync();
|
|
2653
|
+
return () => {
|
|
2654
|
+
events.forEach((event) => media.removeEventListener(event, sync));
|
|
2655
|
+
trackEvents.forEach((event) => tracks?.removeEventListener(event, notify2));
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2613
2659
|
// src/video/format.ts
|
|
2614
2660
|
var DEFAULT_PANO_VIDEO_PLAYBACK_RATES = [0.5, 1, 1.25, 1.5, 2];
|
|
2615
2661
|
var MEDIA_PROBE_EPSILON = 1e-3;
|
|
@@ -2739,6 +2785,29 @@ function cueText(cue) {
|
|
|
2739
2785
|
return cue.text.replace(/<[^>]+>/g, "").replace(/\r\n|\r/g, "\n");
|
|
2740
2786
|
}
|
|
2741
2787
|
|
|
2788
|
+
// src/controls/layout.ts
|
|
2789
|
+
var CONTROL_GROUP_DEFAULTS = { size: 48, gap: 8, padding: 8 };
|
|
2790
|
+
function controlGroupLayout(count, width, size = CONTROL_GROUP_DEFAULTS.size, gap = CONTROL_GROUP_DEFAULTS.gap, padding = CONTROL_GROUP_DEFAULTS.padding) {
|
|
2791
|
+
size = Math.max(1, size);
|
|
2792
|
+
gap = Math.max(0, gap);
|
|
2793
|
+
padding = Math.max(0, padding);
|
|
2794
|
+
const columns = Math.max(1, Math.floor((width - 2 * padding + gap) / (size + gap)));
|
|
2795
|
+
const rows = Math.ceil(count / columns);
|
|
2796
|
+
return {
|
|
2797
|
+
height: count ? rows * size + Math.max(0, rows - 1) * gap + 2 * padding : 0,
|
|
2798
|
+
buttons: Array.from({ length: count }, (_, i) => {
|
|
2799
|
+
const row = Math.floor(i / columns), items = Math.min(columns, count - row * columns);
|
|
2800
|
+
return { x: (width - items * size - (items - 1) * gap) / 2 + i % columns * (size + gap), y: padding + row * (size + gap), w: size, h: size };
|
|
2801
|
+
})
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
function allowsAutomaticMediaControls(media) {
|
|
2805
|
+
return media.getAttribute("data-pano-media-controls") !== "none";
|
|
2806
|
+
}
|
|
2807
|
+
function shouldPauseSurfaceMedia(connected, registered) {
|
|
2808
|
+
return !connected || !registered;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2742
2811
|
// src/controls/media.ts
|
|
2743
2812
|
import { useEffect, useMemo, useRef } from "react";
|
|
2744
2813
|
var mediaControllers = /* @__PURE__ */ new WeakMap();
|
|
@@ -2763,6 +2832,7 @@ function usePanoMediaController(ref) {
|
|
|
2763
2832
|
playing: media ? !media.paused && !media.ended : false,
|
|
2764
2833
|
currentTime: media?.currentTime ?? 0,
|
|
2765
2834
|
duration: media?.duration ?? 0,
|
|
2835
|
+
hasAudio: media ? probeMediaHasAudio(media) : void 0,
|
|
2766
2836
|
volume: media?.volume ?? 1,
|
|
2767
2837
|
volumeAdjustable: adjustable.current,
|
|
2768
2838
|
muted: media?.muted ?? true
|
|
@@ -2800,19 +2870,17 @@ function usePanoMediaController(ref) {
|
|
|
2800
2870
|
adjustable.current = probeVolumeAdjustable(media);
|
|
2801
2871
|
const notify2 = () => listeners.current.forEach((fn) => fn());
|
|
2802
2872
|
const events = [
|
|
2803
|
-
"loadedmetadata",
|
|
2804
2873
|
"durationchange",
|
|
2805
|
-
"timeupdate",
|
|
2806
2874
|
"play",
|
|
2807
2875
|
"pause",
|
|
2808
2876
|
"ended",
|
|
2809
2877
|
"volumechange",
|
|
2810
|
-
"error"
|
|
2811
|
-
"emptied"
|
|
2878
|
+
"error"
|
|
2812
2879
|
];
|
|
2813
2880
|
events.forEach((event) => media.addEventListener(event, notify2));
|
|
2814
|
-
notify2
|
|
2881
|
+
const unsubscribeAudio = subscribeMediaAudio(media, notify2);
|
|
2815
2882
|
return () => {
|
|
2883
|
+
unsubscribeAudio();
|
|
2816
2884
|
if (mediaControllers.get(media) === controller) mediaControllers.delete(media);
|
|
2817
2885
|
events.forEach((event) => media.removeEventListener(event, notify2));
|
|
2818
2886
|
};
|
|
@@ -3152,6 +3220,7 @@ function PanoControlGroup({
|
|
|
3152
3220
|
{
|
|
3153
3221
|
...props,
|
|
3154
3222
|
role: "group",
|
|
3223
|
+
style: { gap: `var(--pano-control-gap, ${CONTROL_GROUP_DEFAULTS.gap}px)`, padding: `var(--pano-control-padding, ${CONTROL_GROUP_DEFAULTS.padding}px)`, ...props.style },
|
|
3155
3224
|
"data-pano-control-group": "",
|
|
3156
3225
|
className: `pano-control-group ${props.className ?? ""}`,
|
|
3157
3226
|
children: children ? children(actions) : actions.map((action) => /* @__PURE__ */ jsx2(
|
|
@@ -3958,7 +4027,7 @@ function SurfaceMediaControls({ surface }) {
|
|
|
3958
4027
|
const sync = () => {
|
|
3959
4028
|
const videos = [...surface.element.querySelectorAll("video")];
|
|
3960
4029
|
const automatic = videos.filter((video) => {
|
|
3961
|
-
if (explicitTargets.has(mediaControllers.get(video) ?? video)) return false;
|
|
4030
|
+
if (!allowsAutomaticMediaControls(video) || explicitTargets.has(mediaControllers.get(video) ?? video)) return false;
|
|
3962
4031
|
return ![...host.controls].some(([node]) => node === video || node.contains(video));
|
|
3963
4032
|
});
|
|
3964
4033
|
let changed = false;
|
|
@@ -4031,7 +4100,7 @@ function makeCanvas() {
|
|
|
4031
4100
|
canvas.height = HEIGHT * RASTER_SCALE;
|
|
4032
4101
|
return canvas;
|
|
4033
4102
|
}
|
|
4034
|
-
async function capture(element, owner) {
|
|
4103
|
+
async function capture(element, owner, footer = false) {
|
|
4035
4104
|
const canvas = document.createElement("canvas");
|
|
4036
4105
|
const ctx = canvas.getContext("2d");
|
|
4037
4106
|
const native = ctx;
|
|
@@ -4065,6 +4134,10 @@ async function capture(element, owner) {
|
|
|
4065
4134
|
transform: "none",
|
|
4066
4135
|
position: "relative"
|
|
4067
4136
|
});
|
|
4137
|
+
if (footer) {
|
|
4138
|
+
clone2.style.borderBottomLeftRadius = "0";
|
|
4139
|
+
clone2.style.borderBottomRightRadius = "0";
|
|
4140
|
+
}
|
|
4068
4141
|
clone2.setAttribute("drawable", "");
|
|
4069
4142
|
canvas.append(clone2);
|
|
4070
4143
|
owner.append(canvas);
|
|
@@ -4159,6 +4232,10 @@ async function capture(element, owner) {
|
|
|
4159
4232
|
top: "0",
|
|
4160
4233
|
transform: "none"
|
|
4161
4234
|
});
|
|
4235
|
+
if (footer) {
|
|
4236
|
+
clone.style.borderBottomLeftRadius = "0";
|
|
4237
|
+
clone.style.borderBottomRightRadius = "0";
|
|
4238
|
+
}
|
|
4162
4239
|
owner.append(clone);
|
|
4163
4240
|
originals.forEach((node, i) => {
|
|
4164
4241
|
copies[i].scrollTop = node.scrollTop;
|
|
@@ -4236,6 +4313,7 @@ function SurfacePanel({ surface }) {
|
|
|
4236
4313
|
edgeButtons: [],
|
|
4237
4314
|
target: surface.element,
|
|
4238
4315
|
adapter: null,
|
|
4316
|
+
modelAdapter: null,
|
|
4239
4317
|
scrollTarget: null,
|
|
4240
4318
|
mediaTarget: null
|
|
4241
4319
|
});
|
|
@@ -4246,7 +4324,7 @@ function SurfacePanel({ surface }) {
|
|
|
4246
4324
|
controller = {
|
|
4247
4325
|
subscribe: () => () => {
|
|
4248
4326
|
},
|
|
4249
|
-
getSnapshot: () => ({ ready: media.readyState >= 1, playing: !media.paused && !media.ended, currentTime: media.currentTime, duration: media.duration, volume: media.volume, volumeAdjustable: volumeCapabilities.get(media) ?? false, muted: media.muted }),
|
|
4327
|
+
getSnapshot: () => ({ ready: media.readyState >= 1, playing: !media.paused && !media.ended, currentTime: media.currentTime, duration: media.duration, hasAudio: probeMediaHasAudio(media), volume: media.volume, volumeAdjustable: volumeCapabilities.get(media) ?? false, muted: media.muted }),
|
|
4250
4328
|
play: async () => {
|
|
4251
4329
|
try {
|
|
4252
4330
|
await media.play();
|
|
@@ -4263,7 +4341,7 @@ function SurfacePanel({ surface }) {
|
|
|
4263
4341
|
if (Number.isFinite(time) && Number.isFinite(media.duration)) media.currentTime = Math.max(0, Math.min(media.duration, time));
|
|
4264
4342
|
},
|
|
4265
4343
|
setVolume: (value) => {
|
|
4266
|
-
media.volume = value;
|
|
4344
|
+
if (Number.isFinite(value) && volumeCapabilities.get(media)) media.volume = clampVolume(value);
|
|
4267
4345
|
},
|
|
4268
4346
|
setMuted: (value) => {
|
|
4269
4347
|
media.muted = value;
|
|
@@ -4280,6 +4358,7 @@ function SurfacePanel({ surface }) {
|
|
|
4280
4358
|
s.generation++;
|
|
4281
4359
|
s.target = surface.element;
|
|
4282
4360
|
s.adapter = null;
|
|
4361
|
+
s.modelAdapter = null;
|
|
4283
4362
|
s.dirty = true;
|
|
4284
4363
|
s.image = null;
|
|
4285
4364
|
s.mediaBlocked = false;
|
|
@@ -4359,7 +4438,7 @@ function SurfacePanel({ surface }) {
|
|
|
4359
4438
|
videos.current.clear();
|
|
4360
4439
|
controllers.current.clear();
|
|
4361
4440
|
blockedMedia.current = /* @__PURE__ */ new WeakSet();
|
|
4362
|
-
surface.element.querySelectorAll("video,audio").forEach((m) => m.pause());
|
|
4441
|
+
if (shouldPauseSurfaceMedia(surface.element.isConnected, host.surfaces.has(surface))) surface.element.querySelectorAll("video,audio").forEach((m) => m.pause());
|
|
4363
4442
|
};
|
|
4364
4443
|
}, [surface, camera, gl, host, sceneHost, scratch]);
|
|
4365
4444
|
useEffect3(
|
|
@@ -4477,9 +4556,15 @@ function SurfacePanel({ surface }) {
|
|
|
4477
4556
|
s.dirty = true;
|
|
4478
4557
|
}
|
|
4479
4558
|
const ctx = canvas.getContext("2d");
|
|
4559
|
+
const appearance = getComputedStyle(surface.element);
|
|
4560
|
+
const token = (name, fallback) => {
|
|
4561
|
+
const value = parseFloat(appearance.getPropertyValue(`--pano-control-${name}`));
|
|
4562
|
+
return Number.isFinite(value) ? value : fallback;
|
|
4563
|
+
};
|
|
4564
|
+
const controlSize = token("size", CONTROL_GROUP_DEFAULTS.size), controlGap = token("gap", CONTROL_GROUP_DEFAULTS.gap), controlPadding = token("padding", CONTROL_GROUP_DEFAULTS.padding);
|
|
4480
4565
|
const nextHeight = Math.max(
|
|
4481
4566
|
surface.anchor ? 1 : 200,
|
|
4482
|
-
Math.min(BODY_HEIGHT, surface.element.offsetHeight) + (s.buttons.length ?
|
|
4567
|
+
Math.min(BODY_HEIGHT, surface.element.offsetHeight) + (s.buttons.length ? controlGroupLayout(s.buttons.length, WIDTH, controlSize, controlGap, controlPadding).height : 0)
|
|
4483
4568
|
);
|
|
4484
4569
|
if (canvas.height !== nextHeight * RASTER_SCALE) {
|
|
4485
4570
|
texture.dispose();
|
|
@@ -4516,7 +4601,7 @@ function SurfacePanel({ surface }) {
|
|
|
4516
4601
|
});
|
|
4517
4602
|
owner.setAttribute("aria-hidden", "true");
|
|
4518
4603
|
surface.element.closest("[data-pano-viewer]")?.append(owner);
|
|
4519
|
-
capture(surface.element, owner).then((image) => {
|
|
4604
|
+
capture(surface.element, owner, s.buttons.length > 0).then((image) => {
|
|
4520
4605
|
if (!s.disposed && generation === s.generation && revision === s.revision) {
|
|
4521
4606
|
s.image = image;
|
|
4522
4607
|
s.failed = false;
|
|
@@ -4532,7 +4617,6 @@ function SurfacePanel({ surface }) {
|
|
|
4532
4617
|
s.busy = false;
|
|
4533
4618
|
});
|
|
4534
4619
|
}
|
|
4535
|
-
const appearance = getComputedStyle(surface.element);
|
|
4536
4620
|
const radius = Math.max(0, parseFloat(appearance.borderTopLeftRadius) || 0);
|
|
4537
4621
|
ctx.clearRect(0, 0, WIDTH, s.height);
|
|
4538
4622
|
ctx.save();
|
|
@@ -4681,31 +4765,55 @@ function SurfacePanel({ surface }) {
|
|
|
4681
4765
|
s.edgeButtons = !host.input.hasStick && !surface.anchor && s.scrollTarget && !explicitTargets.has(s.scrollTarget) ? scrollEdges(s.scrollTarget, scrollViewportBounds(surface.element, s.scrollTarget, Math.min(BODY_HEIGHT, surface.element.offsetHeight)), () => {
|
|
4682
4766
|
s.dirty = true;
|
|
4683
4767
|
}) : [];
|
|
4684
|
-
const adapter = s.adapter;
|
|
4768
|
+
const adapter = resolveSurfaceModelAdapter(surface.element, host.controls, s.adapter, s.modelAdapter);
|
|
4769
|
+
s.modelAdapter = adapter;
|
|
4685
4770
|
if (adapter && !explicitTargets.has(adapter)) for (const action of modelActions(adapter)) {
|
|
4686
4771
|
if (host.input.canDrag && adapter.rotateBy && ["left", "right", "up", "down"].includes(action.id) || host.input.hasStick && adapter.zoomBy && ["in", "out"].includes(action.id)) continue;
|
|
4687
4772
|
add(action.id, action.label, action.update, action.activate, action.enabled);
|
|
4688
4773
|
}
|
|
4689
|
-
if (media instanceof HTMLAudioElement && !adapter && !explicitTargets.has(mediaControllers.get(media) ?? media)) {
|
|
4774
|
+
if (media instanceof HTMLAudioElement && allowsAutomaticMediaControls(media) && !adapter && !explicitTargets.has(mediaControllers.get(media) ?? media)) {
|
|
4690
4775
|
for (const action of mediaActions(controllerFor(media))) add(action.id === "pause" ? "play" : action.id === "unmute" ? "mute" : action.id, action.label, action.update, action.activate, action.enabled);
|
|
4691
4776
|
}
|
|
4692
|
-
s.buttons = surface.anchor ? [] : buttons;
|
|
4693
4777
|
if (surface.anchor) buttons.length = 0;
|
|
4778
|
+
const layout = controlGroupLayout(buttons.length, WIDTH, controlSize, controlGap, controlPadding);
|
|
4779
|
+
const bodyHeight = Math.min(BODY_HEIGHT, surface.element.offsetHeight);
|
|
4780
|
+
buttons.forEach((button, i) => Object.assign(button, layout.buttons[i], { y: bodyHeight + layout.buttons[i].y }));
|
|
4781
|
+
if (s.buttons.length !== buttons.length) s.dirty = true;
|
|
4782
|
+
s.buttons = buttons;
|
|
4783
|
+
if (buttons.length) {
|
|
4784
|
+
ctx.fillStyle = "#161b22";
|
|
4785
|
+
ctx.fillRect(0, bodyHeight, WIDTH, layout.height);
|
|
4786
|
+
}
|
|
4694
4787
|
for (const b of buttons) {
|
|
4695
|
-
ctx.
|
|
4696
|
-
ctx.
|
|
4697
|
-
ctx.fillStyle = "#
|
|
4788
|
+
ctx.save();
|
|
4789
|
+
ctx.globalAlpha = b.action.enabled?.() === false ? 0.4 : 1;
|
|
4790
|
+
ctx.fillStyle = appearance.getPropertyValue("--pano-control-background").trim() || "#39424e";
|
|
4791
|
+
ctx.beginPath();
|
|
4792
|
+
ctx.roundRect(b.x, b.y, b.w, b.h, token("radius", b.w / 2));
|
|
4793
|
+
ctx.fill();
|
|
4794
|
+
const border = /^(\d+(?:\.\d+)?)px\s+solid\s+(.+)$/.exec(appearance.getPropertyValue("--pano-control-border").trim() || "1px solid #68778a");
|
|
4795
|
+
if (border) {
|
|
4796
|
+
ctx.strokeStyle = border[2];
|
|
4797
|
+
ctx.lineWidth = Number(border[1]);
|
|
4798
|
+
ctx.stroke();
|
|
4799
|
+
}
|
|
4800
|
+
if (host.action?.id === b.action.id) {
|
|
4801
|
+
ctx.strokeStyle = appearance.getPropertyValue("--pano-control-focus").trim() || "#ffb300";
|
|
4802
|
+
ctx.lineWidth = 3;
|
|
4803
|
+
ctx.stroke();
|
|
4804
|
+
}
|
|
4698
4805
|
const id = String(b.action.id).replace("scroll-", "");
|
|
4699
4806
|
const icon = id === "play" && media && !media.paused ? "pause" : id === "mute" && media?.muted ? "unmute" : id;
|
|
4700
4807
|
ctx.save();
|
|
4701
|
-
ctx.translate(b.x + b.w / 2 -
|
|
4702
|
-
ctx.scale(
|
|
4703
|
-
ctx.strokeStyle = "#
|
|
4808
|
+
ctx.translate(b.x + b.w / 2 - 12, b.y + b.h / 2 - 12);
|
|
4809
|
+
ctx.scale(1, 1);
|
|
4810
|
+
ctx.strokeStyle = appearance.getPropertyValue("--pano-control-color").trim() || "#baf5ff";
|
|
4704
4811
|
ctx.lineWidth = 2;
|
|
4705
4812
|
ctx.lineCap = "round";
|
|
4706
4813
|
ctx.lineJoin = "round";
|
|
4707
4814
|
ctx.stroke(new Path2D(CONTROL_PATHS[icon] ?? CONTROL_PATHS.reset));
|
|
4708
4815
|
ctx.restore();
|
|
4816
|
+
ctx.restore();
|
|
4709
4817
|
}
|
|
4710
4818
|
for (const button of s.edgeButtons) {
|
|
4711
4819
|
const focused = host.action?.id === button.action.id || [...contacts.current.values()].some((contact) => contact.action?.id === button.action.id);
|
|
@@ -9683,6 +9791,7 @@ function VolumeControl({
|
|
|
9683
9791
|
const mutedOrSilent = muted || volume === 0;
|
|
9684
9792
|
useEffect15(() => {
|
|
9685
9793
|
onOpenChange(open);
|
|
9794
|
+
return () => onOpenChange(false);
|
|
9686
9795
|
}, [onOpenChange, open]);
|
|
9687
9796
|
useEffect15(() => {
|
|
9688
9797
|
if (!open || !volumeAdjustable || hasFinePointerHover()) {
|
|
@@ -10130,7 +10239,7 @@ function PanoVideoControlsHud({
|
|
|
10130
10239
|
}
|
|
10131
10240
|
),
|
|
10132
10241
|
hideTime ? null : /* @__PURE__ */ jsx12("span", { className: "pano-video-time", children: formatMediaTime(duration) }),
|
|
10133
|
-
/* @__PURE__ */ jsx12(
|
|
10242
|
+
snapshot.hasAudio === false ? null : /* @__PURE__ */ jsx12(
|
|
10134
10243
|
VolumeControl,
|
|
10135
10244
|
{
|
|
10136
10245
|
appearance: resolved,
|
|
@@ -15168,6 +15277,32 @@ function suppressFollowingClick(target) {
|
|
|
15168
15277
|
setTimeout(cleanup, CLICK_SUPPRESS_MS);
|
|
15169
15278
|
}
|
|
15170
15279
|
|
|
15280
|
+
// src/hotspot/tooltip-layout.ts
|
|
15281
|
+
function tooltipPadding(value) {
|
|
15282
|
+
const values = String(value).trim().split(/\s+/).map((value2) => Math.max(0, parseFloat(value2) || 0));
|
|
15283
|
+
const [top = 0, right2 = top, bottom = top, left = right2] = values;
|
|
15284
|
+
return [top, right2, bottom, left];
|
|
15285
|
+
}
|
|
15286
|
+
function tooltipBaseline(top, lineHeight, ascent, descent) {
|
|
15287
|
+
return top + (lineHeight - ascent - descent) / 2 + ascent;
|
|
15288
|
+
}
|
|
15289
|
+
function tooltipLines(text, maxWidth, measure) {
|
|
15290
|
+
const lines = [];
|
|
15291
|
+
if (!text) return lines;
|
|
15292
|
+
for (const paragraph of text.split("\n")) {
|
|
15293
|
+
let line = "";
|
|
15294
|
+
for (const char of paragraph) {
|
|
15295
|
+
if (line && measure(line + char) > maxWidth) {
|
|
15296
|
+
lines.push(line);
|
|
15297
|
+
line = "";
|
|
15298
|
+
}
|
|
15299
|
+
line += char;
|
|
15300
|
+
}
|
|
15301
|
+
lines.push(line);
|
|
15302
|
+
}
|
|
15303
|
+
return lines;
|
|
15304
|
+
}
|
|
15305
|
+
|
|
15171
15306
|
// src/hotspot/tooltip-transform.ts
|
|
15172
15307
|
import {
|
|
15173
15308
|
MathUtils as MathUtils8,
|
|
@@ -15273,25 +15408,17 @@ function SpatialHotspotTooltip({
|
|
|
15273
15408
|
}, [content.image]);
|
|
15274
15409
|
const raster = useMemo21(() => {
|
|
15275
15410
|
const fontSize = Math.max(1, number(appearance.fontSize, 12));
|
|
15276
|
-
const
|
|
15411
|
+
const [pt, pr, pb, pl] = tooltipPadding(appearance.padding);
|
|
15412
|
+
const padding = Math.max(pl, pr);
|
|
15413
|
+
const letterSpacing = appearance.letterSpacing === "normal" ? 0 : number(appearance.letterSpacing ?? 0, 0);
|
|
15277
15414
|
const leading = appearance.lineHeight;
|
|
15278
15415
|
const lineHeight = typeof leading === "string" && leading.endsWith("px") ? number(leading, fontSize * 1.4) : fontSize * number(leading, 1.4) / (typeof leading === "string" && leading.endsWith("%") ? 100 : 1);
|
|
15279
15416
|
const canvas = document.createElement("canvas");
|
|
15280
15417
|
const ctx = canvas.getContext("2d");
|
|
15281
15418
|
const font = `${fontSize}px ${appearance.fontFamily}`;
|
|
15282
15419
|
ctx.font = font;
|
|
15283
|
-
const
|
|
15284
|
-
|
|
15285
|
-
let line = "";
|
|
15286
|
-
for (const char of paragraph) {
|
|
15287
|
-
if (line && ctx.measureText(line + char).width > 220 - padding * 2) {
|
|
15288
|
-
lines.push(line);
|
|
15289
|
-
line = "";
|
|
15290
|
-
}
|
|
15291
|
-
line += char;
|
|
15292
|
-
}
|
|
15293
|
-
if (line || content.text) lines.push(line);
|
|
15294
|
-
}
|
|
15420
|
+
const measure = (text) => (letterSpacing ? [...text].reduce((width2, char) => width2 + ctx.measureText(char).width, 0) : ctx.measureText(text).width) + Math.max(0, [...text].length - 1) * letterSpacing;
|
|
15421
|
+
const lines = tooltipLines(content.text ?? "", 220 - pl - pr, measure);
|
|
15295
15422
|
const imageWidth = image ? Math.min(
|
|
15296
15423
|
220 - padding * 2,
|
|
15297
15424
|
120 * image.naturalWidth / image.naturalHeight
|
|
@@ -15300,12 +15427,12 @@ function SpatialHotspotTooltip({
|
|
|
15300
15427
|
const width = Math.ceil(
|
|
15301
15428
|
Math.max(
|
|
15302
15429
|
imageWidth,
|
|
15303
|
-
...lines.map(
|
|
15430
|
+
...lines.map(measure),
|
|
15304
15431
|
1
|
|
15305
|
-
) +
|
|
15432
|
+
) + pl + pr
|
|
15306
15433
|
);
|
|
15307
15434
|
const height = Math.ceil(
|
|
15308
|
-
|
|
15435
|
+
pt + pb + imageHeight + (image && lines.length ? padding : 0) + lines.length * lineHeight
|
|
15309
15436
|
);
|
|
15310
15437
|
canvas.width = width * 2;
|
|
15311
15438
|
canvas.height = height * 2;
|
|
@@ -15328,17 +15455,26 @@ function SpatialHotspotTooltip({
|
|
|
15328
15455
|
}
|
|
15329
15456
|
ctx.save();
|
|
15330
15457
|
ctx.clip();
|
|
15331
|
-
let top =
|
|
15458
|
+
let top = pt;
|
|
15332
15459
|
if (image) {
|
|
15333
|
-
ctx.drawImage(image,
|
|
15460
|
+
ctx.drawImage(image, pl, top, imageWidth, imageHeight);
|
|
15334
15461
|
top += imageHeight + padding;
|
|
15335
15462
|
}
|
|
15336
15463
|
ctx.font = font;
|
|
15337
15464
|
ctx.fillStyle = appearance.color;
|
|
15338
|
-
ctx.textBaseline = "
|
|
15339
|
-
lines.forEach(
|
|
15340
|
-
|
|
15341
|
-
|
|
15465
|
+
ctx.textBaseline = "alphabetic";
|
|
15466
|
+
lines.forEach((line, i) => {
|
|
15467
|
+
const metrics = ctx.measureText(line || "Mg");
|
|
15468
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
15469
|
+
const descent = metrics.actualBoundingBoxDescent ?? fontSize * 0.2;
|
|
15470
|
+
let x = pl;
|
|
15471
|
+
const y = tooltipBaseline(top + i * lineHeight, lineHeight, ascent, descent);
|
|
15472
|
+
if (!letterSpacing) ctx.fillText(line, x, y);
|
|
15473
|
+
else for (const char of line) {
|
|
15474
|
+
ctx.fillText(char, x, y);
|
|
15475
|
+
x += ctx.measureText(char).width + letterSpacing;
|
|
15476
|
+
}
|
|
15477
|
+
});
|
|
15342
15478
|
ctx.restore();
|
|
15343
15479
|
const texture = new CanvasTexture2(canvas);
|
|
15344
15480
|
texture.colorSpace = SRGBColorSpace4;
|
|
@@ -19450,7 +19586,7 @@ function BackgroundAudio({
|
|
|
19450
19586
|
setInternalPlaying((prev) => !prev);
|
|
19451
19587
|
},
|
|
19452
19588
|
setVolume: (vol) => {
|
|
19453
|
-
if (isControlledRef.current) {
|
|
19589
|
+
if (isControlledRef.current || !Number.isFinite(vol)) {
|
|
19454
19590
|
return;
|
|
19455
19591
|
}
|
|
19456
19592
|
const next = clampAudioVolume(vol);
|
|
@@ -20409,7 +20545,7 @@ import {
|
|
|
20409
20545
|
useContext as useContext35,
|
|
20410
20546
|
useEffect as useEffect46,
|
|
20411
20547
|
useImperativeHandle as useImperativeHandle5,
|
|
20412
|
-
useMemo as
|
|
20548
|
+
useMemo as useMemo33,
|
|
20413
20549
|
useRef as useRef47,
|
|
20414
20550
|
useState as useState28,
|
|
20415
20551
|
useSyncExternalStore as useSyncExternalStore16
|
|
@@ -20806,9 +20942,23 @@ function subscribeWebVRPresentationExit(element, onExit) {
|
|
|
20806
20942
|
});
|
|
20807
20943
|
}
|
|
20808
20944
|
|
|
20945
|
+
// src/webvr/reticle-geometry.ts
|
|
20946
|
+
import { RingGeometry } from "three";
|
|
20947
|
+
function createReticleRing(inner, outer) {
|
|
20948
|
+
const geometry = new RingGeometry(inner, outer, 96, 1, Math.PI / 2, -Math.PI * 2);
|
|
20949
|
+
const source = geometry.index;
|
|
20950
|
+
const indices = [];
|
|
20951
|
+
for (let segment = 0; segment < 96; segment++) {
|
|
20952
|
+
if (segment % 4 >= 2) continue;
|
|
20953
|
+
for (let vertex = 0; vertex < 6; vertex++) indices.push(source.getX(segment * 6 + vertex));
|
|
20954
|
+
}
|
|
20955
|
+
geometry.setIndex(indices);
|
|
20956
|
+
return geometry;
|
|
20957
|
+
}
|
|
20958
|
+
|
|
20809
20959
|
// src/webvr/reticle.tsx
|
|
20810
20960
|
import { useFrame as useFrame24, useThree as useThree29 } from "@react-three/fiber";
|
|
20811
|
-
import { useContext as useContext33, useEffect as useEffect42, useRef as useRef44 } from "react";
|
|
20961
|
+
import { useContext as useContext33, useEffect as useEffect42, useMemo as useMemo31, useRef as useRef44 } from "react";
|
|
20812
20962
|
import {
|
|
20813
20963
|
DoubleSide as DoubleSide6,
|
|
20814
20964
|
Raycaster as Raycaster5,
|
|
@@ -20822,10 +20972,9 @@ var RETICLE_SCALE = 1;
|
|
|
20822
20972
|
var MOBILE_VR_RETICLE_SCALE = 3;
|
|
20823
20973
|
var RETICLE_RENDER_ORDER = 1e5;
|
|
20824
20974
|
var CROSSHAIR_LENGTH = 0.044;
|
|
20825
|
-
var CROSSHAIR_THICKNESS =
|
|
20975
|
+
var CROSSHAIR_THICKNESS = 6e-3;
|
|
20826
20976
|
var RING_INNER_RADIUS = 0.05;
|
|
20827
20977
|
var RING_OUTER_RADIUS = 0.058;
|
|
20828
|
-
var RING_SEGMENTS = 96;
|
|
20829
20978
|
var RING_INDICES_PER_SEGMENT = 6;
|
|
20830
20979
|
var GAZE_POINTER_ID = 1000001;
|
|
20831
20980
|
var DEFAULT_WEBVR_CURSOR_DWELL_MS = 1500;
|
|
@@ -20907,6 +21056,15 @@ function WebVRReticle({
|
|
|
20907
21056
|
}, []);
|
|
20908
21057
|
const groupRef = useRef44(null);
|
|
20909
21058
|
const ringMeshRef = useRef44(null);
|
|
21059
|
+
const ringBackdropRef = useRef44(null);
|
|
21060
|
+
const rings = useMemo31(() => ({
|
|
21061
|
+
outline: createReticleRing(RING_INNER_RADIUS - 3e-3, RING_OUTER_RADIUS + 3e-3),
|
|
21062
|
+
track: createReticleRing(RING_INNER_RADIUS, RING_OUTER_RADIUS),
|
|
21063
|
+
progress: createReticleRing(RING_INNER_RADIUS, RING_OUTER_RADIUS)
|
|
21064
|
+
}), []);
|
|
21065
|
+
useEffect42(() => () => {
|
|
21066
|
+
Object.values(rings).forEach((geometry) => geometry.dispose());
|
|
21067
|
+
}, [rings]);
|
|
20910
21068
|
const directionRef = useRef44(new Vector324());
|
|
20911
21069
|
const raycasterRef = useRef44(new Raycaster5());
|
|
20912
21070
|
const hoveredRef = useRef44(null);
|
|
@@ -20933,6 +21091,7 @@ function WebVRReticle({
|
|
|
20933
21091
|
}
|
|
20934
21092
|
dwellRef.current = 0;
|
|
20935
21093
|
clickedRef.current = false;
|
|
21094
|
+
if (ringBackdropRef.current) ringBackdropRef.current.visible = false;
|
|
20936
21095
|
if (ringMesh) {
|
|
20937
21096
|
ringMesh.visible = false;
|
|
20938
21097
|
ringMesh.geometry.setDrawRange(0, 0);
|
|
@@ -20995,8 +21154,9 @@ function WebVRReticle({
|
|
|
20995
21154
|
dwellRef.current = 0;
|
|
20996
21155
|
}
|
|
20997
21156
|
const progress = spatialOpen ? spatialProgress : target && !clickedRef.current ? resolvedDwellMs > 0 ? Math.min(dwellRef.current / resolvedDwellMs, 1) : 1 : 0;
|
|
21157
|
+
if (ringBackdropRef.current) ringBackdropRef.current.visible = progress > 1e-3;
|
|
20998
21158
|
if (ringMesh) {
|
|
20999
|
-
const segments = Math.max(0, Math.ceil(progress *
|
|
21159
|
+
const segments = Math.max(0, Math.ceil(progress * rings.progress.index.count / RING_INDICES_PER_SEGMENT));
|
|
21000
21160
|
ringMesh.visible = progress > 1e-3;
|
|
21001
21161
|
ringMesh.geometry.setDrawRange(0, segments * RING_INDICES_PER_SEGMENT);
|
|
21002
21162
|
}
|
|
@@ -21012,6 +21172,14 @@ function WebVRReticle({
|
|
|
21012
21172
|
renderOrder: RETICLE_RENDER_ORDER,
|
|
21013
21173
|
visible,
|
|
21014
21174
|
children: [
|
|
21175
|
+
[false, true].map((vertical) => /* @__PURE__ */ jsxs23("mesh", { frustumCulled: false, raycast: noopRaycast, renderOrder: RETICLE_RENDER_ORDER, children: [
|
|
21176
|
+
/* @__PURE__ */ jsx44("boxGeometry", { args: vertical ? [CROSSHAIR_THICKNESS + 6e-3, CROSSHAIR_LENGTH + 6e-3, 1e-3] : [CROSSHAIR_LENGTH + 6e-3, CROSSHAIR_THICKNESS + 6e-3, 1e-3] }),
|
|
21177
|
+
/* @__PURE__ */ jsx44("meshBasicMaterial", { color: "#101419", transparent: true, depthTest: false, depthWrite: false, toneMapped: false })
|
|
21178
|
+
] }, String(vertical))),
|
|
21179
|
+
/* @__PURE__ */ jsxs23("group", { ref: ringBackdropRef, renderOrder: RETICLE_RENDER_ORDER, visible: false, children: [
|
|
21180
|
+
/* @__PURE__ */ jsx44("mesh", { geometry: rings.outline, frustumCulled: false, raycast: noopRaycast, renderOrder: RETICLE_RENDER_ORDER, children: /* @__PURE__ */ jsx44("meshBasicMaterial", { color: "#101419", transparent: true, side: DoubleSide6, depthTest: false, depthWrite: false, toneMapped: false }) }),
|
|
21181
|
+
/* @__PURE__ */ jsx44("mesh", { geometry: rings.track, frustumCulled: false, raycast: noopRaycast, renderOrder: RETICLE_RENDER_ORDER + 1, children: /* @__PURE__ */ jsx44("meshBasicMaterial", { color: "#ffffff", transparent: true, opacity: 0.45, side: DoubleSide6, depthTest: false, depthWrite: false, toneMapped: false }) })
|
|
21182
|
+
] }),
|
|
21015
21183
|
/* @__PURE__ */ jsxs23(
|
|
21016
21184
|
"mesh",
|
|
21017
21185
|
{
|
|
@@ -21054,40 +21222,26 @@ function WebVRReticle({
|
|
|
21054
21222
|
]
|
|
21055
21223
|
}
|
|
21056
21224
|
),
|
|
21057
|
-
/* @__PURE__ */
|
|
21225
|
+
/* @__PURE__ */ jsx44(
|
|
21058
21226
|
"mesh",
|
|
21059
21227
|
{
|
|
21060
21228
|
frustumCulled: false,
|
|
21061
21229
|
raycast: noopRaycast,
|
|
21062
21230
|
ref: ringMeshRef,
|
|
21231
|
+
geometry: rings.progress,
|
|
21063
21232
|
renderOrder: RETICLE_RENDER_ORDER + 2,
|
|
21064
21233
|
visible: false,
|
|
21065
|
-
children:
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
]
|
|
21077
|
-
}
|
|
21078
|
-
),
|
|
21079
|
-
/* @__PURE__ */ jsx44(
|
|
21080
|
-
"meshBasicMaterial",
|
|
21081
|
-
{
|
|
21082
|
-
color: "#ffffff",
|
|
21083
|
-
depthTest: false,
|
|
21084
|
-
depthWrite: false,
|
|
21085
|
-
side: DoubleSide6,
|
|
21086
|
-
toneMapped: false,
|
|
21087
|
-
transparent: true
|
|
21088
|
-
}
|
|
21089
|
-
)
|
|
21090
|
-
]
|
|
21234
|
+
children: /* @__PURE__ */ jsx44(
|
|
21235
|
+
"meshBasicMaterial",
|
|
21236
|
+
{
|
|
21237
|
+
color: "#ffffff",
|
|
21238
|
+
depthTest: false,
|
|
21239
|
+
depthWrite: false,
|
|
21240
|
+
side: DoubleSide6,
|
|
21241
|
+
toneMapped: false,
|
|
21242
|
+
transparent: true
|
|
21243
|
+
}
|
|
21244
|
+
)
|
|
21091
21245
|
}
|
|
21092
21246
|
)
|
|
21093
21247
|
]
|
|
@@ -21118,7 +21272,7 @@ void main() {
|
|
|
21118
21272
|
|
|
21119
21273
|
// src/webvr/stereo.tsx
|
|
21120
21274
|
import { useFrame as useFrame25, useThree as useThree30 } from "@react-three/fiber";
|
|
21121
|
-
import { useContext as useContext34, useEffect as useEffect43, useMemo as
|
|
21275
|
+
import { useContext as useContext34, useEffect as useEffect43, useMemo as useMemo32, useRef as useRef45 } from "react";
|
|
21122
21276
|
import {
|
|
21123
21277
|
Matrix4 as Matrix410,
|
|
21124
21278
|
Mesh as Mesh9,
|
|
@@ -21343,7 +21497,7 @@ function WebVRStereoRenderer({
|
|
|
21343
21497
|
const stereo = runtime?.stereoView.stereo ?? fallbackStereoRef.current;
|
|
21344
21498
|
const stereoRef = useRef45(stereo);
|
|
21345
21499
|
stereoRef.current = stereo;
|
|
21346
|
-
const resources =
|
|
21500
|
+
const resources = useMemo32(createDistortionResources, []);
|
|
21347
21501
|
const sizeRef = useRef45(new Vector28());
|
|
21348
21502
|
const viewportRef = useRef45(new Vector42());
|
|
21349
21503
|
const scissorRef = useRef45(new Vector42());
|
|
@@ -21866,7 +22020,7 @@ var WebVR = forwardRef4(function WebVR2({
|
|
|
21866
22020
|
() => host.snapshot.setupOpen,
|
|
21867
22021
|
() => false
|
|
21868
22022
|
);
|
|
21869
|
-
const resolvedProfile =
|
|
22023
|
+
const resolvedProfile = useMemo33(
|
|
21870
22024
|
() => resolveWebVRProfile(profile, settings.profileId),
|
|
21871
22025
|
[profile, settings.profileId]
|
|
21872
22026
|
);
|
|
@@ -22111,7 +22265,7 @@ var WebVR = forwardRef4(function WebVR2({
|
|
|
22111
22265
|
clearDetectedScreensize();
|
|
22112
22266
|
}
|
|
22113
22267
|
}, [clearDetectedScreensize, host, resolveAutoScreensize]);
|
|
22114
|
-
const controller =
|
|
22268
|
+
const controller = useMemo33(
|
|
22115
22269
|
() => ({
|
|
22116
22270
|
closeSetup: () => updateWebVRHost(host, { setupOpen: false }),
|
|
22117
22271
|
enterVR,
|
|
@@ -22316,7 +22470,7 @@ import {
|
|
|
22316
22470
|
useEffect as useEffect47,
|
|
22317
22471
|
useId as useId3,
|
|
22318
22472
|
useLayoutEffect as useLayoutEffect11,
|
|
22319
|
-
useMemo as
|
|
22473
|
+
useMemo as useMemo34,
|
|
22320
22474
|
useRef as useRef48,
|
|
22321
22475
|
useState as useState29
|
|
22322
22476
|
} from "react";
|
|
@@ -22595,7 +22749,7 @@ function PanoSceneMenu({
|
|
|
22595
22749
|
const animRef = useRef48(null);
|
|
22596
22750
|
const maxOffset = Math.max(0, contentSize - viewportSize);
|
|
22597
22751
|
offsetRef.current = offset;
|
|
22598
|
-
const activeIndex =
|
|
22752
|
+
const activeIndex = useMemo34(() => {
|
|
22599
22753
|
if (activeId == null) {
|
|
22600
22754
|
return -1;
|
|
22601
22755
|
}
|
|
@@ -22737,7 +22891,7 @@ function PanoSceneMenu({
|
|
|
22737
22891
|
expanded,
|
|
22738
22892
|
horizontal
|
|
22739
22893
|
]);
|
|
22740
|
-
const rootStyle =
|
|
22894
|
+
const rootStyle = useMemo34(() => {
|
|
22741
22895
|
const vars = {
|
|
22742
22896
|
"--pano-scene-menu-background": resolved.background,
|
|
22743
22897
|
"--pano-scene-menu-padding": cssLength(resolved.padding),
|
|
@@ -23124,7 +23278,7 @@ function PanoSceneMenu({
|
|
|
23124
23278
|
}
|
|
23125
23279
|
|
|
23126
23280
|
// src/use-pano-viewer.ts
|
|
23127
|
-
import { useMemo as
|
|
23281
|
+
import { useMemo as useMemo35, useRef as useRef49 } from "react";
|
|
23128
23282
|
function createPanoViewerHandle(ref) {
|
|
23129
23283
|
return {
|
|
23130
23284
|
getView: () => ref.current?.getView() ?? { yaw: 0, pitch: 0, fov: 75 },
|
|
@@ -23237,8 +23391,8 @@ function createPanoViewerHandle(ref) {
|
|
|
23237
23391
|
}
|
|
23238
23392
|
function usePanoViewer() {
|
|
23239
23393
|
const ref = useRef49(null);
|
|
23240
|
-
const handle =
|
|
23241
|
-
return
|
|
23394
|
+
const handle = useMemo35(() => createPanoViewerHandle(ref), []);
|
|
23395
|
+
return useMemo35(() => ({ ref, ...handle }), [handle]);
|
|
23242
23396
|
}
|
|
23243
23397
|
|
|
23244
23398
|
// src/video/pano-video.tsx
|
|
@@ -23248,7 +23402,7 @@ import {
|
|
|
23248
23402
|
useContext as useContext36,
|
|
23249
23403
|
useEffect as useEffect48,
|
|
23250
23404
|
useLayoutEffect as useLayoutEffect12,
|
|
23251
|
-
useMemo as
|
|
23405
|
+
useMemo as useMemo36,
|
|
23252
23406
|
useRef as useRef50,
|
|
23253
23407
|
useState as useState30
|
|
23254
23408
|
} from "react";
|
|
@@ -23447,13 +23601,13 @@ function PanoVideo({
|
|
|
23447
23601
|
usePanoMediaActivationIntent(autoPlay);
|
|
23448
23602
|
const gl = useThree32((state) => state.gl);
|
|
23449
23603
|
const host = useContext36(PanoVideoHostContext);
|
|
23450
|
-
const variants =
|
|
23604
|
+
const variants = useMemo36(
|
|
23451
23605
|
() => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
|
|
23452
23606
|
[poster, src, variantsProp]
|
|
23453
23607
|
);
|
|
23454
23608
|
const tracks = tracksProp ?? EMPTY_TRACKS;
|
|
23455
23609
|
const captionsEnabled = captions !== false;
|
|
23456
|
-
const captionAppearance =
|
|
23610
|
+
const captionAppearance = useMemo36(
|
|
23457
23611
|
() => resolvePanoVideoCaptionAppearance(
|
|
23458
23612
|
typeof captions === "object" ? captions : void 0
|
|
23459
23613
|
),
|
|
@@ -23518,7 +23672,7 @@ function PanoVideo({
|
|
|
23518
23672
|
onEndedRef.current = onEnded;
|
|
23519
23673
|
onVariantChangeRef.current = onVariantChange;
|
|
23520
23674
|
onTrackChangeRef.current = onTrackChange;
|
|
23521
|
-
const resource =
|
|
23675
|
+
const resource = useMemo36(() => {
|
|
23522
23676
|
if (typeof document === "undefined") {
|
|
23523
23677
|
return null;
|
|
23524
23678
|
}
|
|
@@ -23590,6 +23744,7 @@ function PanoVideo({
|
|
|
23590
23744
|
ended: playbackState === "ended",
|
|
23591
23745
|
currentTime: video?.currentTime ?? 0,
|
|
23592
23746
|
duration: video && Number.isFinite(video.duration) ? video.duration : 0,
|
|
23747
|
+
hasAudio: video ? probeMediaHasAudio(video) : void 0,
|
|
23593
23748
|
volume: video ? clampVolume(video.volume) : clampVolume(volume),
|
|
23594
23749
|
volumeAdjustable: mediaCapabilitiesRef.current.volumeAdjustable,
|
|
23595
23750
|
muted: video?.muted ?? muted,
|
|
@@ -23622,7 +23777,7 @@ function PanoVideo({
|
|
|
23622
23777
|
captionAppearance
|
|
23623
23778
|
]
|
|
23624
23779
|
);
|
|
23625
|
-
const controller =
|
|
23780
|
+
const controller = useMemo36(
|
|
23626
23781
|
() => ({
|
|
23627
23782
|
subscribe: (onStoreChange) => {
|
|
23628
23783
|
const store = storeRef.current;
|
|
@@ -23791,7 +23946,7 @@ function PanoVideo({
|
|
|
23791
23946
|
emitSnapshot();
|
|
23792
23947
|
},
|
|
23793
23948
|
setVolume: (nextVolume) => {
|
|
23794
|
-
if (!mediaCapabilitiesRef.current.volumeAdjustable) {
|
|
23949
|
+
if (!Number.isFinite(nextVolume) || !mediaCapabilitiesRef.current.volumeAdjustable) {
|
|
23795
23950
|
return;
|
|
23796
23951
|
}
|
|
23797
23952
|
video.volume = clampVolume(nextVolume);
|
|
@@ -23895,7 +24050,7 @@ function PanoVideo({
|
|
|
23895
24050
|
const handleVolume = () => {
|
|
23896
24051
|
emitSnapshot();
|
|
23897
24052
|
};
|
|
23898
|
-
video
|
|
24053
|
+
const unsubscribeAudio = subscribeMediaAudio(video, handleTimeUpdate);
|
|
23899
24054
|
video.addEventListener("durationchange", handleTimeUpdate);
|
|
23900
24055
|
video.addEventListener("progress", handleTimeUpdate);
|
|
23901
24056
|
video.addEventListener("play", handlePlay);
|
|
@@ -23905,7 +24060,7 @@ function PanoVideo({
|
|
|
23905
24060
|
video.addEventListener("ratechange", handleVolume);
|
|
23906
24061
|
return () => {
|
|
23907
24062
|
active = false;
|
|
23908
|
-
|
|
24063
|
+
unsubscribeAudio();
|
|
23909
24064
|
video.removeEventListener("durationchange", handleTimeUpdate);
|
|
23910
24065
|
video.removeEventListener("progress", handleTimeUpdate);
|
|
23911
24066
|
video.removeEventListener("play", handlePlay);
|
|
@@ -23990,7 +24145,7 @@ import {
|
|
|
23990
24145
|
useEffect as useEffect50,
|
|
23991
24146
|
useId as useId4,
|
|
23992
24147
|
useLayoutEffect as useLayoutEffect13,
|
|
23993
|
-
useMemo as
|
|
24148
|
+
useMemo as useMemo37,
|
|
23994
24149
|
useRef as useRef52,
|
|
23995
24150
|
useState as useState32
|
|
23996
24151
|
} from "react";
|
|
@@ -24243,11 +24398,11 @@ function PanoMap({
|
|
|
24243
24398
|
const collapseControl = showCollapseControl ?? map.controls?.collapse === true;
|
|
24244
24399
|
const collapsed = controlledCollapsed ?? uncontrolledCollapsed;
|
|
24245
24400
|
const editing = mode === "edit";
|
|
24246
|
-
const fit =
|
|
24401
|
+
const fit = useMemo37(
|
|
24247
24402
|
() => containRect(size.width, size.height, imageSize2.width, imageSize2.height),
|
|
24248
24403
|
[size, imageSize2]
|
|
24249
24404
|
);
|
|
24250
|
-
const viewport =
|
|
24405
|
+
const viewport = useMemo37(
|
|
24251
24406
|
() => ({ fit, scale: scale2, panX: pan.x, panY: pan.y }),
|
|
24252
24407
|
[fit, scale2, pan]
|
|
24253
24408
|
);
|
|
@@ -24877,7 +25032,7 @@ function PanoMap({
|
|
|
24877
25032
|
import {
|
|
24878
25033
|
useEffect as useEffect51,
|
|
24879
25034
|
useId as useId5,
|
|
24880
|
-
useMemo as
|
|
25035
|
+
useMemo as useMemo38,
|
|
24881
25036
|
useRef as useRef53,
|
|
24882
25037
|
useState as useState33
|
|
24883
25038
|
} from "react";
|
|
@@ -25044,7 +25199,7 @@ function PanoMapOverlay({
|
|
|
25044
25199
|
);
|
|
25045
25200
|
const width = widthProp ?? basePresentation.width ?? (presentationMode === "dialog" ? 48 : 220);
|
|
25046
25201
|
const height = heightProp ?? basePresentation.height ?? (presentationMode === "dialog" ? 48 : 160);
|
|
25047
|
-
const resolvedMap =
|
|
25202
|
+
const resolvedMap = useMemo38(() => {
|
|
25048
25203
|
if (interaction === void 0 && maxScale === void 0 && iconSize === void 0) {
|
|
25049
25204
|
return map;
|
|
25050
25205
|
}
|
|
@@ -25072,7 +25227,7 @@ function PanoMapOverlay({
|
|
|
25072
25227
|
if (presentationMode === "dialog") setOpen(false);
|
|
25073
25228
|
}
|
|
25074
25229
|
};
|
|
25075
|
-
const editorBox =
|
|
25230
|
+
const editorBox = useMemo38(() => {
|
|
25076
25231
|
const panelWidth = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
|
|
25077
25232
|
const panelHeight = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
|
|
25078
25233
|
return {
|
|
@@ -25198,7 +25353,7 @@ function PanoMapOverlay({
|
|
|
25198
25353
|
import {
|
|
25199
25354
|
useContext as useContext38,
|
|
25200
25355
|
useEffect as useEffect52,
|
|
25201
|
-
useMemo as
|
|
25356
|
+
useMemo as useMemo39,
|
|
25202
25357
|
useRef as useRef54,
|
|
25203
25358
|
useState as useState34,
|
|
25204
25359
|
useSyncExternalStore as useSyncExternalStore17
|
|
@@ -25426,7 +25581,7 @@ function MenuSurface({
|
|
|
25426
25581
|
timer: 0
|
|
25427
25582
|
});
|
|
25428
25583
|
const group = useRef54(null);
|
|
25429
|
-
const direction =
|
|
25584
|
+
const direction = useMemo39(() => new Vector325(), []);
|
|
25430
25585
|
const [reduced, setReduced] = useState34(false);
|
|
25431
25586
|
useEffect52(() => {
|
|
25432
25587
|
const media = matchMedia("(prefers-reduced-motion: reduce)");
|
|
@@ -25470,7 +25625,7 @@ function MenuSurface({
|
|
|
25470
25625
|
const h = controls.height;
|
|
25471
25626
|
const radius = bounded(distance, 2, 1.5, 3);
|
|
25472
25627
|
const menuScale = bounded(scale2, 1, 0.75, 1.25);
|
|
25473
|
-
const surfaces =
|
|
25628
|
+
const surfaces = useMemo39(() => {
|
|
25474
25629
|
const width = open ? W : 1024;
|
|
25475
25630
|
const result = [];
|
|
25476
25631
|
for (const { x: left, y: top, w, h: atlasHeight } of sceneMenuAtlasRects(
|
|
@@ -25589,7 +25744,7 @@ function MenuSurface({
|
|
|
25589
25744
|
},
|
|
25590
25745
|
[surfaces]
|
|
25591
25746
|
);
|
|
25592
|
-
const blocker =
|
|
25747
|
+
const blocker = useMemo39(() => {
|
|
25593
25748
|
if (!open || layout !== "arc") return null;
|
|
25594
25749
|
const geometry = new PlaneGeometry7(1, 1, Math.ceil(degrees / 3), 1);
|
|
25595
25750
|
const positions = geometry.attributes.position;
|