@moq/watch 0.2.10 → 0.2.12

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.
Files changed (119) hide show
  1. package/README.md +7 -7
  2. package/audio/backend.d.ts +1 -0
  3. package/audio/backend.d.ts.map +1 -1
  4. package/audio/buffer.d.ts +1 -1
  5. package/audio/buffer.d.ts.map +1 -1
  6. package/audio/decoder.d.ts +1 -1
  7. package/audio/decoder.d.ts.map +1 -1
  8. package/audio/mse.d.ts +1 -0
  9. package/audio/mse.d.ts.map +1 -1
  10. package/audio/render.d.ts +1 -1
  11. package/audio/render.d.ts.map +1 -1
  12. package/audio/ring-buffer.d.ts +1 -1
  13. package/audio/ring-buffer.d.ts.map +1 -1
  14. package/audio/shared-ring-buffer.d.ts +1 -1
  15. package/audio/shared-ring-buffer.d.ts.map +1 -1
  16. package/audio/source.d.ts.map +1 -1
  17. package/backend.d.ts +3 -2
  18. package/backend.d.ts.map +1 -1
  19. package/base64.d.ts +3 -0
  20. package/base64.d.ts.map +1 -0
  21. package/{broadcast-BImEYz4N.js → broadcast-CLjMphZS.js} +1652 -1432
  22. package/broadcast-CLjMphZS.js.map +1 -0
  23. package/broadcast.d.ts +8 -5
  24. package/broadcast.d.ts.map +1 -1
  25. package/chat/index.d.ts +1 -1
  26. package/chat/index.d.ts.map +1 -1
  27. package/chat/message.d.ts +1 -1
  28. package/chat/message.d.ts.map +1 -1
  29. package/chat/typing.d.ts +1 -1
  30. package/chat/typing.d.ts.map +1 -1
  31. package/dom-BsFrLb_F.js +35 -0
  32. package/dom-BsFrLb_F.js.map +1 -0
  33. package/element.d.ts +11 -4
  34. package/element.d.ts.map +1 -1
  35. package/element.js +65 -48
  36. package/element.js.map +1 -1
  37. package/index.d.ts +1 -1
  38. package/index.d.ts.map +1 -1
  39. package/index.js +37 -35
  40. package/index.js.map +1 -1
  41. package/libav-opus-af-BlMWboA7.js.map +1 -1
  42. package/location/index.d.ts +1 -1
  43. package/location/index.d.ts.map +1 -1
  44. package/location/peers.d.ts +1 -1
  45. package/location/peers.d.ts.map +1 -1
  46. package/location/window.d.ts +1 -1
  47. package/location/window.d.ts.map +1 -1
  48. package/main-DGBFe0O7.js.map +1 -1
  49. package/msf.d.ts.map +1 -1
  50. package/package.json +7 -8
  51. package/preview.d.ts +1 -1
  52. package/preview.d.ts.map +1 -1
  53. package/support/element.js +49 -60
  54. package/support/element.js.map +1 -1
  55. package/sync.d.ts +6 -6
  56. package/sync.d.ts.map +1 -1
  57. package/ui/components/buffer-control.d.ts +5 -0
  58. package/ui/components/buffer-control.d.ts.map +1 -0
  59. package/ui/components/buffering-indicator.d.ts +4 -0
  60. package/ui/components/buffering-indicator.d.ts.map +1 -0
  61. package/ui/components/fullscreen-button.d.ts +4 -0
  62. package/ui/components/fullscreen-button.d.ts.map +1 -0
  63. package/ui/components/offline-indicator.d.ts +4 -0
  64. package/ui/components/offline-indicator.d.ts.map +1 -0
  65. package/ui/components/play-pause.d.ts +4 -0
  66. package/ui/components/play-pause.d.ts.map +1 -0
  67. package/ui/components/quality-selector.d.ts +4 -0
  68. package/ui/components/quality-selector.d.ts.map +1 -0
  69. package/ui/components/stats-button.d.ts +3 -0
  70. package/ui/components/stats-button.d.ts.map +1 -0
  71. package/ui/components/volume-slider.d.ts +4 -0
  72. package/ui/components/volume-slider.d.ts.map +1 -0
  73. package/ui/components/watch-status-indicator.d.ts +4 -0
  74. package/ui/components/watch-status-indicator.d.ts.map +1 -0
  75. package/ui/element.d.ts +11 -4
  76. package/ui/element.d.ts.map +1 -1
  77. package/ui/element.js +382 -0
  78. package/ui/element.js.map +1 -0
  79. package/ui/icons.d.ts +16 -0
  80. package/ui/icons.d.ts.map +1 -0
  81. package/ui/stats.d.ts +4 -0
  82. package/ui/stats.d.ts.map +1 -0
  83. package/video/backend.d.ts +1 -1
  84. package/video/backend.d.ts.map +1 -1
  85. package/video/decoder.d.ts +1 -1
  86. package/video/decoder.d.ts.map +1 -1
  87. package/video/mse.d.ts +1 -1
  88. package/video/mse.d.ts.map +1 -1
  89. package/video/renderer.d.ts +1 -1
  90. package/video/renderer.d.ts.map +1 -1
  91. package/video/source.d.ts +2 -0
  92. package/video/source.d.ts.map +1 -1
  93. package/broadcast-BImEYz4N.js.map +0 -1
  94. package/ui/components/BufferControl.d.ts +0 -8
  95. package/ui/components/BufferControl.d.ts.map +0 -1
  96. package/ui/components/BufferingIndicator.d.ts +0 -2
  97. package/ui/components/BufferingIndicator.d.ts.map +0 -1
  98. package/ui/components/FullscreenButton.d.ts +0 -2
  99. package/ui/components/FullscreenButton.d.ts.map +0 -1
  100. package/ui/components/PlayPauseButton.d.ts +0 -2
  101. package/ui/components/PlayPauseButton.d.ts.map +0 -1
  102. package/ui/components/QualitySelector.d.ts +0 -3
  103. package/ui/components/QualitySelector.d.ts.map +0 -1
  104. package/ui/components/StatsButton.d.ts +0 -5
  105. package/ui/components/StatsButton.d.ts.map +0 -1
  106. package/ui/components/VolumeSlider.d.ts +0 -2
  107. package/ui/components/VolumeSlider.d.ts.map +0 -1
  108. package/ui/components/WatchControls.d.ts +0 -2
  109. package/ui/components/WatchControls.d.ts.map +0 -1
  110. package/ui/components/WatchStatusIndicator.d.ts +0 -2
  111. package/ui/components/WatchStatusIndicator.d.ts.map +0 -1
  112. package/ui/context.d.ts +0 -44
  113. package/ui/context.d.ts.map +0 -1
  114. package/ui/hooks/use-watch-ui.d.ts +0 -3
  115. package/ui/hooks/use-watch-ui.d.ts.map +0 -1
  116. package/ui/index.d.ts +0 -7
  117. package/ui/index.d.ts.map +0 -1
  118. package/ui/index.js +0 -1058
  119. package/ui/index.js.map +0 -1
