@moq/watch 0.2.18 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/element.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as e, r as t, t as n } from "./broadcast-oQKyLEQ1.js";
1
+ import { i as e, r as t, t as n } from "./broadcast-OttaL0p4.js";
2
2
  import { n as r, r as i } from "./sync-D67IxmxJ.js";
3
3
  import * as a from "@moq/net";
4
4
  import { Effect as o, Signal as s } from "@moq/signals";
@@ -19,9 +19,14 @@ var c = [
19
19
  ];
20
20
  function l(e) {
21
21
  let t = e?.trim();
22
- return t ? t === "never" || t === "always" || /^-?\d+(\.\d+)?(px|%)$/.test(t) ? t : /^-?\d+(\.\d+)?$/.test(t) ? `${t}px` : (console.warn(`moq-watch: invalid visible="${e}", expected "never", "always", or a CSS length like "200px"`), "0px") : "0px";
22
+ return t ? t === "never" || t === "always" || /^-?\d+(\.\d+)?(px|%)$/.test(t) ? t : /^-?\d+(\.\d+)?$/.test(t) ? `${t}px` : (console.warn(`moq-watch: invalid visible="${e}", expected "never", "always", or a CSS length like "200px"`), "20%") : "20%";
23
23
  }
24
- var u = new FinalizationRegistry((e) => e.close()), d = class extends HTMLElement {
24
+ function u(e, t) {
25
+ if (e === null) return t;
26
+ let n = e.trim().toLowerCase();
27
+ return n !== "false" && n !== "0";
28
+ }
29
+ var d = new FinalizationRegistry((e) => e.close()), f = class extends HTMLElement {
25
30
  static observedAttributes = c;
26
31
  connection;
27
32
  broadcast;
@@ -29,7 +34,7 @@ var u = new FinalizationRegistry((e) => e.close()), d = class extends HTMLElemen
29
34
  #e = new s(!1);
30
35
  signals = new o();
31
36
  constructor() {
32
- super(), u.register(this, this.signals), this.connection = new a.Connection.Reload({ enabled: this.#e }), this.signals.cleanup(() => this.connection.close()), this.broadcast = new n({
37
+ super(), d.register(this, this.signals), this.connection = new a.Connection.Reload({ enabled: this.#e }), this.signals.cleanup(() => this.connection.close()), this.broadcast = new n({
33
38
  connection: this.connection.established,
34
39
  announced: this.connection.announced,
35
40
  enabled: this.#e
@@ -105,7 +110,7 @@ var u = new FinalizationRegistry((e) => e.close()), d = class extends HTMLElemen
105
110
  this.backend.audio.volume.set(e);
106
111
  } else if (e === "muted") this.backend.audio.muted.set(r !== null);
107
112
  else if (e === "visible") this.backend.visible.set(l(r));
108
- else if (e === "reload") this.broadcast.reload.set(r !== null);
113
+ else if (e === "reload") this.broadcast.reload.set(u(r, !0));
109
114
  else if (e === "latency") this.latency = this.#t(r);
110
115
  else if (e === "latency-min") this.latencyMin = this.#t(r);
111
116
  else if (e === "latency-max") this.latencyMax = this.#t(r);
@@ -197,8 +202,8 @@ var u = new FinalizationRegistry((e) => e.close()), d = class extends HTMLElemen
197
202
  this.broadcast.catalog.set(e);
198
203
  }
199
204
  };
200
- customElements.define("moq-watch", d);
205
+ customElements.define("moq-watch", f);
201
206
  //#endregion
202
- export { d as default };
207
+ export { f as default };
203
208
 
204
209
  //# sourceMappingURL=element.js.map
package/element.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","names":["#enabled","#parseBound"],"sources":["../src/element.ts"],"sourcesContent":["import type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal } from \"@moq/signals\";\nimport { MultiBackend } from \"./backend\";\nimport { Broadcast, type CatalogFormat, parseCatalogFormat } from \"./broadcast\";\nimport { type Bound, type Latency, latencyBounds, latencyFromBounds } from \"./sync\";\nimport type { Visible } from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"latency-max\",\n\t\"jitter\",\n\t\"catalog-format\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"0px\" (on screen only).\nfunction parseVisible(value: string | null): Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"0px\";\n\tif (trimmed === \"never\" || trimmed === \"always\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", or a CSS length like \"200px\"`);\n\treturn \"0px\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\tconnection: Moq.Connection.Reload;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t// The backend that powers this element.\n\tbackend: MultiBackend;\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Expose the Effect class, so users can easily create effects scoped to this element.\n\tsignals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.connection = new Moq.Connection.Reload({\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.broadcast = new Broadcast({\n\t\t\tconnection: this.connection.established,\n\t\t\tannounced: this.connection.announced,\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.broadcast.close());\n\n\t\tthis.backend = new MultiBackend({\n\t\t\tbroadcast: this.broadcast,\n\t\t\tconnection: this.connection.established,\n\t\t});\n\t\tthis.signals.cleanup(() => this.backend.close());\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setElement = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") as HTMLCanvasElement | undefined;\n\t\t\tconst video = this.querySelector(\"video\") as HTMLVideoElement | undefined;\n\t\t\tif (canvas && video) {\n\t\t\t\tthrow new Error(\"Cannot have both canvas and video elements\");\n\t\t\t}\n\t\t\tthis.backend.element.set(canvas ?? video);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setElement);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetElement();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.connection.url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.broadcast.name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.backend.audio.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.backend.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"true\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.backend.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.backend.audio.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst { min, max } = latencyBounds(effect.get(this.backend.latency));\n\t\t\t// Only reflect the collapsed `latency` sugar attribute when the range is actually\n\t\t\t// collapsed. An open range is expressed via latency-min/latency-max, and writing\n\t\t\t// `latency` here would round-trip back through attributeChangedCallback and collapse it.\n\t\t\tif (min !== max) return;\n\t\t\tif (min === \"real-time\") {\n\t\t\t\tthis.setAttribute(\"latency\", \"real-time\");\n\t\t\t} else {\n\t\t\t\tconst jitter = Math.floor(effect.get(this.backend.jitter));\n\t\t\t\tthis.setAttribute(\"latency\", jitter.toString());\n\t\t\t}\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.backend.video.source.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\t// Parse a single latency bound: absent or \"real-time\" is adaptive, otherwise a fixed ms value.\n\t#parseBound(value: string | null): Bound {\n\t\tif (!value || value === \"real-time\") return \"real-time\";\n\t\tconst parsed = Number.parseFloat(value);\n\t\treturn (Number.isFinite(parsed) ? parsed : 100) as Time.Milli;\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.connection.url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.broadcast.name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.backend.paused.set(newValue !== null);\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.backend.audio.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.backend.audio.muted.set(newValue !== null);\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.backend.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tthis.broadcast.reload.set(newValue !== null);\n\t\t} else if (name === \"latency\") {\n\t\t\t// Sugar: collapse the floor and ceiling to a single value.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-min\") {\n\t\t\tthis.latencyMin = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-max\") {\n\t\t\tthis.latencyMax = this.#parseBound(newValue);\n\t\t} else if (name === \"jitter\") {\n\t\t\t// Deprecated: use latency=\"<number>\" instead.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.broadcast.catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.connection.url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.connection.url.set(value ? new URL(value) : undefined);\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.broadcast.name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.broadcast.name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.backend.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.backend.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.backend.audio.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.backend.audio.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.backend.audio.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.backend.audio.muted.set(value);\n\t}\n\n\t/** When video is downloaded relative to the canvas position. See {@link Visible}. */\n\tget visible(): Visible {\n\t\treturn this.backend.visible.peek();\n\t}\n\n\tset visible(value: Visible) {\n\t\tthis.backend.visible.set(value);\n\t}\n\n\tget reload(): boolean {\n\t\treturn this.broadcast.reload.peek();\n\t}\n\n\tset reload(value: boolean) {\n\t\tthis.broadcast.reload.set(value);\n\t}\n\n\t/**\n\t * The latency target. Assign a scalar (or `\"real-time\"`) to minimize latency, or an object\n\t * `{ min, max }` to open a range and buffer future-dated frames. See {@link Latency}.\n\t */\n\tget latency(): Latency {\n\t\treturn this.backend.latency.peek();\n\t}\n\n\tset latency(value: Latency) {\n\t\tthis.backend.latency.set(value);\n\t}\n\n\t/** The latency floor (jitter/startup buffer). Read-modify-writes `latency`, leaving the ceiling. */\n\tget latencyMin(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).min;\n\t}\n\n\tset latencyMin(value: Bound) {\n\t\tconst { max } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(value, max));\n\t}\n\n\t/**\n\t * The latency ceiling: `\"real-time\"` (default) minimizes, a number caps at that many ms. A\n\t * ceiling above the floor enables buffered playback: build up a buffer from future-dated frames\n\t * (e.g. TTS written faster than real-time) and only skip ahead past the cap. Call `reset()` at\n\t * each utterance boundary. Read-modify-writes `latency`, leaving the floor untouched.\n\t */\n\tget latencyMax(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).max;\n\t}\n\n\tset latencyMax(value: Bound) {\n\t\tconst { min } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(min, value));\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.backend.jitter.peek();\n\t}\n\n\t/** @deprecated Use `latency = <number>` instead. */\n\tset jitter(value: number) {\n\t\tthis.latency = value as Time.Milli;\n\t}\n\n\t/** Re-anchor playback and flush the audio buffer at an utterance boundary (buffered mode). */\n\treset(): void {\n\t\tthis.backend.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.broadcast.catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.broadcast.catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.broadcast.catalog.set(value);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"element.js","names":["#enabled","#parseBound"],"sources":["../src/element.ts"],"sourcesContent":["import type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal } from \"@moq/signals\";\nimport { MultiBackend } from \"./backend\";\nimport { Broadcast, type CatalogFormat, parseCatalogFormat } from \"./broadcast\";\nimport { type Bound, type Latency, latencyBounds, latencyFromBounds } from \"./sync\";\nimport type { Visible } from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"latency-max\",\n\t\"jitter\",\n\t\"catalog-format\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"20%\".\nfunction parseVisible(value: string | null): Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"20%\";\n\tif (trimmed === \"never\" || trimmed === \"always\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", or a CSS length like \"200px\"`);\n\treturn \"20%\";\n}\n\nfunction parseBoolean(value: string | null, defaultValue: boolean): boolean {\n\tif (value === null) return defaultValue;\n\tconst normalized = value.trim().toLowerCase();\n\treturn normalized !== \"false\" && normalized !== \"0\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\tconnection: Moq.Connection.Reload;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t// The backend that powers this element.\n\tbackend: MultiBackend;\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Expose the Effect class, so users can easily create effects scoped to this element.\n\tsignals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.connection = new Moq.Connection.Reload({\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.broadcast = new Broadcast({\n\t\t\tconnection: this.connection.established,\n\t\t\tannounced: this.connection.announced,\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.broadcast.close());\n\n\t\tthis.backend = new MultiBackend({\n\t\t\tbroadcast: this.broadcast,\n\t\t\tconnection: this.connection.established,\n\t\t});\n\t\tthis.signals.cleanup(() => this.backend.close());\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setElement = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") as HTMLCanvasElement | undefined;\n\t\t\tconst video = this.querySelector(\"video\") as HTMLVideoElement | undefined;\n\t\t\tif (canvas && video) {\n\t\t\t\tthrow new Error(\"Cannot have both canvas and video elements\");\n\t\t\t}\n\t\t\tthis.backend.element.set(canvas ?? video);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setElement);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetElement();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.connection.url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.broadcast.name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.backend.audio.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.backend.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"true\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.backend.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.backend.audio.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst { min, max } = latencyBounds(effect.get(this.backend.latency));\n\t\t\t// Only reflect the collapsed `latency` sugar attribute when the range is actually\n\t\t\t// collapsed. An open range is expressed via latency-min/latency-max, and writing\n\t\t\t// `latency` here would round-trip back through attributeChangedCallback and collapse it.\n\t\t\tif (min !== max) return;\n\t\t\tif (min === \"real-time\") {\n\t\t\t\tthis.setAttribute(\"latency\", \"real-time\");\n\t\t\t} else {\n\t\t\t\tconst jitter = Math.floor(effect.get(this.backend.jitter));\n\t\t\t\tthis.setAttribute(\"latency\", jitter.toString());\n\t\t\t}\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.backend.video.source.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\t// Parse a single latency bound: absent or \"real-time\" is adaptive, otherwise a fixed ms value.\n\t#parseBound(value: string | null): Bound {\n\t\tif (!value || value === \"real-time\") return \"real-time\";\n\t\tconst parsed = Number.parseFloat(value);\n\t\treturn (Number.isFinite(parsed) ? parsed : 100) as Time.Milli;\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.connection.url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.broadcast.name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.backend.paused.set(newValue !== null);\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.backend.audio.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.backend.audio.muted.set(newValue !== null);\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.backend.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tthis.broadcast.reload.set(parseBoolean(newValue, true));\n\t\t} else if (name === \"latency\") {\n\t\t\t// Sugar: collapse the floor and ceiling to a single value.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-min\") {\n\t\t\tthis.latencyMin = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-max\") {\n\t\t\tthis.latencyMax = this.#parseBound(newValue);\n\t\t} else if (name === \"jitter\") {\n\t\t\t// Deprecated: use latency=\"<number>\" instead.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.broadcast.catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.connection.url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.connection.url.set(value ? new URL(value) : undefined);\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.broadcast.name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.broadcast.name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.backend.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.backend.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.backend.audio.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.backend.audio.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.backend.audio.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.backend.audio.muted.set(value);\n\t}\n\n\t/** When video is downloaded relative to the canvas position. See {@link Visible}. */\n\tget visible(): Visible {\n\t\treturn this.backend.visible.peek();\n\t}\n\n\tset visible(value: Visible) {\n\t\tthis.backend.visible.set(value);\n\t}\n\n\tget reload(): boolean {\n\t\treturn this.broadcast.reload.peek();\n\t}\n\n\tset reload(value: boolean) {\n\t\tthis.broadcast.reload.set(value);\n\t}\n\n\t/**\n\t * The latency target. Assign a scalar (or `\"real-time\"`) to minimize latency, or an object\n\t * `{ min, max }` to open a range and buffer future-dated frames. See {@link Latency}.\n\t */\n\tget latency(): Latency {\n\t\treturn this.backend.latency.peek();\n\t}\n\n\tset latency(value: Latency) {\n\t\tthis.backend.latency.set(value);\n\t}\n\n\t/** The latency floor (jitter/startup buffer). Read-modify-writes `latency`, leaving the ceiling. */\n\tget latencyMin(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).min;\n\t}\n\n\tset latencyMin(value: Bound) {\n\t\tconst { max } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(value, max));\n\t}\n\n\t/**\n\t * The latency ceiling: `\"real-time\"` (default) minimizes, a number caps at that many ms. A\n\t * ceiling above the floor enables buffered playback: build up a buffer from future-dated frames\n\t * (e.g. TTS written faster than real-time) and only skip ahead past the cap. Call `reset()` at\n\t * each utterance boundary. Read-modify-writes `latency`, leaving the floor untouched.\n\t */\n\tget latencyMax(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).max;\n\t}\n\n\tset latencyMax(value: Bound) {\n\t\tconst { min } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(min, value));\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.backend.jitter.peek();\n\t}\n\n\t/** @deprecated Use `latency = <number>` instead. */\n\tset jitter(value: number) {\n\t\tthis.latency = value as Time.Milli;\n\t}\n\n\t/** Re-anchor playback and flush the audio buffer at an utterance boundary (buffered mode). */\n\treset(): void {\n\t\tthis.backend.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.broadcast.catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.broadcast.catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.broadcast.catalog.set(value);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./broadcast-oQKyLEQ1.js";
1
+ import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./broadcast-OttaL0p4.js";
2
2
  import { n as c, r as l, t as u } from "./sync-D67IxmxJ.js";
3
3
  import * as d from "@moq/hang";
4
4
  import * as f from "@moq/json";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moq/watch",
3
3
  "type": "module",
4
- "version": "0.2.18",
4
+ "version": "0.3.0",
5
5
  "description": "Watch Media over QUIC broadcasts",
6
6
  "license": "(MIT OR Apache-2.0)",
7
7
  "repository": "github:moq-dev/moq",
@@ -33,10 +33,10 @@
33
33
  "./support/element.js"
34
34
  ],
35
35
  "dependencies": {
36
- "@moq/hang": "^0.2.12",
37
- "@moq/json": "^0.1.1",
38
- "@moq/net": "^0.1.6",
39
- "@moq/msf": "^0.1.3",
36
+ "@moq/hang": "^0.2.14",
37
+ "@moq/json": "^0.2.0",
38
+ "@moq/net": "^0.1.8",
39
+ "@moq/msf": "^0.1.4",
40
40
  "@moq/signals": "^0.1.10"
41
41
  }
42
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"buffering-indicator.d.ts","sourceRoot":"","sources":["../../../src/ui/components/buffering-indicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,WAAW,CAc/E"}
1
+ {"version":3,"file":"buffering-indicator.d.ts","sourceRoot":"","sources":["../../../src/ui/components/buffering-indicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,WAAW,CAe/E"}
@@ -1 +1 @@
1
- {"version":3,"file":"center-play.d.ts","sourceRoot":"","sources":["../../../src/ui/components/center-play.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAG1C,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,WAAW,CAkBvE"}
1
+ {"version":3,"file":"center-play.d.ts","sourceRoot":"","sources":["../../../src/ui/components/center-play.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAG1C,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,WAAW,CAmBvE"}
@@ -0,0 +1,5 @@
1
+ import type { Effect } from "@moq/signals";
2
+ import type MoqWatch from "../../element";
3
+ /** Shows why video cannot start when every catalog rendition is unsupported. */
4
+ export declare function unsupportedIndicator(parent: Effect, watch: MoqWatch): HTMLElement;
5
+ //# sourceMappingURL=unsupported-indicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsupported-indicator.d.ts","sourceRoot":"","sources":["../../../src/ui/components/unsupported-indicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,WAAW,CAyBjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../../src/ui/element.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;;IAMlD,cASC;IAED,iBAAiB,SAOhB;IAED,oBAAoB,SAInB;CAgKD;AAID,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,cAAc,EAAE,UAAU,CAAC;KAC3B;CACD"}
1
+ {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../../src/ui/element.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;;IAMlD,cASC;IAED,iBAAiB,SAOhB;IAED,oBAAoB,SAInB;CAqKD;AAID,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,cAAc,EAAE,UAAU,CAAC;KAC3B;CACD"}
package/ui/element.js CHANGED
@@ -7,8 +7,8 @@ function i(e, t) {
7
7
  n.className = "buffering";
8
8
  let r = document.createElement("div");
9
9
  return r.className = "buffering-spinner", n.appendChild(r), e.run((e) => {
10
- let r = e.get(t.backend.video.stalled), i = e.get(t.broadcast.status) === "offline";
11
- n.style.display = r && !i ? "" : "none";
10
+ let r = e.get(t.backend.video.stalled), i = e.get(t.broadcast.status) === "offline", a = e.get(t.backend.video.source.error) === "unsupported";
11
+ n.style.display = r && !i && !a ? "" : "none";
12
12
  }), n;
13
13
  }
14
14
  //#endregion
@@ -25,8 +25,8 @@ function x(e) {
25
25
  function S(e, t) {
26
26
  let n = document.createElement("button");
27
27
  return n.type = "button", n.className = "center-play flex-center", n.title = "Play", n.setAttribute("aria-label", "Play"), n.replaceChildren(x(m)), e.run((e) => {
28
- let r = e.get(t.backend.paused);
29
- n.style.display = r ? "" : "none";
28
+ let r = e.get(t.backend.paused), i = e.get(t.backend.video.source.error);
29
+ n.style.display = r && !i ? "" : "none";
30
30
  }), e.event(n, "click", () => {
31
31
  t.paused = !1;
32
32
  }), n;
@@ -616,8 +616,21 @@ function le(e, t, n) {
616
616
  }), i;
617
617
  }
618
618
  //#endregion
619
+ //#region src/ui/components/unsupported-indicator.ts
620
+ function ue(e, t) {
621
+ let n = document.createElement("div");
622
+ n.className = "watch-ui__unsupported-indicator", n.setAttribute("role", "status"), n.setAttribute("aria-live", "polite");
623
+ let r = document.createElement("span");
624
+ return r.className = "watch-ui__unsupported-text", n.appendChild(r), e.run((e) => {
625
+ let i = e.get(t.backend.video.source.error) === "unsupported", a = e.get(t.broadcast.status) === "offline", o = i && !a;
626
+ if (n.style.display = o ? "" : "none", !o) return;
627
+ let s = e.get(t.backend.video.source.catalog)?.renditions ?? {}, c = [...new Set(Object.values(s).map((e) => e.codec))].join(", ");
628
+ r.textContent = c ? `This video codec is not supported by your browser: ${c}` : "This video codec is not supported by your browser";
629
+ }), n;
630
+ }
631
+ //#endregion
619
632
  //#region src/ui/styles/index.css?inline
620
- var ue = "*,:before,:after{box-sizing:border-box}:host{--color-white:#fff;--color-white-alpha-05:#ffffff0d;--color-white-alpha-10:#ffffff1a;--color-white-alpha-15:#ffffff26;--color-white-alpha-20:#fff3;--color-white-alpha-25:#ffffff40;--color-white-alpha-30:#ffffff4d;--color-white-alpha-40:#fff6;--color-white-alpha-60:#fff9;--color-white-alpha-70:#ffffffb3;--color-white-alpha-90:#ffffffe6;--color-gray-100:#9ca3af;--color-gray-400:#6b7280;--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-500:#22c55e;--color-green-900:#3d4928;--color-yellow-400:#facc15;--color-orange-400:#f90;--color-orange-450:#fb923c;--color-orange-500:#f97316;--color-orange-900:#4d301b;--color-red-400:#f87171;--color-amber-600:#d97706;--color-orange-500-alpha-60:#f9731699;--color-orange-500-alpha-80:#f97316cc;--color-black-alpha-08:#00000014;--color-black-alpha-10:#0000001a;--color-black-alpha-40:#0006;--color-black-alpha-60:#0009;--color-black-alpha-85:#000000d9;--color-panel:#121214eb;--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:.375rem;--border-radius-md:.5rem;--border-radius-lg:.75rem;--accent:var(--color-orange-400);--ease:cubic-bezier(.4, 0, .2, 1);font-family:system-ui,Segoe UI,Roboto,sans-serif}.flex-center{justify-content:center;align-items:center;display:flex}.flex-align-center{align-items:center;display:flex}.control{width:2.25rem;height:2.25rem;color:var(--color-white);cursor:pointer;transition:background-color .12s var(--ease), transform .12s var(--ease), color .12s var(--ease);background:0 0;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:inline-flex}.control svg{width:1.25rem;height:1.25rem}.control:hover:not(:disabled){background-color:var(--color-white-alpha-15)}.control:active:not(:disabled){transform:scale(.92)}.control--active{color:var(--accent)}.control:disabled{opacity:.4;cursor:default}.chip{padding:var(--spacing-4) var(--spacing-12);color:var(--color-white-alpha-70);background-color:var(--color-white-alpha-10);border:var(--spacing-1) solid transparent;cursor:pointer;transition:background-color .12s var(--ease), color .12s var(--ease), border-color .12s var(--ease);border-radius:999px;font-size:.75rem;font-weight:500}.chip:hover{background-color:var(--color-white-alpha-20);color:var(--color-white)}.chip--active{color:var(--color-gray-950);background-color:var(--accent);border-color:var(--accent)}:host{display:block;position:relative}.player{border-radius:var(--spacing-8);background:#000;justify-content:center;align-items:center;width:100%;line-height:0;display:flex;position:relative;overflow:hidden}.player ::slotted(moq-watch){width:100%;line-height:0;display:block}.player:not(.player--chrome){cursor:none}.player:fullscreen,.player--pseudo-fullscreen{border-radius:0;justify-content:center;align-items:center;display:flex}.player:-webkit-full-screen{border-radius:0;justify-content:center;align-items:center;display:flex}.player--pseudo-fullscreen{z-index:2147483647;width:100vw;height:100vh;position:fixed;inset:0}@supports (height:100dvh){.player--pseudo-fullscreen{height:100dvh}}.player:fullscreen ::slotted(moq-watch),.player--pseudo-fullscreen ::slotted(moq-watch){width:100%;height:100%;max-height:100%}.player:-webkit-full-screen ::slotted(moq-watch){width:100%;height:100%;max-height:100%}.scrim{pointer-events:none;opacity:0;transition:opacity .2s var(--ease);position:absolute;left:0;right:0}.scrim--top{background:linear-gradient(to bottom, var(--color-black-alpha-40), transparent);height:25%;top:0}.scrim--bottom{background:linear-gradient(to top, var(--color-black-alpha-60), transparent 60%);height:100%;bottom:0}.player--chrome .scrim{opacity:1}.center{pointer-events:none;justify-content:center;align-items:center;line-height:1;display:flex;position:absolute;inset:0}.center-play{pointer-events:auto;width:4.5rem;height:4.5rem;color:var(--color-white);background:linear-gradient(to bottom right, var(--color-orange-400), var(--color-orange-500), var(--color-amber-600));cursor:pointer;box-shadow:0 var(--spacing-8) var(--spacing-24) var(--color-black-alpha-40);transition:transform .15s var(--ease), box-shadow .15s var(--ease);border:none;border-radius:50%}.center-play svg{width:2rem;height:2rem}.center-play:hover{box-shadow:0 var(--spacing-8) var(--spacing-32) var(--color-orange-500-alpha-60);transform:scale(1.06)}.center-play:active{transform:scale(.97)}.chrome{opacity:0;pointer-events:none;transition:opacity .2s var(--ease), transform .2s var(--ease);flex-direction:column;justify-content:flex-end;line-height:1.4;display:flex;position:absolute;bottom:0;left:0;right:0;transform:translateY(.5rem)}.player--chrome .chrome{opacity:1;pointer-events:auto;transform:translateY(0)}.controls{align-items:center;gap:var(--spacing-4);padding:var(--spacing-8) var(--spacing-12) var(--spacing-12);display:flex;position:relative}.controls-group{align-items:center;gap:var(--spacing-4);display:flex}.controls-spacer{flex:1}.volume{gap:var(--spacing-4)}.volume-track{appearance:none;background:linear-gradient(to right, var(--color-white) 0%, var(--color-white) var(--fill,50%), var(--color-white-alpha-30) var(--fill,50%));cursor:pointer;opacity:0;width:0;max-width:0;height:.25rem;transition:width .16s var(--ease), max-width .16s var(--ease), opacity .16s var(--ease);border-radius:999px;outline:none;margin:0}.volume:hover .volume-track,.volume:focus-within .volume-track{opacity:1;width:4.5rem;max-width:4.5rem}.volume-track::-webkit-slider-thumb{appearance:none;background:var(--color-white);cursor:pointer;border:none;border-radius:50%;width:.75rem;height:.75rem}.volume-track::-moz-range-thumb{background:var(--color-white);cursor:pointer;border:none;border-radius:50%;width:.75rem;height:.75rem}.badge{gap:var(--spacing-8);height:2.25rem;padding:0 var(--spacing-12);margin-left:var(--spacing-4);color:var(--color-white);border-radius:var(--border-radius-sm);cursor:pointer;transition:background-color .12s var(--ease);background:0 0;border:none;font-size:.8125rem}.badge:hover{background-color:var(--color-white-alpha-10)}.badge-dot{background:var(--color-gray-400);border-radius:50%;flex-shrink:0;width:.5rem;height:.5rem}.badge-text{letter-spacing:.04em;font-weight:600}.badge-latency{max-width:0;margin-left:calc(-1 * var(--spacing-8));opacity:0;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--color-white-alpha-60);transition:max-width .16s var(--ease), margin-left .16s var(--ease), opacity .16s var(--ease);overflow:hidden}.badge:hover .badge-latency,.badge:focus-visible .badge-latency{opacity:1;max-width:9rem;margin-left:0}.badge[data-variant=live] .badge-dot{background:var(--color-red-400);animation:badge-pulse 2s var(--ease) infinite;box-shadow:0 0 #f87171b3}.badge[data-variant=live] .badge-text{color:var(--color-red-400)}.badge[data-variant=loading] .badge-dot,.badge[data-variant=connecting] .badge-dot{background:var(--color-yellow-400)}.badge[data-variant=error] .badge-dot{background:var(--color-gray-400)}.badge[data-variant=error] .badge-text{color:var(--color-gray-100)}@keyframes badge-pulse{0%{box-shadow:0 0 #f8717199}70%{box-shadow:0 0 0 .4rem #f8717100}to{box-shadow:0 0 #f8717100}}.panel{top:var(--spacing-8);right:var(--spacing-8);bottom:var(--spacing-8);width:20rem;max-width:calc(100% - var(--spacing-16));background:var(--color-panel);backdrop-filter:blur(12px);border:var(--spacing-1) solid var(--color-white-alpha-15);border-radius:var(--border-radius-lg);box-shadow:0 var(--spacing-8) var(--spacing-32) var(--color-black-alpha-60);color:var(--color-white);z-index:4;opacity:0;pointer-events:none;transition:opacity .2s var(--ease), transform .2s var(--ease);flex-direction:column;line-height:1.4;display:flex;position:absolute;overflow:hidden;transform:translate(.75rem)}.panel--open{opacity:1;pointer-events:auto;transform:translate(0)}.panel-head{padding:var(--spacing-8) var(--spacing-8) var(--spacing-8) var(--spacing-16);border-bottom:var(--spacing-1) solid var(--color-white-alpha-10);justify-content:space-between;align-items:center;display:flex}.panel-head-title{gap:var(--spacing-8);font-size:.875rem;font-weight:600}.panel-head-title svg{width:1.125rem;height:1.125rem}.panel-head .control{width:2rem;height:2rem}.panel-tabs{padding:var(--spacing-8);gap:var(--spacing-4);display:flex}.panel-tab{align-items:center;gap:var(--spacing-4);padding:var(--spacing-8) var(--spacing-4);color:var(--color-white-alpha-60);border-radius:var(--border-radius-md);cursor:pointer;transition:background-color .12s var(--ease), color .12s var(--ease);background:0 0;border:none;flex-direction:column;flex:1;font-size:.6875rem;font-weight:500;display:flex}.panel-tab svg{width:1.125rem;height:1.125rem}.panel-tab:hover{background-color:var(--color-white-alpha-05);color:var(--color-white)}.panel-tab--active{background-color:var(--color-white-alpha-10);color:var(--color-white)}.panel-body{scrollbar-width:thin;scrollbar-color:var(--color-white-alpha-20) transparent;flex:1;min-height:0;overflow-y:auto}.panel-body::-webkit-scrollbar{width:.375rem}.panel-body::-webkit-scrollbar-thumb{background:var(--color-white-alpha-20);border-radius:999px}.tab-body{gap:var(--spacing-12);padding:var(--spacing-12) var(--spacing-16) var(--spacing-16);flex-direction:column;display:flex}.tab-empty{padding:var(--spacing-24) 0;text-align:center;color:var(--color-white-alpha-40);font-size:.8125rem}.tab-hint{color:var(--color-white-alpha-40);font-size:.6875rem;line-height:1.5}.q-list{gap:var(--spacing-2)}.q-row{align-items:center;gap:var(--spacing-8);width:100%;padding:var(--spacing-8);text-align:left;color:var(--color-white);border-radius:var(--border-radius-md);cursor:pointer;transition:background-color .12s var(--ease);background:0 0;border:none;display:flex}.q-row:hover{background-color:var(--color-white-alpha-05)}.q-row--selected{background-color:var(--color-white-alpha-10)}.q-row-check{width:1rem;color:var(--accent);flex-shrink:0;justify-content:center;align-items:center;display:flex}.q-row-check svg{width:1rem;height:1rem}.q-row-body{flex-direction:column;gap:.1rem;min-width:0;display:flex}.q-row-title{align-items:center;gap:var(--spacing-8);font-size:.8125rem;font-weight:600;display:flex}.q-row-playing{letter-spacing:.06em;text-transform:uppercase;color:var(--color-green-500);background:var(--color-green-900);padding:.05rem var(--spacing-4);border-radius:var(--border-radius-sm);font-size:.5625rem;font-weight:600}.q-row-sub{color:var(--color-white-alpha-60);text-overflow:ellipsis;white-space:nowrap;font-size:.6875rem;overflow:hidden}.latency-presets{gap:var(--spacing-4);flex-wrap:wrap;display:flex}.latency-readout{gap:var(--spacing-8);display:flex}.latency-stat{gap:var(--spacing-2);padding:var(--spacing-8) var(--spacing-12);background:var(--color-white-alpha-05);border-radius:var(--border-radius-md);flex-direction:column;flex:1;display:flex}.latency-stat-label{text-transform:uppercase;letter-spacing:.05em;color:var(--color-white-alpha-40);font-size:.625rem}.latency-stat-value{font-variant-numeric:tabular-nums;color:var(--accent);font-size:.9375rem;font-weight:600}.stat-card{gap:var(--spacing-8);padding:var(--spacing-12);background:var(--color-white-alpha-05);border-radius:var(--border-radius-md);flex-direction:column;display:flex}.stat-head{align-items:center;gap:var(--spacing-8);display:flex}.stat-icon{border-radius:var(--border-radius-sm);justify-content:center;align-items:center;width:1.5rem;height:1.5rem;display:flex}.stat-icon svg{width:.875rem;height:.875rem}.stat-title{text-transform:uppercase;letter-spacing:.05em;font-size:.75rem;font-weight:600}.stat-status{letter-spacing:.06em;text-transform:uppercase;color:var(--color-gray-100);background:var(--color-white-alpha-10);padding:.05rem var(--spacing-8);border-radius:var(--border-radius-sm);margin-left:auto;font-size:.5625rem;font-weight:600}.stat-card--video .stat-icon{background:var(--color-purple-900);color:var(--color-purple-400)}.stat-card--video .stat-title{color:var(--color-purple-400)}.stat-card--audio .stat-icon{background:var(--color-green-900);color:var(--color-green-400)}.stat-card--audio .stat-title{color:var(--color-green-400)}.stat-card--network .stat-icon{background:var(--color-blue-900);color:var(--color-blue-400)}.stat-card--network .stat-title{color:var(--color-blue-400)}.stat-grid{gap:var(--spacing-2) var(--spacing-12);grid-template-columns:auto 1fr;display:grid}.stat-line{display:contents}.stat-key{color:var(--color-white-alpha-60);font-size:.75rem}.stat-value{text-align:right;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;font-weight:600;overflow:hidden}.graph{gap:var(--spacing-2);margin-top:var(--spacing-4);flex-direction:column;display:flex}.graph-header{justify-content:space-between;align-items:baseline;display:flex}.graph-label{text-transform:uppercase;letter-spacing:.05em;color:var(--color-white-alpha-40);font-size:.625rem}.graph-value{font-variant-numeric:tabular-nums;font-size:.6875rem;font-weight:600}.graph-canvas{width:100%;height:2.5rem;display:block}.buffer{width:100%}.buffer-visualization{background:var(--color-white-alpha-10);border-radius:var(--spacing-4);cursor:pointer;user-select:none;width:100%;height:3.75rem;padding-left:var(--spacing-48);box-sizing:border-box;position:relative}.buffer-playhead{left:var(--spacing-48);width:var(--spacing-2);background:var(--color-white);z-index:3;position:absolute;top:0;bottom:0}.buffer-track{left:var(--spacing-48);height:var(--spacing-24);align-items:center;display:flex;position:absolute;right:0}.buffer-track--video{top:var(--spacing-4)}.buffer-track--audio{top:var(--spacing-32)}.buffer-track-label{width:2.5rem;color:var(--color-white-alpha-70);text-align:right;padding-right:var(--spacing-4);pointer-events:none;box-sizing:border-box;font-size:.625rem;position:absolute;left:-2.875rem}.buffer-canvas{width:100%;height:100%;position:absolute;top:0;left:0}.buffer-target-area{top:0;bottom:0;left:var(--spacing-48);pointer-events:none;position:absolute;right:0}.buffer-target-line{background:var(--color-orange-400,var(--color-orange-450));z-index:2;cursor:ew-resize;border-radius:var(--spacing-1);pointer-events:auto;width:.1875rem;position:absolute;top:0;bottom:0}.buffer-target-label{color:var(--color-orange-400,var(--color-orange-450));white-space:nowrap;margin-bottom:var(--spacing-2);opacity:0;pointer-events:none;font-size:.6875rem;font-weight:500;transition:opacity .15s;position:absolute;bottom:100%;left:50%;transform:translate(-50%)}.buffer-visualization:hover .buffer-target-label,.buffer-visualization--dragging .buffer-target-label{opacity:1}.buffer-help{color:var(--color-white-alpha-30,#ffffff4d);pointer-events:none;white-space:nowrap;font-size:.75rem;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.buffering{z-index:1;background-color:var(--color-black-alpha-40);width:100%;height:100%;backdrop-filter:blur(var(--spacing-2));pointer-events:auto;justify-content:center;align-items:center;display:flex;position:absolute;top:0;left:0}.buffering-spinner{border:var(--spacing-4) solid var(--color-white-alpha-20);border-top:var(--spacing-4) solid var(--color-white);border-radius:50%;width:2.5rem;height:2.5rem;animation:1s linear infinite buffer-spin}@keyframes buffer-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.watch-ui__offline-indicator{z-index:4;pointer-events:auto;background-color:#000;border-radius:.25rem;justify-content:center;align-items:center;padding:.5rem 1rem;display:flex;position:absolute;bottom:1rem;right:1rem}.watch-ui__offline-text{color:#fff;letter-spacing:.08em;text-shadow:none;font-size:.875rem;font-weight:700}@media (width<=768px){.watch-ui__offline-indicator{padding:.375rem .75rem;bottom:.75rem;right:.75rem}.watch-ui__offline-text{font-size:.75rem}}@media (width<=480px){.watch-ui__offline-indicator{padding:.25rem .5rem;bottom:.5rem;right:.5rem}.watch-ui__offline-text{font-size:.75rem}}", de = 2800, $ = class extends HTMLElement {
633
+ var de = "*,:before,:after{box-sizing:border-box}:host{--color-white:#fff;--color-white-alpha-05:#ffffff0d;--color-white-alpha-10:#ffffff1a;--color-white-alpha-15:#ffffff26;--color-white-alpha-20:#fff3;--color-white-alpha-25:#ffffff40;--color-white-alpha-30:#ffffff4d;--color-white-alpha-40:#fff6;--color-white-alpha-60:#fff9;--color-white-alpha-70:#ffffffb3;--color-white-alpha-90:#ffffffe6;--color-gray-100:#9ca3af;--color-gray-400:#6b7280;--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-500:#22c55e;--color-green-900:#3d4928;--color-yellow-400:#facc15;--color-orange-400:#f90;--color-orange-450:#fb923c;--color-orange-500:#f97316;--color-orange-900:#4d301b;--color-red-400:#f87171;--color-amber-600:#d97706;--color-orange-500-alpha-60:#f9731699;--color-orange-500-alpha-80:#f97316cc;--color-black-alpha-08:#00000014;--color-black-alpha-10:#0000001a;--color-black-alpha-40:#0006;--color-black-alpha-60:#0009;--color-black-alpha-85:#000000d9;--color-panel:#121214eb;--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:.375rem;--border-radius-md:.5rem;--border-radius-lg:.75rem;--accent:var(--color-orange-400);--ease:cubic-bezier(.4, 0, .2, 1);font-family:system-ui,Segoe UI,Roboto,sans-serif}.flex-center{justify-content:center;align-items:center;display:flex}.flex-align-center{align-items:center;display:flex}.control{width:2.25rem;height:2.25rem;color:var(--color-white);cursor:pointer;transition:background-color .12s var(--ease), transform .12s var(--ease), color .12s var(--ease);background:0 0;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:inline-flex}.control svg{width:1.25rem;height:1.25rem}.control:hover:not(:disabled){background-color:var(--color-white-alpha-15)}.control:active:not(:disabled){transform:scale(.92)}.control--active{color:var(--accent)}.control:disabled{opacity:.4;cursor:default}.chip{padding:var(--spacing-4) var(--spacing-12);color:var(--color-white-alpha-70);background-color:var(--color-white-alpha-10);border:var(--spacing-1) solid transparent;cursor:pointer;transition:background-color .12s var(--ease), color .12s var(--ease), border-color .12s var(--ease);border-radius:999px;font-size:.75rem;font-weight:500}.chip:hover{background-color:var(--color-white-alpha-20);color:var(--color-white)}.chip--active{color:var(--color-gray-950);background-color:var(--accent);border-color:var(--accent)}:host{display:block;position:relative}.player{border-radius:var(--spacing-8);background:#000;justify-content:center;align-items:center;width:100%;line-height:0;display:flex;position:relative;overflow:hidden}.player ::slotted(moq-watch){width:100%;line-height:0;display:block}.player:not(.player--chrome){cursor:none}.player:fullscreen,.player--pseudo-fullscreen{border-radius:0;justify-content:center;align-items:center;display:flex}.player:-webkit-full-screen{border-radius:0;justify-content:center;align-items:center;display:flex}.player--pseudo-fullscreen{z-index:2147483647;width:100vw;height:100vh;position:fixed;inset:0}@supports (height:100dvh){.player--pseudo-fullscreen{height:100dvh}}.player:fullscreen ::slotted(moq-watch),.player--pseudo-fullscreen ::slotted(moq-watch){width:100%;height:100%;max-height:100%}.player:-webkit-full-screen ::slotted(moq-watch){width:100%;height:100%;max-height:100%}.scrim{pointer-events:none;opacity:0;transition:opacity .2s var(--ease);position:absolute;left:0;right:0}.scrim--top{background:linear-gradient(to bottom, var(--color-black-alpha-40), transparent);height:25%;top:0}.scrim--bottom{background:linear-gradient(to top, var(--color-black-alpha-60), transparent 60%);height:100%;bottom:0}.player--chrome .scrim{opacity:1}.center{pointer-events:none;justify-content:center;align-items:center;line-height:1;display:flex;position:absolute;inset:0}.center-play{pointer-events:auto;width:4.5rem;height:4.5rem;color:var(--color-white);background:linear-gradient(to bottom right, var(--color-orange-400), var(--color-orange-500), var(--color-amber-600));cursor:pointer;box-shadow:0 var(--spacing-8) var(--spacing-24) var(--color-black-alpha-40);transition:transform .15s var(--ease), box-shadow .15s var(--ease);border:none;border-radius:50%}.center-play svg{width:2rem;height:2rem}.center-play:hover{box-shadow:0 var(--spacing-8) var(--spacing-32) var(--color-orange-500-alpha-60);transform:scale(1.06)}.center-play:active{transform:scale(.97)}.chrome{opacity:0;pointer-events:none;transition:opacity .2s var(--ease), transform .2s var(--ease);flex-direction:column;justify-content:flex-end;line-height:1.4;display:flex;position:absolute;bottom:0;left:0;right:0;transform:translateY(.5rem)}.player--chrome .chrome{opacity:1;pointer-events:auto;transform:translateY(0)}.controls{align-items:center;gap:var(--spacing-4);padding:var(--spacing-8) var(--spacing-12) var(--spacing-12);display:flex;position:relative}.controls-group{align-items:center;gap:var(--spacing-4);display:flex}.controls-spacer{flex:1}.volume{gap:var(--spacing-4)}.volume-track{appearance:none;background:linear-gradient(to right, var(--color-white) 0%, var(--color-white) var(--fill,50%), var(--color-white-alpha-30) var(--fill,50%));cursor:pointer;opacity:0;width:0;max-width:0;height:.25rem;transition:width .16s var(--ease), max-width .16s var(--ease), opacity .16s var(--ease);border-radius:999px;outline:none;margin:0}.volume:hover .volume-track,.volume:focus-within .volume-track{opacity:1;width:4.5rem;max-width:4.5rem}.volume-track::-webkit-slider-thumb{appearance:none;background:var(--color-white);cursor:pointer;border:none;border-radius:50%;width:.75rem;height:.75rem}.volume-track::-moz-range-thumb{background:var(--color-white);cursor:pointer;border:none;border-radius:50%;width:.75rem;height:.75rem}.badge{gap:var(--spacing-8);height:2.25rem;padding:0 var(--spacing-12);margin-left:var(--spacing-4);color:var(--color-white);border-radius:var(--border-radius-sm);cursor:pointer;transition:background-color .12s var(--ease);background:0 0;border:none;font-size:.8125rem}.badge:hover{background-color:var(--color-white-alpha-10)}.badge-dot{background:var(--color-gray-400);border-radius:50%;flex-shrink:0;width:.5rem;height:.5rem}.badge-text{letter-spacing:.04em;font-weight:600}.badge-latency{max-width:0;margin-left:calc(-1 * var(--spacing-8));opacity:0;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--color-white-alpha-60);transition:max-width .16s var(--ease), margin-left .16s var(--ease), opacity .16s var(--ease);overflow:hidden}.badge:hover .badge-latency,.badge:focus-visible .badge-latency{opacity:1;max-width:9rem;margin-left:0}.badge[data-variant=live] .badge-dot{background:var(--color-red-400);animation:badge-pulse 2s var(--ease) infinite;box-shadow:0 0 #f87171b3}.badge[data-variant=live] .badge-text{color:var(--color-red-400)}.badge[data-variant=loading] .badge-dot,.badge[data-variant=connecting] .badge-dot{background:var(--color-yellow-400)}.badge[data-variant=error] .badge-dot{background:var(--color-gray-400)}.badge[data-variant=error] .badge-text{color:var(--color-gray-100)}@keyframes badge-pulse{0%{box-shadow:0 0 #f8717199}70%{box-shadow:0 0 0 .4rem #f8717100}to{box-shadow:0 0 #f8717100}}.panel{top:var(--spacing-8);right:var(--spacing-8);bottom:var(--spacing-8);width:20rem;max-width:calc(100% - var(--spacing-16));background:var(--color-panel);backdrop-filter:blur(12px);border:var(--spacing-1) solid var(--color-white-alpha-15);border-radius:var(--border-radius-lg);box-shadow:0 var(--spacing-8) var(--spacing-32) var(--color-black-alpha-60);color:var(--color-white);z-index:4;opacity:0;pointer-events:none;transition:opacity .2s var(--ease), transform .2s var(--ease);flex-direction:column;line-height:1.4;display:flex;position:absolute;overflow:hidden;transform:translate(.75rem)}.panel--open{opacity:1;pointer-events:auto;transform:translate(0)}.panel-head{padding:var(--spacing-8) var(--spacing-8) var(--spacing-8) var(--spacing-16);border-bottom:var(--spacing-1) solid var(--color-white-alpha-10);justify-content:space-between;align-items:center;display:flex}.panel-head-title{gap:var(--spacing-8);font-size:.875rem;font-weight:600}.panel-head-title svg{width:1.125rem;height:1.125rem}.panel-head .control{width:2rem;height:2rem}.panel-tabs{padding:var(--spacing-8);gap:var(--spacing-4);display:flex}.panel-tab{align-items:center;gap:var(--spacing-4);padding:var(--spacing-8) var(--spacing-4);color:var(--color-white-alpha-60);border-radius:var(--border-radius-md);cursor:pointer;transition:background-color .12s var(--ease), color .12s var(--ease);background:0 0;border:none;flex-direction:column;flex:1;font-size:.6875rem;font-weight:500;display:flex}.panel-tab svg{width:1.125rem;height:1.125rem}.panel-tab:hover{background-color:var(--color-white-alpha-05);color:var(--color-white)}.panel-tab--active{background-color:var(--color-white-alpha-10);color:var(--color-white)}.panel-body{scrollbar-width:thin;scrollbar-color:var(--color-white-alpha-20) transparent;flex:1;min-height:0;overflow-y:auto}.panel-body::-webkit-scrollbar{width:.375rem}.panel-body::-webkit-scrollbar-thumb{background:var(--color-white-alpha-20);border-radius:999px}.tab-body{gap:var(--spacing-12);padding:var(--spacing-12) var(--spacing-16) var(--spacing-16);flex-direction:column;display:flex}.tab-empty{padding:var(--spacing-24) 0;text-align:center;color:var(--color-white-alpha-40);font-size:.8125rem}.tab-hint{color:var(--color-white-alpha-40);font-size:.6875rem;line-height:1.5}.q-list{gap:var(--spacing-2)}.q-row{align-items:center;gap:var(--spacing-8);width:100%;padding:var(--spacing-8);text-align:left;color:var(--color-white);border-radius:var(--border-radius-md);cursor:pointer;transition:background-color .12s var(--ease);background:0 0;border:none;display:flex}.q-row:hover{background-color:var(--color-white-alpha-05)}.q-row--selected{background-color:var(--color-white-alpha-10)}.q-row-check{width:1rem;color:var(--accent);flex-shrink:0;justify-content:center;align-items:center;display:flex}.q-row-check svg{width:1rem;height:1rem}.q-row-body{flex-direction:column;gap:.1rem;min-width:0;display:flex}.q-row-title{align-items:center;gap:var(--spacing-8);font-size:.8125rem;font-weight:600;display:flex}.q-row-playing{letter-spacing:.06em;text-transform:uppercase;color:var(--color-green-500);background:var(--color-green-900);padding:.05rem var(--spacing-4);border-radius:var(--border-radius-sm);font-size:.5625rem;font-weight:600}.q-row-sub{color:var(--color-white-alpha-60);text-overflow:ellipsis;white-space:nowrap;font-size:.6875rem;overflow:hidden}.latency-presets{gap:var(--spacing-4);flex-wrap:wrap;display:flex}.latency-readout{gap:var(--spacing-8);display:flex}.latency-stat{gap:var(--spacing-2);padding:var(--spacing-8) var(--spacing-12);background:var(--color-white-alpha-05);border-radius:var(--border-radius-md);flex-direction:column;flex:1;display:flex}.latency-stat-label{text-transform:uppercase;letter-spacing:.05em;color:var(--color-white-alpha-40);font-size:.625rem}.latency-stat-value{font-variant-numeric:tabular-nums;color:var(--accent);font-size:.9375rem;font-weight:600}.stat-card{gap:var(--spacing-8);padding:var(--spacing-12);background:var(--color-white-alpha-05);border-radius:var(--border-radius-md);flex-direction:column;display:flex}.stat-head{align-items:center;gap:var(--spacing-8);display:flex}.stat-icon{border-radius:var(--border-radius-sm);justify-content:center;align-items:center;width:1.5rem;height:1.5rem;display:flex}.stat-icon svg{width:.875rem;height:.875rem}.stat-title{text-transform:uppercase;letter-spacing:.05em;font-size:.75rem;font-weight:600}.stat-status{letter-spacing:.06em;text-transform:uppercase;color:var(--color-gray-100);background:var(--color-white-alpha-10);padding:.05rem var(--spacing-8);border-radius:var(--border-radius-sm);margin-left:auto;font-size:.5625rem;font-weight:600}.stat-card--video .stat-icon{background:var(--color-purple-900);color:var(--color-purple-400)}.stat-card--video .stat-title{color:var(--color-purple-400)}.stat-card--audio .stat-icon{background:var(--color-green-900);color:var(--color-green-400)}.stat-card--audio .stat-title{color:var(--color-green-400)}.stat-card--network .stat-icon{background:var(--color-blue-900);color:var(--color-blue-400)}.stat-card--network .stat-title{color:var(--color-blue-400)}.stat-grid{gap:var(--spacing-2) var(--spacing-12);grid-template-columns:auto 1fr;display:grid}.stat-line{display:contents}.stat-key{color:var(--color-white-alpha-60);font-size:.75rem}.stat-value{text-align:right;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;font-weight:600;overflow:hidden}.graph{gap:var(--spacing-2);margin-top:var(--spacing-4);flex-direction:column;display:flex}.graph-header{justify-content:space-between;align-items:baseline;display:flex}.graph-label{text-transform:uppercase;letter-spacing:.05em;color:var(--color-white-alpha-40);font-size:.625rem}.graph-value{font-variant-numeric:tabular-nums;font-size:.6875rem;font-weight:600}.graph-canvas{width:100%;height:2.5rem;display:block}.buffer{width:100%}.buffer-visualization{background:var(--color-white-alpha-10);border-radius:var(--spacing-4);cursor:pointer;user-select:none;width:100%;height:3.75rem;padding-left:var(--spacing-48);box-sizing:border-box;position:relative}.buffer-playhead{left:var(--spacing-48);width:var(--spacing-2);background:var(--color-white);z-index:3;position:absolute;top:0;bottom:0}.buffer-track{left:var(--spacing-48);height:var(--spacing-24);align-items:center;display:flex;position:absolute;right:0}.buffer-track--video{top:var(--spacing-4)}.buffer-track--audio{top:var(--spacing-32)}.buffer-track-label{width:2.5rem;color:var(--color-white-alpha-70);text-align:right;padding-right:var(--spacing-4);pointer-events:none;box-sizing:border-box;font-size:.625rem;position:absolute;left:-2.875rem}.buffer-canvas{width:100%;height:100%;position:absolute;top:0;left:0}.buffer-target-area{top:0;bottom:0;left:var(--spacing-48);pointer-events:none;position:absolute;right:0}.buffer-target-line{background:var(--color-orange-400,var(--color-orange-450));z-index:2;cursor:ew-resize;border-radius:var(--spacing-1);pointer-events:auto;width:.1875rem;position:absolute;top:0;bottom:0}.buffer-target-label{color:var(--color-orange-400,var(--color-orange-450));white-space:nowrap;margin-bottom:var(--spacing-2);opacity:0;pointer-events:none;font-size:.6875rem;font-weight:500;transition:opacity .15s;position:absolute;bottom:100%;left:50%;transform:translate(-50%)}.buffer-visualization:hover .buffer-target-label,.buffer-visualization--dragging .buffer-target-label{opacity:1}.buffer-help{color:var(--color-white-alpha-30,#ffffff4d);pointer-events:none;white-space:nowrap;font-size:.75rem;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.buffering{z-index:1;background-color:var(--color-black-alpha-40);width:100%;height:100%;backdrop-filter:blur(var(--spacing-2));pointer-events:auto;justify-content:center;align-items:center;display:flex;position:absolute;top:0;left:0}.buffering-spinner{border:var(--spacing-4) solid var(--color-white-alpha-20);border-top:var(--spacing-4) solid var(--color-white);border-radius:50%;width:2.5rem;height:2.5rem;animation:1s linear infinite buffer-spin}@keyframes buffer-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.watch-ui__offline-indicator{z-index:4;pointer-events:auto;background-color:#000;border-radius:.25rem;justify-content:center;align-items:center;padding:.5rem 1rem;display:flex;position:absolute;bottom:1rem;right:1rem}.watch-ui__offline-text{color:#fff;letter-spacing:.08em;text-shadow:none;font-size:.875rem;font-weight:700}@media (width<=768px){.watch-ui__offline-indicator{padding:.375rem .75rem;bottom:.75rem;right:.75rem}.watch-ui__offline-text{font-size:.75rem}}@media (width<=480px){.watch-ui__offline-indicator{padding:.25rem .5rem;bottom:.5rem;right:.5rem}.watch-ui__offline-text{font-size:.75rem}}.watch-ui__unsupported-indicator{text-align:center;background-color:var(--color-black-alpha-85);z-index:4;pointer-events:auto;border-radius:.25rem;justify-content:center;align-items:center;max-width:min(80%,32rem);padding:.75rem 1.25rem;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.watch-ui__unsupported-text{color:var(--color-white);font-size:.875rem;font-weight:600;line-height:1.4}@media (width<=480px){.watch-ui__unsupported-indicator{max-width:90%;padding:.625rem .875rem}.watch-ui__unsupported-text{font-size:.75rem}}", fe = 2800, $ = class extends HTMLElement {
621
634
  #e;
622
635
  #t;
623
636
  #n = new n(void 0);
@@ -625,7 +638,7 @@ var ue = "*,:before,:after{box-sizing:border-box}:host{--color-white:#fff;--colo
625
638
  constructor() {
626
639
  super(), this.#t = this.attachShadow({ mode: "open" });
627
640
  let e = document.createElement("style");
628
- e.textContent = ue, this.#t.appendChild(e), this.#r = new MutationObserver(() => this.#i());
641
+ e.textContent = de, this.#t.appendChild(e), this.#r = new MutationObserver(() => this.#i());
629
642
  }
630
643
  connectedCallback() {
631
644
  this.#i(), this.#r.observe(this, { childList: !0 });
@@ -649,7 +662,7 @@ var ue = "*,:before,:after{box-sizing:border-box}:host{--color-white:#fff;--colo
649
662
  }, o = r.create("div", { className: "player" });
650
663
  o.appendChild(r.create("slot"));
651
664
  let s = r.create("div", { className: "center" });
652
- s.append(S(e, t), i(e, t), I(e, t));
665
+ s.append(S(e, t), i(e, t), I(e, t), ue(e, t));
653
666
  let c = r.create("div", { className: "scrim scrim--top" }), l = r.create("div", { className: "chrome" });
654
667
  l.append(r.create("div", { className: "scrim scrim--bottom" }), F(e, t, a, o));
655
668
  let u = le(e, t, a);
@@ -686,7 +699,7 @@ var ue = "*,:before,:after{box-sizing:border-box}:host{--color-white:#fff;--colo
686
699
  let l = () => t.backend.paused.peek() || r.panel.peek();
687
700
  e.run((e) => {
688
701
  let n = e.get(t.backend.paused) || e.get(r.panel);
689
- e.get(s), r.chrome.set(!0), !n && e.timer(() => r.chrome.set(!1), de);
702
+ e.get(s), r.chrome.set(!0), !n && e.timer(() => r.chrome.set(!1), fe);
690
703
  }), e.event(this, "pointerleave", () => {
691
704
  l() || r.chrome.set(!1);
692
705
  }), e.run((e) => {