three-vr-player 0.6.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,29 +1,24 @@
1
- import { P as X, V as Z, S as tt, L as et, a as st, b as it } from "./VideoSource-CMM_vlRP.js";
2
- import { M as dt, c as ht, d as ut, i as vt } from "./VideoSource-CMM_vlRP.js";
3
- function J(c) {
4
- if (!Number.isFinite(c) || c < 0) return "0:00";
5
- const t = Math.floor(c % 60), e = Math.floor(c / 60 % 60), s = Math.floor(c / 3600), a = String(t).padStart(2, "0");
6
- return s > 0 ? `${s}:${String(e).padStart(2, "0")}:${a}` : `${e}:${a}`;
7
- }
8
- function o(c, t = {}, e = []) {
9
- const s = document.createElement(c), { class: a, ...d } = t;
10
- a && (s.className = a), Object.assign(s, d);
11
- for (const u of e) s.append(u);
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;
5
+ p && (s.className = p), Object.assign(s, c);
6
+ for (const h of e) s.append(h);
12
7
  return s;
13
8
  }
14
9
  class ot {
15
10
  constructor(t, e) {
16
11
  this.nodes = [], this.disposers = [];
17
- const s = e.video, a = o("button", { class: "tvp-btn tvp-play", title: "Play/Pause", textContent: "▶" }), d = o("input", { class: "tvp-seek", type: "range", min: "0", max: "1000", value: "0" }), u = 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: "🌐" }), h = o("div", { class: "tvp-projmenu", hidden: !0 });
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 });
18
13
  for (const i of X) {
19
14
  const r = o("button", { textContent: i.label });
20
- r.dataset.mode = i.value, h.append(r);
15
+ r.dataset.mode = i.value, d.append(r);
21
16
  }
22
- const O = o("button", { textContent: "Off (native player)" });
23
- O.dataset.mode = "off", h.append(o("hr", { class: "tvp-sep" }), O);
24
- const H = o("span", { class: "tvp-projwrap" }, [k, h]), j = o("button", { class: "tvp-btn tvp-settingsbtn", title: "Settings", textContent: "⚙" }), A = o("button", { class: "tvp-btn tvp-fullscreen", title: "Fullscreen", textContent: "⛶" }), C = o("footer", { class: "tvp-controls" }, [a, d, u, _, y, H, j, A]), F = o("input", { type: "checkbox", checked: e.initial.swapEyes }), w = o("input", { type: "range", min: "30", max: "100", step: "1", value: String(e.initial.fov) }), T = 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" }, [
25
- o("label", { class: "row" }, [F, "Swap eyes (if depth looks wrong)"]),
26
- o("label", {}, [W("Field of view (zoom): ", T, "°"), w]),
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]),
27
22
  o("label", {}, [W("Supersampling: ", I, "× (sharpness)"), R]),
28
23
  o("hr", { class: "tvp-sep" }),
29
24
  o("label", { class: "row" }, [L, "Use CORS proxy"]),
@@ -32,94 +27,94 @@ class ot {
32
27
  ]), S = o("div", { class: "tvp-toast" });
33
28
  this.nodes.push(C, b, S);
34
29
  for (const i of this.nodes) t.append(i);
35
- const p = (i, r, l, v) => {
36
- i.addEventListener(r, l, v), this.disposers.push(() => i.removeEventListener(r, l, v));
37
- }, $ = (i) => d.style.setProperty("--seek", `${i}%`), E = () => {
38
- g.hidden = !0, h.hidden = !0, b.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");
39
34
  };
