three-vr-player 0.5.0 → 0.7.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.
@@ -1,43 +1,56 @@
1
- import { P as Y, V as _, S as H, L as K, a as Q, b as X } from "./VideoSource-BGaVaGUn.js";
2
- import { M as rt, c as at, d as pt, i as lt } from "./VideoSource-BGaVaGUn.js";
3
- function $(l) {
4
- if (!Number.isFinite(l) || l < 0) return "0:00";
5
- const t = Math.floor(l % 60), e = Math.floor(l / 60 % 60), s = Math.floor(l / 3600), p = String(t).padStart(2, "0");
6
- return s > 0 ? `${s}:${String(e).padStart(2, "0")}:${p}` : `${e}:${p}`;
7
- }
8
- function o(l, t = {}, e = []) {
9
- const s = document.createElement(l), { class: p, ...c } = t;
1
+ import { P as X, f as J, V as Z, S as tt, L as et, a as st, b as it } from "./VideoSource-Cx7XPpvZ.js";
2
+ import { M as dt, c as ht, d as ut, i as vt } from "./VideoSource-Cx7XPpvZ.js";
3
+ function o(v, t = {}, e = []) {
4
+ const s = document.createElement(v), { class: p, ...c } = t;
10
5
  p && (s.className = p), Object.assign(s, c);
11
- for (const u of e) s.append(u);
6
+ for (const h of e) s.append(h);
12
7
  return s;
13
8
  }
14
- class Z {
9
+ class ot {
15
10
  constructor(t, e) {
16
11
  this.nodes = [], this.disposers = [];
17
- const s = e.video, p = o("button", { class: "tvp-btn tvp-play", title: "Play/Pause", textContent: "▶" }), c = o("input", { class: "tvp-seek", type: "range", min: "0", max: "1000", value: "0" }), u = o("span", { class: "tvp-time", textContent: "0:00 / 0:00" }), r = o("button", { class: "tvp-btn tvp-mute", title: "Volume", textContent: "🔊" }), v = o("input", { class: "tvp-volume", type: "range", min: "0", max: "1", step: "0.01", value: "1" }), f = o("div", { class: "tvp-volpopup", hidden: !0 }, [v]), G = o("span", { class: "tvp-volwrap" }, [r, f]), z = o("span", { class: "tvp-vrslot" }), b = o("button", { class: "tvp-btn tvp-projbtn", title: "Projection", textContent: "🌐" }), d = o("div", { class: "tvp-projmenu", hidden: !0 });
18
- for (const i of Y) {
19
- const n = o("button", { textContent: i.label });
20
- n.dataset.mode = i.value, d.append(n);
12
+ const s = e.video, p = o("button", { class: "tvp-btn tvp-play", title: "Play/Pause", textContent: "▶" }), c = o("input", { class: "tvp-seek", type: "range", min: "0", max: "1000", value: "0" }), h = o("span", { class: "tvp-time", textContent: "0:00 / 0:00" }), n = o("button", { class: "tvp-btn tvp-mute", title: "Volume", textContent: "🔊" }), m = o("input", { class: "tvp-volume", type: "range", min: "0", max: "1", step: "0.01", value: "1" }), g = o("div", { class: "tvp-volpopup", hidden: !0 }, [m]), _ = o("span", { class: "tvp-volwrap" }, [n, g]), y = o("button", { class: "tvp-btn tvp-vrbtn", title: "Enter VR", textContent: "🥽", hidden: !0 }), k = o("button", { class: "tvp-btn tvp-projbtn", title: "Projection", textContent: "🌐" }), d = o("div", { class: "tvp-projmenu", hidden: !0 });
13
+ for (const i of X) {
14
+ const r = o("button", { textContent: i.label });
15
+ r.dataset.mode = i.value, d.append(r);
21
16
  }
22
- const M = o("button", { textContent: "Off (native player)" });
23
- M.dataset.mode = "off", d.append(o("hr", { class: "tvp-sep" }), M);
24
- const J = o("span", { class: "tvp-projwrap" }, [b, d]), k = o("button", { class: "tvp-btn tvp-settingsbtn", title: "Settings", textContent: "⚙" }), O = o("button", { class: "tvp-btn tvp-fullscreen", title: "Fullscreen", textContent: "⛶" }), S = o("footer", { class: "tvp-controls" }, [p, c, u, G, z, J, k, O]), P = o("input", { type: "checkbox", checked: e.initial.swapEyes }), x = o("input", { type: "range", min: "30", max: "100", step: "1", value: String(e.initial.fov) }), j = o("span", { textContent: String(e.initial.fov) }), E = o("input", { type: "range", min: "1", max: "2", step: "0.25", value: String(e.initial.supersampling) }), A = o("span", { textContent: String(e.initial.supersampling) }), C = o("input", { type: "checkbox", checked: e.proxy.enabled }), F = o("input", { type: "text", value: e.proxy.url, placeholder: "http://localhost:8888", spellcheck: !1 }), N = o("input", { type: "password", value: e.proxy.apiPassword, placeholder: "API password" }), g = o("section", { class: "tvp-settings" }, [
25
- o("label", { class: "row" }, [P, "Swap eyes (if depth looks wrong)"]),
26
- o("label", {}, [D("Field of view (zoom): ", j, "°"), x]),
27
- o("label", {}, [D("Supersampling: ", A, "× (sharpness)"), E]),
17
+ const A = o("button", { textContent: "Off (native player)" });
18
+ A.dataset.mode = "off", d.append(o("hr", { class: "tvp-sep" }), A);
19
+ const H = o("span", { class: "tvp-projwrap" }, [k, d]), j = o("button", { class: "tvp-btn tvp-settingsbtn", title: "Settings", textContent: "⚙" }), M = o("button", { class: "tvp-btn tvp-fullscreen", title: "Fullscreen", textContent: "⛶" }), C = o("footer", { class: "tvp-controls" }, [p, c, h, _, y, H, j, M]), T = o("input", { type: "checkbox", checked: e.initial.swapEyes }), w = o("input", { type: "range", min: "30", max: "100", step: "1", value: String(e.initial.fov) }), F = o("span", { textContent: String(e.initial.fov) }), R = o("input", { type: "range", min: "1", max: "2", step: "0.25", value: String(e.initial.supersampling) }), I = o("span", { textContent: String(e.initial.supersampling) }), L = o("input", { type: "checkbox", checked: e.proxy.enabled }), V = o("input", { type: "text", value: e.proxy.url, placeholder: "http://localhost:8888", spellcheck: !1 }), N = o("input", { type: "password", value: e.proxy.apiPassword, placeholder: "API password" }), b = o("section", { class: "tvp-settings" }, [
20
+ o("label", { class: "row" }, [T, "Swap eyes (if depth looks wrong)"]),
21
+ o("label", {}, [W("Field of view (zoom): ", F, "°"), w]),
22
+ o("label", {}, [W("Supersampling: ", I, "× (sharpness)"), R]),
28
23
  o("hr", { class: "tvp-sep" }),
29
- o("label", { class: "row" }, [C, "Use CORS proxy"]),
30
- o("label", {}, ["Proxy URL", F]),
24
+ o("label", { class: "row" }, [L, "Use CORS proxy"]),
25
+ o("label", {}, ["Proxy URL", V]),
31
26
  o("label", {}, ["API password", N])
32
- ]), W = o("div", { class: "tvp-toast" });
33
- this.nodes.push(S, g, W);
27
+ ]), S = o("div", { class: "tvp-toast" });
28
+ this.nodes.push(C, b, S);
34
29
  for (const i of this.nodes) t.append(i);
35
- const a = (i, n, h, m) => {
36
- i.addEventListener(n, h, m), this.disposers.push(() => i.removeEventListener(n, h, m));
37
- }, I = (i) => c.style.setProperty("--seek", `${i}%`), y = () => {
38
- f.hidden = !0, d.hidden = !0, g.classList.remove("open");
30
+ const a = (i, r, l, u) => {
31
+ i.addEventListener(r, l, u), this.disposers.push(() => i.removeEventListener(r, l, u));
32
+ }, B = (i) => c.style.setProperty("--seek", `${i}%`), E = () => {
33
+ g.hidden = !0, d.hidden = !0, b.classList.remove("open");
34
+ };
35
+ let D = 0;
36
+ const K = (i) => {
37
+ S.textContent = i, S.classList.add("show"), clearTimeout(D), D = window.setTimeout(() => S.classList.remove("show"), 4500);
38
+ }, f = (i, r) => {
39
+ let l = !1, u = -1e9;
40
+ const G = (x) => {
41
+ u = x.timeStamp, r(x);
42
+ };
43
+ a(i, "pointerdown", () => {
44
+ l = !0;
45
+ }), a(i, "pointercancel", () => {
46
+ l = !1;
47
+ }), a(i, "pointerup", (x) => {
48
+ l && (l = !1, G(x));
49
+ }), a(i, "click", (x) => {
50
+ x.timeStamp - u > 700 && G(x);
51
+ });
39
52
  };
40
- a(p, "click", () => {
53
+ f(p, () => {
41
54
  s.paused ? s.play() : s.pause();
42
55
  }), a(s, "play", () => {
43
56
  p.textContent = "⏸";
@@ -46,66 +59,71 @@ class Z {
46
59
  }), a(s, "timeupdate", () => {
47
60
  if (s.duration) {
48
61
  const i = s.currentTime / s.duration * 100;
49
- c.value = String(i * 10), I(i), u.textContent = `${$(s.currentTime)} / ${$(s.duration)}`;
62
+ c.value = String(i * 10), B(i), h.textContent = `${J(s.currentTime)} / ${J(s.duration)}`;
50
63
  }
51
64
  }), a(c, "input", () => {
52
- I(Number(c.value) / 10), s.duration && (s.currentTime = Number(c.value) / 1e3 * s.duration);
53
- }), a(r, "click", (i) => {
54
- i.stopPropagation();
55
- const n = f.hidden;
56
- y(), f.hidden = !n;
57
- }), a(v, "input", () => {
58
- s.volume = Number(v.value), s.muted = s.volume === 0, r.textContent = s.muted ? "🔇" : "🔊";
65
+ B(Number(c.value) / 10), s.duration && (s.currentTime = Number(c.value) / 1e3 * s.duration);
66
+ }), f(n, () => {
67
+ const i = g.hidden;
68
+ E(), g.hidden = !i;
69
+ }), a(m, "input", () => {
70
+ s.volume = Number(m.value), s.muted = s.volume === 0, n.textContent = s.muted ? "🔇" : "🔊";
59
71
  });
60
- const B = () => {
72
+ const U = () => {
61
73
  const i = e.getProjection();
62
- let n = "Projection";
63
- d.querySelectorAll("button").forEach((h) => {
64
- const m = h.dataset.mode === i;
65
- h.classList.toggle("active", m), m && (n = `Projection: ${h.textContent}`);
66
- }), b.title = n;
74
+ let r = "Projection";
75
+ d.querySelectorAll("button").forEach((l) => {
76
+ const u = l.dataset.mode === i;
77
+ l.classList.toggle("active", u), u && (r = `Projection: ${l.textContent}`);
78
+ }), k.title = r;
67
79
  };
68
- a(b, "click", (i) => {
69
- i.stopPropagation();
70
- const n = d.hidden;
71
- y(), d.hidden = !n;
72
- }), a(d, "click", (i) => {
73
- const n = i.target.closest("button[data-mode]");
74
- n && (e.setProjection(n.dataset.mode), B(), d.hidden = !0);
75
- }), B(), a(k, "click", (i) => {
76
- i.stopPropagation();
77
- const n = !g.classList.contains("open");
78
- y(), g.classList.toggle("open", n);
79
- }), a(P, "change", () => e.setSwapEyes(P.checked)), a(x, "input", () => {
80
- const i = Number(x.value);
81
- j.textContent = String(i), e.setFov(i);
82
- }), a(E, "input", () => {
83
- const i = Number(E.value);
84
- A.textContent = String(i), e.setSupersampling(i);
80
+ f(k, () => {
81
+ const i = d.hidden;
82
+ E(), d.hidden = !i;
83
+ }), f(d, (i) => {
84
+ const r = i.target.closest("button[data-mode]");
85
+ r && (e.setProjection(r.dataset.mode), U(), d.hidden = !0);
86
+ }), U(), f(j, () => {
87
+ const i = !b.classList.contains("open");
88
+ E(), b.classList.toggle("open", i);
89
+ }), a(T, "change", () => e.setSwapEyes(T.checked)), a(w, "input", () => {
90
+ const i = Number(w.value);
91
+ F.textContent = String(i), e.setFov(i);
92
+ }), a(R, "input", () => {
93
+ const i = Number(R.value);
94
+ I.textContent = String(i), e.setSupersampling(i);
85
95
  });
86
- const T = () => e.setProxy({ url: F.value.trim(), apiPassword: N.value, enabled: C.checked });
87
- a(C, "change", T), a(F, "change", T), a(N, "change", T), a(e.surface, "wheel", (i) => {
96
+ const z = () => e.setProxy({ url: V.value.trim(), apiPassword: N.value, enabled: L.checked });
97
+ a(L, "change", z), a(V, "change", z), a(N, "change", z), a(e.surface, "wheel", (i) => {
88
98
  i.preventDefault();
89
- const n = Math.max(30, Math.min(100, Number(x.value) + Math.sign(i.deltaY) * 3));
90
- x.value = String(n), j.textContent = String(n), e.setFov(n);
91
- }, { passive: !1 }), a(O, "click", () => {
92
- var n, h;
99
+ const r = Math.max(30, Math.min(100, Number(w.value) + Math.sign(i.deltaY) * 3));
100
+ w.value = String(r), F.textContent = String(r), e.setFov(r);
101
+ }, { passive: !1 }), f(M, () => {
102
+ var r, l;
93
103
  const i = e.fullscreenTarget;
94
- document.fullscreenElement ? (h = document.exitFullscreen) == null || h.call(document) : (n = i.requestFullscreen) == null || n.call(i);
95
- }), a(document, "click", (i) => {
96
- const n = i.composedPath(), h = (m) => n.includes(m);
97
- [f, r, d, b, g, k].some(h) || y();
104
+ document.fullscreenElement ? (l = document.exitFullscreen) == null || l.call(document) : (r = i.requestFullscreen) == null || r.call(i);
105
+ }), a(document, "pointerdown", (i) => {
106
+ const r = i.composedPath(), l = (u) => r.includes(u);
107
+ [g, n, d, k, b, j].some(l) || E();
98
108
  });
99
- let w = 0;
100
- const q = () => !f.hidden || !d.hidden || g.classList.contains("open"), R = () => {
101
- q() || S.classList.add("tvp-hidden");
102
- }, L = () => {
103
- S.classList.remove("tvp-hidden"), clearTimeout(w), w = window.setTimeout(R, 5e3);
109
+ let P = 0;
110
+ const Q = () => !g.hidden || !d.hidden || b.classList.contains("open"), $ = () => {
111
+ Q() || C.classList.add("tvp-hidden");
112
+ }, O = () => {
113
+ C.classList.remove("tvp-hidden"), clearTimeout(P), P = window.setTimeout($, 5e3);
104
114
  };
105
- a(e.fullscreenTarget, "pointermove", L), a(e.fullscreenTarget, "pointerdown", L), a(e.fullscreenTarget, "pointerleave", () => {
106
- clearTimeout(w), R();
107
- }), this.disposers.push(() => clearTimeout(w)), L(), e.vrSupported().then((i) => {
108
- i && (Object.assign(e.vrButton.style, { position: "static", left: "auto", bottom: "auto", transform: "none", margin: "0" }), z.append(e.vrButton));
115
+ a(e.fullscreenTarget, "pointermove", O), a(e.fullscreenTarget, "pointerdown", O), a(e.fullscreenTarget, "pointerleave", () => {
116
+ clearTimeout(P), $();
117
+ }), this.disposers.push(() => clearTimeout(P)), O(), e.vrSupported().then((i) => {
118
+ y.hidden = !i;
119
+ }), f(y, () => {
120
+ if (e.isPresenting()) {
121
+ e.exitVR();
122
+ return;
123
+ }
124
+ e.enterVR().catch((i) => K(i instanceof Error ? i.message : "Could not enter VR."));
125
+ }), e.onVrChange((i) => {
126
+ y.classList.toggle("active", i), y.title = i ? "Exit VR" : "Enter VR";
109
127
  });
110
128
  }
111
129
  dispose() {
@@ -113,15 +131,15 @@ class Z {
113
131
  for (const t of this.nodes) t.remove();
114
132
  }
115
133
  }
116
- function D(l, t, e) {
134
+ function W(v, t, e) {
117
135
  const s = document.createDocumentFragment();
118
- return s.append(l, t, e), s;
136
+ return s.append(v, t, e), s;
119
137
  }
120
- const U = ":host,:host *,.tvp,.tvp *{box-sizing:border-box}.tvp-canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block;touch-action:none;cursor:grab}.tvp-canvas:active{cursor:grabbing}.tvp-video{display:none}.tvp-controls{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:12px;background:linear-gradient(transparent,#000c);z-index:5;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed;transition:opacity .3s}.tvp-controls.tvp-hidden{opacity:0;pointer-events:none}.tvp-btn{padding:4px;border:0;background:none;color:#e8eaed;cursor:pointer;font-size:22px;line-height:1}.tvp-btn:hover{opacity:.7}.tvp-seek{flex:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:14px;background:transparent;cursor:pointer;margin:0}.tvp-seek::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(to right,#4f8cff var(--seek,0%),#3a4150 var(--seek,0%))}.tvp-seek::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-seek::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-seek::-moz-range-progress{height:4px;border-radius:2px;background:#4f8cff}.tvp-seek::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-time{font-variant-numeric:tabular-nums;min-width:96px;text-align:center;color:#cfd6e2}.tvp-volwrap,.tvp-projwrap{position:relative;display:inline-flex}.tvp-volpopup{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);width:40px;height:132px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-volpopup[hidden]{display:none}.tvp-volume{position:absolute;top:50%;left:50%;width:104px;height:14px;margin:0;transform:translate(-50%,-50%) rotate(-90deg);-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;cursor:pointer}.tvp-volume::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-volume::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-projmenu{position:absolute;bottom:calc(100% + 8px);right:0;min-width:210px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;padding:6px;display:flex;flex-direction:column;gap:2px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-projmenu[hidden]{display:none}.tvp-projmenu button{text-align:left;background:transparent;border:0;color:#e8eaed;padding:8px 10px;border-radius:7px;cursor:pointer;font:inherit;white-space:nowrap}.tvp-projmenu button:hover{background:#252b39}.tvp-projmenu button.active{background:#4f8cff;color:#fff}.tvp-vrslot button{position:static!important}.tvp-settings{position:absolute;bottom:62px;right:10px;width:300px;max-width:calc(100% - 20px);max-height:70%;overflow-y:auto;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:12px;padding:14px;display:none;flex-direction:column;gap:12px;z-index:6;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed}.tvp-settings.open{display:flex}.tvp-settings label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:#aab2c0}.tvp-settings label.row{flex-direction:row;align-items:center;gap:8px;color:#e8eaed;font-size:13px}.tvp-settings input[type=range]{width:100%;accent-color:#4f8cff}.tvp-settings input[type=text],.tvp-settings input[type=password]{width:100%;padding:7px 9px;border-radius:7px;border:1px solid #333;background:#151821;color:#e8eaed;font:inherit}.tvp-sep{width:100%;border:0;border-top:1px solid #2a2f3a;margin:2px 0}.tvp-toast{position:absolute;left:50%;bottom:74px;transform:translate(-50%);max-width:80%;padding:10px 16px;border-radius:10px;background:#1b1f2aed;border:1px solid #2a2f3a;color:#e8eaed;opacity:0;pointer-events:none;transition:opacity .2s;z-index:7;font:13px system-ui}.tvp-toast.show{opacity:1}", V = "three-vr-player:settings";
121
- class tt {
138
+ const q = ":host,:host *,.tvp,.tvp *{box-sizing:border-box}.tvp-canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block;touch-action:none;cursor:grab}.tvp-canvas:active{cursor:grabbing}.tvp-video{display:none}.tvp-controls{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:12px;background:linear-gradient(transparent,#000c);z-index:5;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed;transition:opacity .3s}.tvp-controls.tvp-hidden{opacity:0;pointer-events:none}.tvp-btn{padding:4px;border:0;background:none;color:#e8eaed;cursor:pointer;font-size:22px;line-height:1}.tvp-btn:hover{opacity:.7}.tvp-seek{flex:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:14px;background:transparent;cursor:pointer;margin:0}.tvp-seek::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(to right,#4f8cff var(--seek,0%),#3a4150 var(--seek,0%))}.tvp-seek::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-seek::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-seek::-moz-range-progress{height:4px;border-radius:2px;background:#4f8cff}.tvp-seek::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-time{font-variant-numeric:tabular-nums;min-width:96px;text-align:center;color:#cfd6e2}.tvp-volwrap,.tvp-projwrap{position:relative;display:inline-flex}.tvp-volpopup{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);width:40px;height:132px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-volpopup[hidden]{display:none}.tvp-volume{position:absolute;top:50%;left:50%;width:104px;height:14px;margin:0;transform:translate(-50%,-50%) rotate(-90deg);-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;cursor:pointer}.tvp-volume::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-volume::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-projmenu{position:absolute;bottom:calc(100% + 8px);right:0;min-width:210px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;padding:6px;display:flex;flex-direction:column;gap:2px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-projmenu[hidden]{display:none}.tvp-projmenu button{text-align:left;background:transparent;border:0;color:#e8eaed;padding:8px 10px;border-radius:7px;cursor:pointer;font:inherit;white-space:nowrap}.tvp-projmenu button:hover{background:#252b39}.tvp-projmenu button.active{background:#4f8cff;color:#fff}.tvp-btn.active{color:#4f8cff}.tvp-settings{position:absolute;bottom:62px;right:10px;width:300px;max-width:calc(100% - 20px);max-height:70%;overflow-y:auto;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:12px;padding:14px;display:none;flex-direction:column;gap:12px;z-index:6;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed}.tvp-settings.open{display:flex}.tvp-settings label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:#aab2c0}.tvp-settings label.row{flex-direction:row;align-items:center;gap:8px;color:#e8eaed;font-size:13px}.tvp-settings input[type=range]{width:100%;accent-color:#4f8cff}.tvp-settings input[type=text],.tvp-settings input[type=password]{width:100%;padding:7px 9px;border-radius:7px;border:1px solid #333;background:#151821;color:#e8eaed;font:inherit}.tvp-sep{width:100%;border:0;border-top:1px solid #2a2f3a;margin:2px 0}.tvp-toast{position:absolute;left:50%;bottom:74px;transform:translate(-50%);max-width:80%;padding:10px 16px;border-radius:10px;background:#1b1f2aed;border:1px solid #2a2f3a;color:#e8eaed;opacity:0;pointer-events:none;transition:opacity .2s;z-index:7;font:13px system-ui}.tvp-toast.show{opacity:1}", Y = "three-vr-player:settings";
139
+ class nt {
122
140
  constructor(t, e = {}) {
123
- var c, u;
124
- this.source = new _(), this.listeners = /* @__PURE__ */ new Map(), this.readyEmitted = !1, this.native = !1, this.tainted = !1, this.loading = !1, this.useProxy = !1, this.opts = e, this.proxyConfig = e.proxy, this.useProxy = !!e.proxy;
141
+ var c, h;
142
+ this.source = new Z(), this.listeners = /* @__PURE__ */ new Map(), this.readyEmitted = !1, this.native = !1, this.tainted = !1, this.loading = !1, this.useProxy = !1, this.opts = e, this.proxyConfig = e.proxy, this.useProxy = !!e.proxy;
125
143
  const s = e.persistSettings ? this.loadSettings() : null;
126
144
  this.view = {
127
145
  projection: e.projection ?? (s == null ? void 0 : s.projection) ?? "180-sbs",
@@ -131,64 +149,71 @@ class tt {
131
149
  }, this.displayMode = this.view.projection, this.wrap = document.createElement("div"), this.wrap.className = "tvp", this.wrap.style.cssText = "position:relative;width:100%;height:100%;overflow:hidden;", t.appendChild(this.wrap);
132
150
  let p;
133
151
  if (e.shadowDom !== !1) {
134
- const r = this.wrap.attachShadow({ mode: "open" }), v = document.createElement("style");
135
- v.textContent = U, r.appendChild(v), p = r;
152
+ const n = this.wrap.attachShadow({ mode: "open" }), m = document.createElement("style");
153
+ m.textContent = q, n.appendChild(m), p = n;
136
154
  } else {
137
155
  if (!document.getElementById("tvp-styles")) {
138
- const r = document.createElement("style");
139
- r.id = "tvp-styles", r.textContent = U, document.head.appendChild(r);
156
+ const n = document.createElement("style");
157
+ n.id = "tvp-styles", n.textContent = q, document.head.appendChild(n);
140
158
  }
141
159
  p = this.wrap;
142
160
  }
143
- this.canvas = document.createElement("canvas"), this.canvas.className = "tvp-canvas", this.video = document.createElement("video"), this.video.className = "tvp-video", this.video.playsInline = !0, e.crossOrigin !== null && (this.video.crossOrigin = e.crossOrigin ?? "anonymous"), p.append(this.canvas, this.video), this.scene = new H({
161
+ this.canvas = document.createElement("canvas"), this.canvas.className = "tvp-canvas", this.video = document.createElement("video"), this.video.className = "tvp-video", this.video.playsInline = !0, e.crossOrigin !== null && (this.video.crossOrigin = e.crossOrigin ?? "anonymous"), p.append(this.canvas, this.video), this.scene = new tt({
144
162
  canvas: this.canvas,
145
163
  video: this.video,
146
164
  projection: this.view.projection,
147
165
  swapEyes: this.view.swapEyes,
148
166
  fov: this.view.fov,
149
167
  supersampling: this.view.supersampling
150
- }), this.look = new K(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting }), this.scene.onFrame(() => this.look.update()), this.look.setEnabled(!this.scene.isFlat()), e.controls !== !1 && (this.ui = new Z(p, {
168
+ }), e.title && this.scene.setVRTitle(e.title), this.look = new et(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting }), this.scene.onFrame(() => this.look.update()), this.look.setEnabled(!this.scene.isFlat()), e.controls !== !1 && (this.ui = new ot(p, {
151
169
  video: this.video,
152
170
  surface: this.canvas,
153
171
  fullscreenTarget: this.wrap,
154
- vrButton: this.scene.vrButton,
155
172
  vrSupported: () => this.vrSupported(),
173
+ enterVR: () => this.enterVR(),
174
+ exitVR: () => this.scene.exitVR(),
175
+ isPresenting: () => this.scene.renderer.xr.isPresenting,
176
+ onVrChange: (n) => {
177
+ this.on("enterxr", () => n(!0)), this.on("exitxr", () => n(!1));
178
+ },
156
179
  getProjection: () => this.displayMode,
157
- setProjection: (r) => this.setProjection(r),
158
- setSwapEyes: (r) => this.setSwapEyes(r),
159
- setFov: (r) => this.setFov(r),
160
- setSupersampling: (r) => this.setSupersampling(r),
180
+ setProjection: (n) => this.setProjection(n),
181
+ setSwapEyes: (n) => this.setSwapEyes(n),
182
+ setFov: (n) => this.setFov(n),
183
+ setSupersampling: (n) => this.setSupersampling(n),
161
184
  initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },
162
- proxy: { url: ((c = this.proxyConfig) == null ? void 0 : c.url) ?? "", apiPassword: ((u = this.proxyConfig) == null ? void 0 : u.apiPassword) ?? "", enabled: this.useProxy },
163
- setProxy: (r) => this.setProxy(r)
185
+ proxy: { url: ((c = this.proxyConfig) == null ? void 0 : c.url) ?? "", apiPassword: ((h = this.proxyConfig) == null ? void 0 : h.apiPassword) ?? "", enabled: this.useProxy },
186
+ setProxy: (n) => this.setProxy(n)
164
187
  })), this.video.addEventListener("play", () => this.emit("play")), this.video.addEventListener("pause", () => this.emit("pause")), this.video.addEventListener("ended", () => this.emit("ended")), this.video.addEventListener("timeupdate", () => this.emit("timeupdate", this.video.currentTime)), this.video.addEventListener("error", () => {
165
188
  this.loading || this.emit("error", this.video.error);
166
189
  }), this.video.addEventListener("loadedmetadata", () => {
167
190
  this.readyEmitted || (this.readyEmitted = !0, this.emit("ready"));
168
- }), this.scene.renderer.xr.addEventListener("sessionstart", () => this.emit("enterxr")), this.scene.renderer.xr.addEventListener("sessionend", () => this.emit("exitxr")), e.src && this.load(e.src, { projection: e.projection });
191
+ }), this.scene.renderer.xr.addEventListener("sessionstart", () => this.emit("enterxr")), this.scene.renderer.xr.addEventListener("sessionend", () => this.emit("exitxr")), this.vrSupported().then((n) => {
192
+ n && this.scene.offerVR();
193
+ }), e.src && this.load(e.src, { projection: e.projection });
169
194
  }
170
195
  vrSupported() {
171
196
  return this.opts.vrButton === !1 || !navigator.xr ? Promise.resolve(!1) : navigator.xr.isSessionSupported("immersive-vr").catch(() => !1);
172
197
  }
173
198
  async load(t, e = {}) {
174
199
  this.currentSrc = t;
175
- const s = e.projection ?? (this.opts.autoDetect !== !1 ? Q(t) : null);
200
+ const s = e.projection ?? (this.opts.autoDetect !== !1 ? st(t) : null);
176
201
  s && this.applyProjectionGeometry(s), this.displayMode !== "off" && (this.displayMode = this.view.projection);
177
- const { url: p, format: c } = X(t, this.useProxy ? this.proxyConfig : void 0), u = this.opts.crossOrigin === void 0 ? "anonymous" : this.opts.crossOrigin;
202
+ const { url: p, format: c } = it(t, this.useProxy ? this.proxyConfig : void 0), h = this.opts.crossOrigin === void 0 ? "anonymous" : this.opts.crossOrigin;
178
203
  this.loading = !0;
179
204
  try {
180
- await this.source.attach(this.video, { url: p, format: c }, { crossOrigin: u }), this.tainted = !1, this.applyDisplay(), await this.video.play().catch(() => {
205
+ await this.source.attach(this.video, { url: p, format: c }, { crossOrigin: h }), this.tainted = !1, this.applyDisplay(), await this.video.play().catch(() => {
181
206
  });
182
- } catch (r) {
183
- if (this.opts.nativeFallback !== !1 && c === "progressive" && u !== null)
207
+ } catch (n) {
208
+ if (this.opts.nativeFallback !== !1 && c === "progressive" && h !== null)
184
209
  try {
185
210
  this.tainted = !0, this.setNativeFallback(!0), await this.source.attach(this.video, { url: p, format: c }, { crossOrigin: null }), await this.video.play().catch(() => {
186
211
  }), this.emit("fallback");
187
212
  return;
188
- } catch (v) {
189
- throw this.tainted = !1, this.setNativeFallback(!1), this.emit("error", v), v;
213
+ } catch (m) {
214
+ throw this.tainted = !1, this.setNativeFallback(!1), this.emit("error", m), m;
190
215
  }
191
- throw this.setNativeFallback(!1), this.emit("error", r), r;
216
+ throw this.setNativeFallback(!1), this.emit("error", n), n;
192
217
  } finally {
193
218
  this.loading = !1;
194
219
  }
@@ -239,10 +264,13 @@ class tt {
239
264
  setProxy(t) {
240
265
  this.proxyConfig = t.url ? { url: t.url, apiPassword: t.apiPassword || void 0 } : void 0, this.useProxy = t.enabled, this.currentSrc && this.load(this.currentSrc);
241
266
  }
267
+ /** Enter immersive VR. Rejects if the WebXR session request fails (e.g. no headset,
268
+ * or a non-secure origin) — the built-in controls surface the reason as a toast. */
242
269
  async enterVR() {
243
- var e;
244
- const t = this.scene.vrButton;
245
- (e = t == null ? void 0 : t.click) == null || e.call(t);
270
+ await this.scene.enterVR();
271
+ }
272
+ exitVR() {
273
+ this.scene.exitVR();
246
274
  }
247
275
  get three() {
248
276
  return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera };
@@ -262,13 +290,13 @@ class tt {
262
290
  persist() {
263
291
  if (this.opts.persistSettings)
264
292
  try {
265
- localStorage.setItem(V, JSON.stringify(this.view));
293
+ localStorage.setItem(Y, JSON.stringify(this.view));
266
294
  } catch {
267
295
  }
268
296
  }
269
297
  loadSettings() {
270
298
  try {
271
- const t = localStorage.getItem(V);
299
+ const t = localStorage.getItem(Y);
272
300
  return t ? JSON.parse(t) : null;
273
301
  } catch {
274
302
  return null;
@@ -279,12 +307,12 @@ class tt {
279
307
  (t = this.ui) == null || t.dispose(), this.look.dispose(), this.source.dispose(), this.scene.dispose(), this.video.remove(), this.canvas.remove(), this.wrap.remove(), this.listeners.clear();
280
308
  }
281
309
  }
282
- class et extends HTMLElement {
310
+ class rt extends HTMLElement {
283
311
  static get observedAttributes() {
284
312
  return ["src", "projection"];
285
313
  }
286
314
  connectedCallback() {
287
- this.player || (this.style.display || (this.style.display = "block"), this.player = new tt(this, {
315
+ this.player || (this.style.display || (this.style.display = "block"), this.player = new nt(this, {
288
316
  src: this.getAttribute("src") ?? void 0,
289
317
  projection: this.getAttribute("projection") ?? void 0,
290
318
  controls: this.hasAttribute("controls"),
@@ -311,23 +339,23 @@ class et extends HTMLElement {
311
339
  return this.player;
312
340
  }
313
341
  }
314
- function st(l = "three-video") {
315
- typeof customElements < "u" && !customElements.get(l) && customElements.define(l, et);
342
+ function at(v = "three-video") {
343
+ typeof customElements < "u" && !customElements.get(v) && customElements.define(v, rt);
316
344
  }
317
- st();
345
+ at();
318
346
  export {
319
- K as LookControls,
320
- rt as MODES,
321
- Y as PROJECTIONS,
322
- tt as Player,
323
- H as StereoScene,
324
- et as ThreeVideoElement,
325
- _ as VideoSource,
326
- X as buildProxyUrl,
327
- at as clampAngles,
328
- pt as detectFormat,
329
- Q as detectProjection,
330
- lt as isFlatMode,
331
- st as registerWebComponent
347
+ et as LookControls,
348
+ dt as MODES,
349
+ X as PROJECTIONS,
350
+ nt as Player,
351
+ tt as StereoScene,
352
+ rt as ThreeVideoElement,
353
+ Z as VideoSource,
354
+ it as buildProxyUrl,
355
+ ht as clampAngles,
356
+ ut as detectFormat,
357
+ st as detectProjection,
358
+ vt as isFlatMode,
359
+ at as registerWebComponent
332
360
  };
333
361
  //# sourceMappingURL=three-vr-player.js.map