package/ui/element.js ADDED
@@ -0,0 +1,382 @@
1
+ import { Signal as R, Effect as T } from "@moq/signals";
2
+ import { c as v, r as C } from "../dom-BsFrLb_F.js";
3
+ const j = 0, E = 10, P = 4e3, F = 48;
4
+ function S(o, t, e, r, a) {
5
+ const n = o.getContext("2d");
6
+ if (!n) return;
7
+ const i = window.devicePixelRatio || 1, s = o.getBoundingClientRect(), l = s.width, c = s.height, p = Math.round(l * i), d = Math.round(c * i);
8
+ if ((o.width !== p || o.height !== d) && (o.width = p, o.height = d), n.setTransform(i, 0, 0, i, 0, 0), n.clearRect(0, 0, l, c), e === void 0) return;
9
+ const h = 2, f = c - h * 2, b = 2;
10
+ for (let m = 0; m < t.length; m++) {
11
+ const x = t[m], A = x.start - e, k = x.end - e, y = Math.max(0, A), N = Math.min(k, r);
12
+ if (N <= y) continue;
13
+ const u = y / r * l, g = Math.max(2, (N - y) / r * l);
14
+ if (n.globalAlpha = 0.85, n.fillStyle = a ? "#f87171" : m > 0 ? "#facc15" : "#4ade80", typeof n.roundRect == "function" ? (n.beginPath(), n.roundRect(u, h, g, f, b), n.fill()) : n.fillRect(u, h, g, f), k > r) {
15
+ const w = ((k - r) / 1e3).toFixed(1);
16
+ n.globalAlpha = 0.7, n.fillStyle = "black", n.font = "500 9px system-ui, sans-serif", n.textAlign = "right", n.textBaseline = "middle", n.fillText(`+${w}s`, u + g - 4, c / 2);
17
+ }
18
+ }
19
+ }
20
+ function V(o, t, e = P) {
21
+ const r = document.createElement("div");
22
+ r.className = "buffer";
23
+ const a = document.createElement("div");
24
+ a.className = "buffer-visualization", a.setAttribute("role", "slider"), a.tabIndex = 0, a.setAttribute("aria-valuemin", j.toString()), a.setAttribute("aria-valuemax", e.toString()), a.setAttribute("aria-label", "Buffer jitter");
25
+ const n = document.createElement("div");
26
+ n.className = "buffer-playhead";
27
+ const i = document.createElement("div");
28
+ i.className = "buffer-track buffer-track--video";
29
+ const s = document.createElement("span");
30
+ s.className = "buffer-track-label", s.textContent = "Video";
31
+ const l = document.createElement("canvas");
32
+ l.className = "buffer-canvas", i.append(s, l);
33
+ const c = document.createElement("div");
34
+ c.className = "buffer-track buffer-track--audio";
35
+ const p = document.createElement("span");
36
+ p.className = "buffer-track-label", p.textContent = "Audio";
37
+ const d = document.createElement("canvas");
38
+ d.className = "buffer-canvas", c.append(p, d);
39
+ const h = document.createElement("div");
40
+ h.className = "buffer-target-area";
41
+ const f = document.createElement("div");
42
+ f.className = "buffer-target-line";
43
+ const b = document.createElement("span");
44
+ b.className = "buffer-target-label", f.appendChild(b), h.appendChild(f);
45
+ const m = document.createElement("span");
46
+ m.className = "buffer-help", m.textContent = "click to change latency", a.append(n, i, c, h, m), r.appendChild(a);
47
+ let x = !1, A = !1;
48
+ o.run((u) => {
49
+ const g = u.get(t.backend.jitter), w = g / e * 100;
50
+ f.style.left = `${w}%`, b.textContent = `${Math.round(g)}ms`, a.setAttribute("aria-valuenow", g.toString());
51
+ });
52
+ const k = (u) => {
53
+ const g = a.getBoundingClientRect(), w = g.width - F, H = Math.max(0, Math.min(u - g.left - F, w)) / w * e, I = Math.round(H / E) * E, W = Math.max(j, Math.min(e, I));
54
+ t.backend.latency.set(W);
55
+ }, y = () => {
56
+ A || (A = !0, m.style.display = "none");
57
+ };
58
+ o.event(a, "mousedown", (u) => {
59
+ x = !0, a.classList.add("buffer-visualization--dragging"), y(), k(u.clientX);
60
+ }), o.event(document, "mousemove", (u) => {
61
+ x && k(u.clientX);
62
+ }), o.event(document, "mouseup", () => {
63
+ x && (x = !1, a.classList.remove("buffer-visualization--dragging"));
64
+ }), o.event(a, "keydown", (u) => {
65
+ let g = 0;
66
+ if (u.key === "ArrowRight" || u.key === "ArrowUp")
67
+ g = E;
68
+ else if (u.key === "ArrowLeft" || u.key === "ArrowDown")
69
+ g = -E;
70
+ else
71
+ return;
72
+ u.preventDefault(), y();
73
+ const w = t.backend.jitter.peek(), L = Math.max(j, Math.min(e, w + g));
74
+ t.backend.latency.set(L);
75
+ });
76
+ const N = () => {
77
+ const u = t.backend.sync.now(), g = t.backend.video.stalled.peek();
78
+ S(l, t.backend.video.buffered.peek(), u, e, g), S(d, t.backend.audio.buffered.peek(), u, e, g), o.animate(N);
79
+ };
80
+ return o.animate(N), r;
81
+ }
82
+ function O(o, t) {
83
+ const e = document.createElement("div");
84
+ e.className = "buffering";
85
+ const r = document.createElement("div");
86
+ return r.className = "buffering-spinner", e.appendChild(r), o.run((a) => {
87
+ const n = a.get(t.backend.video.stalled), i = a.get(t.broadcast.status) === "offline";
88
+ e.style.display = n && !i ? "" : "none";
89
+ }), e;
90
+ }
91
+ const U = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
92
+ <path d="M9 18V5l12-2v13" />
93
+ <circle cx="6" cy="18" r="3" />
94
+ <circle cx="18" cy="16" r="3" />
95
+ </svg>`, D = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
96
+ <path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" />
97
+ </svg>`, _ = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
98
+ <path d="M8 3H5a2 2 0 0 0-2 2v3" />
99
+ <path d="M21 8V5a2 2 0 0 0-2-2h-3" />
100
+ <path d="M3 16v3a2 2 0 0 0 2 2h3" />
101
+ <path d="M16 21h3a2 2 0 0 0 2-2v-3" />
102
+ </svg>`, X = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
103
+ <path d="M8 3v3a2 2 0 0 1-2 2H3" />
104
+ <path d="M21 8h-3a2 2 0 0 1-2-2V3" />
105
+ <path d="M3 16h3a2 2 0 0 1 2 2v3" />
106
+ <path d="M16 21v-3a2 2 0 0 1 2-2h3" />
107
+ </svg>`, G = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
108
+ <path d="M16 9a5 5 0 0 1 .95 2.293" />
109
+ <path d="M19.364 5.636a9 9 0 0 1 1.889 9.96" />
110
+ <path d="m2 2 20 20" />
111
+ <path d="m7 7-.587.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298V11" />
112
+ <path d="M9.828 4.172A.686.686 0 0 1 11 4.657v.686" />
113
+ </svg>`, Q = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
114
+ <path d="M16.247 7.761a6 6 0 0 1 0 8.478" />
115
+ <path d="M19.075 4.933a10 10 0 0 1 0 14.134" />
116
+ <path d="M4.925 19.067a10 10 0 0 1 0-14.134" />
117
+ <path d="M7.753 16.239a6 6 0 0 1 0-8.478" />
118
+ <circle cx="12" cy="12" r="2" />
119
+ </svg>`, J = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
120
+ <rect x="14" y="3" width="5" height="18" rx="1" />
121
+ <rect x="5" y="3" width="5" height="18" rx="1" />
122
+ </svg>`, K = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
123
+ <path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" />
124
+ </svg>`, Y = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
125
+ <path d="M5 21v-6" />
126
+ <path d="M12 21V3" />
127
+ <path d="M19 21V9" />
128
+ </svg>`, Z = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
129
+ <path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
130
+ <rect x="2" y="6" width="14" height="12" rx="2" />
131
+ </svg>`, tt = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
132
+ <path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" />
133
+ <path d="M16 9a5 5 0 0 1 0 6" />
134
+ <path d="M19.364 18.364a9 9 0 0 0 0-12.728" />
135
+ </svg>`, et = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
136
+ <path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" />
137
+ </svg>`, at = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="var(--color-white)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
138
+ <path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" />
139
+ <path d="M16 9a5 5 0 0 1 0 6" />
140
+ </svg>`;
141
+ function M(o) {
142
+ const t = document.createElement("span");
143
+ return t.className = "flex-center", t.setAttribute("role", "img"), t.setAttribute("aria-hidden", "true"), t.innerHTML = o, t;
144
+ }
145
+ function ot(o, t) {
146
+ const e = document.createElement("button");
147
+ e.type = "button", e.className = "button flex-center", e.title = "Fullscreen", e.setAttribute("aria-label", "Fullscreen"), e.replaceChildren(M(_));
148
+ const r = () => {
149
+ const a = !!document.fullscreenElement;
150
+ e.replaceChildren(M(a ? X : _));
151
+ };
152
+ return o.event(document, "fullscreenchange", r), o.event(e, "click", () => {
153
+ document.fullscreenElement ? document.exitFullscreen() : t.requestFullscreen();
154
+ }), e;
155
+ }
156
+ function nt(o, t) {
157
+ const e = document.createElement("div");
158
+ e.className = "watch-ui__offline-indicator", e.setAttribute("role", "status"), e.setAttribute("aria-live", "polite");
159
+ const r = document.createElement("span");
160
+ return r.className = "watch-ui__offline-text", r.textContent = "OFFLINE", e.appendChild(r), o.run((a) => {
161
+ const n = a.get(t.broadcast.status) === "offline";
162
+ e.style.display = n ? "" : "none";
163
+ }), e;
164
+ }
165
+ function rt(o, t) {
166
+ const e = document.createElement("button");
167
+ return e.type = "button", e.className = "button button--playback flex-center", o.run((r) => {
168
+ const a = r.get(t.backend.paused);
169
+ e.title = a ? "Play" : "Pause", e.setAttribute("aria-label", a ? "Play" : "Pause"), e.replaceChildren(M(a ? K : J));
170
+ }), o.event(e, "click", () => {
171
+ t.paused = !t.paused;
172
+ }), e;
173
+ }
174
+ function it(o) {
175
+ return o >= 1e6 ? `${(o / 1e6).toFixed(1)} Mbps` : o >= 1e3 ? `${(o / 1e3).toFixed(0)} kbps` : `${o} bps`;
176
+ }
177
+ function st(o, t) {
178
+ const e = document.createElement("div");
179
+ e.className = "quality-selector";
180
+ const r = document.createElement("label");
181
+ r.htmlFor = "moq-watch-quality-select", r.className = "quality-label", r.textContent = "Quality: ";
182
+ const a = document.createElement("select");
183
+ return a.id = "moq-watch-quality-select", a.className = "quality-select", e.append(r, a), o.run((n) => {
184
+ const i = n.get(t.backend.video.source.catalog), s = n.get(t.backend.video.source.track), l = i?.renditions ?? {};
185
+ a.replaceChildren();
186
+ const c = document.createElement("option");
187
+ c.value = "", c.textContent = "Auto", a.appendChild(c);
188
+ for (const [p, d] of Object.entries(l)) {
189
+ const h = document.createElement("option");
190
+ h.value = p;
191
+ const f = d.codedWidth && d.codedHeight ? ` (${d.codedWidth}x${d.codedHeight})` : "", b = d.bitrate ? ` ${it(d.bitrate)}` : "";
192
+ h.textContent = `${p}${f}${b}`, a.appendChild(h);
193
+ }
194
+ a.value = s ?? "";
195
+ }), o.event(a, "change", () => {
196
+ const n = a.value || void 0;
197
+ t.backend.video.source.target.update((i) => ({ ...i, name: n }));
198
+ }), e;
199
+ }
200
+ function lt(o, t) {
201
+ const e = document.createElement("button");
202
+ return e.type = "button", e.className = "button flex-center", e.replaceChildren(M(Y)), o.run((r) => {
203
+ const a = r.get(t);
204
+ e.title = a ? "Hide stats" : "Show stats", e.setAttribute("aria-label", e.title);
205
+ }), o.event(e, "click", () => {
206
+ t.update((r) => !r);
207
+ }), e;
208
+ }
209
+ function ct(o, t) {
210
+ return t || o === 0 ? G : o <= 33 ? et : o <= 66 ? at : tt;
211
+ }
212
+ function dt(o, t) {
213
+ const e = document.createElement("div");
214
+ e.className = "volume-slider flex-center";
215
+ const r = document.createElement("button");
216
+ r.type = "button", r.className = "button flex-center";
217
+ const a = document.createElement("input");
218
+ a.type = "range", a.min = "0", a.max = "100";
219
+ const n = document.createElement("span");
220
+ return n.className = "volume-label", e.append(r, a, n), o.run((i) => {
221
+ const s = i.get(t.backend.audio.volume), l = i.get(t.backend.audio.muted), c = Math.round(s * 100);
222
+ a.value = c.toString(), n.textContent = c.toString(), r.title = l ? "Unmute" : "Mute", r.setAttribute("aria-label", r.title), r.replaceChildren(M(ct(c, l)));
223
+ }), o.event(a, "input", () => {
224
+ t.backend.audio.volume.set(Number.parseFloat(a.value) / 100);
225
+ }), o.event(r, "click", () => {
226
+ t.backend.audio.muted.update((i) => !i);
227
+ }), e;
228
+ }
229
+ function ut(o, t, e) {
230
+ return o ? t === "disconnected" ? { variant: "error", text: "Disconnected" } : t === "connecting" ? { variant: "connecting", text: "Connecting..." } : e === "offline" ? { variant: "error", text: "Offline" } : e === "loading" ? { variant: "loading", text: "Loading..." } : e === "live" ? { variant: "live", text: "Live" } : { variant: "connected", text: "Connected" } : { variant: "error", text: "No URL" };
231
+ }
232
+ function pt(o, t) {
233
+ const e = document.createElement("div");
234
+ e.className = "status-indicator flex-center";
235
+ const r = document.createElement("span"), a = document.createElement("span");
236
+ return e.append(r, a), o.run((n) => {
237
+ const i = n.get(t.connection.url), s = n.get(t.connection.status), l = n.get(t.broadcast.status), { variant: c, text: p } = ut(i, s, l);
238
+ r.className = `status-indicator-dot status-indicator-dot--${c}`, a.className = `status-indicator-text status-indicator-text--${c}`, a.textContent = p;
239
+ }), e;
240
+ }
241
+ const $ = 250;
242
+ function z(o, t, e) {
243
+ const r = v("div", { className: `stats-item stats-item--${o}` }), a = v("div", { className: "stats-icon-wrapper" });
244
+ a.appendChild(M(e));
245
+ const n = v("span", { className: "stats-item-title" }, t), i = v("span", { className: "stats-item-data" }, "N/A"), s = v("div", { className: "stats-item-detail" });
246
+ return s.append(n, i), r.append(a, s), { el: r, data: i };
247
+ }
248
+ function B(o) {
249
+ return o >= 1e6 ? `${(o / 1e6).toFixed(1)}Mbps` : o >= 1e3 ? `${(o / 1e3).toFixed(0)}kbps` : `${o.toFixed(0)}bps`;
250
+ }
251
+ function q(o, t) {
252
+ if (o === void 0 || o <= 0) return null;
253
+ const e = t === "down" ? "↓" : "↑";
254
+ return o >= 1e9 ? `${e} ${(o / 1e9).toFixed(1)}Gbps` : `${e} ${B(o)}`;
255
+ }
256
+ function ht(o, t) {
257
+ const { el: e, data: r } = z("network", "network", Q);
258
+ return o.interval(() => {
259
+ const a = t.connection.established.peek();
260
+ if (!a) {
261
+ r.textContent = "N/A";
262
+ return;
263
+ }
264
+ const n = a.rtt?.peek(), i = [
265
+ q(a.recvBandwidth?.peek(), "down"),
266
+ q(a.sendBandwidth?.peek(), "up"),
267
+ n !== void 0 && n > 0 ? `${n.toFixed(0)}ms` : null
268
+ ].filter((s) => s !== null);
269
+ r.textContent = i.length > 0 ? i.join(`
270
+ `) : "N/A";
271
+ }, $), e;
272
+ }
273
+ function gt(o, t) {
274
+ const { el: e, data: r } = z("video", "video", Z);
275
+ let a = 0, n = 0, i = performance.now();
276
+ return o.interval(() => {
277
+ const s = t.backend.video.source.catalog.peek(), l = t.backend.video.stats.peek(), c = performance.now(), p = c - i;
278
+ let d;
279
+ if (l && a > 0 && p > 0) {
280
+ const m = l.frameCount - a;
281
+ m > 0 && (d = m / (p / 1e3));
282
+ }
283
+ let h;
284
+ if (l && n > 0 && p > 0) {
285
+ const m = l.bytesReceived - n;
286
+ m > 0 && (h = B(m * 8 * (1e3 / p)));
287
+ }
288
+ l && (a = l.frameCount, n = l.bytesReceived, i = c);
289
+ const { width: f, height: b } = s?.display ?? {};
290
+ r.textContent = [
291
+ f && b ? `${f}x${b}` : "N/A",
292
+ d !== void 0 ? `@${d.toFixed(1)} fps` : "N/A",
293
+ h ?? "N/A"
294
+ ].join(`
295
+ `);
296
+ }, $), e;
297
+ }
298
+ function ft(o, t) {
299
+ const { el: e, data: r } = z("audio", "audio", U);
300
+ let a = 0, n = performance.now();
301
+ return o.interval(() => {
302
+ const i = t.backend.audio.source.track.peek(), s = t.backend.audio.source.config.peek(), l = t.backend.audio.stats.peek();
303
+ if (!i || !s) {
304
+ r.textContent = "N/A";
305
+ return;
306
+ }
307
+ const c = performance.now();
308
+ let p;
309
+ if (l && a > 0) {
310
+ const h = l.bytesReceived - a, f = c - n;
311
+ h > 0 && f > 0 && (p = B(h * 8 * (1e3 / f)));
312
+ }
313
+ l && (a = l.bytesReceived, n = c);
314
+ const d = [];
315
+ s.sampleRate && d.push(`${(s.sampleRate / 1e3).toFixed(1)}kHz`), s.numberOfChannels && d.push(`${s.numberOfChannels}ch`), d.push(p ?? "N/A"), s.codec && d.push(s.codec), r.textContent = d.length > 0 ? d.join(`
316
+ `) : "N/A";
317
+ }, $), e;
318
+ }
319
+ function mt(o, t) {
320
+ const { el: e, data: r } = z("buffer", "buffer", D);
321
+ return o.run((a) => {
322
+ const n = a.get(t.backend.jitter);
323
+ r.textContent = `${Math.round(n)}ms`;
324
+ }), e;
325
+ }
326
+ function vt(o, t, e) {
327
+ const r = v("div", { className: "stats" }), a = v("div", { className: "stats-panel" });
328
+ return r.appendChild(a), o.run((n) => {
329
+ const i = n.get(e);
330
+ r.style.display = i ? "" : "none", i && (C(n, a, ht(n, t)), C(n, a, gt(n, t)), C(n, a, ft(n, t)), C(n, a, mt(n, t)));
331
+ }), r;
332
+ }
333
+ const bt = ':host{--color-white: #ffffff;--color-white-alpha-05: rgba(255, 255, 255, .05);--color-white-alpha-10: rgba(255, 255, 255, .1);--color-white-alpha-15: rgba(255, 255, 255, .15);--color-white-alpha-20: rgba(255, 255, 255, .2);--color-white-alpha-25: rgba(255, 255, 255, .25);--color-white-alpha-30: rgba(255, 255, 255, .3);--color-white-alpha-40: rgba(255, 255, 255, .4);--color-white-alpha-70: rgba(255, 255, 255, .7);--color-white-alpha-90: rgba(255, 255, 255, .9);--color-gray-100: #9ca3af;--color-gray-950: #1a1a1a;--color-blue-400: #00dfff;--color-blue-500: #52a1ff;--color-blue-900: #1b3243;--color-purple-400: #a855f7;--color-purple-900: #31144a;--color-green-400: #65ee2e;--color-green-900: #3d4928;--color-yellow-400: #facc15;--color-orange-400: #ff9900;--color-orange-450: #fb923c;--color-orange-500: #f97316;--color-orange-900: #4d301b;--color-amber-600: #d97706;--color-orange-500-alpha-60: rgba(249, 115, 22, .6);--color-orange-500-alpha-80: rgba(249, 115, 22, .8);--color-black-alpha-08: rgba(0, 0, 0, .08);--color-black-alpha-10: rgba(0, 0, 0, .1);--color-black-alpha-40: rgba(0, 0, 0, .4);--color-black-alpha-85: rgba(0, 0, 0, .85);--spacing-1: .0625rem;--spacing-2: .125rem;--spacing-4: .25rem;--spacing-8: .5rem;--spacing-12: .75rem;--spacing-16: 1rem;--spacing-24: 1.5rem;--spacing-32: 2rem;--spacing-48: 3rem;--border-radius-sm: .5rem;--border-radius-md: .75rem;--border-radius-lg: 1rem}.flex-center{display:flex;justify-content:center;align-items:center}.flex-align-center{align-items:center}.button{color:var(--color-white);cursor:pointer;background-color:var(--color-white-alpha-10);border:var(--spacing-1) solid var(--color-white-alpha-20);border-radius:50%;width:2.25rem;height:2.25rem;transform-origin:center;transition:transform .1s cubic-bezier(.4,0,.2,1)}.button:hover:not(:disabled){transform:scale(1.02);background-color:var(--color-white-alpha-20);box-shadow:0 var(--spacing-2) var(--spacing-8) var(--color-black-alpha-10)}.button:active:not(:disabled){transform:scale(.98);box-shadow:0 var(--spacing-1) var(--spacing-4) var(--color-black-alpha-08)}.button:disabled{opacity:.5;cursor:default;color:var(--color-gray-100)}.stats{display:flex;align-items:center;justify-content:center;position:absolute;inset:0;pointer-events:none;z-index:2;width:100%;height:inherit;container-type:inline-size}.stats .stats-panel{position:absolute;display:flex;align-items:flex-start;justify-content:center;flex-direction:row;flex-wrap:wrap;gap:var(--spacing-12);max-width:240px;width:100%;background-color:var(--color-black-alpha-85);border:1px solid var(--color-white-alpha-25);border-radius:8px;padding:var(--spacing-12);pointer-events:auto;top:8px}@container (width > 480px) and (width <= 768px){.stats .stats-panel{max-width:360px;padding:var(--spacing-24)}}@container (width > 768px){.stats .stats-panel{max-width:600px;padding:var(--spacing-24)}}.stats .stats-panel .stats-item{display:flex;align-items:flex-start;justify-content:flex-start;flex-direction:row;height:auto;column-gap:var(--spacing-8);flex-basis:calc(50% - var(--spacing-12));flex-grow:1;flex-shrink:1}@container (width > 480px){.stats .stats-panel .stats-item{flex-basis:calc(25% - var(--spacing-12))}}.stats .stats-panel .stats-item .stats-icon-wrapper{display:none;padding:var(--spacing-8);border-radius:8px;width:fit-content;height:fit-content;flex-shrink:0}@container (width > 480px){.stats .stats-panel .stats-item .stats-icon-wrapper{width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-direction:row}}.stats .stats-panel .stats-item .stats-icon-wrapper svg{width:100%;height:100%}.stats .stats-panel .stats-item.stats-item--network .stats-icon-wrapper{background-color:var(--color-blue-900)}.stats .stats-panel .stats-item.stats-item--network,.stats .stats-panel .stats-item.stats-item--network .stats-item-data{color:var(--color-blue-400)}.stats .stats-panel .stats-item.stats-item--video .stats-icon-wrapper{background-color:var(--color-purple-900)}.stats .stats-panel .stats-item.stats-item--video,.stats .stats-panel .stats-item.stats-item--video .stats-item-data{color:var(--color-purple-400)}.stats .stats-panel .stats-item.stats-item--audio .stats-icon-wrapper{background-color:var(--color-green-900)}.stats .stats-panel .stats-item.stats-item--audio,.stats .stats-panel .stats-item.stats-item--audio .stats-item-data{color:var(--color-green-400)}.stats .stats-panel .stats-item.stats-item--buffer .stats-icon-wrapper{background-color:var(--color-orange-900)}.stats .stats-panel .stats-item.stats-item--buffer,.stats .stats-panel .stats-item.stats-item--buffer .stats-item-data{color:var(--color-orange-400)}.stats .stats-panel .stats-item .stats-item-detail{display:flex;align-items:flex-start;justify-content:space-evenly;flex-direction:column;gap:var(--spacing-4)}.stats .stats-panel .stats-item .stats-item-detail .stats-item-title{font-size:12px;font-weight:500;text-transform:capitalize;color:var(--color-gray-100);font-family:Segoe UI,Roboto,sans-serif;letter-spacing:1px;line-height:12px}.stats .stats-panel .stats-item .stats-item-detail .stats-item-data{font-size:16px;font-weight:700;font-family:Segoe UI,Roboto,sans-serif;white-space:pre-wrap;line-height:1.5}.video-container{display:block;position:relative;width:100%;height:auto;border-radius:var(--spacing-4);margin:0 auto;pointer-events:none}.controls{display:flex;flex-direction:column;gap:var(--spacing-24)}.button.button--playback{width:3rem;height:3rem;background:linear-gradient(to bottom right,var(--color-orange-400),var(--color-orange-500),var(--color-amber-600));box-shadow:0 0 var(--spacing-16) var(--color-orange-500-alpha-60);transition:box-shadow .15s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}.button.button--playback:hover:not(:disabled){box-shadow:0 0 var(--spacing-24) var(--color-orange-500-alpha-80);transform:scale(1.02)}.button.button--playback:active:not(:disabled){transform:scale(.98)}.button.button--playback:before{content:"";position:absolute;inset:0;background:linear-gradient(to bottom right,var(--color-white-alpha-30),transparent);opacity:0;transition:opacity .15s cubic-bezier(.4,0,.2,1);pointer-events:none}.button.button--playback:hover:before{opacity:1}.button.button--playback:after{content:"";position:absolute;inset:0;background-color:var(--color-white-alpha-20);border-radius:50%;filter:blur(var(--spacing-24));pointer-events:none}.playback-controls{display:flex;place-content:center space-around;gap:var(--spacing-8);padding:var(--spacing-8) 0}.latency-controls{display:flex;flex-direction:column;gap:var(--spacing-16)}.volume-slider{gap:var(--spacing-4)}.volume-label{width:var(--spacing-16);text-align:right}.quality-selector{display:flex;align-items:center;gap:var(--spacing-8);background:transparent;border-radius:var(--spacing-8)}.quality-label{font-size:1.25rem;font-weight:500;color:var(--color-white);white-space:nowrap}.quality-select{flex:0 1 auto;font-size:1rem;padding:var(--spacing-8);border-radius:var(--spacing-4);background:var(--color-white-alpha-10);color:var(--color-white);border:var(--spacing-1) solid var(--color-white-alpha-20);cursor:pointer;transition:background .2s ease}.quality-select:hover{background:var(--color-white-alpha-15)}.quality-select:focus{outline:var(--spacing-2) solid var(--color-white-alpha-30);outline-offset:var(--spacing-2)}.quality-select option{background:var(--color-gray-950);color:var(--color-white)}.buffer{width:100%}.buffer-visualization{position:relative;width:100%;height:3.75rem;background:var(--color-white-alpha-10);border-radius:var(--spacing-4);cursor:pointer;user-select:none;padding-left:var(--spacing-48);box-sizing:border-box}.buffer-playhead{position:absolute;left:var(--spacing-48);top:0;bottom:0;width:var(--spacing-2);background:var(--color-white);z-index:3}.buffer-track{display:flex;align-items:center;position:absolute;left:var(--spacing-48);right:0;height:var(--spacing-24)}.buffer-track--video{top:var(--spacing-4)}.buffer-track--audio{top:var(--spacing-32)}.buffer-track-label{position:absolute;left:-2.875rem;width:2.5rem;font-size:.625rem;color:var(--color-white-alpha-70);text-align:right;padding-right:var(--spacing-4);pointer-events:none;box-sizing:border-box}.buffer-canvas{position:absolute;top:0;left:0;width:100%;height:100%}.buffer-target-area{position:absolute;top:0;bottom:0;left:var(--spacing-48);right:0;pointer-events:none}.buffer-target-line{position:absolute;top:0;bottom:0;width:.1875rem;background:var(--color-orange-400, var(--color-orange-450));z-index:2;cursor:ew-resize;border-radius:var(--spacing-1);pointer-events:auto}.buffer-target-label{position:absolute;bottom:100%;left:50%;transform:translate(-50%);font-size:.6875rem;font-weight:500;color:var(--color-orange-400, var(--color-orange-450));white-space:nowrap;margin-bottom:var(--spacing-2);opacity:0;transition:opacity .15s ease;pointer-events:none}.buffer-visualization:hover .buffer-target-label,.buffer-visualization--dragging .buffer-target-label{opacity:1}.buffer-help{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:.75rem;color:var(--color-white-alpha-30, rgba(255, 255, 255, .3));pointer-events:none;white-space:nowrap}.buffering{position:absolute;display:flex;align-items:center;justify-content:center;width:100%;height:100%;top:0;left:0;z-index:1;background-color:var(--color-black-alpha-40);backdrop-filter:blur(var(--spacing-2));pointer-events:auto}.buffering-spinner{width:2.5rem;height:2.5rem;border:var(--spacing-4) solid var(--color-white-alpha-20);border-top:var(--spacing-4) solid var(--color-white);border-radius:50%;animation:buffer-spin 1s linear infinite}@keyframes buffer-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.watch-ui__offline-indicator{position:absolute;bottom:1rem;right:1rem;padding:.5rem 1rem;display:flex;align-items:center;justify-content:center;background-color:#000;z-index:4;pointer-events:auto;border-radius:.25rem}.watch-ui__offline-text{font-size:.875rem;font-weight:700;color:#fff;letter-spacing:.08em;text-shadow:none}@media(max-width:768px){.watch-ui__offline-indicator{bottom:.75rem;right:.75rem;padding:.375rem .75rem}.watch-ui__offline-text{font-size:.75rem}}@media(max-width:480px){.watch-ui__offline-indicator{bottom:.5rem;right:.5rem;padding:.25rem .5rem}.watch-ui__offline-text{font-size:.75rem}}.status-indicator{gap:var(--spacing-12);backdrop-filter:blur(var(--spacing-16))}.status-indicator-dot{position:relative;width:.75rem;height:.75rem;border-radius:50%;flex-shrink:0;z-index:1}.status-indicator-dot:after{content:"";position:absolute;inset:0;border-radius:50%;background:currentcolor;transform:scale(1);opacity:.5;animation:status-pulse 1.5s ease-out infinite;z-index:-1}.status-indicator-dot:after{display:none}.status-indicator-dot--live:after,.status-indicator-dot--connected:after,.status-indicator-dot--loading:after,.status-indicator-dot--connecting:after{display:block}.status-indicator-dot--error{background:var(--color-gray-100);color:var(--color-gray-100)}.status-indicator-text--error{color:var(--color-gray-100)}.status-indicator-dot--connecting{background:var(--color-yellow-400);color:var(--color-yellow-400)}.status-indicator-text--connecting{color:var(--color-yellow-400)}.status-indicator-dot--loading{background:var(--color-yellow-400);color:var(--color-yellow-400)}.status-indicator-text--loading{color:var(--color-yellow-400)}.status-indicator-dot--live{background:var(--color-green-400);color:var(--color-green-400)}.status-indicator-text--live{color:var(--color-green-400)}.status-indicator-dot--connected{background:var(--color-blue-500);color:var(--color-blue-500)}.status-indicator-text--connected{color:var(--color-blue-500)}.status-indicator-text{font-size:1rem;letter-spacing:.1rem}@keyframes status-pulse{0%{transform:scale(1);opacity:.5}70%{opacity:.25}to{transform:scale(2.5);opacity:0}}';
334
+ class wt extends HTMLElement {
335
+ #e;
336
+ #t;
337
+ #o = new R(void 0);
338
+ #a;
339
+ constructor() {
340
+ super(), this.#t = this.attachShadow({ mode: "open" });
341
+ const t = document.createElement("style");
342
+ t.textContent = bt, this.#t.appendChild(t), this.#a = new MutationObserver(() => this.#n());
343
+ }
344
+ connectedCallback() {
345
+ this.#n(), this.#a.observe(this, { childList: !0 });
346
+ const t = new T();
347
+ this.#e = t, t.run(this.#r.bind(this));
348
+ }
349
+ disconnectedCallback() {
350
+ this.#a.disconnect(), this.#e?.close(), this.#e = void 0;
351
+ }
352
+ #n() {
353
+ const t = this.querySelector("moq-watch");
354
+ this.#o.set(t ?? void 0);
355
+ }
356
+ #r(t) {
357
+ const e = t.get(this.#o);
358
+ if (!e) return;
359
+ const r = new R(!1), a = v("div", { className: "video-container" });
360
+ a.append(
361
+ v("slot"),
362
+ vt(t, e, r),
363
+ O(t, e),
364
+ nt(t, e)
365
+ );
366
+ const n = v("div", { className: "controls" }), i = v("div", { className: "playback-controls flex-align-center" });
367
+ i.append(
368
+ rt(t, e),
369
+ dt(t, e),
370
+ pt(t, e),
371
+ lt(t, r),
372
+ ot(t, e)
373
+ );
374
+ const s = v("div", { className: "latency-controls" });
375
+ s.append(V(t, e), st(t, e)), n.append(i, s), C(t, this.#t, a), C(t, this.#t, n);
376
+ }
377
+ }
378
+ customElements.define("moq-watch-ui", wt);
379
+ export {
380
+ wt as default
381
+ };
382
+ //# sourceMappingURL=element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.js","sources":["../../src/ui/components/buffer-control.ts","../../src/ui/components/buffering-indicator.ts","../../src/ui/icons/audio.svg?raw","../../src/ui/icons/buffer.svg?raw","../../src/ui/icons/fullscreen-enter.svg?raw","../../src/ui/icons/fullscreen-exit.svg?raw","../../src/ui/icons/mute.svg?raw","../../src/ui/icons/network.svg?raw","../../src/ui/icons/pause.svg?raw","../../src/ui/icons/play.svg?raw","../../src/ui/icons/stats.svg?raw","../../src/ui/icons/video.svg?raw","../../src/ui/icons/volume-high.svg?raw","../../src/ui/icons/volume-low.svg?raw","../../src/ui/icons/volume-medium.svg?raw","../../src/ui/icons.ts","../../src/ui/components/fullscreen-button.ts","../../src/ui/components/offline-indicator.ts","../../src/ui/components/play-pause.ts","../../src/ui/components/quality-selector.ts","../../src/ui/components/stats-button.ts","../../src/ui/components/volume-slider.ts","../../src/ui/components/watch-status-indicator.ts","../../src/ui/stats.ts","../../src/ui/element.ts"],"sourcesContent":["import type { Moq } from \"@moq/hang\";\nimport type { Effect } from \"@moq/signals\";\nimport type { BufferedRanges } from \"../..\";\nimport type MoqWatch from \"../../element\";\n\nconst MIN_RANGE = 0 as Moq.Time.Milli;\nconst RANGE_STEP = 10 as Moq.Time.Milli;\nconst DEFAULT_MAX = 4000 as Moq.Time.Milli;\nconst LABEL_WIDTH = 48;\n\nfunction drawRanges(\n\tcanvas: HTMLCanvasElement,\n\tranges: BufferedRanges,\n\ttimestamp: Moq.Time.Milli | undefined,\n\tmax: Moq.Time.Milli,\n\tisBuffering: boolean,\n) {\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return;\n\n\tconst dpr = window.devicePixelRatio || 1;\n\tconst rect = canvas.getBoundingClientRect();\n\tconst width = rect.width;\n\tconst height = rect.height;\n\n\tconst canvasW = Math.round(width * dpr);\n\tconst canvasH = Math.round(height * dpr);\n\tif (canvas.width !== canvasW || canvas.height !== canvasH) {\n\t\tcanvas.width = canvasW;\n\t\tcanvas.height = canvasH;\n\t}\n\n\tctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n\tctx.clearRect(0, 0, width, height);\n\n\tif (timestamp === undefined) return;\n\n\tconst padding = 2;\n\tconst rangeHeight = height - padding * 2;\n\tconst radius = 2;\n\n\tfor (let i = 0; i < ranges.length; i++) {\n\t\tconst range = ranges[i];\n\t\tconst startMs = (range.start - timestamp) as Moq.Time.Milli;\n\t\tconst endMs = (range.end - timestamp) as Moq.Time.Milli;\n\t\tconst visibleStart = Math.max(0, startMs);\n\t\tconst visibleEnd = Math.min(endMs, max);\n\n\t\tif (visibleEnd <= visibleStart) continue;\n\n\t\tconst x = (visibleStart / max) * width;\n\t\tconst w = Math.max(2, ((visibleEnd - visibleStart) / max) * width);\n\n\t\tctx.globalAlpha = 0.85;\n\t\tctx.fillStyle = isBuffering ? \"#f87171\" : i > 0 ? \"#facc15\" : \"#4ade80\";\n\n\t\tif (typeof ctx.roundRect === \"function\") {\n\t\t\tctx.beginPath();\n\t\t\tctx.roundRect(x, padding, w, rangeHeight, radius);\n\t\t\tctx.fill();\n\t\t} else {\n\t\t\tctx.fillRect(x, padding, w, rangeHeight);\n\t\t}\n\n\t\tif (endMs > max) {\n\t\t\tconst overflowSec = ((endMs - max) / 1000).toFixed(1);\n\t\t\tctx.globalAlpha = 0.7;\n\t\t\tctx.fillStyle = \"black\";\n\t\t\tctx.font = \"500 9px system-ui, sans-serif\";\n\t\t\tctx.textAlign = \"right\";\n\t\t\tctx.textBaseline = \"middle\";\n\t\t\tctx.fillText(`+${overflowSec}s`, x + w - 4, height / 2);\n\t\t}\n\t}\n}\n\nexport function bufferControl(parent: Effect, watch: MoqWatch, max: Moq.Time.Milli = DEFAULT_MAX): HTMLElement {\n\tconst wrapper = document.createElement(\"div\");\n\twrapper.className = \"buffer\";\n\n\tconst viz = document.createElement(\"div\");\n\tviz.className = \"buffer-visualization\";\n\tviz.setAttribute(\"role\", \"slider\");\n\tviz.tabIndex = 0;\n\tviz.setAttribute(\"aria-valuemin\", MIN_RANGE.toString());\n\tviz.setAttribute(\"aria-valuemax\", max.toString());\n\tviz.setAttribute(\"aria-label\", \"Buffer jitter\");\n\n\tconst playhead = document.createElement(\"div\");\n\tplayhead.className = \"buffer-playhead\";\n\n\tconst videoTrack = document.createElement(\"div\");\n\tvideoTrack.className = \"buffer-track buffer-track--video\";\n\tconst videoLabel = document.createElement(\"span\");\n\tvideoLabel.className = \"buffer-track-label\";\n\tvideoLabel.textContent = \"Video\";\n\tconst videoCanvas = document.createElement(\"canvas\");\n\tvideoCanvas.className = \"buffer-canvas\";\n\tvideoTrack.append(videoLabel, videoCanvas);\n\n\tconst audioTrack = document.createElement(\"div\");\n\taudioTrack.className = \"buffer-track buffer-track--audio\";\n\tconst audioLabel = document.createElement(\"span\");\n\taudioLabel.className = \"buffer-track-label\";\n\taudioLabel.textContent = \"Audio\";\n\tconst audioCanvas = document.createElement(\"canvas\");\n\taudioCanvas.className = \"buffer-canvas\";\n\taudioTrack.append(audioLabel, audioCanvas);\n\n\tconst targetArea = document.createElement(\"div\");\n\ttargetArea.className = \"buffer-target-area\";\n\tconst targetLine = document.createElement(\"div\");\n\ttargetLine.className = \"buffer-target-line\";\n\tconst targetLabel = document.createElement(\"span\");\n\ttargetLabel.className = \"buffer-target-label\";\n\ttargetLine.appendChild(targetLabel);\n\ttargetArea.appendChild(targetLine);\n\n\tconst help = document.createElement(\"span\");\n\thelp.className = \"buffer-help\";\n\thelp.textContent = \"click to change latency\";\n\n\tviz.append(playhead, videoTrack, audioTrack, targetArea, help);\n\twrapper.appendChild(viz);\n\n\tlet dragging = false;\n\tlet hasInteracted = false;\n\n\tparent.run((effect) => {\n\t\tconst jitter = effect.get(watch.backend.jitter);\n\t\tconst pct = (jitter / max) * 100;\n\t\ttargetLine.style.left = `${pct}%`;\n\t\ttargetLabel.textContent = `${Math.round(jitter)}ms`;\n\t\tviz.setAttribute(\"aria-valuenow\", jitter.toString());\n\t});\n\n\tconst updateFromX = (clientX: number) => {\n\t\tconst rect = viz.getBoundingClientRect();\n\t\tconst trackWidth = rect.width - LABEL_WIDTH;\n\t\tconst x = Math.max(0, Math.min(clientX - rect.left - LABEL_WIDTH, trackWidth));\n\t\tconst ms = (x / trackWidth) * max;\n\t\tconst snapped = (Math.round(ms / RANGE_STEP) * RANGE_STEP) as Moq.Time.Milli;\n\t\tconst clamped = Math.max(MIN_RANGE, Math.min(max, snapped)) as Moq.Time.Milli;\n\t\twatch.backend.latency.set(clamped);\n\t};\n\n\tconst interact = () => {\n\t\tif (!hasInteracted) {\n\t\t\thasInteracted = true;\n\t\t\thelp.style.display = \"none\";\n\t\t}\n\t};\n\n\tparent.event(viz, \"mousedown\", (e) => {\n\t\tdragging = true;\n\t\tviz.classList.add(\"buffer-visualization--dragging\");\n\t\tinteract();\n\t\tupdateFromX(e.clientX);\n\t});\n\n\tparent.event(document, \"mousemove\", (e) => {\n\t\tif (dragging) updateFromX(e.clientX);\n\t});\n\n\tparent.event(document, \"mouseup\", () => {\n\t\tif (!dragging) return;\n\t\tdragging = false;\n\t\tviz.classList.remove(\"buffer-visualization--dragging\");\n\t});\n\n\tparent.event(viz, \"keydown\", (e) => {\n\t\tlet delta = 0 as Moq.Time.Milli;\n\t\tif (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") {\n\t\t\tdelta = RANGE_STEP;\n\t\t} else if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") {\n\t\t\tdelta = -RANGE_STEP as Moq.Time.Milli;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t\te.preventDefault();\n\t\tinteract();\n\t\tconst current = watch.backend.jitter.peek();\n\t\tconst value = Math.max(MIN_RANGE, Math.min(max, current + delta)) as Moq.Time.Milli;\n\t\twatch.backend.latency.set(value);\n\t});\n\n\tconst draw = () => {\n\t\tconst timestamp = watch.backend.sync.now();\n\t\tconst isBuffering = watch.backend.video.stalled.peek();\n\t\tdrawRanges(videoCanvas, watch.backend.video.buffered.peek(), timestamp, max, isBuffering);\n\t\tdrawRanges(audioCanvas, watch.backend.audio.buffered.peek(), timestamp, max, isBuffering);\n\t\tparent.animate(draw);\n\t};\n\tparent.animate(draw);\n\n\treturn wrapper;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\n\nexport function bufferingIndicator(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst container = document.createElement(\"div\");\n\tcontainer.className = \"buffering\";\n\tconst spinner = document.createElement(\"div\");\n\tspinner.className = \"buffering-spinner\";\n\tcontainer.appendChild(spinner);\n\n\tparent.run((effect) => {\n\t\tconst buffering = effect.get(watch.backend.video.stalled);\n\t\tconst offline = effect.get(watch.broadcast.status) === \"offline\";\n\t\tcontainer.style.display = buffering && !offline ? \"\" : \"none\";\n\t});\n\n\treturn container;\n}\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M9 18V5l12-2v13\\\" />\\n\\t<circle cx=\\\"6\\\" cy=\\\"18\\\" r=\\\"3\\\" />\\n\\t<circle cx=\\\"18\\\" cy=\\\"16\\\" r=\\\"3\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M8 3H5a2 2 0 0 0-2 2v3\\\" />\\n\\t<path d=\\\"M21 8V5a2 2 0 0 0-2-2h-3\\\" />\\n\\t<path d=\\\"M3 16v3a2 2 0 0 0 2 2h3\\\" />\\n\\t<path d=\\\"M16 21h3a2 2 0 0 0 2-2v-3\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M8 3v3a2 2 0 0 1-2 2H3\\\" />\\n\\t<path d=\\\"M21 8h-3a2 2 0 0 1-2-2V3\\\" />\\n\\t<path d=\\\"M3 16h3a2 2 0 0 1 2 2v3\\\" />\\n\\t<path d=\\\"M16 21v-3a2 2 0 0 1 2-2h3\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M16 9a5 5 0 0 1 .95 2.293\\\" />\\n\\t<path d=\\\"M19.364 5.636a9 9 0 0 1 1.889 9.96\\\" />\\n\\t<path d=\\\"m2 2 20 20\\\" />\\n\\t<path d=\\\"m7 7-.587.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298V11\\\" />\\n\\t<path d=\\\"M9.828 4.172A.686.686 0 0 1 11 4.657v.686\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M16.247 7.761a6 6 0 0 1 0 8.478\\\" />\\n\\t<path d=\\\"M19.075 4.933a10 10 0 0 1 0 14.134\\\" />\\n\\t<path d=\\\"M4.925 19.067a10 10 0 0 1 0-14.134\\\" />\\n\\t<path d=\\\"M7.753 16.239a6 6 0 0 1 0-8.478\\\" />\\n\\t<circle cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"2\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<rect x=\\\"14\\\" y=\\\"3\\\" width=\\\"5\\\" height=\\\"18\\\" rx=\\\"1\\\" />\\n <rect x=\\\"5\\\" y=\\\"3\\\" width=\\\"5\\\" height=\\\"18\\\" rx=\\\"1\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M5 21v-6\\\" />\\n\\t<path d=\\\"M12 21V3\\\" />\\n\\t<path d=\\\"M19 21V9\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5\\\" />\\n\\t<rect x=\\\"2\\\" y=\\\"6\\\" width=\\\"14\\\" height=\\\"12\\\" rx=\\\"2\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\\\" />\\n\\t<path d=\\\"M16 9a5 5 0 0 1 0 6\\\" />\\n\\t<path d=\\\"M19.364 18.364a9 9 0 0 0 0-12.728\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n\\t<path d=\\\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\\\" />\\n</svg>\"","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"24\\\" height=\\\"24\\\" viewBox=\\\"0 0 24 24\\\" fill=\\\"var(--color-white)\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"2\\\" stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\">\\n <path d=\\\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\\\" />\\n <path d=\\\"M16 9a5 5 0 0 1 0 6\\\" />\\n</svg>\"","import audio from \"./icons/audio.svg?raw\";\nimport buffer from \"./icons/buffer.svg?raw\";\nimport fullscreenEnter from \"./icons/fullscreen-enter.svg?raw\";\nimport fullscreenExit from \"./icons/fullscreen-exit.svg?raw\";\nimport mute from \"./icons/mute.svg?raw\";\nimport network from \"./icons/network.svg?raw\";\nimport pause from \"./icons/pause.svg?raw\";\nimport play from \"./icons/play.svg?raw\";\nimport stats from \"./icons/stats.svg?raw\";\nimport video from \"./icons/video.svg?raw\";\nimport volumeHigh from \"./icons/volume-high.svg?raw\";\nimport volumeLow from \"./icons/volume-low.svg?raw\";\nimport volumeMedium from \"./icons/volume-medium.svg?raw\";\n\nexport {\n\taudio,\n\tbuffer,\n\tfullscreenEnter,\n\tfullscreenExit,\n\tmute,\n\tnetwork,\n\tpause,\n\tplay,\n\tstats,\n\tvideo,\n\tvolumeHigh,\n\tvolumeLow,\n\tvolumeMedium,\n};\n\nexport function icon(svg: string): HTMLElement {\n\tconst span = document.createElement(\"span\");\n\tspan.className = \"flex-center\";\n\tspan.setAttribute(\"role\", \"img\");\n\tspan.setAttribute(\"aria-hidden\", \"true\");\n\tspan.innerHTML = svg;\n\treturn span;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\nimport { fullscreenEnter, fullscreenExit, icon } from \"../icons\";\n\nexport function fullscreenButton(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst button = document.createElement(\"button\");\n\tbutton.type = \"button\";\n\tbutton.className = \"button flex-center\";\n\tbutton.title = \"Fullscreen\";\n\tbutton.setAttribute(\"aria-label\", \"Fullscreen\");\n\tbutton.replaceChildren(icon(fullscreenEnter));\n\n\tconst updateIcon = () => {\n\t\tconst isFull = !!document.fullscreenElement;\n\t\tbutton.replaceChildren(icon(isFull ? fullscreenExit : fullscreenEnter));\n\t};\n\tparent.event(document, \"fullscreenchange\", updateIcon);\n\n\tparent.event(button, \"click\", () => {\n\t\tif (document.fullscreenElement) {\n\t\t\tdocument.exitFullscreen();\n\t\t} else {\n\t\t\twatch.requestFullscreen();\n\t\t}\n\t});\n\n\treturn button;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\n\nexport function offlineIndicator(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst container = document.createElement(\"div\");\n\tcontainer.className = \"watch-ui__offline-indicator\";\n\tcontainer.setAttribute(\"role\", \"status\");\n\tcontainer.setAttribute(\"aria-live\", \"polite\");\n\n\tconst text = document.createElement(\"span\");\n\ttext.className = \"watch-ui__offline-text\";\n\ttext.textContent = \"OFFLINE\";\n\tcontainer.appendChild(text);\n\n\tparent.run((effect) => {\n\t\tconst offline = effect.get(watch.broadcast.status) === \"offline\";\n\t\tcontainer.style.display = offline ? \"\" : \"none\";\n\t});\n\n\treturn container;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\nimport { icon, pause, play } from \"../icons\";\n\nexport function playPauseButton(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst button = document.createElement(\"button\");\n\tbutton.type = \"button\";\n\tbutton.className = \"button button--playback flex-center\";\n\n\tparent.run((effect) => {\n\t\tconst paused = effect.get(watch.backend.paused);\n\t\tbutton.title = paused ? \"Play\" : \"Pause\";\n\t\tbutton.setAttribute(\"aria-label\", paused ? \"Play\" : \"Pause\");\n\t\tbutton.replaceChildren(icon(paused ? play : pause));\n\t});\n\n\tparent.event(button, \"click\", () => {\n\t\twatch.paused = !watch.paused;\n\t});\n\n\treturn button;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\n\nfunction formatBitrate(bps: number): string {\n\tif (bps >= 1_000_000) return `${(bps / 1_000_000).toFixed(1)} Mbps`;\n\tif (bps >= 1_000) return `${(bps / 1_000).toFixed(0)} kbps`;\n\treturn `${bps} bps`;\n}\n\nexport function qualitySelector(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst wrapper = document.createElement(\"div\");\n\twrapper.className = \"quality-selector\";\n\n\tconst label = document.createElement(\"label\");\n\tlabel.htmlFor = \"moq-watch-quality-select\";\n\tlabel.className = \"quality-label\";\n\tlabel.textContent = \"Quality: \";\n\n\tconst select = document.createElement(\"select\");\n\tselect.id = \"moq-watch-quality-select\";\n\tselect.className = \"quality-select\";\n\n\twrapper.append(label, select);\n\n\tparent.run((effect) => {\n\t\tconst catalog = effect.get(watch.backend.video.source.catalog);\n\t\tconst active = effect.get(watch.backend.video.source.track);\n\t\tconst renditions = catalog?.renditions ?? {};\n\n\t\tselect.replaceChildren();\n\t\tconst auto = document.createElement(\"option\");\n\t\tauto.value = \"\";\n\t\tauto.textContent = \"Auto\";\n\t\tselect.appendChild(auto);\n\n\t\tfor (const [name, config] of Object.entries(renditions)) {\n\t\t\tconst opt = document.createElement(\"option\");\n\t\t\topt.value = name;\n\t\t\tconst dims = config.codedWidth && config.codedHeight ? ` (${config.codedWidth}x${config.codedHeight})` : \"\";\n\t\t\tconst rate = config.bitrate ? ` ${formatBitrate(config.bitrate)}` : \"\";\n\t\t\topt.textContent = `${name}${dims}${rate}`;\n\t\t\tselect.appendChild(opt);\n\t\t}\n\n\t\tselect.value = active ?? \"\";\n\t});\n\n\tparent.event(select, \"change\", () => {\n\t\tconst value = select.value || undefined;\n\t\twatch.backend.video.source.target.update((prev) => ({ ...prev, name: value }));\n\t});\n\n\treturn wrapper;\n}\n","import type { Effect, Signal } from \"@moq/signals\";\nimport { icon, stats } from \"../icons\";\n\nexport function statsButton(parent: Effect, visible: Signal<boolean>): HTMLElement {\n\tconst button = document.createElement(\"button\");\n\tbutton.type = \"button\";\n\tbutton.className = \"button flex-center\";\n\tbutton.replaceChildren(icon(stats));\n\n\tparent.run((effect) => {\n\t\tconst showing = effect.get(visible);\n\t\tbutton.title = showing ? \"Hide stats\" : \"Show stats\";\n\t\tbutton.setAttribute(\"aria-label\", button.title);\n\t});\n\n\tparent.event(button, \"click\", () => {\n\t\tvisible.update((v) => !v);\n\t});\n\n\treturn button;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\nimport { icon, mute, volumeHigh, volumeLow, volumeMedium } from \"../icons\";\n\nfunction volumeIcon(volumePct: number, muted: boolean): string {\n\tif (muted || volumePct === 0) return mute;\n\tif (volumePct <= 33) return volumeLow;\n\tif (volumePct <= 66) return volumeMedium;\n\treturn volumeHigh;\n}\n\nexport function volumeSlider(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst wrapper = document.createElement(\"div\");\n\twrapper.className = \"volume-slider flex-center\";\n\n\tconst button = document.createElement(\"button\");\n\tbutton.type = \"button\";\n\tbutton.className = \"button flex-center\";\n\n\tconst slider = document.createElement(\"input\");\n\tslider.type = \"range\";\n\tslider.min = \"0\";\n\tslider.max = \"100\";\n\n\tconst label = document.createElement(\"span\");\n\tlabel.className = \"volume-label\";\n\n\twrapper.append(button, slider, label);\n\n\tparent.run((effect) => {\n\t\tconst volume = effect.get(watch.backend.audio.volume);\n\t\tconst muted = effect.get(watch.backend.audio.muted);\n\t\tconst pct = Math.round(volume * 100);\n\t\tslider.value = pct.toString();\n\t\tlabel.textContent = pct.toString();\n\t\tbutton.title = muted ? \"Unmute\" : \"Mute\";\n\t\tbutton.setAttribute(\"aria-label\", button.title);\n\t\tbutton.replaceChildren(icon(volumeIcon(pct, muted)));\n\t});\n\n\tparent.event(slider, \"input\", () => {\n\t\twatch.backend.audio.volume.set(Number.parseFloat(slider.value) / 100);\n\t});\n\n\tparent.event(button, \"click\", () => {\n\t\twatch.backend.audio.muted.update((m) => !m);\n\t});\n\n\treturn wrapper;\n}\n","import type { Effect } from \"@moq/signals\";\nimport type MoqWatch from \"../../element\";\n\ntype StatusConfig = { variant: string; text: string };\n\nfunction deriveStatus(\n\turl: URL | undefined,\n\tconnection: \"connecting\" | \"connected\" | \"disconnected\",\n\tbroadcast: \"offline\" | \"loading\" | \"live\",\n): StatusConfig {\n\tif (!url) return { variant: \"error\", text: \"No URL\" };\n\tif (connection === \"disconnected\") return { variant: \"error\", text: \"Disconnected\" };\n\tif (connection === \"connecting\") return { variant: \"connecting\", text: \"Connecting...\" };\n\tif (broadcast === \"offline\") return { variant: \"error\", text: \"Offline\" };\n\tif (broadcast === \"loading\") return { variant: \"loading\", text: \"Loading...\" };\n\tif (broadcast === \"live\") return { variant: \"live\", text: \"Live\" };\n\treturn { variant: \"connected\", text: \"Connected\" };\n}\n\nexport function watchStatusIndicator(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst wrapper = document.createElement(\"div\");\n\twrapper.className = \"status-indicator flex-center\";\n\n\tconst dot = document.createElement(\"span\");\n\tconst text = document.createElement(\"span\");\n\twrapper.append(dot, text);\n\n\tparent.run((effect) => {\n\t\tconst url = effect.get(watch.connection.url);\n\t\tconst conn = effect.get(watch.connection.status);\n\t\tconst broadcast = effect.get(watch.broadcast.status);\n\t\tconst { variant, text: label } = deriveStatus(url, conn, broadcast);\n\n\t\tdot.className = `status-indicator-dot status-indicator-dot--${variant}`;\n\t\ttext.className = `status-indicator-text status-indicator-text--${variant}`;\n\t\ttext.textContent = label;\n\t});\n\n\treturn wrapper;\n}\n","import type { Effect, Signal } from \"@moq/signals\";\nimport * as DOM from \"@moq/signals/dom\";\nimport type MoqWatch from \"../element\";\nimport { audio, buffer, icon, network, video } from \"./icons\";\n\nconst POLL_MS = 250;\n\ntype Kind = \"network\" | \"video\" | \"audio\" | \"buffer\";\n\nfunction row(kind: Kind, label: string, svg: string): { el: HTMLElement; data: HTMLSpanElement } {\n\tconst el = DOM.create(\"div\", { className: `stats-item stats-item--${kind}` });\n\n\tconst iconWrap = DOM.create(\"div\", { className: \"stats-icon-wrapper\" });\n\ticonWrap.appendChild(icon(svg));\n\n\tconst title = DOM.create(\"span\", { className: \"stats-item-title\" }, label);\n\tconst data = DOM.create(\"span\", { className: \"stats-item-data\" }, \"N/A\");\n\n\tconst detail = DOM.create(\"div\", { className: \"stats-item-detail\" });\n\tdetail.append(title, data);\n\n\tel.append(iconWrap, detail);\n\treturn { el, data };\n}\n\nfunction formatBitrate(bps: number): string {\n\tif (bps >= 1_000_000) return `${(bps / 1_000_000).toFixed(1)}Mbps`;\n\tif (bps >= 1_000) return `${(bps / 1_000).toFixed(0)}kbps`;\n\treturn `${bps.toFixed(0)}bps`;\n}\n\nfunction formatBandwidth(bps: number | undefined, dir: \"up\" | \"down\"): string | null {\n\tif (bps === undefined || bps <= 0) return null;\n\tconst arrow = dir === \"down\" ? \"↓\" : \"↑\";\n\tif (bps >= 1_000_000_000) return `${arrow} ${(bps / 1_000_000_000).toFixed(1)}Gbps`;\n\treturn `${arrow} ${formatBitrate(bps)}`;\n}\n\nfunction networkRow(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst { el, data } = row(\"network\", \"network\", network);\n\n\tparent.interval(() => {\n\t\tconst conn = watch.connection.established.peek();\n\t\tif (!conn) {\n\t\t\tdata.textContent = \"N/A\";\n\t\t\treturn;\n\t\t}\n\t\tconst rtt = conn.rtt?.peek();\n\t\tconst parts = [\n\t\t\tformatBandwidth(conn.recvBandwidth?.peek(), \"down\"),\n\t\t\tformatBandwidth(conn.sendBandwidth?.peek(), \"up\"),\n\t\t\trtt !== undefined && rtt > 0 ? `${rtt.toFixed(0)}ms` : null,\n\t\t].filter((p): p is string => p !== null);\n\t\tdata.textContent = parts.length > 0 ? parts.join(\"\\n\") : \"N/A\";\n\t}, POLL_MS);\n\n\treturn el;\n}\n\nfunction videoRow(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst { el, data } = row(\"video\", \"video\", video);\n\tlet prevFrames = 0;\n\tlet prevBytes = 0;\n\tlet prevWhen = performance.now();\n\n\tparent.interval(() => {\n\t\tconst catalog = watch.backend.video.source.catalog.peek();\n\t\tconst stats = watch.backend.video.stats.peek();\n\t\tconst now = performance.now();\n\t\tconst elapsedMs = now - prevWhen;\n\n\t\tlet fps: number | undefined;\n\t\tif (stats && prevFrames > 0 && elapsedMs > 0) {\n\t\t\tconst delta = stats.frameCount - prevFrames;\n\t\t\tif (delta > 0) fps = delta / (elapsedMs / 1000);\n\t\t}\n\n\t\tlet bitrate: string | undefined;\n\t\tif (stats && prevBytes > 0 && elapsedMs > 0) {\n\t\t\tconst delta = stats.bytesReceived - prevBytes;\n\t\t\tif (delta > 0) bitrate = formatBitrate(delta * 8 * (1000 / elapsedMs));\n\t\t}\n\n\t\tif (stats) {\n\t\t\tprevFrames = stats.frameCount;\n\t\t\tprevBytes = stats.bytesReceived;\n\t\t\tprevWhen = now;\n\t\t}\n\n\t\tconst { width, height } = catalog?.display ?? {};\n\t\tdata.textContent = [\n\t\t\twidth && height ? `${width}x${height}` : \"N/A\",\n\t\t\tfps !== undefined ? `@${fps.toFixed(1)} fps` : \"N/A\",\n\t\t\tbitrate ?? \"N/A\",\n\t\t].join(\"\\n\");\n\t}, POLL_MS);\n\n\treturn el;\n}\n\nfunction audioRow(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst { el, data } = row(\"audio\", \"audio\", audio);\n\tlet prevBytes = 0;\n\tlet prevWhen = performance.now();\n\n\tparent.interval(() => {\n\t\tconst track = watch.backend.audio.source.track.peek();\n\t\tconst config = watch.backend.audio.source.config.peek();\n\t\tconst stats = watch.backend.audio.stats.peek();\n\n\t\tif (!track || !config) {\n\t\t\tdata.textContent = \"N/A\";\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = performance.now();\n\t\tlet bitrate: string | undefined;\n\t\tif (stats && prevBytes > 0) {\n\t\t\tconst delta = stats.bytesReceived - prevBytes;\n\t\t\tconst elapsedMs = now - prevWhen;\n\t\t\tif (delta > 0 && elapsedMs > 0) bitrate = formatBitrate(delta * 8 * (1000 / elapsedMs));\n\t\t}\n\n\t\tif (stats) {\n\t\t\tprevBytes = stats.bytesReceived;\n\t\t\tprevWhen = now;\n\t\t}\n\n\t\tconst parts: string[] = [];\n\t\tif (config.sampleRate) parts.push(`${(config.sampleRate / 1000).toFixed(1)}kHz`);\n\t\tif (config.numberOfChannels) parts.push(`${config.numberOfChannels}ch`);\n\t\tparts.push(bitrate ?? \"N/A\");\n\t\tif (config.codec) parts.push(config.codec);\n\t\tdata.textContent = parts.length > 0 ? parts.join(\"\\n\") : \"N/A\";\n\t}, POLL_MS);\n\n\treturn el;\n}\n\nfunction bufferRow(parent: Effect, watch: MoqWatch): HTMLElement {\n\tconst { el, data } = row(\"buffer\", \"buffer\", buffer);\n\n\tparent.run((effect) => {\n\t\tconst jitter = effect.get(watch.backend.jitter);\n\t\tdata.textContent = `${Math.round(jitter)}ms`;\n\t});\n\n\treturn el;\n}\n\nexport function statsPanel(parent: Effect, watch: MoqWatch, visible: Signal<boolean>): HTMLElement {\n\tconst wrap = DOM.create(\"div\", { className: \"stats\" });\n\tconst panel = DOM.create(\"div\", { className: \"stats-panel\" });\n\twrap.appendChild(panel);\n\n\tparent.run((effect) => {\n\t\tconst showing = effect.get(visible);\n\t\twrap.style.display = showing ? \"\" : \"none\";\n\t\tif (!showing) return;\n\n\t\tDOM.render(effect, panel, networkRow(effect, watch));\n\t\tDOM.render(effect, panel, videoRow(effect, watch));\n\t\tDOM.render(effect, panel, audioRow(effect, watch));\n\t\tDOM.render(effect, panel, bufferRow(effect, watch));\n\t});\n\n\treturn wrap;\n}\n","import { Effect, Signal } from \"@moq/signals\";\nimport * as DOM from \"@moq/signals/dom\";\nimport type MoqWatch from \"../element\";\nimport { bufferControl } from \"./components/buffer-control\";\nimport { bufferingIndicator } from \"./components/buffering-indicator\";\nimport { fullscreenButton } from \"./components/fullscreen-button\";\nimport { offlineIndicator } from \"./components/offline-indicator\";\nimport { playPauseButton } from \"./components/play-pause\";\nimport { qualitySelector } from \"./components/quality-selector\";\nimport { statsButton } from \"./components/stats-button\";\nimport { volumeSlider } from \"./components/volume-slider\";\nimport { watchStatusIndicator } from \"./components/watch-status-indicator\";\nimport { statsPanel } from \"./stats\";\nimport styles from \"./styles/index.css?inline\";\n\nexport default class MoqWatchUi extends HTMLElement {\n\t#signals?: Effect;\n\t#root: ShadowRoot;\n\t#watch = new Signal<MoqWatch | undefined>(undefined);\n\t#observer: MutationObserver;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.#root = this.attachShadow({ mode: \"open\" });\n\n\t\tconst style = document.createElement(\"style\");\n\t\tstyle.textContent = styles;\n\t\tthis.#root.appendChild(style);\n\n\t\tthis.#observer = new MutationObserver(() => this.#updateWatch());\n\t}\n\n\tconnectedCallback() {\n\t\tthis.#updateWatch();\n\t\tthis.#observer.observe(this, { childList: true });\n\n\t\tconst signals = new Effect();\n\t\tthis.#signals = signals;\n\t\tsignals.run(this.#render.bind(this));\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.#observer.disconnect();\n\t\tthis.#signals?.close();\n\t\tthis.#signals = undefined;\n\t}\n\n\t#updateWatch() {\n\t\tconst watch = this.querySelector(\"moq-watch\") as MoqWatch | null;\n\t\tthis.#watch.set(watch ?? undefined);\n\t}\n\n\t#render(effect: Effect) {\n\t\tconst watch = effect.get(this.#watch);\n\t\tif (!watch) return;\n\n\t\tconst visible = new Signal(false);\n\n\t\tconst videoContainer = DOM.create(\"div\", { className: \"video-container\" });\n\t\tvideoContainer.append(\n\t\t\tDOM.create(\"slot\"),\n\t\t\tstatsPanel(effect, watch, visible),\n\t\t\tbufferingIndicator(effect, watch),\n\t\t\tofflineIndicator(effect, watch),\n\t\t);\n\n\t\tconst controls = DOM.create(\"div\", { className: \"controls\" });\n\n\t\tconst playback = DOM.create(\"div\", { className: \"playback-controls flex-align-center\" });\n\t\tplayback.append(\n\t\t\tplayPauseButton(effect, watch),\n\t\t\tvolumeSlider(effect, watch),\n\t\t\twatchStatusIndicator(effect, watch),\n\t\t\tstatsButton(effect, visible),\n\t\t\tfullscreenButton(effect, watch),\n\t\t);\n\n\t\tconst latency = DOM.create(\"div\", { className: \"latency-controls\" });\n\t\tlatency.append(bufferControl(effect, watch), qualitySelector(effect, watch));\n\n\t\tcontrols.append(playback, latency);\n\n\t\tDOM.render(effect, this.#root, videoContainer);\n\t\tDOM.render(effect, this.#root, controls);\n\t}\n}\n\ncustomElements.define(\"moq-watch-ui\", MoqWatchUi);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch-ui\": MoqWatchUi;\n\t}\n}\n"],"names":["MIN_RANGE","RANGE_STEP","DEFAULT_MAX","LABEL_WIDTH","drawRanges","canvas","ranges","timestamp","max","isBuffering","ctx","dpr","rect","width","height","canvasW","canvasH","padding","rangeHeight","radius","i","range","startMs","endMs","visibleStart","visibleEnd","x","w","overflowSec","bufferControl","parent","watch","wrapper","viz","playhead","videoTrack","videoLabel","videoCanvas","audioTrack","audioLabel","audioCanvas","targetArea","targetLine","targetLabel","help","dragging","hasInteracted","effect","jitter","pct","updateFromX","clientX","trackWidth","ms","snapped","clamped","interact","e","delta","current","value","draw","bufferingIndicator","container","spinner","buffering","offline","audio","buffer","fullscreenEnter","fullscreenExit","mute","network","pause","play","stats","video","volumeHigh","volumeLow","volumeMedium","icon","svg","span","fullscreenButton","button","updateIcon","isFull","offlineIndicator","text","playPauseButton","paused","formatBitrate","bps","qualitySelector","label","select","catalog","active","renditions","auto","name","config","opt","dims","rate","prev","statsButton","visible","showing","v","volumeIcon","volumePct","muted","volumeSlider","slider","volume","m","deriveStatus","url","connection","broadcast","watchStatusIndicator","dot","conn","variant","POLL_MS","row","kind","el","DOM.create","iconWrap","title","data","detail","formatBandwidth","dir","arrow","networkRow","rtt","parts","p","videoRow","prevFrames","prevBytes","prevWhen","now","elapsedMs","fps","bitrate","audioRow","track","bufferRow","statsPanel","wrap","panel","DOM.render","MoqWatchUi","#signals","#root","#watch","Signal","#observer","style","styles","#updateWatch","signals","Effect","#render","videoContainer","controls","playback","latency"],"mappings":"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"}
package/ui/icons.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import audio from "./icons/audio.svg?raw";
2
+ import buffer from "./icons/buffer.svg?raw";
3
+ import fullscreenEnter from "./icons/fullscreen-enter.svg?raw";
4
+ import fullscreenExit from "./icons/fullscreen-exit.svg?raw";
5
+ import mute from "./icons/mute.svg?raw";
6
+ import network from "./icons/network.svg?raw";
7
+ import pause from "./icons/pause.svg?raw";
8
+ import play from "./icons/play.svg?raw";
9
+ import stats from "./icons/stats.svg?raw";
10
+ import video from "./icons/video.svg?raw";
11
+ import volumeHigh from "./icons/volume-high.svg?raw";
12
+ import volumeLow from "./icons/volume-low.svg?raw";
13
+ import volumeMedium from "./icons/volume-medium.svg?raw";
14
+ export { audio, buffer, fullscreenEnter, fullscreenExit, mute, network, pause, play, stats, video, volumeHigh, volumeLow, volumeMedium, };
15
+ export declare function icon(svg: string): HTMLElement;
16
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/ui/icons.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,uBAAuB,CAAC;AAC1C,OAAO,MAAM,MAAM,wBAAwB,CAAC;AAC5C,OAAO,eAAe,MAAM,kCAAkC,CAAC;AAC/D,OAAO,cAAc,MAAM,iCAAiC,CAAC;AAC7D,OAAO,IAAI,MAAM,sBAAsB,CAAC;AACxC,OAAO,OAAO,MAAM,yBAAyB,CAAC;AAC9C,OAAO,KAAK,MAAM,uBAAuB,CAAC;AAC1C,OAAO,IAAI,MAAM,sBAAsB,CAAC;AACxC,OAAO,KAAK,MAAM,uBAAuB,CAAC;AAC1C,OAAO,KAAK,MAAM,uBAAuB,CAAC;AAC1C,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,SAAS,MAAM,4BAA4B,CAAC;AACnD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AAEzD,OAAO,EACN,KAAK,EACL,MAAM,EACN,eAAe,EACf,cAAc,EACd,IAAI,EACJ,OAAO,EACP,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,YAAY,GACZ,CAAC;AAEF,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAO7C"}
package/ui/stats.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { Effect, Signal } from "@moq/signals";
2
+ import type MoqWatch from "../element";
3
+ export declare function statsPanel(parent: Effect, watch: MoqWatch, visible: Signal<boolean>): HTMLElement;
4
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/ui/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AAoJvC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,WAAW,CAiBjG"}
@@ -1,4 +1,4 @@
1
- import type * as Moq from "@moq/lite";
1
+ import type * as Moq from "@moq/net";
2
2
  import type { Getter } from "@moq/signals";
3
3
  import type { BufferedRanges } from "../backend";
4
4
  import type { Source } from "./source";
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/video/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,WAAW,OAAO;IAEvB,MAAM,EAAE,MAAM,CAAC;IAGf,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAGjC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAGzB,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAGjC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,KAAK;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/video/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,WAAW,OAAO;IAEvB,MAAM,EAAE,MAAM,CAAC;IAGf,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAGjC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAGzB,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAGjC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,KAAK;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACtB"}
@@ -1,4 +1,4 @@
1
- import { Time } from "@moq/lite";
1
+ import { Time } from "@moq/net";
2
2
  import { type Getter, Signal } from "@moq/signals";
3
3
  import type { BufferedRanges } from "../backend";
4
4
  import type { Backend, Stats } from "./backend";
@@ -1 +1 @@
1
- {"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../src/video/decoder.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMvC,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;CACpC,CAAC;AAOF,qBAAa,OAAQ,YAAW,OAAO;;IACtC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IAOf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC,CAAe;IAI7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAmB;IAIrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAiB;IAGxF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IAGlD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAe;IAIxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAkB;gBAI/C,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY;IAyHhD,KAAK;CAQL"}
1
+ {"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../src/video/decoder.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMvC,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;CACpC,CAAC;AAOF,qBAAa,OAAQ,YAAW,OAAO;;IACtC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IAOf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC,CAAe;IAI7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAmB;IAIrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAiB;IAGxF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IAGlD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAe;IAIxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAkB;gBAY/C,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY;IA+HhD,KAAK;CAKL"}
package/video/mse.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as Moq from "@moq/lite";
1
+ import * as Moq from "@moq/net";
2
2
  import { type Getter } from "@moq/signals";
3
3
  import { type BufferedRanges } from "../backend";
4
4
  import type { Muxer } from "../mse";
@@ -1 +1 @@
1
- {"version":3,"file":"mse.d.ts","sourceRoot":"","sources":["../../src/video/mse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AACjC,OAAO,EAAU,KAAK,MAAM,EAAU,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,KAAK,cAAc,EAAqB,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,qBAAa,GAAI,YAAW,OAAO;;IAClC,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IAIf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAe;IAGxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAkB;IAG3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IAGlD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAmB;IAE7D,OAAO,qBAAgB;gBAEX,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;IAiOxC,KAAK,IAAI,IAAI;CAIb"}
1
+ {"version":3,"file":"mse.d.ts","sourceRoot":"","sources":["../../src/video/mse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAU,KAAK,MAAM,EAAU,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,KAAK,cAAc,EAAqB,MAAM,YAAY,CAAC;AAEpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,qBAAa,GAAI,YAAW,OAAO;;IAClC,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IAIf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,CAAe;IAGxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,CAAkB;IAG3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAiB;IAGlD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAmB;IAE7D,OAAO,qBAAgB;gBAEX,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;IAoOxC,KAAK,IAAI,IAAI;CAIb"}
@@ -1,4 +1,4 @@
1
- import { Time } from "@moq/lite";
1
+ import { Time } from "@moq/net";
2
2
  import { Signal } from "@moq/signals";
3
3
  import type { Decoder } from "./decoder";
4
4
  export type RendererProps = {