40
- let B = 0;
35
+ let D = 0;
41
36
  const K = (i) => {
42
- S.textContent = i, S.classList.add("show"), clearTimeout(B), B = window.setTimeout(() => S.classList.remove("show"), 4500);
37
+ S.textContent = i, S.classList.add("show"), clearTimeout(D), D = window.setTimeout(() => S.classList.remove("show"), 4500);
43
38
  }, f = (i, r) => {
44
- let l = !1, v = -1e9;
39
+ let l = !1, u = -1e9;
45
40
  const G = (x) => {
46
- v = x.timeStamp, r(x);
41
+ u = x.timeStamp, r(x);
47
42
  };
48
- p(i, "pointerdown", () => {
43
+ a(i, "pointerdown", () => {
49
44
  l = !0;
50
- }), p(i, "pointercancel", () => {
45
+ }), a(i, "pointercancel", () => {
51
46
  l = !1;
52
- }), p(i, "pointerup", (x) => {
47
+ }), a(i, "pointerup", (x) => {
53
48
  l && (l = !1, G(x));
54
- }), p(i, "click", (x) => {
55
- x.timeStamp - v > 700 && G(x);
49
+ }), a(i, "click", (x) => {
50
+ x.timeStamp - u > 700 && G(x);
56
51
  });
57
52
  };
58
- f(a, () => {
53
+ f(p, () => {
59
54
  s.paused ? s.play() : s.pause();
60
- }), p(s, "play", () => {
61
- a.textContent = "⏸";
62
- }), p(s, "pause", () => {
63
- a.textContent = "▶";
64
- }), p(s, "timeupdate", () => {
55
+ }), a(s, "play", () => {
56
+ p.textContent = "⏸";
57
+ }), a(s, "pause", () => {
58
+ p.textContent = "▶";
59
+ }), a(s, "timeupdate", () => {
65
60
  if (s.duration) {
66
61
  const i = s.currentTime / s.duration * 100;
67
- d.value = String(i * 10), $(i), u.textContent = `${J(s.currentTime)} / ${J(s.duration)}`;
62
+ c.value = String(i * 10), B(i), h.textContent = `${J(s.currentTime)} / ${J(s.duration)}`;
68
63
  }
69
- }), p(d, "input", () => {
70
- $(Number(d.value) / 10), s.duration && (s.currentTime = Number(d.value) / 1e3 * s.duration);
64
+ }), a(c, "input", () => {
65
+ B(Number(c.value) / 10), s.duration && (s.currentTime = Number(c.value) / 1e3 * s.duration);
71
66
  }), f(n, () => {
72
67
  const i = g.hidden;
73
68
  E(), g.hidden = !i;
74
- }), p(m, "input", () => {
69
+ }), a(m, "input", () => {
75
70
  s.volume = Number(m.value), s.muted = s.volume === 0, n.textContent = s.muted ? "🔇" : "🔊";
76
71
  });
77
- const D = () => {
72
+ const U = () => {
78
73
  const i = e.getProjection();
79
74
  let r = "Projection";
80
- h.querySelectorAll("button").forEach((l) => {
81
- const v = l.dataset.mode === i;
82
- l.classList.toggle("active", v), v && (r = `Projection: ${l.textContent}`);
75
+ d.querySelectorAll("button").forEach((l) => {
76
+ const u = l.dataset.mode === i;
77
+ l.classList.toggle("active", u), u && (r = `Projection: ${l.textContent}`);
83
78
  }), k.title = r;
84
79
  };
85
80
  f(k, () => {
86
- const i = h.hidden;
87
- E(), h.hidden = !i;
88
- }), f(h, (i) => {
81
+ const i = d.hidden;
82
+ E(), d.hidden = !i;
83
+ }), f(d, (i) => {
89
84
  const r = i.target.closest("button[data-mode]");
90
- r && (e.setProjection(r.dataset.mode), D(), h.hidden = !0);
91
- }), D(), f(j, () => {
85
+ r && (e.setProjection(r.dataset.mode), U(), d.hidden = !0);
86
+ }), U(), f(j, () => {
92
87
  const i = !b.classList.contains("open");
93
88
  E(), b.classList.toggle("open", i);
94
- }), p(F, "change", () => e.setSwapEyes(F.checked)), p(w, "input", () => {
89
+ }), a(T, "change", () => e.setSwapEyes(T.checked)), a(w, "input", () => {
95
90
  const i = Number(w.value);
96
- T.textContent = String(i), e.setFov(i);
97
- }), p(R, "input", () => {
91
+ F.textContent = String(i), e.setFov(i);
92
+ }), a(R, "input", () => {
98
93
  const i = Number(R.value);
99
94
  I.textContent = String(i), e.setSupersampling(i);
100
95
  });
101
96
  const z = () => e.setProxy({ url: V.value.trim(), apiPassword: N.value, enabled: L.checked });
102
- p(L, "change", z), p(V, "change", z), p(N, "change", z), p(e.surface, "wheel", (i) => {
97
+ a(L, "change", z), a(V, "change", z), a(N, "change", z), a(e.surface, "wheel", (i) => {
103
98
  i.preventDefault();
104
99
  const r = Math.max(30, Math.min(100, Number(w.value) + Math.sign(i.deltaY) * 3));
105
- w.value = String(r), T.textContent = String(r), e.setFov(r);
106
- }, { passive: !1 }), f(A, () => {
100
+ w.value = String(r), F.textContent = String(r), e.setFov(r);
101
+ }, { passive: !1 }), f(M, () => {
107
102
  var r, l;
108
103
  const i = e.fullscreenTarget;
109
104
  document.fullscreenElement ? (l = document.exitFullscreen) == null || l.call(document) : (r = i.requestFullscreen) == null || r.call(i);
110
- }), p(document, "pointerdown", (i) => {
111
- const r = i.composedPath(), l = (v) => r.includes(v);
112
- [g, n, h, k, b, j].some(l) || E();
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();
113
108
  });
114
109
  let P = 0;
115
- const Q = () => !g.hidden || !h.hidden || b.classList.contains("open"), U = () => {
110
+ const Q = () => !g.hidden || !d.hidden || b.classList.contains("open"), $ = () => {
116
111
  Q() || C.classList.add("tvp-hidden");
117
- }, M = () => {
118
- C.classList.remove("tvp-hidden"), clearTimeout(P), P = window.setTimeout(U, 5e3);
112
+ }, O = () => {
113
+ C.classList.remove("tvp-hidden"), clearTimeout(P), P = window.setTimeout($, 5e3);
119
114
  };
120
- p(e.fullscreenTarget, "pointermove", M), p(e.fullscreenTarget, "pointerdown", M), p(e.fullscreenTarget, "pointerleave", () => {
121
- clearTimeout(P), U();
122
- }), this.disposers.push(() => clearTimeout(P)), M(), e.vrSupported().then((i) => {
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) => {
123
118
  y.hidden = !i;
124
119
  }), f(y, () => {
125
120
  if (e.isPresenting()) {
@@ -136,14 +131,14 @@ class ot {
136
131
  for (const t of this.nodes) t.remove();
137
132
  }
138
133
  }
139
- function W(c, t, e) {
134
+ function W(v, t, e) {
140
135
  const s = document.createDocumentFragment();
141
- return s.append(c, t, e), s;
136
+ return s.append(v, t, e), s;
142
137
  }
143
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";
144
139
  class nt {
145
140
  constructor(t, e = {}) {
146
- var d, u;
141
+ var c, h;
147
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;
148
143
  const s = e.persistSettings ? this.loadSettings() : null;
149
144
  this.view = {
@@ -152,25 +147,25 @@ class nt {
152
147
  fov: e.fov ?? (s == null ? void 0 : s.fov) ?? 70,
153
148
  supersampling: e.supersampling ?? (s == null ? void 0 : s.supersampling) ?? 1.5
154
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);
155
- let a;
150
+ let p;
156
151
  if (e.shadowDom !== !1) {
157
152
  const n = this.wrap.attachShadow({ mode: "open" }), m = document.createElement("style");
158
- m.textContent = q, n.appendChild(m), a = n;
153
+ m.textContent = q, n.appendChild(m), p = n;
159
154
  } else {
160
155
  if (!document.getElementById("tvp-styles")) {
161
156
  const n = document.createElement("style");
162
157
  n.id = "tvp-styles", n.textContent = q, document.head.appendChild(n);
163
158
  }
164
- a = this.wrap;
159
+ p = this.wrap;
165
160
  }
166
- 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"), a.append(this.canvas, this.video), this.scene = new tt({
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({
167
162
  canvas: this.canvas,
168
163
  video: this.video,
169
164
  projection: this.view.projection,
170
165
  swapEyes: this.view.swapEyes,
171
166
  fov: this.view.fov,
172
167
  supersampling: this.view.supersampling
173
- }), 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(a, {
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, {
174
169
  video: this.video,
175
170
  surface: this.canvas,
176
171
  fullscreenTarget: this.wrap,
@@ -187,7 +182,7 @@ class nt {
187
182
  setFov: (n) => this.setFov(n),
188
183
  setSupersampling: (n) => this.setSupersampling(n),
189
184
  initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },
190
- proxy: { url: ((d = this.proxyConfig) == null ? void 0 : d.url) ?? "", apiPassword: ((u = this.proxyConfig) == null ? void 0 : u.apiPassword) ?? "", enabled: this.useProxy },
185
+ proxy: { url: ((c = this.proxyConfig) == null ? void 0 : c.url) ?? "", apiPassword: ((h = this.proxyConfig) == null ? void 0 : h.apiPassword) ?? "", enabled: this.useProxy },
191
186
  setProxy: (n) => this.setProxy(n)
192
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", () => {
193
188
  this.loading || this.emit("error", this.video.error);
@@ -204,15 +199,15 @@ class nt {
204
199
  this.currentSrc = t;
205
200
  const s = e.projection ?? (this.opts.autoDetect !== !1 ? st(t) : null);
206
201
  s && this.applyProjectionGeometry(s), this.displayMode !== "off" && (this.displayMode = this.view.projection);
207
- const { url: a, format: d } = it(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;
208
203
  this.loading = !0;
209
204
  try {
210
- await this.source.attach(this.video, { url: a, format: d }, { 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(() => {
211
206
  });
212
207
  } catch (n) {
213
- if (this.opts.nativeFallback !== !1 && d === "progressive" && u !== null)
208
+ if (this.opts.nativeFallback !== !1 && c === "progressive" && h !== null)
214
209
  try {
215
- this.tainted = !0, this.setNativeFallback(!0), await this.source.attach(this.video, { url: a, format: d }, { crossOrigin: null }), await this.video.play().catch(() => {
210
+ this.tainted = !0, this.setNativeFallback(!0), await this.source.attach(this.video, { url: p, format: c }, { crossOrigin: null }), await this.video.play().catch(() => {
216
211
  }), this.emit("fallback");
217
212
  return;
218
213
  } catch (m) {
@@ -290,7 +285,7 @@ class nt {
290
285
  }
291
286
  emit(t, e) {
292
287
  var s;
293
- (s = this.listeners.get(t)) == null || s.forEach((a) => a(e));
288
+ (s = this.listeners.get(t)) == null || s.forEach((p) => p(e));
294
289
  }
295
290
  persist() {
296
291
  if (this.opts.persistSettings)
@@ -344,8 +339,8 @@ class rt extends HTMLElement {
344
339
  return this.player;
345
340
  }
346
341
  }
347
- function at(c = "three-video") {
348
- typeof customElements < "u" && !customElements.get(c) && customElements.define(c, rt);
342
+ function at(v = "three-video") {
343
+ typeof customElements < "u" && !customElements.get(v) && customElements.define(v, rt);
349
344
  }
350
345
  at();
351
346
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"three-vr-player.js","sources":["../src/ui/format.ts","../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["/** Format seconds as m:ss or h:mm:ss. */\r\nexport function formatTime(seconds: number): string {\r\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\r\n const s = Math.floor(seconds % 60);\r\n const m = Math.floor((seconds / 60) % 60);\r\n const h = Math.floor(seconds / 3600);\r\n const ss = String(s).padStart(2, '0');\r\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;\r\n}\r\n","import type { Projection } from '../types.js';\r\nimport { PROJECTIONS } from '../core/projections.js';\r\nimport { formatTime } from './format.js';\r\n\r\n/** What the ControlsUI needs from the Player to drive playback + view. */\r\nexport interface PlayerBridge {\r\n video: HTMLVideoElement;\r\n surface: HTMLElement; // canvas — for wheel-zoom\r\n fullscreenTarget: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n enterVR(): Promise<void>;\r\n exitVR(): void;\r\n isPresenting(): boolean;\r\n onVrChange(cb: (presenting: boolean) => void): void;\r\n getProjection(): Projection | 'off';\r\n setProjection(p: Projection | 'off'): void;\r\n setSwapEyes(v: boolean): void;\r\n setFov(deg: number): void;\r\n setSupersampling(x: number): void;\r\n initial: { swapEyes: boolean; fov: number; supersampling: number };\r\n proxy: { url: string; apiPassword: string; enabled: boolean };\r\n setProxy(p: { url: string; apiPassword: string; enabled: boolean }): void;\r\n}\r\n\r\ntype Props = Record<string, unknown> & { class?: string };\r\nfunction h<K extends keyof HTMLElementTagNameMap>(tag: K, props: Props = {}, children: (Node | string)[] = []): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tag);\r\n const { class: cls, ...rest } = props;\r\n if (cls) el.className = cls;\r\n Object.assign(el, rest);\r\n for (const c of children) el.append(c);\r\n return el;\r\n}\r\n\r\n/** Builds the controls bar + settings/menus inside `root` and wires them to `bridge`. */\r\nexport class ControlsUI {\r\n private readonly nodes: HTMLElement[] = [];\r\n private readonly disposers: (() => void)[] = [];\r\n\r\n constructor(root: ParentNode, bridge: PlayerBridge) {\r\n const v = bridge.video;\r\n\r\n // --- playback ---\r\n const play = h('button', { class: 'tvp-btn tvp-play', title: 'Play/Pause', textContent: '▶' });\r\n const seek = h('input', { class: 'tvp-seek', type: 'range', min: '0', max: '1000', value: '0' });\r\n const time = h('span', { class: 'tvp-time', textContent: '0:00 / 0:00' });\r\n\r\n // --- volume ---\r\n const mute = h('button', { class: 'tvp-btn tvp-mute', title: 'Volume', textContent: '🔊' });\r\n const volume = h('input', { class: 'tvp-volume', type: 'range', min: '0', max: '1', step: '0.01', value: '1' });\r\n const volPopup = h('div', { class: 'tvp-volpopup', hidden: true }, [volume]);\r\n const volWrap = h('span', { class: 'tvp-volwrap' }, [mute, volPopup]);\r\n\r\n // --- VR ---\r\n const vrBtn = h('button', { class: 'tvp-btn tvp-vrbtn', title: 'Enter VR', textContent: '🥽', hidden: true });\r\n\r\n // --- projection menu ---\r\n const projBtn = h('button', { class: 'tvp-btn tvp-projbtn', title: 'Projection', textContent: '🌐' });\r\n const projMenu = h('div', { class: 'tvp-projmenu', hidden: true });\r\n for (const p of PROJECTIONS) {\r\n const b = h('button', { textContent: p.label });\r\n b.dataset.mode = p.value;\r\n projMenu.append(b);\r\n }\r\n const offBtn = h('button', { textContent: 'Off (native player)' });\r\n offBtn.dataset.mode = 'off';\r\n projMenu.append(h('hr', { class: 'tvp-sep' }), offBtn);\r\n const projWrap = h('span', { class: 'tvp-projwrap' }, [projBtn, projMenu]);\r\n\r\n // --- settings ---\r\n const settingsBtn = h('button', { class: 'tvp-btn tvp-settingsbtn', title: 'Settings', textContent: '⚙' });\r\n const fullscreen = h('button', { class: 'tvp-btn tvp-fullscreen', title: 'Fullscreen', textContent: '⛶' });\r\n\r\n const controls = h('footer', { class: 'tvp-controls' }, [play, seek, time, volWrap, vrBtn, projWrap, settingsBtn, fullscreen]);\r\n\r\n const swapCb = h('input', { type: 'checkbox', checked: bridge.initial.swapEyes });\r\n const fovRange = h('input', { type: 'range', min: '30', max: '100', step: '1', value: String(bridge.initial.fov) });\r\n const fovVal = h('span', { textContent: String(bridge.initial.fov) });\r\n const ssRange = h('input', { type: 'range', min: '1', max: '2', step: '0.25', value: String(bridge.initial.supersampling) });\r\n const ssVal = h('span', { textContent: String(bridge.initial.supersampling) });\r\n const useProxyCb = h('input', { type: 'checkbox', checked: bridge.proxy.enabled });\r\n const proxyUrlIn = h('input', { type: 'text', value: bridge.proxy.url, placeholder: 'http://localhost:8888', spellcheck: false });\r\n const proxyPwIn = h('input', { type: 'password', value: bridge.proxy.apiPassword, placeholder: 'API password' });\r\n const settings = h('section', { class: 'tvp-settings' }, [\r\n h('label', { class: 'row' }, [swapCb, 'Swap eyes (if depth looks wrong)']),\r\n h('label', {}, [makeText('Field of view (zoom): ', fovVal, '°'), fovRange]),\r\n h('label', {}, [makeText('Supersampling: ', ssVal, '× (sharpness)'), ssRange]),\r\n h('hr', { class: 'tvp-sep' }),\r\n h('label', { class: 'row' }, [useProxyCb, 'Use CORS proxy']),\r\n h('label', {}, ['Proxy URL', proxyUrlIn]),\r\n h('label', {}, ['API password', proxyPwIn]),\r\n ]);\r\n\r\n const toast = h('div', { class: 'tvp-toast' });\r\n\r\n this.nodes.push(controls, settings, toast);\r\n for (const n of this.nodes) root.append(n);\r\n\r\n // ---- wiring ----\r\n const on = <T extends EventTarget>(t: T, ev: string, fn: (e: any) => void, opts?: AddEventListenerOptions) => {\r\n t.addEventListener(ev, fn as EventListener, opts);\r\n this.disposers.push(() => t.removeEventListener(ev, fn as EventListener, opts));\r\n };\r\n const setSeekFill = (pct: number) => seek.style.setProperty('--seek', `${pct}%`);\r\n // Only one popup (volume / projection / settings) open at a time.\r\n const closeMenus = () => { volPopup.hidden = true; projMenu.hidden = true; settings.classList.remove('open'); };\r\n let toastTimer = 0;\r\n const showToast = (msg: string) => {\r\n toast.textContent = msg;\r\n toast.classList.add('show');\r\n clearTimeout(toastTimer);\r\n toastTimer = window.setTimeout(() => toast.classList.remove('show'), 4500);\r\n };\r\n\r\n // Fire on a real pointer tap. The Meta Quest controller ray often fails to\r\n // synthesize `click` on <button> (tiny ray movement between press/release exceeds\r\n // the click threshold), while pointerup always fires. We trigger on pointerup and\r\n // keep `click` for keyboard/assistive tech, deduped so each activation fires once.\r\n // (pointerup carries transient activation too, so WebXR requestSession still works.)\r\n const onTap = (el: HTMLElement, fn: (e: Event) => void) => {\r\n let downOn = false;\r\n let firedAt = -1e9;\r\n const run = (e: Event) => { firedAt = e.timeStamp; fn(e); };\r\n on(el, 'pointerdown', () => { downOn = true; });\r\n on(el, 'pointercancel', () => { downOn = false; });\r\n on(el, 'pointerup', (e: Event) => { if (downOn) { downOn = false; run(e); } });\r\n on(el, 'click', (e: Event) => { if (e.timeStamp - firedAt > 700) run(e); });\r\n };\r\n\r\n onTap(play, () => { v.paused ? void v.play() : v.pause(); });\r\n on(v, 'play', () => { play.textContent = '⏸'; });\r\n on(v, 'pause', () => { play.textContent = '▶'; });\r\n\r\n on(v, 'timeupdate', () => {\r\n if (v.duration) {\r\n const pct = (v.currentTime / v.duration) * 100;\r\n seek.value = String(pct * 10);\r\n setSeekFill(pct);\r\n time.textContent = `${formatTime(v.currentTime)} / ${formatTime(v.duration)}`;\r\n }\r\n });\r\n on(seek, 'input', () => {\r\n setSeekFill(Number(seek.value) / 10);\r\n if (v.duration) v.currentTime = (Number(seek.value) / 1000) * v.duration;\r\n });\r\n\r\n onTap(mute, () => { const open = volPopup.hidden; closeMenus(); volPopup.hidden = !open; });\r\n on(volume, 'input', () => { v.volume = Number(volume.value); v.muted = v.volume === 0; mute.textContent = v.muted ? '🔇' : '🔊'; });\r\n\r\n const updateProjection = () => {\r\n const mode = bridge.getProjection();\r\n let label = 'Projection';\r\n projMenu.querySelectorAll('button').forEach((b) => {\r\n const on2 = b.dataset.mode === mode;\r\n b.classList.toggle('active', on2);\r\n if (on2) label = `Projection: ${b.textContent}`;\r\n });\r\n projBtn.title = label;\r\n };\r\n onTap(projBtn, () => { const open = projMenu.hidden; closeMenus(); projMenu.hidden = !open; });\r\n onTap(projMenu, (e: Event) => {\r\n const b = (e.target as HTMLElement).closest('button[data-mode]') as HTMLElement | null;\r\n if (b) { bridge.setProjection(b.dataset.mode as Projection | 'off'); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n onTap(settingsBtn, () => { const open = !settings.classList.contains('open'); closeMenus(); settings.classList.toggle('open', open); });\r\n on(swapCb, 'change', () => bridge.setSwapEyes(swapCb.checked));\r\n on(fovRange, 'input', () => { const d = Number(fovRange.value); fovVal.textContent = String(d); bridge.setFov(d); });\r\n on(ssRange, 'input', () => { const x = Number(ssRange.value); ssVal.textContent = String(x); bridge.setSupersampling(x); });\r\n const applyProxy = () => bridge.setProxy({ url: proxyUrlIn.value.trim(), apiPassword: proxyPwIn.value, enabled: useProxyCb.checked });\r\n on(useProxyCb, 'change', applyProxy);\r\n on(proxyUrlIn, 'change', applyProxy);\r\n on(proxyPwIn, 'change', applyProxy);\r\n on(bridge.surface, 'wheel', (e: WheelEvent) => {\r\n e.preventDefault();\r\n const next = Math.max(30, Math.min(100, Number(fovRange.value) + Math.sign(e.deltaY) * 3));\r\n fovRange.value = String(next); fovVal.textContent = String(next); bridge.setFov(next);\r\n }, { passive: false });\r\n\r\n onTap(fullscreen, () => {\r\n const t = bridge.fullscreenTarget;\r\n if (!document.fullscreenElement) void t.requestFullscreen?.();\r\n else void document.exitFullscreen?.();\r\n });\r\n\r\n // outside-tap closes any open popup (pointerdown fires reliably on Quest; click may not)\r\n on(document, 'pointerdown', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n const inside = (el: Node) => path.includes(el);\r\n if (![volPopup, mute, projMenu, projBtn, settings, settingsBtn].some(inside)) closeMenus();\r\n });\r\n\r\n // Auto-hide the control bar after 5s of inactivity; show on movement over the player.\r\n let hideTimer = 0;\r\n const popupOpen = () => !volPopup.hidden || !projMenu.hidden || settings.classList.contains('open');\r\n const hideBar = () => { if (!popupOpen()) controls.classList.add('tvp-hidden'); };\r\n const showBar = () => { controls.classList.remove('tvp-hidden'); clearTimeout(hideTimer); hideTimer = window.setTimeout(hideBar, 5000); };\r\n on(bridge.fullscreenTarget, 'pointermove', showBar);\r\n on(bridge.fullscreenTarget, 'pointerdown', showBar);\r\n on(bridge.fullscreenTarget, 'pointerleave', () => { clearTimeout(hideTimer); hideBar(); });\r\n this.disposers.push(() => clearTimeout(hideTimer));\r\n showBar();\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => { vrBtn.hidden = !ok; });\r\n onTap(vrBtn, () => {\r\n if (bridge.isPresenting()) { bridge.exitVR(); return; }\r\n // requestSession is the first thing off the tap so it keeps the user gesture;\r\n // any rejection is surfaced instead of silently swallowed.\r\n bridge.enterVR().catch((err: unknown) => showToast(err instanceof Error ? err.message : 'Could not enter VR.'));\r\n });\r\n bridge.onVrChange((presenting) => { vrBtn.classList.toggle('active', presenting); vrBtn.title = presenting ? 'Exit VR' : 'Enter VR'; });\r\n }\r\n\r\n dispose() {\r\n for (const d of this.disposers) d();\r\n for (const n of this.nodes) n.remove();\r\n }\r\n}\r\n\r\nfunction makeText(prefix: string, valueSpan: HTMLElement, suffix: string): DocumentFragment {\r\n const frag = document.createDocumentFragment();\r\n frag.append(prefix, valueSpan, suffix);\r\n return frag;\r\n}\r\n","import { StereoScene } from './core/StereoScene.js';\r\nimport { LookControls } from './core/LookControls.js';\r\nimport { VideoSource } from './core/VideoSource.js';\r\nimport { buildProxyUrl, type ProxyConfig } from './core/proxy.js';\r\nimport { detectProjection } from './core/projections.js';\r\nimport { ControlsUI } from './ui/ControlsUI.js';\r\nimport css from './ui/controls.css?inline';\r\nimport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\n\r\ntype ViewSettings = { projection: Projection; swapEyes: boolean; fov: number; supersampling: number };\r\nconst STORE_KEY = 'three-vr-player:settings';\r\n\r\n/**\r\n * A drop-in 3D/VR video player. Mounts a canvas + controls (into a Shadow DOM by\r\n * default) inside `container`, composing the headless core.\r\n */\r\nexport class Player {\r\n readonly video: HTMLVideoElement;\r\n\r\n private readonly opts: PlayerOptions;\r\n private readonly wrap: HTMLElement;\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly scene: StereoScene;\r\n private readonly look: LookControls;\r\n private readonly source = new VideoSource();\r\n private readonly ui?: ControlsUI;\r\n private readonly listeners = new Map<PlayerEvent, Set<(p?: unknown) => void>>();\r\n private view: ViewSettings;\r\n private readyEmitted = false;\r\n private native = false;\r\n private tainted = false; // current video loaded via CORS-fallback (crossOrigin removed)\r\n private displayMode: Projection | 'off';\r\n private loading = false;\r\n private proxyConfig?: ProxyConfig;\r\n private useProxy = false;\r\n private currentSrc?: string;\r\n\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\r\n this.proxyConfig = options.proxy;\r\n this.useProxy = !!options.proxy;\r\n const stored = options.persistSettings ? this.loadSettings() : null;\r\n this.view = {\r\n projection: options.projection ?? stored?.projection ?? '180-sbs',\r\n swapEyes: options.swapEyes ?? stored?.swapEyes ?? false,\r\n fov: options.fov ?? stored?.fov ?? 70,\r\n supersampling: options.supersampling ?? stored?.supersampling ?? 1.5,\r\n };\r\n this.displayMode = this.view.projection;\r\n\r\n this.wrap = document.createElement('div');\r\n this.wrap.className = 'tvp';\r\n this.wrap.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden;';\r\n container.appendChild(this.wrap);\r\n\r\n let root: ParentNode;\r\n if (options.shadowDom !== false) {\r\n const shadow = this.wrap.attachShadow({ mode: 'open' });\r\n const style = document.createElement('style'); style.textContent = css; shadow.appendChild(style);\r\n root = shadow;\r\n } else {\r\n if (!document.getElementById('tvp-styles')) {\r\n const style = document.createElement('style'); style.id = 'tvp-styles'; style.textContent = css; document.head.appendChild(style);\r\n }\r\n root = this.wrap;\r\n }\r\n\r\n this.canvas = document.createElement('canvas'); this.canvas.className = 'tvp-canvas';\r\n this.video = document.createElement('video'); this.video.className = 'tvp-video'; this.video.playsInline = true;\r\n if (options.crossOrigin !== null) this.video.crossOrigin = options.crossOrigin ?? 'anonymous';\r\n root.append(this.canvas, this.video);\r\n\r\n this.scene = new StereoScene({\r\n canvas: this.canvas, video: this.video,\r\n projection: this.view.projection, swapEyes: this.view.swapEyes,\r\n fov: this.view.fov, supersampling: this.view.supersampling,\r\n });\r\n this.look = new LookControls(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting });\r\n this.scene.onFrame(() => this.look.update());\r\n this.look.setEnabled(!this.scene.isFlat());\r\n\r\n if (options.controls !== false) {\r\n this.ui = new ControlsUI(root, {\r\n video: this.video,\r\n surface: this.canvas,\r\n fullscreenTarget: this.wrap,\r\n vrSupported: () => this.vrSupported(),\r\n enterVR: () => this.enterVR(),\r\n exitVR: () => this.scene.exitVR(),\r\n isPresenting: () => this.scene.renderer.xr.isPresenting,\r\n onVrChange: (cb) => { this.on('enterxr', () => cb(true)); this.on('exitxr', () => cb(false)); },\r\n getProjection: () => this.displayMode,\r\n setProjection: (p) => this.setProjection(p),\r\n setSwapEyes: (v) => this.setSwapEyes(v),\r\n setFov: (d) => this.setFov(d),\r\n setSupersampling: (x) => this.setSupersampling(x),\r\n initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },\r\n proxy: { url: this.proxyConfig?.url ?? '', apiPassword: this.proxyConfig?.apiPassword ?? '', enabled: this.useProxy },\r\n setProxy: (p) => this.setProxy(p),\r\n });\r\n }\r\n\r\n this.video.addEventListener('play', () => this.emit('play'));\r\n this.video.addEventListener('pause', () => this.emit('pause'));\r\n this.video.addEventListener('ended', () => this.emit('ended'));\r\n this.video.addEventListener('timeupdate', () => this.emit('timeupdate', this.video.currentTime));\r\n this.video.addEventListener('error', () => { if (!this.loading) this.emit('error', this.video.error); });\r\n this.video.addEventListener('loadedmetadata', () => { if (!this.readyEmitted) { this.readyEmitted = true; this.emit('ready'); } });\r\n this.scene.renderer.xr.addEventListener('sessionstart', () => this.emit('enterxr'));\r\n this.scene.renderer.xr.addEventListener('sessionend', () => this.emit('exitxr'));\r\n // Arm the headset/browser's own \"Enter VR\" affordance when immersive VR is available.\r\n void this.vrSupported().then((ok) => { if (ok) this.scene.offerVR(); });\r\n\r\n if (options.src) void this.load(options.src, { projection: options.projection });\r\n }\r\n\r\n private vrSupported(): Promise<boolean> {\r\n if (this.opts.vrButton === false || !navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-vr').catch(() => false);\r\n }\r\n\r\n async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n this.currentSrc = src;\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.applyProjectionGeometry(proj);\r\n if (this.displayMode !== 'off') this.displayMode = this.view.projection;\r\n const { url, format } = buildProxyUrl(src, this.useProxy ? this.proxyConfig : undefined);\r\n const primaryCO = this.opts.crossOrigin === undefined ? 'anonymous' : this.opts.crossOrigin;\r\n this.loading = true;\r\n try {\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: primaryCO });\r\n this.tainted = false;\r\n this.applyDisplay();\r\n await this.video.play().catch(() => { /* autoplay may be blocked until a user gesture */ });\r\n } catch (err) {\r\n // A cross-origin progressive source that isn't CORS-clean (and isn't proxied) can't be a\r\n // WebGL texture. Retry without crossOrigin and show plain 2D <video> playback if allowed.\r\n if (this.opts.nativeFallback !== false && format === 'progressive' && primaryCO !== null) {\r\n try {\r\n this.tainted = true;\r\n this.setNativeFallback(true); // stop WebGL rendering before the tainted video loads\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: null });\r\n await this.video.play().catch(() => {});\r\n this.emit('fallback');\r\n return;\r\n } catch (err2) {\r\n this.tainted = false;\r\n this.setNativeFallback(false);\r\n this.emit('error', err2);\r\n throw err2;\r\n }\r\n }\r\n this.setNativeFallback(false);\r\n this.emit('error', err);\r\n throw err;\r\n } finally {\r\n this.loading = false;\r\n }\r\n }\r\n\r\n /** Swap between WebGL (3D) rendering and plain 2D `<video>` playback. */\r\n private setNativeFallback(on: boolean) {\r\n if (on === this.native) return;\r\n this.native = on;\r\n if (on) {\r\n this.scene.pauseRendering();\r\n this.canvas.style.display = 'none';\r\n Object.assign(this.video.style, {\r\n display: 'block', position: 'absolute', inset: '0',\r\n width: '100%', height: '100%', objectFit: 'contain', background: '#000', zIndex: '1',\r\n });\r\n } else {\r\n this.canvas.style.display = '';\r\n this.video.style.cssText = '';\r\n this.scene.resumeRendering();\r\n }\r\n }\r\n\r\n async play() { await this.video.play(); }\r\n pause() { this.video.pause(); }\r\n /** Set the display mode: a geometry projection (WebGL 3D) or 'off' for plain 2D `<video>`. */\r\n setProjection(p: Projection | 'off') {\r\n if (p === 'off') {\r\n this.displayMode = 'off';\r\n this.setNativeFallback(true);\r\n this.emit('projectionchange', p);\r\n return;\r\n }\r\n this.displayMode = p;\r\n this.applyProjectionGeometry(p);\r\n // A CORS-tainted (native-fallback) video can't be a WebGL texture — reload to re-attempt 3D.\r\n if (this.tainted && this.currentSrc) void this.load(this.currentSrc);\r\n else this.setNativeFallback(false);\r\n this.emit('projectionchange', p);\r\n }\r\n private applyProjectionGeometry(p: Projection) {\r\n this.scene.setProjection(p);\r\n this.look.setEnabled(!this.scene.isFlat());\r\n this.look.reset();\r\n this.view.projection = p; this.persist();\r\n }\r\n private applyDisplay() { this.setNativeFallback(this.displayMode === 'off'); }\r\n setSwapEyes(v: boolean) { this.scene.setSwapEyes(v); this.view.swapEyes = v; this.persist(); }\r\n setFov(deg: number) { this.scene.setFov(deg); this.view.fov = deg; this.persist(); }\r\n setSupersampling(x: number) { this.scene.setSupersampling(x); this.view.supersampling = x; this.persist(); }\r\n /** Update the CORS proxy config / toggle it, and reload the current source if any. */\r\n setProxy(p: { url: string; apiPassword?: string; enabled: boolean }) {\r\n this.proxyConfig = p.url ? { url: p.url, apiPassword: p.apiPassword || undefined } : undefined;\r\n this.useProxy = p.enabled;\r\n if (this.currentSrc) void this.load(this.currentSrc);\r\n }\r\n\r\n /** Enter immersive VR. Rejects if the WebXR session request fails (e.g. no headset,\r\n * or a non-secure origin) — the built-in controls surface the reason as a toast. */\r\n async enterVR() { await this.scene.enterVR(); }\r\n exitVR() { this.scene.exitVR(); }\r\n\r\n get three() { return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera }; }\r\n\r\n on(e: PlayerEvent, cb: (p?: unknown) => void) {\r\n let s = this.listeners.get(e); if (!s) { s = new Set(); this.listeners.set(e, s); } s.add(cb); return this;\r\n }\r\n off(e: PlayerEvent, cb: (p?: unknown) => void) { this.listeners.get(e)?.delete(cb); return this; }\r\n private emit(e: PlayerEvent, p?: unknown) { this.listeners.get(e)?.forEach((cb) => cb(p)); }\r\n\r\n private persist() {\r\n if (!this.opts.persistSettings) return;\r\n try { localStorage.setItem(STORE_KEY, JSON.stringify(this.view)); } catch { /* private mode */ }\r\n }\r\n private loadSettings(): Partial<ViewSettings> | null {\r\n try { const raw = localStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) as Partial<ViewSettings> : null; }\r\n catch { return null; }\r\n }\r\n\r\n dispose() {\r\n this.ui?.dispose(); this.look.dispose(); this.source.dispose(); this.scene.dispose();\r\n this.video.remove(); this.canvas.remove(); this.wrap.remove(); this.listeners.clear();\r\n }\r\n}\r\n","import { Player } from './Player.js';\r\nimport type { Projection } from './types.js';\r\n\r\n/**\r\n * `<three-video src=\"…\" projection=\"180-sbs\" controls>` — a framework-agnostic\r\n * custom element wrapping {@link Player}. Give it a size via CSS.\r\n */\r\nexport class ThreeVideoElement extends HTMLElement {\r\n private player?: Player;\r\n\r\n static get observedAttributes() { return ['src', 'projection']; }\r\n\r\n connectedCallback() {\r\n if (this.player) return;\r\n if (!this.style.display) this.style.display = 'block';\r\n this.player = new Player(this, {\r\n src: this.getAttribute('src') ?? undefined,\r\n projection: (this.getAttribute('projection') as Projection) ?? undefined,\r\n controls: this.hasAttribute('controls'),\r\n swapEyes: this.hasAttribute('swap-eyes'),\r\n autoDetect: this.getAttribute('auto-detect') !== 'false',\r\n fov: this.numAttr('fov'),\r\n supersampling: this.numAttr('supersampling'),\r\n proxy: this.getAttribute('proxy-url')\r\n ? { url: this.getAttribute('proxy-url')!, apiPassword: this.getAttribute('proxy-password') ?? undefined }\r\n : undefined,\r\n });\r\n }\r\n\r\n attributeChangedCallback(name: string, _old: string | null, value: string | null) {\r\n if (!this.player || value == null) return;\r\n if (name === 'src') void this.player.load(value);\r\n if (name === 'projection') this.player.setProjection(value as Projection);\r\n }\r\n\r\n disconnectedCallback() { this.player?.dispose(); this.player = undefined; }\r\n\r\n private numAttr(n: string): number | undefined {\r\n const v = this.getAttribute(n);\r\n return v == null ? undefined : Number(v);\r\n }\r\n\r\n /** The underlying Player instance (for programmatic control). */\r\n get api(): Player | undefined { return this.player; }\r\n}\r\n\r\nexport function registerWebComponent(tag = 'three-video') {\r\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\r\n customElements.define(tag, ThreeVideoElement);\r\n }\r\n}\r\n","export { Player } from './Player.js';\r\nexport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\nexport { ThreeVideoElement, registerWebComponent } from './webcomponent.js';\r\nexport * from './core/index.js';\r\n\r\nimport { registerWebComponent } from './webcomponent.js';\r\n// Auto-register <three-video> when the main entry is imported.\r\nregisterWebComponent();\r\n"],"names":["formatTime","seconds","s","m","h","ss","tag","props","children","el","cls","rest","c","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrBtn","projBtn","projMenu","p","PROJECTIONS","b","offBtn","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","useProxyCb","proxyUrlIn","proxyPwIn","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","closeMenus","toastTimer","showToast","msg","onTap","downOn","firedAt","run","e","open","updateProjection","mode","label","on2","d","x","applyProxy","next","_b","_a","path","inside","hideTimer","popupOpen","hideBar","showBar","ok","err","presenting","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","cb","src","o","proj","detectProjection","url","format","buildProxyUrl","primaryCO","err2","deg","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}
1
+ {"version":3,"file":"three-vr-player.js","sources":["../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["import type { Projection } from '../types.js';\r\nimport { PROJECTIONS } from '../core/projections.js';\r\nimport { formatTime } from './format.js';\r\n\r\n/** What the ControlsUI needs from the Player to drive playback + view. */\r\nexport interface PlayerBridge {\r\n video: HTMLVideoElement;\r\n surface: HTMLElement; // canvas — for wheel-zoom\r\n fullscreenTarget: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n enterVR(): Promise<void>;\r\n exitVR(): void;\r\n isPresenting(): boolean;\r\n onVrChange(cb: (presenting: boolean) => void): void;\r\n getProjection(): Projection | 'off';\r\n setProjection(p: Projection | 'off'): void;\r\n setSwapEyes(v: boolean): void;\r\n setFov(deg: number): void;\r\n setSupersampling(x: number): void;\r\n initial: { swapEyes: boolean; fov: number; supersampling: number };\r\n proxy: { url: string; apiPassword: string; enabled: boolean };\r\n setProxy(p: { url: string; apiPassword: string; enabled: boolean }): void;\r\n}\r\n\r\ntype Props = Record<string, unknown> & { class?: string };\r\nfunction h<K extends keyof HTMLElementTagNameMap>(tag: K, props: Props = {}, children: (Node | string)[] = []): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tag);\r\n const { class: cls, ...rest } = props;\r\n if (cls) el.className = cls;\r\n Object.assign(el, rest);\r\n for (const c of children) el.append(c);\r\n return el;\r\n}\r\n\r\n/** Builds the controls bar + settings/menus inside `root` and wires them to `bridge`. */\r\nexport class ControlsUI {\r\n private readonly nodes: HTMLElement[] = [];\r\n private readonly disposers: (() => void)[] = [];\r\n\r\n constructor(root: ParentNode, bridge: PlayerBridge) {\r\n const v = bridge.video;\r\n\r\n // --- playback ---\r\n const play = h('button', { class: 'tvp-btn tvp-play', title: 'Play/Pause', textContent: '▶' });\r\n const seek = h('input', { class: 'tvp-seek', type: 'range', min: '0', max: '1000', value: '0' });\r\n const time = h('span', { class: 'tvp-time', textContent: '0:00 / 0:00' });\r\n\r\n // --- volume ---\r\n const mute = h('button', { class: 'tvp-btn tvp-mute', title: 'Volume', textContent: '🔊' });\r\n const volume = h('input', { class: 'tvp-volume', type: 'range', min: '0', max: '1', step: '0.01', value: '1' });\r\n const volPopup = h('div', { class: 'tvp-volpopup', hidden: true }, [volume]);\r\n const volWrap = h('span', { class: 'tvp-volwrap' }, [mute, volPopup]);\r\n\r\n // --- VR ---\r\n const vrBtn = h('button', { class: 'tvp-btn tvp-vrbtn', title: 'Enter VR', textContent: '🥽', hidden: true });\r\n\r\n // --- projection menu ---\r\n const projBtn = h('button', { class: 'tvp-btn tvp-projbtn', title: 'Projection', textContent: '🌐' });\r\n const projMenu = h('div', { class: 'tvp-projmenu', hidden: true });\r\n for (const p of PROJECTIONS) {\r\n const b = h('button', { textContent: p.label });\r\n b.dataset.mode = p.value;\r\n projMenu.append(b);\r\n }\r\n const offBtn = h('button', { textContent: 'Off (native player)' });\r\n offBtn.dataset.mode = 'off';\r\n projMenu.append(h('hr', { class: 'tvp-sep' }), offBtn);\r\n const projWrap = h('span', { class: 'tvp-projwrap' }, [projBtn, projMenu]);\r\n\r\n // --- settings ---\r\n const settingsBtn = h('button', { class: 'tvp-btn tvp-settingsbtn', title: 'Settings', textContent: '⚙' });\r\n const fullscreen = h('button', { class: 'tvp-btn tvp-fullscreen', title: 'Fullscreen', textContent: '⛶' });\r\n\r\n const controls = h('footer', { class: 'tvp-controls' }, [play, seek, time, volWrap, vrBtn, projWrap, settingsBtn, fullscreen]);\r\n\r\n const swapCb = h('input', { type: 'checkbox', checked: bridge.initial.swapEyes });\r\n const fovRange = h('input', { type: 'range', min: '30', max: '100', step: '1', value: String(bridge.initial.fov) });\r\n const fovVal = h('span', { textContent: String(bridge.initial.fov) });\r\n const ssRange = h('input', { type: 'range', min: '1', max: '2', step: '0.25', value: String(bridge.initial.supersampling) });\r\n const ssVal = h('span', { textContent: String(bridge.initial.supersampling) });\r\n const useProxyCb = h('input', { type: 'checkbox', checked: bridge.proxy.enabled });\r\n const proxyUrlIn = h('input', { type: 'text', value: bridge.proxy.url, placeholder: 'http://localhost:8888', spellcheck: false });\r\n const proxyPwIn = h('input', { type: 'password', value: bridge.proxy.apiPassword, placeholder: 'API password' });\r\n const settings = h('section', { class: 'tvp-settings' }, [\r\n h('label', { class: 'row' }, [swapCb, 'Swap eyes (if depth looks wrong)']),\r\n h('label', {}, [makeText('Field of view (zoom): ', fovVal, '°'), fovRange]),\r\n h('label', {}, [makeText('Supersampling: ', ssVal, '× (sharpness)'), ssRange]),\r\n h('hr', { class: 'tvp-sep' }),\r\n h('label', { class: 'row' }, [useProxyCb, 'Use CORS proxy']),\r\n h('label', {}, ['Proxy URL', proxyUrlIn]),\r\n h('label', {}, ['API password', proxyPwIn]),\r\n ]);\r\n\r\n const toast = h('div', { class: 'tvp-toast' });\r\n\r\n this.nodes.push(controls, settings, toast);\r\n for (const n of this.nodes) root.append(n);\r\n\r\n // ---- wiring ----\r\n const on = <T extends EventTarget>(t: T, ev: string, fn: (e: any) => void, opts?: AddEventListenerOptions) => {\r\n t.addEventListener(ev, fn as EventListener, opts);\r\n this.disposers.push(() => t.removeEventListener(ev, fn as EventListener, opts));\r\n };\r\n const setSeekFill = (pct: number) => seek.style.setProperty('--seek', `${pct}%`);\r\n // Only one popup (volume / projection / settings) open at a time.\r\n const closeMenus = () => { volPopup.hidden = true; projMenu.hidden = true; settings.classList.remove('open'); };\r\n let toastTimer = 0;\r\n const showToast = (msg: string) => {\r\n toast.textContent = msg;\r\n toast.classList.add('show');\r\n clearTimeout(toastTimer);\r\n toastTimer = window.setTimeout(() => toast.classList.remove('show'), 4500);\r\n };\r\n\r\n // Fire on a real pointer tap. The Meta Quest controller ray often fails to\r\n // synthesize `click` on <button> (tiny ray movement between press/release exceeds\r\n // the click threshold), while pointerup always fires. We trigger on pointerup and\r\n // keep `click` for keyboard/assistive tech, deduped so each activation fires once.\r\n // (pointerup carries transient activation too, so WebXR requestSession still works.)\r\n const onTap = (el: HTMLElement, fn: (e: Event) => void) => {\r\n let downOn = false;\r\n let firedAt = -1e9;\r\n const run = (e: Event) => { firedAt = e.timeStamp; fn(e); };\r\n on(el, 'pointerdown', () => { downOn = true; });\r\n on(el, 'pointercancel', () => { downOn = false; });\r\n on(el, 'pointerup', (e: Event) => { if (downOn) { downOn = false; run(e); } });\r\n on(el, 'click', (e: Event) => { if (e.timeStamp - firedAt > 700) run(e); });\r\n };\r\n\r\n onTap(play, () => { v.paused ? void v.play() : v.pause(); });\r\n on(v, 'play', () => { play.textContent = '⏸'; });\r\n on(v, 'pause', () => { play.textContent = '▶'; });\r\n\r\n on(v, 'timeupdate', () => {\r\n if (v.duration) {\r\n const pct = (v.currentTime / v.duration) * 100;\r\n seek.value = String(pct * 10);\r\n setSeekFill(pct);\r\n time.textContent = `${formatTime(v.currentTime)} / ${formatTime(v.duration)}`;\r\n }\r\n });\r\n on(seek, 'input', () => {\r\n setSeekFill(Number(seek.value) / 10);\r\n if (v.duration) v.currentTime = (Number(seek.value) / 1000) * v.duration;\r\n });\r\n\r\n onTap(mute, () => { const open = volPopup.hidden; closeMenus(); volPopup.hidden = !open; });\r\n on(volume, 'input', () => { v.volume = Number(volume.value); v.muted = v.volume === 0; mute.textContent = v.muted ? '🔇' : '🔊'; });\r\n\r\n const updateProjection = () => {\r\n const mode = bridge.getProjection();\r\n let label = 'Projection';\r\n projMenu.querySelectorAll('button').forEach((b) => {\r\n const on2 = b.dataset.mode === mode;\r\n b.classList.toggle('active', on2);\r\n if (on2) label = `Projection: ${b.textContent}`;\r\n });\r\n projBtn.title = label;\r\n };\r\n onTap(projBtn, () => { const open = projMenu.hidden; closeMenus(); projMenu.hidden = !open; });\r\n onTap(projMenu, (e: Event) => {\r\n const b = (e.target as HTMLElement).closest('button[data-mode]') as HTMLElement | null;\r\n if (b) { bridge.setProjection(b.dataset.mode as Projection | 'off'); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n onTap(settingsBtn, () => { const open = !settings.classList.contains('open'); closeMenus(); settings.classList.toggle('open', open); });\r\n on(swapCb, 'change', () => bridge.setSwapEyes(swapCb.checked));\r\n on(fovRange, 'input', () => { const d = Number(fovRange.value); fovVal.textContent = String(d); bridge.setFov(d); });\r\n on(ssRange, 'input', () => { const x = Number(ssRange.value); ssVal.textContent = String(x); bridge.setSupersampling(x); });\r\n const applyProxy = () => bridge.setProxy({ url: proxyUrlIn.value.trim(), apiPassword: proxyPwIn.value, enabled: useProxyCb.checked });\r\n on(useProxyCb, 'change', applyProxy);\r\n on(proxyUrlIn, 'change', applyProxy);\r\n on(proxyPwIn, 'change', applyProxy);\r\n on(bridge.surface, 'wheel', (e: WheelEvent) => {\r\n e.preventDefault();\r\n const next = Math.max(30, Math.min(100, Number(fovRange.value) + Math.sign(e.deltaY) * 3));\r\n fovRange.value = String(next); fovVal.textContent = String(next); bridge.setFov(next);\r\n }, { passive: false });\r\n\r\n onTap(fullscreen, () => {\r\n const t = bridge.fullscreenTarget;\r\n if (!document.fullscreenElement) void t.requestFullscreen?.();\r\n else void document.exitFullscreen?.();\r\n });\r\n\r\n // outside-tap closes any open popup (pointerdown fires reliably on Quest; click may not)\r\n on(document, 'pointerdown', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n const inside = (el: Node) => path.includes(el);\r\n if (![volPopup, mute, projMenu, projBtn, settings, settingsBtn].some(inside)) closeMenus();\r\n });\r\n\r\n // Auto-hide the control bar after 5s of inactivity; show on movement over the player.\r\n let hideTimer = 0;\r\n const popupOpen = () => !volPopup.hidden || !projMenu.hidden || settings.classList.contains('open');\r\n const hideBar = () => { if (!popupOpen()) controls.classList.add('tvp-hidden'); };\r\n const showBar = () => { controls.classList.remove('tvp-hidden'); clearTimeout(hideTimer); hideTimer = window.setTimeout(hideBar, 5000); };\r\n on(bridge.fullscreenTarget, 'pointermove', showBar);\r\n on(bridge.fullscreenTarget, 'pointerdown', showBar);\r\n on(bridge.fullscreenTarget, 'pointerleave', () => { clearTimeout(hideTimer); hideBar(); });\r\n this.disposers.push(() => clearTimeout(hideTimer));\r\n showBar();\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => { vrBtn.hidden = !ok; });\r\n onTap(vrBtn, () => {\r\n if (bridge.isPresenting()) { bridge.exitVR(); return; }\r\n // requestSession is the first thing off the tap so it keeps the user gesture;\r\n // any rejection is surfaced instead of silently swallowed.\r\n bridge.enterVR().catch((err: unknown) => showToast(err instanceof Error ? err.message : 'Could not enter VR.'));\r\n });\r\n bridge.onVrChange((presenting) => { vrBtn.classList.toggle('active', presenting); vrBtn.title = presenting ? 'Exit VR' : 'Enter VR'; });\r\n }\r\n\r\n dispose() {\r\n for (const d of this.disposers) d();\r\n for (const n of this.nodes) n.remove();\r\n }\r\n}\r\n\r\nfunction makeText(prefix: string, valueSpan: HTMLElement, suffix: string): DocumentFragment {\r\n const frag = document.createDocumentFragment();\r\n frag.append(prefix, valueSpan, suffix);\r\n return frag;\r\n}\r\n","import { StereoScene } from './core/StereoScene.js';\r\nimport { LookControls } from './core/LookControls.js';\r\nimport { VideoSource } from './core/VideoSource.js';\r\nimport { buildProxyUrl, type ProxyConfig } from './core/proxy.js';\r\nimport { detectProjection } from './core/projections.js';\r\nimport { ControlsUI } from './ui/ControlsUI.js';\r\nimport css from './ui/controls.css?inline';\r\nimport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\n\r\ntype ViewSettings = { projection: Projection; swapEyes: boolean; fov: number; supersampling: number };\r\nconst STORE_KEY = 'three-vr-player:settings';\r\n\r\n/**\r\n * A drop-in 3D/VR video player. Mounts a canvas + controls (into a Shadow DOM by\r\n * default) inside `container`, composing the headless core.\r\n */\r\nexport class Player {\r\n readonly video: HTMLVideoElement;\r\n\r\n private readonly opts: PlayerOptions;\r\n private readonly wrap: HTMLElement;\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly scene: StereoScene;\r\n private readonly look: LookControls;\r\n private readonly source = new VideoSource();\r\n private readonly ui?: ControlsUI;\r\n private readonly listeners = new Map<PlayerEvent, Set<(p?: unknown) => void>>();\r\n private view: ViewSettings;\r\n private readyEmitted = false;\r\n private native = false;\r\n private tainted = false; // current video loaded via CORS-fallback (crossOrigin removed)\r\n private displayMode: Projection | 'off';\r\n private loading = false;\r\n private proxyConfig?: ProxyConfig;\r\n private useProxy = false;\r\n private currentSrc?: string;\r\n\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\r\n this.proxyConfig = options.proxy;\r\n this.useProxy = !!options.proxy;\r\n const stored = options.persistSettings ? this.loadSettings() : null;\r\n this.view = {\r\n projection: options.projection ?? stored?.projection ?? '180-sbs',\r\n swapEyes: options.swapEyes ?? stored?.swapEyes ?? false,\r\n fov: options.fov ?? stored?.fov ?? 70,\r\n supersampling: options.supersampling ?? stored?.supersampling ?? 1.5,\r\n };\r\n this.displayMode = this.view.projection;\r\n\r\n this.wrap = document.createElement('div');\r\n this.wrap.className = 'tvp';\r\n this.wrap.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden;';\r\n container.appendChild(this.wrap);\r\n\r\n let root: ParentNode;\r\n if (options.shadowDom !== false) {\r\n const shadow = this.wrap.attachShadow({ mode: 'open' });\r\n const style = document.createElement('style'); style.textContent = css; shadow.appendChild(style);\r\n root = shadow;\r\n } else {\r\n if (!document.getElementById('tvp-styles')) {\r\n const style = document.createElement('style'); style.id = 'tvp-styles'; style.textContent = css; document.head.appendChild(style);\r\n }\r\n root = this.wrap;\r\n }\r\n\r\n this.canvas = document.createElement('canvas'); this.canvas.className = 'tvp-canvas';\r\n this.video = document.createElement('video'); this.video.className = 'tvp-video'; this.video.playsInline = true;\r\n if (options.crossOrigin !== null) this.video.crossOrigin = options.crossOrigin ?? 'anonymous';\r\n root.append(this.canvas, this.video);\r\n\r\n this.scene = new StereoScene({\r\n canvas: this.canvas, video: this.video,\r\n projection: this.view.projection, swapEyes: this.view.swapEyes,\r\n fov: this.view.fov, supersampling: this.view.supersampling,\r\n });\r\n if (options.title) this.scene.setVRTitle(options.title);\r\n this.look = new LookControls(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting });\r\n this.scene.onFrame(() => this.look.update());\r\n this.look.setEnabled(!this.scene.isFlat());\r\n\r\n if (options.controls !== false) {\r\n this.ui = new ControlsUI(root, {\r\n video: this.video,\r\n surface: this.canvas,\r\n fullscreenTarget: this.wrap,\r\n vrSupported: () => this.vrSupported(),\r\n enterVR: () => this.enterVR(),\r\n exitVR: () => this.scene.exitVR(),\r\n isPresenting: () => this.scene.renderer.xr.isPresenting,\r\n onVrChange: (cb) => { this.on('enterxr', () => cb(true)); this.on('exitxr', () => cb(false)); },\r\n getProjection: () => this.displayMode,\r\n setProjection: (p) => this.setProjection(p),\r\n setSwapEyes: (v) => this.setSwapEyes(v),\r\n setFov: (d) => this.setFov(d),\r\n setSupersampling: (x) => this.setSupersampling(x),\r\n initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },\r\n proxy: { url: this.proxyConfig?.url ?? '', apiPassword: this.proxyConfig?.apiPassword ?? '', enabled: this.useProxy },\r\n setProxy: (p) => this.setProxy(p),\r\n });\r\n }\r\n\r\n this.video.addEventListener('play', () => this.emit('play'));\r\n this.video.addEventListener('pause', () => this.emit('pause'));\r\n this.video.addEventListener('ended', () => this.emit('ended'));\r\n this.video.addEventListener('timeupdate', () => this.emit('timeupdate', this.video.currentTime));\r\n this.video.addEventListener('error', () => { if (!this.loading) this.emit('error', this.video.error); });\r\n this.video.addEventListener('loadedmetadata', () => { if (!this.readyEmitted) { this.readyEmitted = true; this.emit('ready'); } });\r\n this.scene.renderer.xr.addEventListener('sessionstart', () => this.emit('enterxr'));\r\n this.scene.renderer.xr.addEventListener('sessionend', () => this.emit('exitxr'));\r\n // Arm the headset/browser's own \"Enter VR\" affordance when immersive VR is available.\r\n void this.vrSupported().then((ok) => { if (ok) this.scene.offerVR(); });\r\n\r\n if (options.src) void this.load(options.src, { projection: options.projection });\r\n }\r\n\r\n private vrSupported(): Promise<boolean> {\r\n if (this.opts.vrButton === false || !navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-vr').catch(() => false);\r\n }\r\n\r\n async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n this.currentSrc = src;\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.applyProjectionGeometry(proj);\r\n if (this.displayMode !== 'off') this.displayMode = this.view.projection;\r\n const { url, format } = buildProxyUrl(src, this.useProxy ? this.proxyConfig : undefined);\r\n const primaryCO = this.opts.crossOrigin === undefined ? 'anonymous' : this.opts.crossOrigin;\r\n this.loading = true;\r\n try {\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: primaryCO });\r\n this.tainted = false;\r\n this.applyDisplay();\r\n await this.video.play().catch(() => { /* autoplay may be blocked until a user gesture */ });\r\n } catch (err) {\r\n // A cross-origin progressive source that isn't CORS-clean (and isn't proxied) can't be a\r\n // WebGL texture. Retry without crossOrigin and show plain 2D <video> playback if allowed.\r\n if (this.opts.nativeFallback !== false && format === 'progressive' && primaryCO !== null) {\r\n try {\r\n this.tainted = true;\r\n this.setNativeFallback(true); // stop WebGL rendering before the tainted video loads\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: null });\r\n await this.video.play().catch(() => {});\r\n this.emit('fallback');\r\n return;\r\n } catch (err2) {\r\n this.tainted = false;\r\n this.setNativeFallback(false);\r\n this.emit('error', err2);\r\n throw err2;\r\n }\r\n }\r\n this.setNativeFallback(false);\r\n this.emit('error', err);\r\n throw err;\r\n } finally {\r\n this.loading = false;\r\n }\r\n }\r\n\r\n /** Swap between WebGL (3D) rendering and plain 2D `<video>` playback. */\r\n private setNativeFallback(on: boolean) {\r\n if (on === this.native) return;\r\n this.native = on;\r\n if (on) {\r\n this.scene.pauseRendering();\r\n this.canvas.style.display = 'none';\r\n Object.assign(this.video.style, {\r\n display: 'block', position: 'absolute', inset: '0',\r\n width: '100%', height: '100%', objectFit: 'contain', background: '#000', zIndex: '1',\r\n });\r\n } else {\r\n this.canvas.style.display = '';\r\n this.video.style.cssText = '';\r\n this.scene.resumeRendering();\r\n }\r\n }\r\n\r\n async play() { await this.video.play(); }\r\n pause() { this.video.pause(); }\r\n /** Set the display mode: a geometry projection (WebGL 3D) or 'off' for plain 2D `<video>`. */\r\n setProjection(p: Projection | 'off') {\r\n if (p === 'off') {\r\n this.displayMode = 'off';\r\n this.setNativeFallback(true);\r\n this.emit('projectionchange', p);\r\n return;\r\n }\r\n this.displayMode = p;\r\n this.applyProjectionGeometry(p);\r\n // A CORS-tainted (native-fallback) video can't be a WebGL texture — reload to re-attempt 3D.\r\n if (this.tainted && this.currentSrc) void this.load(this.currentSrc);\r\n else this.setNativeFallback(false);\r\n this.emit('projectionchange', p);\r\n }\r\n private applyProjectionGeometry(p: Projection) {\r\n this.scene.setProjection(p);\r\n this.look.setEnabled(!this.scene.isFlat());\r\n this.look.reset();\r\n this.view.projection = p; this.persist();\r\n }\r\n private applyDisplay() { this.setNativeFallback(this.displayMode === 'off'); }\r\n setSwapEyes(v: boolean) { this.scene.setSwapEyes(v); this.view.swapEyes = v; this.persist(); }\r\n setFov(deg: number) { this.scene.setFov(deg); this.view.fov = deg; this.persist(); }\r\n setSupersampling(x: number) { this.scene.setSupersampling(x); this.view.supersampling = x; this.persist(); }\r\n /** Update the CORS proxy config / toggle it, and reload the current source if any. */\r\n setProxy(p: { url: string; apiPassword?: string; enabled: boolean }) {\r\n this.proxyConfig = p.url ? { url: p.url, apiPassword: p.apiPassword || undefined } : undefined;\r\n this.useProxy = p.enabled;\r\n if (this.currentSrc) void this.load(this.currentSrc);\r\n }\r\n\r\n /** Enter immersive VR. Rejects if the WebXR session request fails (e.g. no headset,\r\n * or a non-secure origin) — the built-in controls surface the reason as a toast. */\r\n async enterVR() { await this.scene.enterVR(); }\r\n exitVR() { this.scene.exitVR(); }\r\n\r\n get three() { return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera }; }\r\n\r\n on(e: PlayerEvent, cb: (p?: unknown) => void) {\r\n let s = this.listeners.get(e); if (!s) { s = new Set(); this.listeners.set(e, s); } s.add(cb); return this;\r\n }\r\n off(e: PlayerEvent, cb: (p?: unknown) => void) { this.listeners.get(e)?.delete(cb); return this; }\r\n private emit(e: PlayerEvent, p?: unknown) { this.listeners.get(e)?.forEach((cb) => cb(p)); }\r\n\r\n private persist() {\r\n if (!this.opts.persistSettings) return;\r\n try { localStorage.setItem(STORE_KEY, JSON.stringify(this.view)); } catch { /* private mode */ }\r\n }\r\n private loadSettings(): Partial<ViewSettings> | null {\r\n try { const raw = localStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) as Partial<ViewSettings> : null; }\r\n catch { return null; }\r\n }\r\n\r\n dispose() {\r\n this.ui?.dispose(); this.look.dispose(); this.source.dispose(); this.scene.dispose();\r\n this.video.remove(); this.canvas.remove(); this.wrap.remove(); this.listeners.clear();\r\n }\r\n}\r\n","import { Player } from './Player.js';\r\nimport type { Projection } from './types.js';\r\n\r\n/**\r\n * `<three-video src=\"…\" projection=\"180-sbs\" controls>` — a framework-agnostic\r\n * custom element wrapping {@link Player}. Give it a size via CSS.\r\n */\r\nexport class ThreeVideoElement extends HTMLElement {\r\n private player?: Player;\r\n\r\n static get observedAttributes() { return ['src', 'projection']; }\r\n\r\n connectedCallback() {\r\n if (this.player) return;\r\n if (!this.style.display) this.style.display = 'block';\r\n this.player = new Player(this, {\r\n src: this.getAttribute('src') ?? undefined,\r\n projection: (this.getAttribute('projection') as Projection) ?? undefined,\r\n controls: this.hasAttribute('controls'),\r\n swapEyes: this.hasAttribute('swap-eyes'),\r\n autoDetect: this.getAttribute('auto-detect') !== 'false',\r\n fov: this.numAttr('fov'),\r\n supersampling: this.numAttr('supersampling'),\r\n proxy: this.getAttribute('proxy-url')\r\n ? { url: this.getAttribute('proxy-url')!, apiPassword: this.getAttribute('proxy-password') ?? undefined }\r\n : undefined,\r\n });\r\n }\r\n\r\n attributeChangedCallback(name: string, _old: string | null, value: string | null) {\r\n if (!this.player || value == null) return;\r\n if (name === 'src') void this.player.load(value);\r\n if (name === 'projection') this.player.setProjection(value as Projection);\r\n }\r\n\r\n disconnectedCallback() { this.player?.dispose(); this.player = undefined; }\r\n\r\n private numAttr(n: string): number | undefined {\r\n const v = this.getAttribute(n);\r\n return v == null ? undefined : Number(v);\r\n }\r\n\r\n /** The underlying Player instance (for programmatic control). */\r\n get api(): Player | undefined { return this.player; }\r\n}\r\n\r\nexport function registerWebComponent(tag = 'three-video') {\r\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\r\n customElements.define(tag, ThreeVideoElement);\r\n }\r\n}\r\n","export { Player } from './Player.js';\r\nexport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\nexport { ThreeVideoElement, registerWebComponent } from './webcomponent.js';\r\nexport * from './core/index.js';\r\n\r\nimport { registerWebComponent } from './webcomponent.js';\r\n// Auto-register <three-video> when the main entry is imported.\r\nregisterWebComponent();\r\n"],"names":["h","tag","props","children","el","cls","rest","c","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrBtn","projBtn","projMenu","p","PROJECTIONS","b","offBtn","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","useProxyCb","proxyUrlIn","proxyPwIn","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","closeMenus","toastTimer","showToast","msg","onTap","downOn","firedAt","run","e","formatTime","open","updateProjection","mode","label","on2","d","x","applyProxy","next","_b","_a","path","inside","hideTimer","popupOpen","hideBar","showBar","ok","err","presenting","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","cb","src","o","proj","detectProjection","url","format","buildProxyUrl","primaryCO","err2","deg","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}