@hyperframes/player 0.6.0-alpha.9 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hyperframes-player.cjs +1 -1
- package/dist/hyperframes-player.cjs.map +1 -1
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/hyperframes-player.global.js.map +1 -1
- package/dist/hyperframes-player.js +1 -1
- package/dist/hyperframes-player.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hyperframes-player.ts","../src/styles.ts","../src/controls.ts","../src/shouldInjectRuntime.ts"],"sourcesContent":["import { createControls, SPEED_PRESETS, type ControlsCallbacks } from \"./controls.js\";\nimport { shouldInjectRuntime } from \"./shouldInjectRuntime.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\n\nlet sharedSheet: CSSStyleSheet | null = null;\n\nfunction getSharedSheet(): CSSStyleSheet | null {\n if (sharedSheet) return sharedSheet;\n if (typeof CSSStyleSheet === \"undefined\") return null;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(PLAYER_STYLES);\n sharedSheet = sheet;\n return sheet;\n } catch {\n return null;\n }\n}\n\nconst DEFAULT_FPS = 30;\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\nconst SHADER_CAPTURE_SCALE_ATTR = \"shader-capture-scale\";\nconst SHADER_LOADING_ATTR = \"shader-loading\";\nconst SHADER_CAPTURE_SCALE_PARAM = \"__hf_shader_capture_scale\";\nconst SHADER_LOADING_PARAM = \"__hf_shader_loading\";\n\nexport type ShaderLoadingMode = \"composition\" | \"player\" | \"none\";\n\ninterface ShaderTransitionState {\n ready?: boolean;\n progress?: number;\n total?: number;\n currentTransition?: number;\n transitionTotal?: number;\n transitionFrame?: number;\n transitionFrames?: number;\n phase?: \"cached\" | \"capturing\" | \"finalizing\";\n loading?: boolean;\n}\n\ninterface ShaderLoaderElements {\n root: HTMLDivElement;\n fill: HTMLDivElement;\n title: HTMLSpanElement;\n detail: HTMLDivElement;\n transitionValue: HTMLSpanElement;\n frameLabel: HTMLSpanElement;\n frameValue: HTMLSpanElement;\n frameRow: HTMLDivElement;\n}\n\ninterface RuntimeDurationAdapter {\n getDuration: () => number;\n}\n\ninterface DirectTimelineAdapter {\n duration: () => number;\n time: () => number;\n seek: (timeInSeconds: number) => unknown;\n play: () => unknown;\n pause: () => unknown;\n}\n\ntype PlaybackDurationAdapter =\n | { kind: \"runtime\"; getDuration: () => number }\n | { kind: \"direct-timeline\"; timeline: DirectTimelineAdapter; getDuration: () => number };\n\nconst SHADER_LOADING_PHRASES = [\n \"Preparing scene transitions\",\n \"Sampling outgoing scene motion\",\n \"Sampling incoming scene motion\",\n \"Caching transition frames\",\n \"Finalizing transition preview\",\n];\n\nfunction normalizeShaderCaptureScale(value: string | null): string | null {\n if (value === null) return null;\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed <= 0) return null;\n return String(Math.min(1, Math.max(0.25, parsed)));\n}\n\nfunction normalizeShaderLoadingMode(value: string | null): ShaderLoadingMode {\n if (value === null || value.trim() === \"\") return \"composition\";\n const normalized = value.trim().toLowerCase();\n if (\n normalized === \"none\" ||\n normalized === \"false\" ||\n normalized === \"0\" ||\n normalized === \"off\"\n ) {\n return \"none\";\n }\n if (\n normalized === \"player\" ||\n normalized === \"true\" ||\n normalized === \"1\" ||\n normalized === \"on\"\n ) {\n return \"player\";\n }\n return \"composition\";\n}\n\nfunction setQueryParam(params: URLSearchParams, key: string, value: string | null): void {\n if (value === null) params.delete(key);\n else params.set(key, value);\n}\n\nfunction withShaderQueryParams(\n src: string,\n scale: string | null,\n loadingMode: ShaderLoadingMode,\n): string {\n const hashIndex = src.indexOf(\"#\");\n const beforeHash = hashIndex >= 0 ? src.slice(0, hashIndex) : src;\n const hash = hashIndex >= 0 ? src.slice(hashIndex) : \"\";\n const queryIndex = beforeHash.indexOf(\"?\");\n const path = queryIndex >= 0 ? beforeHash.slice(0, queryIndex) : beforeHash;\n const query = queryIndex >= 0 ? beforeHash.slice(queryIndex + 1) : \"\";\n const params = new URLSearchParams(query);\n setQueryParam(params, SHADER_CAPTURE_SCALE_PARAM, scale);\n setQueryParam(params, SHADER_LOADING_PARAM, loadingMode === \"composition\" ? null : loadingMode);\n const nextQuery = params.toString();\n return `${path}${nextQuery ? `?${nextQuery}` : \"\"}${hash}`;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isRuntimeDurationAdapter(value: unknown): value is RuntimeDurationAdapter {\n return isObjectRecord(value) && typeof value.getDuration === \"function\";\n}\n\nfunction isDirectTimelineAdapter(value: unknown): value is DirectTimelineAdapter {\n return (\n isObjectRecord(value) &&\n typeof value.duration === \"function\" &&\n typeof value.time === \"function\" &&\n typeof value.seek === \"function\" &&\n typeof value.play === \"function\" &&\n typeof value.pause === \"function\"\n );\n}\n\nfunction injectShaderOptionsIntoSrcdoc(\n html: string,\n scale: string | null,\n loadingMode: ShaderLoadingMode,\n): string {\n if (scale === null && loadingMode === \"composition\") return html;\n const lines: string[] = [];\n if (scale !== null) lines.push(`window.__HF_SHADER_CAPTURE_SCALE=${JSON.stringify(scale)};`);\n if (loadingMode !== \"composition\") {\n lines.push(`window.__HF_SHADER_LOADING=${JSON.stringify(loadingMode)};`);\n }\n const script = `<script data-hyperframes-player-shader-options>${lines.join(\"\")}</script>`;\n if (/<head\\b[^>]*>/i.test(html))\n return html.replace(/<head\\b[^>]*>/i, (match) => `${match}${script}`);\n if (/<html\\b[^>]*>/i.test(html))\n return html.replace(/<html\\b[^>]*>/i, (match) => `${match}${script}`);\n return `${script}${html}`;\n}\n\nclass HyperframesPlayer extends HTMLElement {\n static get observedAttributes() {\n return [\n \"src\",\n \"srcdoc\",\n \"width\",\n \"height\",\n \"controls\",\n \"muted\",\n \"volume\",\n \"poster\",\n \"playback-rate\",\n \"audio-src\",\n SHADER_CAPTURE_SCALE_ATTR,\n SHADER_LOADING_ATTR,\n ];\n }\n\n private shadow: ShadowRoot;\n private container: HTMLDivElement;\n private iframe: HTMLIFrameElement;\n private posterEl: HTMLImageElement | null = null;\n private controlsApi: ReturnType<typeof createControls> | null = null;\n private resizeObserver: ResizeObserver;\n private shaderLoaderEl: HTMLDivElement;\n private shaderLoaderFillEl: HTMLDivElement;\n private shaderLoaderTitleEl: HTMLSpanElement;\n private shaderLoaderDetailEl: HTMLDivElement;\n private shaderLoaderTransitionValueEl: HTMLSpanElement;\n private shaderLoaderFrameLabelEl: HTMLSpanElement;\n private shaderLoaderFrameValueEl: HTMLSpanElement;\n private shaderLoaderFrameRowEl: HTMLDivElement;\n private shaderLoaderHideTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private _ready = false;\n private _duration = 0;\n private _currentTime = 0;\n private _paused = true;\n private _volume = 1;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _probeInterval: ReturnType<typeof setInterval> | null = null;\n private _lastUpdateMs = 0;\n private _directTimelineAdapter: DirectTimelineAdapter | null = null;\n private _directTimelineRaf: number | null = null;\n\n /**\n * Parent-frame audio/video proxies, preloaded mirror copies of the iframe's\n * timed media. They exist as a fallback for environments that block iframe\n * `.play()` — mobile browsers require the user gesture to originate in the\n * same frame as the media element, and postMessage doesn't transfer user\n * activation (User Activation v2). The runtime inside the iframe signals\n * `media-autoplay-blocked` the first time a play() attempt rejects with\n * `NotAllowedError`; receiving that message flips `_audioOwner` to `parent`\n * and these proxies start driving audible output while the iframe keeps\n * advancing timed media silently for frame-accurate state.\n *\n * Preloading at iframe-load time (rather than lazily on promotion) keeps\n * the audible audio cut-in tight when the promotion fires mid-playback.\n */\n private _parentMedia: Array<{\n el: HTMLMediaElement;\n start: number;\n duration: number;\n /**\n * Count of consecutive steady-state samples in which the proxy's\n * `currentTime` was found drifted beyond `MIRROR_DRIFT_THRESHOLD_SECONDS`.\n * Reset on every in-threshold sample. `_mirrorParentMediaTime` only\n * issues a write once this passes `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES`,\n * which absorbs single-sample jitter (e.g. one slow bridge tick) without\n * thrashing the media element with seeks. Forced calls (promotion,\n * media-added) bypass the gate and reset the counter.\n */\n driftSamples: number;\n }> = [];\n\n /**\n * Who owns audible playback right now.\n *\n * - `runtime` (default): the iframe's runtime drives timed media; parent\n * proxies stay paused and silent. This is the correct path on desktop,\n * in same-frame embeds, and anywhere the iframe has user activation.\n * - `parent`: parent-frame proxies drive audible output; the iframe keeps\n * syncing timed media but at `muted = true` (orthogonal to author/user\n * volume settings). Entered only in response to an actual autoplay\n * rejection from the runtime — we don't guess device class.\n *\n * The transition is one-way per session; once autoplay is known to be\n * gated, there's no benefit to attempting the iframe path again.\n */\n private _audioOwner: \"runtime\" | \"parent\" = \"runtime\";\n\n /**\n * Watches the iframe document for sub-composition media added after\n * initial setup. Disconnected on iframe reload (fresh iframe = fresh\n * observer against the new document).\n */\n private _mediaObserver?: MutationObserver;\n\n /**\n * One-shot latch for `playbackerror`. Without it, under parent ownership\n * where the parent frame itself lacks activation, every paused→playing\n * transition in the iframe state loop would re-fire `play()` (and its\n * rejection) on each proxy — spamming host subscribers through a whole\n * playback session. Mirrors the `mediaAutoplayBlockedPosted` latch on the\n * runtime side. Cleared on `_onIframeLoad` alongside the owner reset, so\n * a fresh composition gets a fresh shot at surfacing the error.\n */\n private _playbackErrorPosted = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n const sheet = getSharedSheet();\n if (sheet) {\n this.shadow.adoptedStyleSheets = [sheet];\n } else {\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n this.shadow.appendChild(style);\n }\n\n this.container = document.createElement(\"div\");\n this.container.className = \"hfp-container\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.className = \"hfp-iframe\";\n this.iframe.sandbox.add(\"allow-scripts\", \"allow-same-origin\");\n this.iframe.allow = \"autoplay; fullscreen\";\n this.iframe.referrerPolicy = \"no-referrer\";\n this.iframe.title = \"HyperFrames Composition\";\n\n this.container.appendChild(this.iframe);\n this.shadow.appendChild(this.container);\n const shaderLoader = this._createShaderLoader();\n this.shaderLoaderEl = shaderLoader.root;\n this.shaderLoaderFillEl = shaderLoader.fill;\n this.shaderLoaderTitleEl = shaderLoader.title;\n this.shaderLoaderDetailEl = shaderLoader.detail;\n this.shaderLoaderTransitionValueEl = shaderLoader.transitionValue;\n this.shaderLoaderFrameLabelEl = shaderLoader.frameLabel;\n this.shaderLoaderFrameValueEl = shaderLoader.frameValue;\n this.shaderLoaderFrameRowEl = shaderLoader.frameRow;\n this.shadow.appendChild(this.shaderLoaderEl);\n\n // Clicking the bare player surface toggles play/pause.\n // Ignore shadow-DOM control interactions so overlay clicks don't double-handle.\n this.addEventListener(\"click\", (event) => {\n if (this._isControlsClick(event)) return;\n if (this._paused) this.play();\n else this.pause();\n });\n\n this.resizeObserver = new ResizeObserver(() => this._updateScale());\n\n this._onMessage = this._onMessage.bind(this);\n this._onIframeLoad = this._onIframeLoad.bind(this);\n }\n\n connectedCallback() {\n this.resizeObserver.observe(this);\n window.addEventListener(\"message\", this._onMessage);\n this.iframe.addEventListener(\"load\", this._onIframeLoad);\n\n if (this.hasAttribute(\"controls\")) this._setupControls();\n if (this.hasAttribute(\"poster\")) this._setupPoster();\n if (this.hasAttribute(\"audio-src\"))\n this._setupParentAudioFromUrl(this.getAttribute(\"audio-src\")!);\n // srcdoc wins over src per HTML spec when both are set; mirror both attributes\n // so the browser applies the standard precedence rules.\n if (this.hasAttribute(\"srcdoc\"))\n this.iframe.srcdoc = this._prepareSrcdoc(this.getAttribute(\"srcdoc\")!);\n if (this.hasAttribute(\"src\")) this.iframe.src = this._prepareSrc(this.getAttribute(\"src\")!);\n }\n\n disconnectedCallback() {\n this.resizeObserver.disconnect();\n window.removeEventListener(\"message\", this._onMessage);\n this.iframe.removeEventListener(\"load\", this._onIframeLoad);\n if (this._probeInterval) clearInterval(this._probeInterval);\n this._stopDirectTimelineClock();\n this._directTimelineAdapter = null;\n if (this.shaderLoaderHideTimeout) clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n this._teardownMediaObserver();\n this.controlsApi?.destroy();\n for (const m of this._parentMedia) {\n m.el.pause();\n m.el.src = \"\";\n }\n this._parentMedia = [];\n }\n\n attributeChangedCallback(name: string, _old: string | null, val: string | null) {\n switch (name) {\n case \"src\":\n if (val) {\n this._ready = false;\n this.iframe.src = this._prepareSrc(val);\n }\n break;\n case \"srcdoc\":\n // Distinguish removal (null) from empty-string (\"\") so callers can clear\n // srcdoc and let src take over. Always reset readiness; the iframe will\n // load a new document either way.\n this._ready = false;\n if (val !== null) this.iframe.srcdoc = this._prepareSrcdoc(val);\n else this.iframe.removeAttribute(\"srcdoc\");\n break;\n case \"width\":\n this._compositionWidth = parseInt(val || \"1920\", 10);\n this._updateScale();\n break;\n case \"height\":\n this._compositionHeight = parseInt(val || \"1080\", 10);\n this._updateScale();\n break;\n case \"controls\":\n if (val !== null) this._setupControls();\n else {\n this.controlsApi?.destroy();\n this.controlsApi = null;\n }\n break;\n case \"poster\":\n this._setupPoster();\n break;\n case \"playback-rate\": {\n const rate = parseFloat(val || \"1\");\n for (const m of this._parentMedia) m.el.playbackRate = rate;\n this._sendControl(\"set-playback-rate\", { playbackRate: rate });\n this.controlsApi?.updateSpeed(rate);\n this.dispatchEvent(new Event(\"ratechange\"));\n break;\n }\n case \"muted\":\n for (const m of this._parentMedia) m.el.muted = val !== null;\n this._sendControl(\"set-muted\", { muted: val !== null });\n this.controlsApi?.updateMuted(val !== null);\n this.dispatchEvent(new Event(\"volumechange\"));\n break;\n case \"volume\": {\n const v = Math.max(0, Math.min(1, parseFloat(val || \"1\")));\n this._volume = v;\n for (const m of this._parentMedia) m.el.volume = v;\n this._sendControl(\"set-volume\", { volume: v });\n this.controlsApi?.updateVolume(v);\n this.dispatchEvent(new Event(\"volumechange\"));\n break;\n }\n case \"audio-src\":\n if (val) this._setupParentAudioFromUrl(val);\n break;\n case SHADER_CAPTURE_SCALE_ATTR:\n case SHADER_LOADING_ATTR:\n this._reloadShaderOptions();\n break;\n }\n }\n\n // ── Public API ──\n\n /**\n * Access the inner `<iframe>` element rendering the composition.\n *\n * Use this when integrating the player with editors, recorders, or\n * timeline tools (e.g. `@hyperframes/studio`) that need to inspect\n * the composition's DOM or read its `__player` / `__timelines`\n * runtime objects.\n *\n * **Common pitfall:** the iframe lives inside the player's Shadow DOM.\n * Passing the `<hyperframes-player>` element itself to code that expects\n * an `<iframe>` will silently break — `.contentWindow` returns `null`.\n * Always extract `iframeElement` first:\n *\n * ```ts\n * // ❌ Wrong — element ref doesn't expose contentWindow\n * iframeRef.current = playerRef.current;\n *\n * // ✓ Right — bridge the actual iframe\n * iframeRef.current = playerRef.current.iframeElement;\n * ```\n */\n get iframeElement(): HTMLIFrameElement {\n return this.iframe;\n }\n\n play() {\n this._hidePoster();\n if (this._duration > 0 && this._currentTime >= this._duration) {\n this.seek(0);\n }\n // Drive the iframe runtime when present. Same-origin standalone GSAP\n // compositions can expose only `window.__timelines`, so they use a direct\n // timeline adapter instead of a postMessage bridge nobody is listening to.\n const directTimelineStarted = this._tryDirectTimelinePlay();\n if (!directTimelineStarted) this._sendControl(\"play\");\n if (this._audioOwner === \"parent\") this._playParentMedia();\n this._paused = false;\n this.controlsApi?.updatePlaying(true);\n this.dispatchEvent(new Event(\"play\"));\n if (directTimelineStarted) this._startDirectTimelineClock();\n }\n\n pause() {\n if (!this._tryDirectTimelinePause()) this._sendControl(\"pause\");\n this._stopDirectTimelineClock();\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"pause\"));\n }\n\n /**\n * Move playback to `timeInSeconds`.\n *\n * Two transports, with different precision semantics — read this before\n * writing assertions against `seek` from outside the player:\n *\n * - **Same-origin (sync) path** — when the runtime's `window.__player.seek`\n * is reachable, we call it directly. `timeInSeconds` is forwarded\n * *verbatim* (no rounding), so a same-origin scrub of `seek(7.3333)`\n * lands the runtime at `7.3333 s` — sub-frame precision relative to\n * `DEFAULT_FPS` (30). Studio scrub UIs that need fractional-frame\n * alignment (e.g. waveform scrubbing on long-duration audio) get the\n * exact requested time.\n * - **Cross-origin (postMessage) path** — when same-origin access throws\n * or `__player.seek` is missing, we fall back to the postMessage bridge.\n * The wire protocol carries integer frames (`frame: Math.round(t × FPS)`),\n * so cross-origin embeds are *frame-quantized* and `seek(7.3333)` lands\n * at `Math.round(7.3333 × 30) / 30 ≈ 7.3333…` (same value here, but for\n * most fractional inputs you'll see a snap to the nearest 1/30 s).\n *\n * `this._currentTime` always reflects the *requested* `timeInSeconds`\n * regardless of transport, so the controls UI shows the un-quantized value\n * either way; the asymmetry only affects what the runtime actually paints.\n */\n seek(timeInSeconds: number) {\n if (!this._trySyncSeek(timeInSeconds) && !this._tryDirectTimelineSeek(timeInSeconds)) {\n const frame = Math.round(timeInSeconds * DEFAULT_FPS);\n this._sendControl(\"seek\", { frame });\n }\n this._stopDirectTimelineClock();\n this._currentTime = timeInSeconds;\n\n // Mirror parent proxy currentTime only while parent owns audible output.\n // Under `runtime` ownership the proxies are paused and authoritative time\n // lives on the iframe — touching parent currentTime would just trigger\n // needless buffering if ownership later flips.\n if (this._audioOwner === \"parent\") {\n for (const m of this._parentMedia) {\n const relTime = timeInSeconds - m.start;\n if (relTime >= 0 && relTime < m.duration) m.el.currentTime = relTime;\n }\n }\n\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n }\n\n get currentTime() {\n return this._currentTime;\n }\n set currentTime(t: number) {\n this.seek(t);\n }\n\n get duration() {\n return this._duration;\n }\n get paused() {\n return this._paused;\n }\n get ready() {\n return this._ready;\n }\n\n get playbackRate() {\n return parseFloat(this.getAttribute(\"playback-rate\") || \"1\");\n }\n set playbackRate(r: number) {\n this.setAttribute(\"playback-rate\", String(r));\n }\n\n get shaderCaptureScale() {\n return Number(normalizeShaderCaptureScale(this.getAttribute(SHADER_CAPTURE_SCALE_ATTR)) ?? \"1\");\n }\n set shaderCaptureScale(scale: number) {\n this.setAttribute(SHADER_CAPTURE_SCALE_ATTR, String(scale));\n }\n\n get shaderLoading() {\n return normalizeShaderLoadingMode(this.getAttribute(SHADER_LOADING_ATTR));\n }\n set shaderLoading(mode: ShaderLoadingMode) {\n if (mode === \"composition\") this.removeAttribute(SHADER_LOADING_ATTR);\n else this.setAttribute(SHADER_LOADING_ATTR, mode);\n }\n\n get muted() {\n return this.hasAttribute(\"muted\");\n }\n set muted(m: boolean) {\n if (m) this.setAttribute(\"muted\", \"\");\n else this.removeAttribute(\"muted\");\n }\n\n get volume() {\n return this._volume;\n }\n set volume(v: number) {\n this.setAttribute(\"volume\", String(Math.max(0, Math.min(1, v))));\n }\n\n get loop() {\n return this.hasAttribute(\"loop\");\n }\n set loop(l: boolean) {\n if (l) this.setAttribute(\"loop\", \"\");\n else this.removeAttribute(\"loop\");\n }\n\n // ── Private ──\n\n private _sendControl(action: string, extra: Record<string, unknown> = {}) {\n try {\n this.iframe.contentWindow?.postMessage(\n { source: \"hf-parent\", type: \"control\", action, ...extra },\n \"*\",\n );\n } catch {\n /* cross-origin */\n }\n }\n\n private _shaderCaptureScaleParam(): string | null {\n return normalizeShaderCaptureScale(this.getAttribute(SHADER_CAPTURE_SCALE_ATTR));\n }\n\n private _shaderLoadingMode(): ShaderLoadingMode {\n return normalizeShaderLoadingMode(this.getAttribute(SHADER_LOADING_ATTR));\n }\n\n private _prepareSrc(src: string): string {\n return withShaderQueryParams(src, this._shaderCaptureScaleParam(), this._shaderLoadingMode());\n }\n\n private _prepareSrcdoc(srcdoc: string): string {\n return injectShaderOptionsIntoSrcdoc(\n srcdoc,\n this._shaderCaptureScaleParam(),\n this._shaderLoadingMode(),\n );\n }\n\n private _reloadShaderOptions(): void {\n if (this._shaderLoadingMode() !== \"player\") {\n this._resetShaderLoader();\n }\n if (this.hasAttribute(\"srcdoc\")) {\n this.iframe.srcdoc = this._prepareSrcdoc(this.getAttribute(\"srcdoc\") || \"\");\n return;\n }\n if (this.hasAttribute(\"src\")) {\n this.iframe.src = this._prepareSrc(this.getAttribute(\"src\") || \"\");\n }\n }\n\n private _createShaderLoader(): ShaderLoaderElements {\n const root = document.createElement(\"div\");\n root.className = \"hfp-shader-loader\";\n root.setAttribute(\"role\", \"status\");\n root.setAttribute(\"aria-live\", \"polite\");\n root.setAttribute(\"aria-label\", \"Preparing scene transitions\");\n root.setAttribute(\"data-hyperframes-ignore\", \"\");\n root.draggable = false;\n\n const blockOverlayInteraction = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n };\n for (const eventName of [\n \"selectstart\",\n \"dragstart\",\n \"pointerdown\",\n \"mousedown\",\n \"click\",\n \"dblclick\",\n \"contextmenu\",\n \"touchstart\",\n ]) {\n root.addEventListener(eventName, blockOverlayInteraction, { capture: true });\n }\n\n const panel = document.createElement(\"div\");\n panel.className = \"hfp-shader-loader-panel\";\n panel.draggable = false;\n\n const markFrame = document.createElement(\"div\");\n markFrame.className = \"hfp-shader-loader-mark\";\n markFrame.draggable = false;\n markFrame.innerHTML = [\n '<svg width=\"78\" height=\"78\" viewBox=\"0 0 100 100\" fill=\"none\" aria-hidden=\"true\" draggable=\"false\">',\n '<path d=\"M10.1851 57.8021L33.1145 73.8313C36.2202 75.9978 41.5173 73.5433 42.4816 69.4984L51.7611 30.4271C52.7253 26.3822 48.5802 23.9277 44.4602 26.0942L13.917 42.1235C6.96677 45.7676 4.97564 54.1579 10.1851 57.8021Z\" fill=\"url(#hfp-shader-loader-grad-left)\"/>',\n '<path d=\"M87.5129 57.5141L56.9696 73.5433C52.8371 75.7098 48.7046 73.2553 49.6688 69.2104L58.9483 30.1391C59.9125 26.0942 65.2097 23.6397 68.3154 25.8062L91.2447 41.8354C96.4668 45.4796 94.4631 53.8699 87.5129 57.5141Z\" fill=\"url(#hfp-shader-loader-grad-right)\"/>',\n \"<defs>\",\n '<linearGradient id=\"hfp-shader-loader-grad-left\" x1=\"48.5676\" y1=\"25\" x2=\"44.7804\" y2=\"71.9384\" gradientUnits=\"userSpaceOnUse\">',\n '<stop stop-color=\"#06E3FA\"/>',\n '<stop offset=\"1\" stop-color=\"#4FDB5E\"/>',\n \"</linearGradient>\",\n '<linearGradient id=\"hfp-shader-loader-grad-right\" x1=\"54.8282\" y1=\"73.8392\" x2=\"72.0989\" y2=\"32.8932\" gradientUnits=\"userSpaceOnUse\">',\n '<stop stop-color=\"#06E3FA\"/>',\n '<stop offset=\"1\" stop-color=\"#4FDB5E\"/>',\n \"</linearGradient>\",\n \"</defs>\",\n \"</svg>\",\n ].join(\"\");\n\n const title = document.createElement(\"div\");\n title.className = \"hfp-shader-loader-title\";\n const titleText = document.createElement(\"span\");\n titleText.className = \"hfp-shader-loader-title-text\";\n titleText.textContent = SHADER_LOADING_PHRASES[0] || \"Preparing scene transitions\";\n title.appendChild(titleText);\n\n const detail = document.createElement(\"div\");\n detail.className = \"hfp-shader-loader-detail\";\n detail.textContent = \"Rendering animated scene samples for shader transitions.\";\n\n const track = document.createElement(\"div\");\n track.className = \"hfp-shader-loader-track\";\n track.setAttribute(\"aria-hidden\", \"true\");\n const fill = document.createElement(\"div\");\n fill.className = \"hfp-shader-loader-fill\";\n track.appendChild(fill);\n\n const progress = document.createElement(\"div\");\n progress.className = \"hfp-shader-loader-progress\";\n const createProgressRow = (labelText: string) => {\n const row = document.createElement(\"div\");\n row.className = \"hfp-shader-loader-row\";\n const label = document.createElement(\"span\");\n label.className = \"hfp-shader-loader-label\";\n label.textContent = labelText;\n const value = document.createElement(\"span\");\n value.className = \"hfp-shader-loader-value\";\n row.appendChild(label);\n row.appendChild(value);\n progress.appendChild(row);\n return { row, label, value };\n };\n const transitionStatus = createProgressRow(\"transition\");\n const frameStatus = createProgressRow(\"transition frame\");\n\n panel.appendChild(markFrame);\n panel.appendChild(title);\n panel.appendChild(detail);\n panel.appendChild(track);\n panel.appendChild(progress);\n root.appendChild(panel);\n\n return {\n root,\n fill,\n title: titleText,\n detail,\n transitionValue: transitionStatus.value,\n frameLabel: frameStatus.label,\n frameValue: frameStatus.value,\n frameRow: frameStatus.row,\n };\n }\n\n private _showShaderLoader(): void {\n if (this.shaderLoaderHideTimeout) {\n clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n }\n this.shaderLoaderEl.classList.remove(\"hfp-hiding\");\n this.shaderLoaderEl.classList.add(\"hfp-visible\");\n }\n\n private _hideShaderLoader(): void {\n if (this.shaderLoaderEl.classList.contains(\"hfp-hiding\")) {\n if (!this.shaderLoaderHideTimeout) this._scheduleShaderLoaderHideCleanup();\n return;\n }\n if (!this.shaderLoaderEl.classList.contains(\"hfp-visible\")) return;\n this.shaderLoaderEl.classList.add(\"hfp-hiding\");\n this.shaderLoaderEl.classList.remove(\"hfp-visible\");\n this._scheduleShaderLoaderHideCleanup();\n }\n\n private _scheduleShaderLoaderHideCleanup(): void {\n if (this.shaderLoaderHideTimeout) clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = setTimeout(() => {\n this.shaderLoaderEl.classList.remove(\"hfp-hiding\");\n this.shaderLoaderHideTimeout = null;\n }, 420);\n }\n\n private _resetShaderLoader(): void {\n if (this.shaderLoaderHideTimeout) {\n clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n }\n this.shaderLoaderEl.classList.remove(\"hfp-visible\", \"hfp-hiding\");\n this.shaderLoaderFillEl.style.transform = \"scaleX(0)\";\n this.shaderLoaderTransitionValueEl.textContent = \"\";\n this.shaderLoaderFrameValueEl.textContent = \"\";\n this.shaderLoaderFrameRowEl.style.visibility = \"hidden\";\n }\n\n private _updateShaderLoader(status: ShaderTransitionState): void {\n if (this._shaderLoadingMode() !== \"player\") {\n this._resetShaderLoader();\n return;\n }\n if (status.ready || !status.loading) {\n this._hideShaderLoader();\n return;\n }\n\n const progress =\n typeof status.progress === \"number\" && Number.isFinite(status.progress) ? status.progress : 0;\n const total =\n typeof status.total === \"number\" && Number.isFinite(status.total) ? status.total : 0;\n const ratio = total > 0 ? Math.min(1, Math.max(0, progress / total)) : 0;\n const phraseIndex = Math.min(\n SHADER_LOADING_PHRASES.length - 1,\n Math.floor(ratio * SHADER_LOADING_PHRASES.length),\n );\n this.shaderLoaderTitleEl.textContent =\n SHADER_LOADING_PHRASES[phraseIndex] || \"Preparing scene transitions\";\n this.shaderLoaderDetailEl.textContent =\n status.phase === \"cached\"\n ? \"Loading cached transition frames before playback.\"\n : status.phase === \"finalizing\"\n ? \"Uploading transition textures for smooth playback.\"\n : \"Rendering animated scene samples for shader transitions.\";\n this.shaderLoaderFillEl.style.transform = `scaleX(${ratio})`;\n\n this.shaderLoaderTransitionValueEl.textContent =\n status.currentTransition !== undefined && status.transitionTotal !== undefined\n ? `${status.currentTransition}/${status.transitionTotal}`\n : total > 0\n ? `${progress}/${total}`\n : \"\";\n\n const frameValue =\n status.transitionFrame !== undefined && status.transitionFrames !== undefined\n ? `${status.transitionFrame}/${status.transitionFrames}`\n : \"\";\n this.shaderLoaderFrameLabelEl.textContent =\n status.phase === \"cached\"\n ? \"cached transition frames\"\n : status.phase === \"finalizing\"\n ? \"finalizing transition frames\"\n : \"rendering transition frames\";\n this.shaderLoaderFrameValueEl.textContent = frameValue;\n this.shaderLoaderFrameRowEl.style.visibility = frameValue ? \"visible\" : \"hidden\";\n this.shaderLoaderEl.setAttribute(\"aria-valuenow\", String(Math.round(ratio * 100)));\n this._showShaderLoader();\n }\n\n /**\n * Reach into the runtime's `window.__player.seek` directly, skipping the\n * postMessage hop. Same-origin only — cross-origin embeds throw a\n * `SecurityError` on `contentWindow` property access, which we catch and\n * report as a no-op so the caller can transparently fall back to the\n * postMessage bridge. Returns `true` only when the runtime accepted the\n * call (`__player.seek` exists, is callable, and didn't throw).\n *\n * Studio has used this access path privately via `iframe.contentWindow.__player`\n * (see `useTimelinePlayer.ts`); this helper just formalizes the same\n * detection inside the player so external scrub UIs get the same\n * single-task latency. The runtime-side `seek` is the same wrapped\n * function the postMessage handler calls (`installRuntimeControlBridge`\n * routes through `player.seek`), so `markExplicitSeek()` and downstream\n * runtime state stay identical between the two paths.\n */\n private _trySyncSeek(timeInSeconds: number): boolean {\n try {\n const win = this.iframe.contentWindow as\n | (Window & { __player?: { seek?: (t: number) => void } })\n | null;\n const player = win?.__player;\n const seek = player?.seek;\n if (typeof seek !== \"function\") return false;\n seek.call(player, timeInSeconds);\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelineSeek(timeInSeconds: number): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.seek(timeInSeconds);\n // GSAP seek() preserves play state; the player seek() contract lands paused.\n timeline.pause();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelinePlay(): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.play();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelinePause(): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.pause();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _startDirectTimelineClock(): void {\n this._stopDirectTimelineClock();\n\n const tick = () => {\n const timeline = this._directTimelineAdapter;\n if (!timeline || this._paused) {\n this._directTimelineRaf = null;\n return;\n }\n\n let currentTime: number;\n try {\n currentTime = timeline.time();\n } catch {\n this._directTimelineRaf = null;\n return;\n }\n\n if (this._duration > 0) currentTime = Math.min(currentTime, this._duration);\n this._currentTime = currentTime;\n const completedPlayback = this._duration > 0 && currentTime >= this._duration;\n const now = performance.now();\n if (now - this._lastUpdateMs > 100 || completedPlayback) {\n this._lastUpdateMs = now;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n this.dispatchEvent(\n new CustomEvent(\"timeupdate\", { detail: { currentTime: this._currentTime } }),\n );\n }\n\n if (completedPlayback) {\n if (this.loop) {\n this.seek(0);\n this.play();\n return;\n }\n timeline.pause();\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n this._directTimelineRaf = null;\n return;\n }\n\n this._directTimelineRaf = requestAnimationFrame(tick);\n };\n\n this._directTimelineRaf = requestAnimationFrame(tick);\n }\n\n private _stopDirectTimelineClock(): void {\n if (this._directTimelineRaf === null) return;\n cancelAnimationFrame(this._directTimelineRaf);\n this._directTimelineRaf = null;\n }\n\n private _resolveDirectTimelineAdapter(): DirectTimelineAdapter | null {\n try {\n const win = this.iframe.contentWindow;\n if (!win) return null;\n return this._resolveDirectTimelineAdapterFromWindow(win);\n } catch {\n return null;\n }\n }\n\n private _resolveDirectTimelineAdapterFromWindow(win: Window): DirectTimelineAdapter | null {\n if (this._hasRuntimeBridge(win)) return null;\n\n const timelines = Reflect.get(win, \"__timelines\");\n if (!isObjectRecord(timelines)) return null;\n\n const keys = Object.keys(timelines);\n if (keys.length === 0) return null;\n\n const rootId = this.iframe.contentDocument\n ?.querySelector(\"[data-composition-id]\")\n ?.getAttribute(\"data-composition-id\");\n const key = rootId && rootId in timelines ? rootId : keys[keys.length - 1];\n const timeline = timelines[key];\n return isDirectTimelineAdapter(timeline) ? timeline : null;\n }\n\n private _hasRuntimeBridge(win: Window): boolean {\n return Reflect.get(win, \"__hf\") !== undefined || isObjectRecord(Reflect.get(win, \"__player\"));\n }\n\n private _resolvePlaybackDurationAdapter(win: Window): PlaybackDurationAdapter | null {\n const runtimePlayer = Reflect.get(win, \"__player\");\n if (isRuntimeDurationAdapter(runtimePlayer)) {\n return { kind: \"runtime\", getDuration: () => runtimePlayer.getDuration() };\n }\n\n const timeline = this._resolveDirectTimelineAdapterFromWindow(win);\n if (timeline) {\n return {\n kind: \"direct-timeline\",\n timeline,\n getDuration: () => timeline.duration(),\n };\n }\n\n return null;\n }\n\n private _isControlsClick(event: Event) {\n return event\n .composedPath()\n .some((target) => target instanceof HTMLElement && target.classList.contains(\"hfp-controls\"));\n }\n\n private _onMessage(e: MessageEvent) {\n if (e.source !== this.iframe.contentWindow) return;\n const data = e.data;\n if (!data || data.source !== \"hf-preview\") return;\n\n if (data.type === \"shader-transition-state\") {\n const state: ShaderTransitionState =\n data.state && typeof data.state === \"object\" ? data.state : {};\n this._updateShaderLoader(state);\n this.dispatchEvent(\n new CustomEvent(\"shadertransitionstate\", {\n detail: { compositionId: data.compositionId, state },\n }),\n );\n return;\n }\n\n if (data.type === \"state\") {\n const rawTime = (data.frame ?? 0) / DEFAULT_FPS;\n this._currentTime = this._duration > 0 ? Math.min(rawTime, this._duration) : rawTime;\n const wasPlaying = !this._paused;\n const nextPaused = !data.isPlaying;\n const completedPlayback =\n this._duration > 0 && this._currentTime >= this._duration && (wasPlaying || data.isPlaying);\n\n if (completedPlayback && this.loop) {\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = nextPaused;\n this.seek(0);\n this.play();\n return;\n }\n\n this._paused = nextPaused;\n\n // Under parent ownership the proxies are the audible output, so they\n // mirror the iframe's play/pause transitions (externally-driven pause\n // via `__player.pause()`, scrubber interactions, etc.) and their\n // currentTime is slaved to the iframe timeline. Under runtime ownership\n // the proxies stay paused and silent; nothing here should wake them.\n if (this._audioOwner === \"parent\") {\n if (wasPlaying && this._paused) {\n this._pauseParentMedia();\n } else if (!wasPlaying && !this._paused) {\n this._playParentMedia();\n }\n this._mirrorParentMediaTime(this._currentTime);\n }\n\n // Throttle UI updates and event dispatch to ~10fps to avoid excessive re-renders\n const now = performance.now();\n if (now - this._lastUpdateMs > 100 || this._paused !== wasPlaying) {\n this._lastUpdateMs = now;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n this.controlsApi?.updatePlaying(!this._paused);\n this.dispatchEvent(\n new CustomEvent(\"timeupdate\", { detail: { currentTime: this._currentTime } }),\n );\n }\n\n if (completedPlayback) {\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n }\n }\n\n if (data.type === \"media-autoplay-blocked\") {\n this._promoteToParentProxy();\n }\n\n if (data.type === \"timeline\" && data.durationInFrames > 0) {\n // Ignore Infinity duration from runtime (caused by loop-inflated timelines without data-duration)\n // The player already has duration from the initial probe, so keep that.\n if (Number.isFinite(data.durationInFrames)) {\n this._duration = data.durationInFrames / DEFAULT_FPS;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n }\n }\n\n if (data.type === \"stage-size\" && data.width > 0 && data.height > 0) {\n this._compositionWidth = data.width;\n this._compositionHeight = data.height;\n this._updateScale();\n }\n }\n\n private _runtimeInjected = false;\n\n private _onIframeLoad() {\n let attempts = 0;\n this._runtimeInjected = false;\n this._directTimelineAdapter = null;\n this._stopDirectTimelineClock();\n this._resetShaderLoader();\n // A fresh iframe means a fresh runtime — `mediaOutputMuted` and the\n // autoplay-blocked latch are both reset inside it. The web component's\n // `_audioOwner` must reset to match, otherwise a composition switch on\n // a previously-promoted player would leave the parent thinking it owns\n // audio against a runtime that's happily playing the iframe copy again\n // — briefly reintroducing the double-voice bug for one probe window.\n // The next `NotAllowedError` (if any) will re-promote.\n const wasPromoted = this._audioOwner === \"parent\";\n this._audioOwner = \"runtime\";\n this._playbackErrorPosted = false;\n this._pauseParentMedia();\n // The old iframe document is about to go away. Disconnect the\n // MutationObserver now so we don't hold a reference to it; a fresh\n // one will attach once the new document settles in `_setupParentMedia`.\n this._teardownMediaObserver();\n if (wasPromoted) {\n this.dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"runtime\", reason: \"iframe-reload\" },\n }),\n );\n }\n if (this._probeInterval) clearInterval(this._probeInterval);\n\n this._probeInterval = setInterval(() => {\n attempts++;\n try {\n const win = this.iframe.contentWindow as Window & {\n __player?: { getDuration: () => number };\n __timelines?: Record<string, { duration: () => number }>;\n __hf?: unknown;\n };\n if (!win) return;\n\n // Check if the runtime bridge is active (__hf or __player from the runtime)\n const hasRuntime = !!(win.__hf || win.__player);\n const hasTimelines = !!(win.__timelines && Object.keys(win.__timelines).length > 0);\n const hasNestedCompositions =\n !!this.iframe.contentDocument?.querySelector(\"[data-composition-src]\");\n\n if (\n shouldInjectRuntime({\n hasRuntime,\n hasTimelines,\n hasNestedCompositions,\n runtimeInjected: this._runtimeInjected,\n attempts,\n })\n ) {\n this._injectRuntime();\n return; // Wait for runtime to load and initialize\n }\n\n // Runtime was injected but hasn't loaded yet — keep waiting\n if (this._runtimeInjected && !hasRuntime) {\n return;\n }\n\n const adapter = this._resolvePlaybackDurationAdapter(win);\n if (adapter && adapter.getDuration() > 0) {\n clearInterval(this._probeInterval!);\n this._duration = adapter.getDuration();\n this._directTimelineAdapter =\n adapter.kind === \"direct-timeline\" ? adapter.timeline : null;\n this._ready = true;\n this.controlsApi?.updateTime(0, this._duration);\n this.dispatchEvent(new CustomEvent(\"ready\", { detail: { duration: this._duration } }));\n\n // Auto-detect dimensions from composition\n const doc = this.iframe.contentDocument;\n const root = doc?.querySelector(\"[data-composition-id]\");\n if (root) {\n const w = parseInt(root.getAttribute(\"data-width\") || \"0\", 10);\n const h = parseInt(root.getAttribute(\"data-height\") || \"0\", 10);\n if (w > 0 && h > 0) {\n this._compositionWidth = w;\n this._compositionHeight = h;\n this._updateScale();\n }\n }\n\n this._setupParentMedia();\n\n if (this.hasAttribute(\"autoplay\")) {\n this.play();\n }\n return;\n }\n } catch {\n /* cross-origin */\n }\n\n if (attempts >= 40) {\n clearInterval(this._probeInterval!);\n this.dispatchEvent(\n new CustomEvent(\"error\", {\n detail: { message: \"Composition timeline not found after 8s\" },\n }),\n );\n }\n }, 200);\n }\n\n /** Inject the HyperFrames runtime into the iframe if not already present. */\n private _injectRuntime() {\n this._runtimeInjected = true;\n try {\n const doc = this.iframe.contentDocument;\n if (!doc) return;\n const script = doc.createElement(\"script\");\n script.src = RUNTIME_CDN_URL;\n script.onload = () => {\n // Runtime loaded — the probe interval will pick up __hf on next tick\n };\n script.onerror = () => {\n // CDN failed — the probe will continue and eventually timeout\n };\n (doc.head || doc.documentElement).appendChild(script);\n } catch {\n /* cross-origin — can't inject */\n }\n }\n\n private _updateScale() {\n const rect = this.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const scale = Math.min(\n rect.width / this._compositionWidth,\n rect.height / this._compositionHeight,\n );\n this.iframe.style.width = `${this._compositionWidth}px`;\n this.iframe.style.height = `${this._compositionHeight}px`;\n this.iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;\n }\n\n private _setupControls() {\n if (this.controlsApi) return;\n const callbacks: ControlsCallbacks = {\n onPlay: () => this.play(),\n onPause: () => this.pause(),\n onSeek: (fraction) => this.seek(fraction * this._duration),\n onSpeedChange: (speed) => {\n this.playbackRate = speed;\n },\n onMuteToggle: () => {\n this.muted = !this.muted;\n },\n onVolumeChange: (volume) => {\n this.volume = volume;\n },\n };\n const presetsAttr = this.getAttribute(\"speed-presets\");\n const speedPresets = presetsAttr\n ? presetsAttr\n .split(\",\")\n .map(Number)\n .filter((n) => !isNaN(n) && n > 0)\n : undefined;\n this.controlsApi = createControls(this.shadow, callbacks, { speedPresets });\n this.controlsApi.updateMuted(this.muted);\n this.controlsApi.updateVolume(this._volume);\n }\n\n private _setupPoster() {\n const url = this.getAttribute(\"poster\");\n if (!url) {\n this.posterEl?.remove();\n this.posterEl = null;\n return;\n }\n if (!this.posterEl) {\n this.posterEl = document.createElement(\"img\");\n this.posterEl.className = \"hfp-poster\";\n this.shadow.appendChild(this.posterEl);\n }\n this.posterEl.src = url;\n }\n\n private _playParentMedia() {\n for (const m of this._parentMedia) {\n if (!m.el.src) continue;\n // Under parent ownership the proxy is the only audible pipeline. If\n // its `play()` rejects (rare — parent also lacks activation in some\n // programmatic embed flows), swallowing silently leaves the viewer\n // staring at motion with no audio and no signal. Surface it as a\n // `playbackerror` event — but only once per parent-ownership session;\n // see `_playbackErrorPosted` for why.\n m.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n\n private _reportPlaybackError(err: unknown) {\n if (this._playbackErrorPosted) return;\n this._playbackErrorPosted = true;\n this.dispatchEvent(\n new CustomEvent(\"playbackerror\", { detail: { source: \"parent-proxy\", error: err } }),\n );\n }\n\n private _pauseParentMedia() {\n for (const m of this._parentMedia) m.el.pause();\n }\n\n /**\n * Drag parent-proxy `currentTime` onto the iframe's timeline. Called on\n * every runtime state message under parent ownership. Threshold is 50 ms\n * — ITU-R BT.1359 puts A/V offset perceptibility at roughly ±45 ms, so\n * anything looser risks audible lip-sync drift on talking-head content\n * (a core use case). The re-seek cost at this tightness is a handful of\n * extra `currentTime` writes per second; the media element's own buffer\n * smooths them out without visible rebuffer on the mirror path.\n */\n private static readonly MIRROR_DRIFT_THRESHOLD_SECONDS = 0.05;\n\n /**\n * How many *consecutive* over-threshold steady-state samples we wait for\n * before issuing a `currentTime` write. A value of 2 means a single\n * spike (one slow bridge tick, one tab-throttled rAF batch, one GC pause)\n * is absorbed without a seek; sustained drift still corrects on the very\n * next tick after the threshold is crossed twice in a row.\n *\n * **Coupling with the timeline-control bridge** — read before changing:\n * worst_case_correction_latency_ms\n * ≈ MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES × bridgeMaxPostIntervalMs\n *\n * `bridgeMaxPostIntervalMs` (currently `80`) lives at\n * `packages/core/src/runtime/state.ts` (field on `RuntimeState`). At\n * today's values, worst-case is `2 × 80 ms = 160 ms` — still well under\n * the human shot-change tolerance for A/V re-sync. If you bump bridge\n * cadence (raising `bridgeMaxPostIntervalMs`) you may need to drop this\n * constant to `1` to keep the product under ~150 ms; if you tighten\n * cadence you can raise this to absorb more jitter without perceptual\n * cost. There is a back-reference in `state.ts` next to\n * `bridgeMaxPostIntervalMs` so a change to either side surfaces the\n * coupling.\n */\n private static readonly MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES = 2;\n\n /**\n * Mirror parent-proxy `currentTime` to the iframe timeline. Defaults to\n * the *coalesced* path: a single over-threshold sample is treated as\n * jitter and merely increments a per-proxy counter; the actual seek only\n * fires once `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES` consecutive\n * samples agree. Pass `{ force: true }` for one-shot alignment moments\n * (audio-ownership promotion, brand-new proxy initialization) where we\n * cannot tolerate even ~80 ms of misaligned audible playback.\n *\n * The counter is also reset on any in-threshold sample and on any\n * out-of-range timeline position, so a proxy that drops back into a\n * scene later starts fresh rather than carrying stale samples from the\n * last time it was active.\n */\n private _mirrorParentMediaTime(timelineSeconds: number, options?: { force?: boolean }) {\n const force = options?.force === true;\n const requiredSamples = HyperframesPlayer.MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES;\n const threshold = HyperframesPlayer.MIRROR_DRIFT_THRESHOLD_SECONDS;\n for (const m of this._parentMedia) {\n const relTime = timelineSeconds - m.start;\n if (relTime < 0 || relTime >= m.duration) {\n m.driftSamples = 0;\n continue;\n }\n if (Math.abs(m.el.currentTime - relTime) > threshold) {\n m.driftSamples += 1;\n if (force || m.driftSamples >= requiredSamples) {\n m.el.currentTime = relTime;\n m.driftSamples = 0;\n }\n } else {\n m.driftSamples = 0;\n }\n }\n }\n\n /**\n * Take ownership of audible playback. Fired in response to the runtime's\n * `media-autoplay-blocked` signal — the iframe has lost the autoplay lottery\n * and will never produce audio without a fresh gesture inside itself.\n *\n * Effects, in order:\n * 1. Ask the runtime to mute its own media output via the bridge. The\n * runtime then keeps advancing timed media for frame-accurate state\n * but produces no sound of its own, freeing us to be the single\n * audible source without racing a volume-reassert loop.\n * 2. Align every parent proxy's currentTime to the iframe's timeline so\n * the cut-over is imperceptible.\n * 3. If the player is currently playing, start the proxies.\n *\n * Idempotent: repeat calls are a no-op.\n */\n private _promoteToParentProxy() {\n if (this._audioOwner === \"parent\") return;\n this._audioOwner = \"parent\";\n // `_sendControl` is async — the iframe won't see the mute for ~one\n // message-loop tick. In that narrow window the runtime's next\n // `syncRuntimeMedia` pass may still try `el.play()` on the iframe\n // copy; we rely on the autoplay gate (which got us here in the first\n // place) to keep rejecting until our mute lands. This is defensible\n // precisely because the scenario that triggered promotion is\n // \"autoplay blocked\" — the iframe can't make noise on its own.\n this._sendControl(\"set-media-output-muted\", { muted: true });\n // One-shot alignment: a brand-new proxy must pick up the iframe's exact\n // timeline position immediately to avoid an audible jump. Bypass the\n // jitter-coalescing gate.\n this._mirrorParentMediaTime(this._currentTime, { force: true });\n if (!this._paused) this._playParentMedia();\n this.dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"parent\", reason: \"autoplay-blocked\" },\n }),\n );\n }\n\n /**\n * Create a parent-frame media element, configure it, and start preloading.\n * Returns the newly-created proxy entry, or `null` if one already exists for\n * this src (dedup) — callers that need to act on the new element should\n * branch on the return value rather than inferring via `_parentMedia.length`.\n */\n private _createParentMedia(\n src: string,\n tag: \"audio\" | \"video\",\n start: number,\n duration: number,\n ): { el: HTMLMediaElement; start: number; duration: number; driftSamples: number } | null {\n // Deduplicate — browsers normalize URLs so we compare on the element after assignment\n if (this._parentMedia.some((m) => m.el.src === src)) return null;\n\n const el = tag === \"video\" ? document.createElement(\"video\") : new Audio();\n el.preload = \"auto\";\n el.src = src;\n el.load();\n el.muted = this.muted;\n el.volume = this._volume;\n if (this.playbackRate !== 1) el.playbackRate = this.playbackRate;\n\n const entry = { el, start, duration, driftSamples: 0 };\n this._parentMedia.push(entry);\n return entry;\n }\n\n /**\n * Set up a single parent-frame audio from an explicit URL (via `audio-src`).\n * Convenience for the common single-narration case — starts preloading\n * immediately without waiting for the iframe to load.\n */\n private _setupParentAudioFromUrl(audioSrc: string) {\n this._createParentMedia(audioSrc, \"audio\", 0, Infinity);\n }\n\n /**\n * Mirror every timed iframe media element (`audio[data-start]`,\n * `video[data-start]`) into a parent-frame proxy. The proxies preload at\n * iframe-ready time so the cut-over to parent ownership — should the\n * runtime's autoplay attempt later reject — is instantaneous.\n *\n * Under runtime ownership (the default) these proxies stay paused and\n * inert; the iframe is the audible source. Ownership flips only in\n * response to a real `media-autoplay-blocked` message from the runtime.\n *\n * Also installs a MutationObserver so that media added to the iframe\n * *after* the initial scan (sub-composition activation is the common\n * case) gets a proxy on the fly. Without this, under parent ownership\n * late-added `<audio data-start>` would be silenced by the runtime\n * (`outputMuted` sticks per-tick) but have no parent-frame counterpart\n * to play — a silent hole in the audio track.\n */\n private _setupParentMedia() {\n try {\n const doc = this.iframe.contentDocument;\n if (!doc) return;\n\n // Find all timed media — matches the runtime's media.ts selector\n const mediaEls = doc.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n for (const iframeEl of mediaEls) this._adoptIframeMedia(iframeEl);\n\n this._observeDynamicMedia(doc);\n } catch {\n // Cross-origin iframe — can't access DOM, fall back to iframe media\n }\n }\n\n /**\n * Create a parent-frame proxy mirroring a single iframe media element.\n * Extracted so both the initial scan and the MutationObserver path use\n * identical URL-resolution and attribute parsing.\n */\n private _adoptIframeMedia(iframeEl: HTMLMediaElement): void {\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) return;\n\n // Resolve against the iframe's baseURI. The parent-frame <audio>/<video>\n // we create next lives in the host document, whose base URL differs from\n // the iframe's — without this, a relative src like \"assets/narration.wav\"\n // would resolve against the studio root and 404.\n const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\n\n const start = parseFloat(iframeEl.getAttribute(\"data-start\") || \"0\");\n const duration = parseFloat(iframeEl.getAttribute(\"data-duration\") || \"Infinity\");\n const tag = iframeEl.tagName === \"VIDEO\" ? (\"video\" as const) : (\"audio\" as const);\n\n const created = this._createParentMedia(src, tag, start, duration);\n // Iframe originals stay untouched — the runtime's `syncRuntimeMedia`\n // queries `audio[data-start]` for state and needs them addressable.\n // Their audible output is gated later by `set-media-output-muted` when\n // (and only when) parent ownership is promoted.\n\n // If we're already under parent ownership and the player is playing,\n // the new proxy needs to pick up where the timeline currently is and\n // start producing audio right away — otherwise it sits silent through\n // the next several hundred ms until the next runtime state message.\n if (created && this._audioOwner === \"parent\") {\n // One-shot alignment: a freshly-created proxy must catch up to the\n // current timeline position on the very first sample, so bypass the\n // jitter-coalescing gate.\n this._mirrorParentMediaTime(this._currentTime, { force: true });\n if (!this._paused && created.el.src) {\n created.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n }\n\n /**\n * Watch the iframe document for subtree additions of timed media so\n * sub-composition activation (late-attached `<audio data-start>`) grows\n * the parent-proxy set automatically. Disconnected on iframe reload via\n * `_teardownMediaObserver`.\n */\n private _observeDynamicMedia(doc: Document): void {\n this._teardownMediaObserver();\n if (typeof MutationObserver === \"undefined\" || !doc.body) return;\n const obs = new MutationObserver((mutations) => {\n for (const m of mutations) {\n for (const added of m.addedNodes) {\n if (!(added instanceof Element)) continue;\n // Handle both the node itself and any timed media nested inside\n // (sub-compositions typically inject a fragment whose root is a\n // `<div data-composition-id=...>` with `<audio>` children).\n const candidates: HTMLMediaElement[] = [];\n if (added.matches?.(\"audio[data-start], video[data-start]\")) {\n candidates.push(added as HTMLMediaElement);\n }\n const inside = added.querySelectorAll?.<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (inside) for (const el of inside) candidates.push(el);\n for (const el of candidates) this._adoptIframeMedia(el);\n }\n for (const removed of m.removedNodes) {\n if (!(removed instanceof Element)) continue;\n // Symmetric detach: when a sub-composition unmounts, the iframe\n // media it owned is gone but our parent proxies would otherwise\n // linger — accumulating host-document <audio> elements and, under\n // parent ownership, still being played by `_playParentMedia` as\n // orphans. Match by resolved URL (same resolution as adoption).\n const dropped: HTMLMediaElement[] = [];\n if (removed.matches?.(\"audio[data-start], video[data-start]\")) {\n dropped.push(removed as HTMLMediaElement);\n }\n const inside = removed.querySelectorAll?.<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (inside) for (const el of inside) dropped.push(el);\n for (const el of dropped) this._detachIframeMedia(el);\n }\n }\n });\n const hosts = doc.querySelectorAll(\"[data-composition-id]\");\n if (hosts.length > 0) {\n for (const host of hosts) {\n obs.observe(host, { childList: true, subtree: true });\n }\n } else {\n obs.observe(doc.body, { childList: true, subtree: true });\n }\n this._mediaObserver = obs;\n }\n\n private _teardownMediaObserver(): void {\n this._mediaObserver?.disconnect();\n this._mediaObserver = undefined;\n }\n\n /**\n * Inverse of `_adoptIframeMedia`: drop the parent proxy mirroring a removed\n * iframe media element. Resolves the src identically so matching is exact,\n * then pauses, clears the src (frees the decoder), and splices it out.\n */\n private _detachIframeMedia(iframeEl: HTMLMediaElement): void {\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) return;\n const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\n const idx = this._parentMedia.findIndex((m) => m.el.src === src);\n if (idx === -1) return;\n const entry = this._parentMedia[idx];\n entry.el.pause();\n entry.el.src = \"\";\n this._parentMedia.splice(idx, 1);\n }\n\n private _hidePoster() {\n this.posterEl?.remove();\n this.posterEl = null;\n }\n}\n\nif (!customElements.get(\"hyperframes-player\")) {\n customElements.define(\"hyperframes-player\", HyperframesPlayer);\n}\n\nexport { HyperframesPlayer };\nexport { formatTime, formatSpeed, SPEED_PRESETS } from \"./controls.js\";\nexport type { ControlsCallbacks, ControlsOptions } from \"./controls.js\";\n","export const PLAYER_STYLES = /* css */ `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n background: #000;\n contain: layout style;\n }\n\n .hfp-container {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n }\n\n\n .hfp-iframe {\n position: absolute;\n top: 50%;\n left: 50%;\n border: none;\n pointer-events: none;\n }\n\n .hfp-poster {\n position: absolute;\n inset: 0;\n object-fit: contain;\n z-index: 1;\n pointer-events: none;\n }\n\n .hfp-shader-loader {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: grid;\n place-items: center;\n visibility: hidden;\n opacity: 0;\n pointer-events: none;\n background: #030504;\n color: #f4f7fb;\n cursor: default;\n user-select: none;\n -webkit-user-select: none;\n transition: opacity 420ms ease-out, visibility 420ms ease-out;\n }\n\n .hfp-shader-loader.hfp-visible,\n .hfp-shader-loader.hfp-hiding {\n visibility: visible;\n }\n\n .hfp-shader-loader.hfp-visible {\n opacity: 1;\n pointer-events: auto;\n }\n\n .hfp-shader-loader.hfp-hiding {\n opacity: 0;\n pointer-events: none;\n }\n\n .hfp-shader-loader-panel {\n display: grid;\n grid-template-rows: 86px 40px 26px 12px 44px;\n justify-items: center;\n align-items: center;\n gap: 8px;\n width: min(620px, 82%);\n text-align: center;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n\n .hfp-shader-loader-mark {\n width: 86px;\n height: 86px;\n display: grid;\n place-items: center;\n overflow: visible;\n }\n\n .hfp-shader-loader-mark svg {\n display: block;\n overflow: visible;\n filter: drop-shadow(0 0 5px rgba(79, 219, 94, 0.16));\n pointer-events: none;\n }\n\n .hfp-shader-loader-title {\n width: 100%;\n height: 40px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 26px;\n line-height: 40px;\n font-weight: 700;\n letter-spacing: 0;\n }\n\n .hfp-shader-loader-title-text {\n color: transparent;\n background: linear-gradient(\n 90deg,\n rgba(244, 247, 251, 0.84) 0%,\n #ffffff 42%,\n #80efe4 52%,\n #ffffff 62%,\n rgba(244, 247, 251, 0.84) 100%\n );\n background-size: 220% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n animation: hfp-shader-loader-sheen 1.9s linear infinite;\n }\n\n .hfp-shader-loader-detail {\n width: 100%;\n height: 26px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: rgba(244, 247, 251, 0.62);\n font-size: 15px;\n line-height: 26px;\n font-weight: 500;\n }\n\n .hfp-shader-loader-track {\n width: min(360px, 100%);\n height: 8px;\n overflow: hidden;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.1);\n }\n\n .hfp-shader-loader-fill {\n width: 100%;\n height: 100%;\n border-radius: inherit;\n background: linear-gradient(90deg, #06e3fa, #4fdb5e);\n transform: scaleX(0);\n transform-origin: left center;\n transition: transform 160ms ease;\n }\n\n .hfp-shader-loader-progress {\n width: min(420px, 100%);\n height: 44px;\n display: grid;\n grid-template-rows: repeat(2, 22px);\n color: rgba(244, 247, 251, 0.48);\n font: 600 13px/22px \"IBM Plex Mono\", \"SF Mono\", \"Fira Code\", \"Courier New\", monospace;\n font-variant-numeric: tabular-nums;\n }\n\n .hfp-shader-loader-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 74px;\n align-items: center;\n column-gap: 20px;\n width: 100%;\n white-space: nowrap;\n }\n\n .hfp-shader-loader-label {\n min-width: 0;\n overflow: hidden;\n text-align: left;\n text-overflow: ellipsis;\n }\n\n .hfp-shader-loader-value {\n text-align: right;\n }\n\n @keyframes hfp-shader-loader-sheen {\n from {\n background-position: 140% 0;\n }\n to {\n background-position: -140% 0;\n }\n }\n\n /* ── Theming via CSS custom properties ──\n *\n * Override from outside the shadow DOM:\n * hyperframes-player {\n * --hfp-controls-bg: linear-gradient(transparent, rgba(0,0,0,0.9));\n * --hfp-accent: #ff6b6b;\n * --hfp-font: \"Inter\", sans-serif;\n * }\n */\n\n .hfp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n gap: var(--hfp-controls-gap, 12px);\n padding: var(--hfp-controls-padding, 8px 16px);\n background: var(--hfp-controls-bg, linear-gradient(transparent, rgba(0, 0, 0, 0.7)));\n color: var(--hfp-color, #fff);\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: var(--hfp-font-size, 13px);\n z-index: 10;\n pointer-events: auto;\n opacity: 1;\n transition: opacity 0.3s ease;\n user-select: none;\n }\n\n .hfp-controls.hfp-hidden {\n opacity: 0;\n pointer-events: none;\n }\n\n .hfp-play-btn {\n background: none;\n border: none;\n color: var(--hfp-color, #fff);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n z-index: 10;\n }\n\n .hfp-play-btn:hover {\n opacity: 0.8;\n }\n\n .hfp-play-btn svg,\n .hfp-play-btn svg * {\n pointer-events: none;\n }\n\n .hfp-scrubber {\n flex: 1;\n min-width: 0;\n height: var(--hfp-scrubber-height, 4px);\n background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));\n border-radius: var(--hfp-scrubber-radius, 2px);\n cursor: pointer;\n position: relative;\n overflow: hidden;\n }\n\n .hfp-scrubber:hover {\n height: var(--hfp-scrubber-height-hover, 6px);\n }\n\n .hfp-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--hfp-accent, #fff);\n pointer-events: none;\n }\n\n .hfp-time {\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n opacity: 0.9;\n }\n\n .hfp-speed-wrap {\n position: relative;\n flex-shrink: 0;\n }\n\n .hfp-speed-btn {\n background: var(--hfp-speed-btn-bg, rgba(255, 255, 255, 0.15));\n border: none;\n border-radius: var(--hfp-speed-btn-radius, 4px);\n color: var(--hfp-color, #fff);\n cursor: pointer;\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n padding: 4px 8px;\n min-width: 40px;\n text-align: center;\n transition: background 0.15s ease;\n }\n\n .hfp-speed-btn:hover {\n background: var(--hfp-speed-btn-bg-hover, rgba(255, 255, 255, 0.3));\n }\n\n .hfp-speed-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n background: var(--hfp-menu-bg, rgba(20, 20, 20, 0.95));\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border: 1px solid var(--hfp-menu-border, rgba(255, 255, 255, 0.1));\n border-radius: var(--hfp-menu-radius, 8px);\n padding: 4px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 80px;\n opacity: 0;\n visibility: hidden;\n transform: translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;\n box-shadow: var(--hfp-menu-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));\n }\n\n .hfp-speed-menu.hfp-open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n }\n\n .hfp-speed-option {\n background: none;\n border: none;\n border-radius: 4px;\n color: var(--hfp-menu-color, rgba(255, 255, 255, 0.7));\n cursor: pointer;\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n padding: 6px 12px;\n text-align: left;\n transition: background 0.1s ease, color 0.1s ease;\n white-space: nowrap;\n }\n\n .hfp-speed-option:hover {\n background: var(--hfp-menu-hover-bg, rgba(255, 255, 255, 0.1));\n color: var(--hfp-color, #fff);\n }\n\n .hfp-speed-option.hfp-active {\n color: var(--hfp-accent, #fff);\n font-weight: 600;\n }\n\n .hfp-volume-wrap {\n position: relative;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0;\n }\n\n .hfp-mute-btn {\n background: none;\n border: none;\n color: var(--hfp-color, #fff);\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n flex-shrink: 0;\n }\n\n .hfp-mute-btn:hover {\n opacity: 0.8;\n }\n\n .hfp-mute-btn svg,\n .hfp-mute-btn svg * {\n pointer-events: none;\n }\n\n .hfp-volume-slider-wrap {\n width: 0;\n overflow: hidden;\n transition: width 0.2s ease;\n display: flex;\n align-items: center;\n }\n\n .hfp-volume-wrap:hover .hfp-volume-slider-wrap {\n width: 64px;\n }\n\n .hfp-volume-slider {\n width: 56px;\n height: var(--hfp-scrubber-height, 4px);\n background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));\n border-radius: var(--hfp-scrubber-radius, 2px);\n cursor: pointer;\n position: relative;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n }\n\n .hfp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--hfp-accent, #fff);\n pointer-events: none;\n }\n`;\n\nexport const PLAY_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 18 18\" fill=\"currentColor\"><polygon points=\"4,2 16,9 4,16\"/></svg>`;\nexport const PAUSE_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 18 18\" fill=\"currentColor\"><rect x=\"3\" y=\"2\" width=\"4\" height=\"14\"/><rect x=\"11\" y=\"2\" width=\"4\" height=\"14\"/></svg>`;\nexport const VOLUME_HIGH_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/><path d=\"M14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>`;\nexport const VOLUME_LOW_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/></svg>`;\nexport const VOLUME_MUTED_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\" opacity=\"0.3\"/><line x1=\"18\" y1=\"7\" x2=\"14\" y2=\"17\" stroke=\"currentColor\" stroke-width=\"2\"/></svg>`;\n\n/**\n * Process-wide cache for the constructed PLAYER_STYLES sheet. Lazy so the\n * module stays SSR-safe (CSSStyleSheet is window-scoped) and so a single\n * sheet can be shared across every shadow root via `adoptedStyleSheets` —\n * the studio thumbnail grid renders dozens of players, and avoiding N\n * duplicate `<style>` parses + style-recalc invalidations is the win here.\n *\n * `null` after a failed construction attempt = \"fall back forever in this\n * process\" (the usual cause is a missing constructor in older runtimes;\n * retrying every call would just throw the same way).\n */\nlet sharedSheet: CSSStyleSheet | null | undefined;\n\n/**\n * Returns the shared player stylesheet, or `null` if constructable\n * stylesheets aren't available in this environment.\n *\n * The result is memoized for the life of the module — every shadow root\n * adopts the same `CSSStyleSheet` instance.\n */\nexport function getSharedPlayerStyleSheet(): CSSStyleSheet | null {\n if (sharedSheet !== undefined) return sharedSheet;\n\n if (typeof CSSStyleSheet === \"undefined\") {\n sharedSheet = null;\n return null;\n }\n\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(PLAYER_STYLES);\n sharedSheet = sheet;\n return sheet;\n } catch {\n sharedSheet = null;\n return null;\n }\n}\n\n/**\n * Internal hook for tests to clear the memoized sheet. Not part of the\n * public API.\n */\nexport function _resetSharedPlayerStyleSheet(): void {\n sharedSheet = undefined;\n}\n\n/**\n * Install PLAYER_STYLES into a player shadow root. Prefers the shared\n * constructable stylesheet (one parse, one rule tree, N adopters) and\n * falls back to a per-instance `<style>` element when the host runtime\n * lacks `adoptedStyleSheets` support.\n *\n * Idempotent: re-applying to a root that already adopts the shared sheet\n * is a no-op. Pre-existing adopted sheets are preserved (we append, never\n * replace), so callers further up the chain can keep their styles.\n */\nexport function applyPlayerStyles(shadow: ShadowRoot): void {\n const sheet = getSharedPlayerStyleSheet();\n const adopted = (shadow as ShadowRoot & { adoptedStyleSheets?: CSSStyleSheet[] })\n .adoptedStyleSheets;\n\n if (sheet && Array.isArray(adopted)) {\n if (!adopted.includes(sheet)) {\n (shadow as ShadowRoot & { adoptedStyleSheets: CSSStyleSheet[] }).adoptedStyleSheets = [\n ...adopted,\n sheet,\n ];\n }\n return;\n }\n\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n shadow.appendChild(style);\n}\n","import {\n PLAY_ICON,\n PAUSE_ICON,\n VOLUME_HIGH_ICON,\n VOLUME_LOW_ICON,\n VOLUME_MUTED_ICON,\n} from \"./styles.js\";\n\nexport interface ControlsCallbacks {\n onPlay: () => void;\n onPause: () => void;\n onSeek: (fraction: number) => void;\n onSpeedChange: (speed: number) => void;\n onMuteToggle: () => void;\n onVolumeChange: (volume: number) => void;\n}\n\n/** Default logarithmic speed presets — each step roughly doubles/halves. */\nexport const SPEED_PRESETS = [0.25, 0.5, 1, 1.5, 2, 4] as const;\n\nexport interface ControlsOptions {\n /** Speed presets shown in the menu. Defaults to SPEED_PRESETS. */\n speedPresets?: readonly number[];\n}\n\nexport function formatSpeed(speed: number): string {\n return Number.isInteger(speed) ? `${speed}x` : `${speed}x`;\n}\n\nexport function formatTime(seconds: number): string {\n // Handle non-finite values gracefully\n if (!Number.isFinite(seconds) || seconds < 0) {\n return \"0:00\";\n }\n const s = Math.floor(seconds);\n const m = Math.floor(s / 60);\n const sec = s % 60;\n return `${m}:${sec.toString().padStart(2, \"0\")}`;\n}\n\nexport function createControls(\n parent: ShadowRoot | HTMLElement,\n callbacks: ControlsCallbacks,\n options: ControlsOptions = {},\n): {\n updateTime: (current: number, duration: number) => void;\n updatePlaying: (playing: boolean) => void;\n updateSpeed: (speed: number) => void;\n updateMuted: (muted: boolean) => void;\n updateVolume: (volume: number) => void;\n show: () => void;\n hide: () => void;\n destroy: () => void;\n} {\n const presets = options.speedPresets ?? SPEED_PRESETS;\n\n const controls = document.createElement(\"div\");\n controls.className = \"hfp-controls\";\n // Keep overlay interactions from falling through to the host-level click toggle.\n controls.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n });\n\n const playBtn = document.createElement(\"button\");\n playBtn.className = \"hfp-play-btn\";\n playBtn.type = \"button\";\n playBtn.innerHTML = PLAY_ICON;\n playBtn.setAttribute(\"aria-label\", \"Play\");\n\n const scrubber = document.createElement(\"div\");\n scrubber.className = \"hfp-scrubber\";\n const progress = document.createElement(\"div\");\n progress.className = \"hfp-progress\";\n progress.style.width = \"0%\";\n scrubber.appendChild(progress);\n\n const time = document.createElement(\"span\");\n time.className = \"hfp-time\";\n time.textContent = \"0:00 / 0:00\";\n\n const speedWrap = document.createElement(\"div\");\n speedWrap.className = \"hfp-speed-wrap\";\n\n const speedBtn = document.createElement(\"button\");\n speedBtn.className = \"hfp-speed-btn\";\n speedBtn.type = \"button\";\n speedBtn.textContent = \"1x\";\n speedBtn.setAttribute(\"aria-label\", \"Playback speed\");\n\n const speedMenu = document.createElement(\"div\");\n speedMenu.className = \"hfp-speed-menu\";\n speedMenu.setAttribute(\"role\", \"menu\");\n for (const preset of presets) {\n const item = document.createElement(\"button\");\n item.className = \"hfp-speed-option\";\n item.type = \"button\";\n item.setAttribute(\"role\", \"menuitem\");\n item.dataset.speed = String(preset);\n item.textContent = formatSpeed(preset);\n if (preset === 1) item.classList.add(\"hfp-active\");\n speedMenu.appendChild(item);\n }\n\n speedWrap.appendChild(speedMenu);\n speedWrap.appendChild(speedBtn);\n\n const volumeWrap = document.createElement(\"div\");\n volumeWrap.className = \"hfp-volume-wrap\";\n\n const muteBtn = document.createElement(\"button\");\n muteBtn.className = \"hfp-mute-btn\";\n muteBtn.type = \"button\";\n muteBtn.innerHTML = VOLUME_HIGH_ICON;\n muteBtn.setAttribute(\"aria-label\", \"Mute\");\n\n const volumeSliderWrap = document.createElement(\"div\");\n volumeSliderWrap.className = \"hfp-volume-slider-wrap\";\n\n const volumeSlider = document.createElement(\"div\");\n volumeSlider.className = \"hfp-volume-slider\";\n volumeSlider.setAttribute(\"role\", \"slider\");\n volumeSlider.setAttribute(\"aria-label\", \"Volume\");\n volumeSlider.setAttribute(\"aria-valuemin\", \"0\");\n volumeSlider.setAttribute(\"aria-valuemax\", \"100\");\n volumeSlider.setAttribute(\"aria-valuenow\", \"100\");\n volumeSlider.tabIndex = 0;\n const volumeFill = document.createElement(\"div\");\n volumeFill.className = \"hfp-volume-fill\";\n volumeFill.style.width = \"100%\";\n volumeSlider.appendChild(volumeFill);\n volumeSliderWrap.appendChild(volumeSlider);\n\n volumeWrap.appendChild(volumeSliderWrap);\n volumeWrap.appendChild(muteBtn);\n\n controls.appendChild(playBtn);\n controls.appendChild(scrubber);\n controls.appendChild(time);\n controls.appendChild(volumeWrap);\n controls.appendChild(speedWrap);\n parent.appendChild(controls);\n\n let isPlaying = false;\n let isMuted = false;\n let currentVolume = 1;\n let hideTimeout: ReturnType<typeof setTimeout> | null = null;\n let speedIndex = presets.indexOf(1); // start at 1x\n if (speedIndex === -1) speedIndex = 0;\n\n const getVolumeIcon = (muted: boolean, volume: number): string => {\n if (muted) return VOLUME_MUTED_ICON;\n if (volume === 0) return VOLUME_LOW_ICON;\n if (volume < 0.5) return VOLUME_LOW_ICON;\n return VOLUME_HIGH_ICON;\n };\n\n playBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (isPlaying) callbacks.onPause();\n else callbacks.onPlay();\n });\n\n muteBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n callbacks.onMuteToggle();\n });\n\n let volumeScrubbing = false;\n\n const handleVolumeAt = (clientX: number) => {\n const rect = volumeSlider.getBoundingClientRect();\n const fraction = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n currentVolume = fraction;\n volumeFill.style.width = `${fraction * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(fraction * 100)));\n if (isMuted && fraction > 0) callbacks.onMuteToggle();\n muteBtn.innerHTML = getVolumeIcon(isMuted, fraction);\n callbacks.onVolumeChange(fraction);\n };\n\n volumeSlider.addEventListener(\"mousedown\", (e) => {\n e.stopPropagation();\n volumeScrubbing = true;\n handleVolumeAt(e.clientX);\n });\n const onVolumeMouseMove = (e: MouseEvent) => {\n if (volumeScrubbing) handleVolumeAt(e.clientX);\n };\n const onVolumeMouseUp = () => {\n volumeScrubbing = false;\n };\n document.addEventListener(\"mousemove\", onVolumeMouseMove);\n document.addEventListener(\"mouseup\", onVolumeMouseUp);\n\n volumeSlider.addEventListener(\n \"touchstart\",\n (e) => {\n volumeScrubbing = true;\n const touch = e.touches[0];\n if (touch) handleVolumeAt(touch.clientX);\n },\n { passive: true },\n );\n const onVolumeTouchMove = (e: TouchEvent) => {\n if (volumeScrubbing) {\n const touch = e.touches[0];\n if (touch) handleVolumeAt(touch.clientX);\n }\n };\n const onVolumeTouchEnd = () => {\n volumeScrubbing = false;\n };\n document.addEventListener(\"touchmove\", onVolumeTouchMove, { passive: true });\n document.addEventListener(\"touchend\", onVolumeTouchEnd);\n\n const VOLUME_STEP = 0.05;\n volumeSlider.addEventListener(\"keydown\", (e) => {\n let newVol = currentVolume;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") {\n newVol = Math.min(1, currentVolume + VOLUME_STEP);\n } else if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") {\n newVol = Math.max(0, currentVolume - VOLUME_STEP);\n } else {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n currentVolume = newVol;\n volumeFill.style.width = `${newVol * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(newVol * 100)));\n if (isMuted && newVol > 0) callbacks.onMuteToggle();\n muteBtn.innerHTML = getVolumeIcon(isMuted, newVol);\n callbacks.onVolumeChange(newVol);\n });\n\n const setActiveOption = (speed: number) => {\n for (const opt of speedMenu.querySelectorAll(\".hfp-speed-option\")) {\n opt.classList.toggle(\"hfp-active\", (opt as HTMLElement).dataset.speed === String(speed));\n }\n };\n\n speedBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const isOpen = speedMenu.classList.toggle(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", String(isOpen));\n });\n\n speedMenu.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const target = (e.target as HTMLElement).closest(\".hfp-speed-option\") as HTMLElement | null;\n if (!target) return;\n const newSpeed = parseFloat(target.dataset.speed!);\n speedIndex = presets.indexOf(newSpeed);\n speedBtn.textContent = formatSpeed(newSpeed);\n setActiveOption(newSpeed);\n speedMenu.classList.remove(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", \"false\");\n callbacks.onSpeedChange(newSpeed);\n });\n\n // Close menu when clicking outside\n const onDocClick = () => {\n speedMenu.classList.remove(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", \"false\");\n };\n document.addEventListener(\"click\", onDocClick);\n\n const handleScrubAt = (clientX: number) => {\n const rect = scrubber.getBoundingClientRect();\n const fraction = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n callbacks.onSeek(fraction);\n };\n\n let scrubbing = false;\n\n scrubber.addEventListener(\"mousedown\", (e) => {\n e.stopPropagation();\n scrubbing = true;\n handleScrubAt(e.clientX);\n });\n const onMouseMove = (e: MouseEvent) => {\n if (scrubbing) handleScrubAt(e.clientX);\n };\n const onMouseUp = () => {\n scrubbing = false;\n };\n document.addEventListener(\"mousemove\", onMouseMove);\n document.addEventListener(\"mouseup\", onMouseUp);\n\n scrubber.addEventListener(\n \"touchstart\",\n (e) => {\n scrubbing = true;\n const touch = e.touches[0];\n if (touch) handleScrubAt(touch.clientX);\n },\n { passive: true },\n );\n const onTouchMove = (e: TouchEvent) => {\n if (scrubbing) {\n const touch = e.touches[0];\n if (touch) handleScrubAt(touch.clientX);\n }\n };\n const onTouchEnd = () => {\n scrubbing = false;\n };\n document.addEventListener(\"touchmove\", onTouchMove, { passive: true });\n document.addEventListener(\"touchend\", onTouchEnd);\n\n const startHideTimer = () => {\n if (hideTimeout) clearTimeout(hideTimeout);\n hideTimeout = setTimeout(() => {\n if (isPlaying) controls.classList.add(\"hfp-hidden\");\n }, 3000);\n };\n\n const host = parent instanceof ShadowRoot ? (parent.host as HTMLElement) : parent;\n host.addEventListener(\"mousemove\", () => {\n controls.classList.remove(\"hfp-hidden\");\n startHideTimer();\n });\n host.addEventListener(\"mouseleave\", () => {\n if (isPlaying) controls.classList.add(\"hfp-hidden\");\n });\n\n return {\n updateTime(current: number, duration: number) {\n // Defensive: source should already clamp, but guard here so the UI never overflows.\n const clampedCurrent = duration > 0 ? Math.min(current, duration) : current;\n const pct = duration > 0 ? (clampedCurrent / duration) * 100 : 0;\n progress.style.width = `${pct}%`;\n time.textContent = `${formatTime(clampedCurrent)} / ${formatTime(duration)}`;\n },\n updatePlaying(playing: boolean) {\n isPlaying = playing;\n playBtn.innerHTML = playing ? PAUSE_ICON : PLAY_ICON;\n playBtn.setAttribute(\"aria-label\", playing ? \"Pause\" : \"Play\");\n if (playing) startHideTimer();\n else controls.classList.remove(\"hfp-hidden\");\n },\n updateSpeed(speed: number) {\n const idx = presets.indexOf(speed);\n if (idx !== -1) speedIndex = idx;\n speedBtn.textContent = formatSpeed(speed);\n setActiveOption(speed);\n },\n updateMuted(muted: boolean) {\n isMuted = muted;\n muteBtn.innerHTML = getVolumeIcon(muted, currentVolume);\n muteBtn.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n },\n updateVolume(volume: number) {\n currentVolume = volume;\n volumeFill.style.width = `${volume * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(volume * 100)));\n muteBtn.innerHTML = getVolumeIcon(isMuted, volume);\n },\n show() {\n controls.style.display = \"\";\n },\n hide() {\n controls.style.display = \"none\";\n },\n destroy() {\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"mouseup\", onMouseUp);\n document.removeEventListener(\"touchmove\", onTouchMove);\n document.removeEventListener(\"touchend\", onTouchEnd);\n document.removeEventListener(\"mousemove\", onVolumeMouseMove);\n document.removeEventListener(\"mouseup\", onVolumeMouseUp);\n document.removeEventListener(\"touchmove\", onVolumeTouchMove);\n document.removeEventListener(\"touchend\", onVolumeTouchEnd);\n document.removeEventListener(\"click\", onDocClick);\n if (hideTimeout) clearTimeout(hideTimeout);\n },\n };\n}\n","/**\n * Decide whether the player should inject the HyperFrames runtime on the\n * current probe tick.\n *\n * The player polls the loaded iframe every 200ms to discover either:\n * - a runtime bridge already installed (`window.__hf` / `window.__player`), or\n * - GSAP timelines registered at `window.__timelines`.\n *\n * Two classes of composition require different injection timing:\n *\n * Nested — the composition uses `data-composition-src` on child elements to\n * lazy-load sub-scenes. The runtime is what loads those children, so the\n * composition cannot possibly render on its own. We inject immediately; if\n * we waited, an inline pre-runtime `gsap.timeline` (common for authoring a\n * preview before the runtime rebuilds the master timeline) would register\n * at `__timelines[\"main\"]` with a partial duration, and the adapter path\n * would then lock the player into `ready` against that incomplete timeline.\n *\n * Self-contained — the composition has no nested scenes and ships all of\n * its animation inline (timelines registered under `__timelines`). These\n * don't strictly need the runtime; the adapter can drive them directly.\n * We give the adapter path first shot (a 5-tick grace period) and only\n * inject the runtime as a fallback if no adapter emerges.\n */\nexport interface ProbeState {\n hasRuntime: boolean;\n hasTimelines: boolean;\n hasNestedCompositions: boolean;\n runtimeInjected: boolean;\n attempts: number;\n}\n\nexport function shouldInjectRuntime(state: ProbeState): boolean {\n if (state.hasRuntime || state.runtimeInjected) return false;\n if (state.hasNestedCompositions) return true;\n if (state.hasTimelines && state.attempts >= 5) return true;\n return false;\n}\n"],"mappings":"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","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","__toCommonJS","PLAYER_STYLES","PLAY_ICON","PAUSE_ICON","VOLUME_HIGH_ICON","VOLUME_LOW_ICON","VOLUME_MUTED_ICON","SPEED_PRESETS","formatSpeed","speed","formatTime","seconds","s","m","sec","createControls","parent","callbacks","options","presets","controls","e","playBtn","PLAY_ICON","scrubber","progress","time","speedWrap","speedBtn","speedMenu","preset","item","volumeWrap","muteBtn","VOLUME_HIGH_ICON","volumeSliderWrap","volumeSlider","volumeFill","isPlaying","isMuted","currentVolume","hideTimeout","speedIndex","getVolumeIcon","muted","volume","VOLUME_MUTED_ICON","VOLUME_LOW_ICON","volumeScrubbing","handleVolumeAt","clientX","rect","fraction","onVolumeMouseMove","onVolumeMouseUp","touch","onVolumeTouchMove","onVolumeTouchEnd","VOLUME_STEP","newVol","setActiveOption","opt","isOpen","target","newSpeed","onDocClick","handleScrubAt","scrubbing","onMouseMove","onMouseUp","onTouchMove","onTouchEnd","startHideTimer","host","current","duration","clampedCurrent","pct","playing","PAUSE_ICON","idx","shouldInjectRuntime","state","sharedSheet","getSharedSheet","sheet","PLAYER_STYLES","DEFAULT_FPS","RUNTIME_CDN_URL","SHADER_CAPTURE_SCALE_ATTR","SHADER_LOADING_ATTR","SHADER_CAPTURE_SCALE_PARAM","SHADER_LOADING_PARAM","SHADER_LOADING_PHRASES","normalizeShaderCaptureScale","value","parsed","normalizeShaderLoadingMode","normalized","setQueryParam","params","key","withShaderQueryParams","src","scale","loadingMode","hashIndex","beforeHash","hash","queryIndex","path","query","nextQuery","isObjectRecord","isRuntimeDurationAdapter","isDirectTimelineAdapter","injectShaderOptionsIntoSrcdoc","html","lines","script","match","HyperframesPlayer","_HyperframesPlayer","style","shaderLoader","event","m","name","_old","val","rate","v","directTimelineStarted","timeInSeconds","frame","relTime","t","r","mode","l","action","extra","srcdoc","root","blockOverlayInteraction","eventName","panel","markFrame","title","titleText","detail","track","fill","progress","createProgressRow","labelText","row","label","transitionStatus","frameStatus","status","total","ratio","phraseIndex","frameValue","player","seek","timeline","tick","currentTime","completedPlayback","now","win","timelines","keys","rootId","runtimePlayer","target","data","state","rawTime","wasPlaying","nextPaused","attempts","wasPromoted","hasRuntime","hasTimelines","hasNestedCompositions","shouldInjectRuntime","adapter","w","h","doc","rect","callbacks","fraction","speed","volume","presetsAttr","speedPresets","n","createControls","url","err","timelineSeconds","options","force","requiredSamples","threshold","tag","start","duration","el","entry","audioSrc","mediaEls","iframeEl","rawSrc","created","obs","mutations","added","candidates","inside","removed","dropped","hosts","host","idx"]}
|
|
1
|
+
{"version":3,"sources":["../src/hyperframes-player.ts","../src/styles.ts","../src/controls.ts","../src/shouldInjectRuntime.ts"],"sourcesContent":["import { createControls, type ControlsCallbacks } from \"./controls.js\";\nimport { shouldInjectRuntime } from \"./shouldInjectRuntime.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\n\nlet sharedSheet: CSSStyleSheet | null = null;\n\nfunction getSharedSheet(): CSSStyleSheet | null {\n if (sharedSheet) return sharedSheet;\n if (typeof CSSStyleSheet === \"undefined\") return null;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(PLAYER_STYLES);\n sharedSheet = sheet;\n return sheet;\n } catch {\n return null;\n }\n}\n\nconst DEFAULT_FPS = 30;\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\nconst SHADER_CAPTURE_SCALE_ATTR = \"shader-capture-scale\";\nconst SHADER_LOADING_ATTR = \"shader-loading\";\nconst SHADER_CAPTURE_SCALE_PARAM = \"__hf_shader_capture_scale\";\nconst SHADER_LOADING_PARAM = \"__hf_shader_loading\";\n\nexport type ShaderLoadingMode = \"composition\" | \"player\" | \"none\";\n\ninterface ShaderTransitionState {\n ready?: boolean;\n progress?: number;\n total?: number;\n currentTransition?: number;\n transitionTotal?: number;\n transitionFrame?: number;\n transitionFrames?: number;\n phase?: \"cached\" | \"capturing\" | \"finalizing\";\n loading?: boolean;\n}\n\ninterface ShaderLoaderElements {\n root: HTMLDivElement;\n fill: HTMLDivElement;\n title: HTMLSpanElement;\n detail: HTMLDivElement;\n transitionValue: HTMLSpanElement;\n frameLabel: HTMLSpanElement;\n frameValue: HTMLSpanElement;\n frameRow: HTMLDivElement;\n}\n\ninterface RuntimeDurationAdapter {\n getDuration: () => number;\n}\n\ninterface DirectTimelineAdapter {\n duration: () => number;\n time: () => number;\n seek: (timeInSeconds: number) => unknown;\n play: () => unknown;\n pause: () => unknown;\n}\n\ntype PlaybackDurationAdapter =\n | { kind: \"runtime\"; getDuration: () => number }\n | { kind: \"direct-timeline\"; timeline: DirectTimelineAdapter; getDuration: () => number };\n\nconst SHADER_LOADING_PHRASES = [\n \"Preparing scene transitions\",\n \"Sampling outgoing scene motion\",\n \"Sampling incoming scene motion\",\n \"Caching transition frames\",\n \"Finalizing transition preview\",\n];\n\nfunction normalizeShaderCaptureScale(value: string | null): string | null {\n if (value === null) return null;\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed <= 0) return null;\n return String(Math.min(1, Math.max(0.25, parsed)));\n}\n\nfunction normalizeShaderLoadingMode(value: string | null): ShaderLoadingMode {\n if (value === null || value.trim() === \"\") return \"composition\";\n const normalized = value.trim().toLowerCase();\n if (\n normalized === \"none\" ||\n normalized === \"false\" ||\n normalized === \"0\" ||\n normalized === \"off\"\n ) {\n return \"none\";\n }\n if (\n normalized === \"player\" ||\n normalized === \"true\" ||\n normalized === \"1\" ||\n normalized === \"on\"\n ) {\n return \"player\";\n }\n return \"composition\";\n}\n\nfunction setQueryParam(params: URLSearchParams, key: string, value: string | null): void {\n if (value === null) params.delete(key);\n else params.set(key, value);\n}\n\nfunction withShaderQueryParams(\n src: string,\n scale: string | null,\n loadingMode: ShaderLoadingMode,\n): string {\n const hashIndex = src.indexOf(\"#\");\n const beforeHash = hashIndex >= 0 ? src.slice(0, hashIndex) : src;\n const hash = hashIndex >= 0 ? src.slice(hashIndex) : \"\";\n const queryIndex = beforeHash.indexOf(\"?\");\n const path = queryIndex >= 0 ? beforeHash.slice(0, queryIndex) : beforeHash;\n const query = queryIndex >= 0 ? beforeHash.slice(queryIndex + 1) : \"\";\n const params = new URLSearchParams(query);\n setQueryParam(params, SHADER_CAPTURE_SCALE_PARAM, scale);\n setQueryParam(params, SHADER_LOADING_PARAM, loadingMode === \"composition\" ? null : loadingMode);\n const nextQuery = params.toString();\n return `${path}${nextQuery ? `?${nextQuery}` : \"\"}${hash}`;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isRuntimeDurationAdapter(value: unknown): value is RuntimeDurationAdapter {\n return isObjectRecord(value) && typeof value.getDuration === \"function\";\n}\n\nfunction isDirectTimelineAdapter(value: unknown): value is DirectTimelineAdapter {\n return (\n isObjectRecord(value) &&\n typeof value.duration === \"function\" &&\n typeof value.time === \"function\" &&\n typeof value.seek === \"function\" &&\n typeof value.play === \"function\" &&\n typeof value.pause === \"function\"\n );\n}\n\nfunction injectShaderOptionsIntoSrcdoc(\n html: string,\n scale: string | null,\n loadingMode: ShaderLoadingMode,\n): string {\n if (scale === null && loadingMode === \"composition\") return html;\n const lines: string[] = [];\n if (scale !== null) lines.push(`window.__HF_SHADER_CAPTURE_SCALE=${JSON.stringify(scale)};`);\n if (loadingMode !== \"composition\") {\n lines.push(`window.__HF_SHADER_LOADING=${JSON.stringify(loadingMode)};`);\n }\n const script = `<script data-hyperframes-player-shader-options>${lines.join(\"\")}</script>`;\n if (/<head\\b[^>]*>/i.test(html))\n return html.replace(/<head\\b[^>]*>/i, (match) => `${match}${script}`);\n if (/<html\\b[^>]*>/i.test(html))\n return html.replace(/<html\\b[^>]*>/i, (match) => `${match}${script}`);\n return `${script}${html}`;\n}\n\nclass HyperframesPlayer extends HTMLElement {\n static get observedAttributes() {\n return [\n \"src\",\n \"srcdoc\",\n \"width\",\n \"height\",\n \"controls\",\n \"muted\",\n \"volume\",\n \"poster\",\n \"playback-rate\",\n \"audio-src\",\n SHADER_CAPTURE_SCALE_ATTR,\n SHADER_LOADING_ATTR,\n ];\n }\n\n private shadow: ShadowRoot;\n private container: HTMLDivElement;\n private iframe: HTMLIFrameElement;\n private posterEl: HTMLImageElement | null = null;\n private controlsApi: ReturnType<typeof createControls> | null = null;\n private resizeObserver: ResizeObserver;\n private shaderLoaderEl: HTMLDivElement;\n private shaderLoaderFillEl: HTMLDivElement;\n private shaderLoaderTitleEl: HTMLSpanElement;\n private shaderLoaderDetailEl: HTMLDivElement;\n private shaderLoaderTransitionValueEl: HTMLSpanElement;\n private shaderLoaderFrameLabelEl: HTMLSpanElement;\n private shaderLoaderFrameValueEl: HTMLSpanElement;\n private shaderLoaderFrameRowEl: HTMLDivElement;\n private shaderLoaderHideTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private _ready = false;\n private _duration = 0;\n private _currentTime = 0;\n private _paused = true;\n private _volume = 1;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _probeInterval: ReturnType<typeof setInterval> | null = null;\n private _lastUpdateMs = 0;\n private _directTimelineAdapter: DirectTimelineAdapter | null = null;\n private _directTimelineRaf: number | null = null;\n\n /**\n * Parent-frame audio/video proxies, preloaded mirror copies of the iframe's\n * timed media. They exist as a fallback for environments that block iframe\n * `.play()` — mobile browsers require the user gesture to originate in the\n * same frame as the media element, and postMessage doesn't transfer user\n * activation (User Activation v2). The runtime inside the iframe signals\n * `media-autoplay-blocked` the first time a play() attempt rejects with\n * `NotAllowedError`; receiving that message flips `_audioOwner` to `parent`\n * and these proxies start driving audible output while the iframe keeps\n * advancing timed media silently for frame-accurate state.\n *\n * Preloading at iframe-load time (rather than lazily on promotion) keeps\n * the audible audio cut-in tight when the promotion fires mid-playback.\n */\n private _parentMedia: Array<{\n el: HTMLMediaElement;\n start: number;\n duration: number;\n /**\n * Count of consecutive steady-state samples in which the proxy's\n * `currentTime` was found drifted beyond `MIRROR_DRIFT_THRESHOLD_SECONDS`.\n * Reset on every in-threshold sample. `_mirrorParentMediaTime` only\n * issues a write once this passes `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES`,\n * which absorbs single-sample jitter (e.g. one slow bridge tick) without\n * thrashing the media element with seeks. Forced calls (promotion,\n * media-added) bypass the gate and reset the counter.\n */\n driftSamples: number;\n }> = [];\n\n /**\n * Who owns audible playback right now.\n *\n * - `runtime` (default): the iframe's runtime drives timed media; parent\n * proxies stay paused and silent. This is the correct path on desktop,\n * in same-frame embeds, and anywhere the iframe has user activation.\n * - `parent`: parent-frame proxies drive audible output; the iframe keeps\n * syncing timed media but at `muted = true` (orthogonal to author/user\n * volume settings). Entered only in response to an actual autoplay\n * rejection from the runtime — we don't guess device class.\n *\n * The transition is one-way per session; once autoplay is known to be\n * gated, there's no benefit to attempting the iframe path again.\n */\n private _audioOwner: \"runtime\" | \"parent\" = \"runtime\";\n\n /**\n * Watches the iframe document for sub-composition media added after\n * initial setup. Disconnected on iframe reload (fresh iframe = fresh\n * observer against the new document).\n */\n private _mediaObserver?: MutationObserver;\n\n /**\n * One-shot latch for `playbackerror`. Without it, under parent ownership\n * where the parent frame itself lacks activation, every paused→playing\n * transition in the iframe state loop would re-fire `play()` (and its\n * rejection) on each proxy — spamming host subscribers through a whole\n * playback session. Mirrors the `mediaAutoplayBlockedPosted` latch on the\n * runtime side. Cleared on `_onIframeLoad` alongside the owner reset, so\n * a fresh composition gets a fresh shot at surfacing the error.\n */\n private _playbackErrorPosted = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n const sheet = getSharedSheet();\n if (sheet) {\n this.shadow.adoptedStyleSheets = [sheet];\n } else {\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n this.shadow.appendChild(style);\n }\n\n this.container = document.createElement(\"div\");\n this.container.className = \"hfp-container\";\n\n this.iframe = document.createElement(\"iframe\");\n this.iframe.className = \"hfp-iframe\";\n this.iframe.sandbox.add(\"allow-scripts\", \"allow-same-origin\");\n this.iframe.allow = \"autoplay; fullscreen\";\n this.iframe.referrerPolicy = \"no-referrer\";\n this.iframe.title = \"HyperFrames Composition\";\n\n this.container.appendChild(this.iframe);\n this.shadow.appendChild(this.container);\n const shaderLoader = this._createShaderLoader();\n this.shaderLoaderEl = shaderLoader.root;\n this.shaderLoaderFillEl = shaderLoader.fill;\n this.shaderLoaderTitleEl = shaderLoader.title;\n this.shaderLoaderDetailEl = shaderLoader.detail;\n this.shaderLoaderTransitionValueEl = shaderLoader.transitionValue;\n this.shaderLoaderFrameLabelEl = shaderLoader.frameLabel;\n this.shaderLoaderFrameValueEl = shaderLoader.frameValue;\n this.shaderLoaderFrameRowEl = shaderLoader.frameRow;\n this.shadow.appendChild(this.shaderLoaderEl);\n\n // Clicking the bare player surface toggles play/pause.\n // Ignore shadow-DOM control interactions so overlay clicks don't double-handle.\n this.addEventListener(\"click\", (event) => {\n if (this._isControlsClick(event)) return;\n if (this._paused) this.play();\n else this.pause();\n });\n\n this.resizeObserver = new ResizeObserver(() => this._updateScale());\n\n this._onMessage = this._onMessage.bind(this);\n this._onIframeLoad = this._onIframeLoad.bind(this);\n }\n\n connectedCallback() {\n this.resizeObserver.observe(this);\n window.addEventListener(\"message\", this._onMessage);\n this.iframe.addEventListener(\"load\", this._onIframeLoad);\n\n if (this.hasAttribute(\"controls\")) this._setupControls();\n if (this.hasAttribute(\"poster\")) this._setupPoster();\n if (this.hasAttribute(\"audio-src\"))\n this._setupParentAudioFromUrl(this.getAttribute(\"audio-src\")!);\n // srcdoc wins over src per HTML spec when both are set; mirror both attributes\n // so the browser applies the standard precedence rules.\n if (this.hasAttribute(\"srcdoc\"))\n this.iframe.srcdoc = this._prepareSrcdoc(this.getAttribute(\"srcdoc\")!);\n if (this.hasAttribute(\"src\")) this.iframe.src = this._prepareSrc(this.getAttribute(\"src\")!);\n }\n\n disconnectedCallback() {\n this.resizeObserver.disconnect();\n window.removeEventListener(\"message\", this._onMessage);\n this.iframe.removeEventListener(\"load\", this._onIframeLoad);\n if (this._probeInterval) clearInterval(this._probeInterval);\n this._stopDirectTimelineClock();\n this._directTimelineAdapter = null;\n if (this.shaderLoaderHideTimeout) clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n this._teardownMediaObserver();\n this.controlsApi?.destroy();\n for (const m of this._parentMedia) {\n m.el.pause();\n m.el.src = \"\";\n }\n this._parentMedia = [];\n }\n\n attributeChangedCallback(name: string, _old: string | null, val: string | null) {\n switch (name) {\n case \"src\":\n if (val) {\n this._ready = false;\n this.iframe.src = this._prepareSrc(val);\n }\n break;\n case \"srcdoc\":\n // Distinguish removal (null) from empty-string (\"\") so callers can clear\n // srcdoc and let src take over. Always reset readiness; the iframe will\n // load a new document either way.\n this._ready = false;\n if (val !== null) this.iframe.srcdoc = this._prepareSrcdoc(val);\n else this.iframe.removeAttribute(\"srcdoc\");\n break;\n case \"width\":\n this._compositionWidth = parseInt(val || \"1920\", 10);\n this._updateScale();\n break;\n case \"height\":\n this._compositionHeight = parseInt(val || \"1080\", 10);\n this._updateScale();\n break;\n case \"controls\":\n if (val !== null) this._setupControls();\n else {\n this.controlsApi?.destroy();\n this.controlsApi = null;\n }\n break;\n case \"poster\":\n this._setupPoster();\n break;\n case \"playback-rate\": {\n const rate = parseFloat(val || \"1\");\n for (const m of this._parentMedia) m.el.playbackRate = rate;\n this._sendControl(\"set-playback-rate\", { playbackRate: rate });\n this.controlsApi?.updateSpeed(rate);\n this.dispatchEvent(new Event(\"ratechange\"));\n break;\n }\n case \"muted\":\n for (const m of this._parentMedia) m.el.muted = val !== null;\n this._sendControl(\"set-muted\", { muted: val !== null });\n this.controlsApi?.updateMuted(val !== null);\n this.dispatchEvent(new Event(\"volumechange\"));\n break;\n case \"volume\": {\n const v = Math.max(0, Math.min(1, parseFloat(val || \"1\")));\n this._volume = v;\n for (const m of this._parentMedia) m.el.volume = v;\n this._sendControl(\"set-volume\", { volume: v });\n this.controlsApi?.updateVolume(v);\n this.dispatchEvent(new Event(\"volumechange\"));\n break;\n }\n case \"audio-src\":\n if (val) this._setupParentAudioFromUrl(val);\n break;\n case SHADER_CAPTURE_SCALE_ATTR:\n case SHADER_LOADING_ATTR:\n this._reloadShaderOptions();\n break;\n }\n }\n\n // ── Public API ──\n\n /**\n * Access the inner `<iframe>` element rendering the composition.\n *\n * Use this when integrating the player with editors, recorders, or\n * timeline tools (e.g. `@hyperframes/studio`) that need to inspect\n * the composition's DOM or read its `__player` / `__timelines`\n * runtime objects.\n *\n * **Common pitfall:** the iframe lives inside the player's Shadow DOM.\n * Passing the `<hyperframes-player>` element itself to code that expects\n * an `<iframe>` will silently break — `.contentWindow` returns `null`.\n * Always extract `iframeElement` first:\n *\n * ```ts\n * // ❌ Wrong — element ref doesn't expose contentWindow\n * iframeRef.current = playerRef.current;\n *\n * // ✓ Right — bridge the actual iframe\n * iframeRef.current = playerRef.current.iframeElement;\n * ```\n */\n get iframeElement(): HTMLIFrameElement {\n return this.iframe;\n }\n\n play() {\n this._hidePoster();\n if (this._duration > 0 && this._currentTime >= this._duration) {\n this.seek(0);\n }\n // Drive the iframe runtime when present. Same-origin standalone GSAP\n // compositions can expose only `window.__timelines`, so they use a direct\n // timeline adapter instead of a postMessage bridge nobody is listening to.\n const directTimelineStarted = this._tryDirectTimelinePlay();\n if (!directTimelineStarted) this._sendControl(\"play\");\n if (this._audioOwner === \"parent\") this._playParentMedia();\n this._paused = false;\n this.controlsApi?.updatePlaying(true);\n this.dispatchEvent(new Event(\"play\"));\n if (directTimelineStarted) this._startDirectTimelineClock();\n }\n\n pause() {\n if (!this._tryDirectTimelinePause()) this._sendControl(\"pause\");\n this._stopDirectTimelineClock();\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"pause\"));\n }\n\n /**\n * Move playback to `timeInSeconds`.\n *\n * Two transports, with different precision semantics — read this before\n * writing assertions against `seek` from outside the player:\n *\n * - **Same-origin (sync) path** — when the runtime's `window.__player.seek`\n * is reachable, we call it directly. `timeInSeconds` is forwarded\n * *verbatim* (no rounding), so a same-origin scrub of `seek(7.3333)`\n * lands the runtime at `7.3333 s` — sub-frame precision relative to\n * `DEFAULT_FPS` (30). Studio scrub UIs that need fractional-frame\n * alignment (e.g. waveform scrubbing on long-duration audio) get the\n * exact requested time.\n * - **Cross-origin (postMessage) path** — when same-origin access throws\n * or `__player.seek` is missing, we fall back to the postMessage bridge.\n * The wire protocol carries integer frames (`frame: Math.round(t × FPS)`),\n * so cross-origin embeds are *frame-quantized* and `seek(7.3333)` lands\n * at `Math.round(7.3333 × 30) / 30 ≈ 7.3333…` (same value here, but for\n * most fractional inputs you'll see a snap to the nearest 1/30 s).\n *\n * `this._currentTime` always reflects the *requested* `timeInSeconds`\n * regardless of transport, so the controls UI shows the un-quantized value\n * either way; the asymmetry only affects what the runtime actually paints.\n */\n seek(timeInSeconds: number) {\n if (!this._trySyncSeek(timeInSeconds) && !this._tryDirectTimelineSeek(timeInSeconds)) {\n const frame = Math.round(timeInSeconds * DEFAULT_FPS);\n this._sendControl(\"seek\", { frame });\n }\n this._stopDirectTimelineClock();\n this._currentTime = timeInSeconds;\n\n // Mirror parent proxy currentTime only while parent owns audible output.\n // Under `runtime` ownership the proxies are paused and authoritative time\n // lives on the iframe — touching parent currentTime would just trigger\n // needless buffering if ownership later flips.\n if (this._audioOwner === \"parent\") {\n for (const m of this._parentMedia) {\n const relTime = timeInSeconds - m.start;\n if (relTime >= 0 && relTime < m.duration) m.el.currentTime = relTime;\n }\n }\n\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n }\n\n get currentTime() {\n return this._currentTime;\n }\n set currentTime(t: number) {\n this.seek(t);\n }\n\n get duration() {\n return this._duration;\n }\n get paused() {\n return this._paused;\n }\n get ready() {\n return this._ready;\n }\n\n get playbackRate() {\n return parseFloat(this.getAttribute(\"playback-rate\") || \"1\");\n }\n set playbackRate(r: number) {\n this.setAttribute(\"playback-rate\", String(r));\n }\n\n get shaderCaptureScale() {\n return Number(normalizeShaderCaptureScale(this.getAttribute(SHADER_CAPTURE_SCALE_ATTR)) ?? \"1\");\n }\n set shaderCaptureScale(scale: number) {\n this.setAttribute(SHADER_CAPTURE_SCALE_ATTR, String(scale));\n }\n\n get shaderLoading() {\n return normalizeShaderLoadingMode(this.getAttribute(SHADER_LOADING_ATTR));\n }\n set shaderLoading(mode: ShaderLoadingMode) {\n if (mode === \"composition\") this.removeAttribute(SHADER_LOADING_ATTR);\n else this.setAttribute(SHADER_LOADING_ATTR, mode);\n }\n\n get muted() {\n return this.hasAttribute(\"muted\");\n }\n set muted(m: boolean) {\n if (m) this.setAttribute(\"muted\", \"\");\n else this.removeAttribute(\"muted\");\n }\n\n get volume() {\n return this._volume;\n }\n set volume(v: number) {\n this.setAttribute(\"volume\", String(Math.max(0, Math.min(1, v))));\n }\n\n get loop() {\n return this.hasAttribute(\"loop\");\n }\n set loop(l: boolean) {\n if (l) this.setAttribute(\"loop\", \"\");\n else this.removeAttribute(\"loop\");\n }\n\n // ── Private ──\n\n private _sendControl(action: string, extra: Record<string, unknown> = {}) {\n try {\n this.iframe.contentWindow?.postMessage(\n { source: \"hf-parent\", type: \"control\", action, ...extra },\n \"*\",\n );\n } catch {\n /* cross-origin */\n }\n }\n\n private _shaderCaptureScaleParam(): string | null {\n return normalizeShaderCaptureScale(this.getAttribute(SHADER_CAPTURE_SCALE_ATTR));\n }\n\n private _shaderLoadingMode(): ShaderLoadingMode {\n return normalizeShaderLoadingMode(this.getAttribute(SHADER_LOADING_ATTR));\n }\n\n private _prepareSrc(src: string): string {\n return withShaderQueryParams(src, this._shaderCaptureScaleParam(), this._shaderLoadingMode());\n }\n\n private _prepareSrcdoc(srcdoc: string): string {\n return injectShaderOptionsIntoSrcdoc(\n srcdoc,\n this._shaderCaptureScaleParam(),\n this._shaderLoadingMode(),\n );\n }\n\n private _reloadShaderOptions(): void {\n if (this._shaderLoadingMode() !== \"player\") {\n this._resetShaderLoader();\n }\n if (this.hasAttribute(\"srcdoc\")) {\n this.iframe.srcdoc = this._prepareSrcdoc(this.getAttribute(\"srcdoc\") || \"\");\n return;\n }\n if (this.hasAttribute(\"src\")) {\n this.iframe.src = this._prepareSrc(this.getAttribute(\"src\") || \"\");\n }\n }\n\n private _createShaderLoader(): ShaderLoaderElements {\n const root = document.createElement(\"div\");\n root.className = \"hfp-shader-loader\";\n root.setAttribute(\"role\", \"status\");\n root.setAttribute(\"aria-live\", \"polite\");\n root.setAttribute(\"aria-label\", \"Preparing scene transitions\");\n root.setAttribute(\"data-hyperframes-ignore\", \"\");\n root.draggable = false;\n\n const blockOverlayInteraction = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n };\n for (const eventName of [\n \"selectstart\",\n \"dragstart\",\n \"pointerdown\",\n \"mousedown\",\n \"click\",\n \"dblclick\",\n \"contextmenu\",\n \"touchstart\",\n ]) {\n root.addEventListener(eventName, blockOverlayInteraction, { capture: true });\n }\n\n const panel = document.createElement(\"div\");\n panel.className = \"hfp-shader-loader-panel\";\n panel.draggable = false;\n\n const markFrame = document.createElement(\"div\");\n markFrame.className = \"hfp-shader-loader-mark\";\n markFrame.draggable = false;\n markFrame.innerHTML = [\n '<svg width=\"78\" height=\"78\" viewBox=\"0 0 100 100\" fill=\"none\" aria-hidden=\"true\" draggable=\"false\">',\n '<path d=\"M10.1851 57.8021L33.1145 73.8313C36.2202 75.9978 41.5173 73.5433 42.4816 69.4984L51.7611 30.4271C52.7253 26.3822 48.5802 23.9277 44.4602 26.0942L13.917 42.1235C6.96677 45.7676 4.97564 54.1579 10.1851 57.8021Z\" fill=\"url(#hfp-shader-loader-grad-left)\"/>',\n '<path d=\"M87.5129 57.5141L56.9696 73.5433C52.8371 75.7098 48.7046 73.2553 49.6688 69.2104L58.9483 30.1391C59.9125 26.0942 65.2097 23.6397 68.3154 25.8062L91.2447 41.8354C96.4668 45.4796 94.4631 53.8699 87.5129 57.5141Z\" fill=\"url(#hfp-shader-loader-grad-right)\"/>',\n \"<defs>\",\n '<linearGradient id=\"hfp-shader-loader-grad-left\" x1=\"48.5676\" y1=\"25\" x2=\"44.7804\" y2=\"71.9384\" gradientUnits=\"userSpaceOnUse\">',\n '<stop stop-color=\"#06E3FA\"/>',\n '<stop offset=\"1\" stop-color=\"#4FDB5E\"/>',\n \"</linearGradient>\",\n '<linearGradient id=\"hfp-shader-loader-grad-right\" x1=\"54.8282\" y1=\"73.8392\" x2=\"72.0989\" y2=\"32.8932\" gradientUnits=\"userSpaceOnUse\">',\n '<stop stop-color=\"#06E3FA\"/>',\n '<stop offset=\"1\" stop-color=\"#4FDB5E\"/>',\n \"</linearGradient>\",\n \"</defs>\",\n \"</svg>\",\n ].join(\"\");\n\n const title = document.createElement(\"div\");\n title.className = \"hfp-shader-loader-title\";\n const titleText = document.createElement(\"span\");\n titleText.className = \"hfp-shader-loader-title-text\";\n titleText.textContent = SHADER_LOADING_PHRASES[0] || \"Preparing scene transitions\";\n title.appendChild(titleText);\n\n const detail = document.createElement(\"div\");\n detail.className = \"hfp-shader-loader-detail\";\n detail.textContent = \"Rendering animated scene samples for shader transitions.\";\n\n const track = document.createElement(\"div\");\n track.className = \"hfp-shader-loader-track\";\n track.setAttribute(\"aria-hidden\", \"true\");\n const fill = document.createElement(\"div\");\n fill.className = \"hfp-shader-loader-fill\";\n track.appendChild(fill);\n\n const progress = document.createElement(\"div\");\n progress.className = \"hfp-shader-loader-progress\";\n const createProgressRow = (labelText: string) => {\n const row = document.createElement(\"div\");\n row.className = \"hfp-shader-loader-row\";\n const label = document.createElement(\"span\");\n label.className = \"hfp-shader-loader-label\";\n label.textContent = labelText;\n const value = document.createElement(\"span\");\n value.className = \"hfp-shader-loader-value\";\n row.appendChild(label);\n row.appendChild(value);\n progress.appendChild(row);\n return { row, label, value };\n };\n const transitionStatus = createProgressRow(\"transition\");\n const frameStatus = createProgressRow(\"transition frame\");\n\n panel.appendChild(markFrame);\n panel.appendChild(title);\n panel.appendChild(detail);\n panel.appendChild(track);\n panel.appendChild(progress);\n root.appendChild(panel);\n\n return {\n root,\n fill,\n title: titleText,\n detail,\n transitionValue: transitionStatus.value,\n frameLabel: frameStatus.label,\n frameValue: frameStatus.value,\n frameRow: frameStatus.row,\n };\n }\n\n private _showShaderLoader(): void {\n if (this.shaderLoaderHideTimeout) {\n clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n }\n this.shaderLoaderEl.classList.remove(\"hfp-hiding\");\n this.shaderLoaderEl.classList.add(\"hfp-visible\");\n }\n\n private _hideShaderLoader(): void {\n if (this.shaderLoaderEl.classList.contains(\"hfp-hiding\")) {\n if (!this.shaderLoaderHideTimeout) this._scheduleShaderLoaderHideCleanup();\n return;\n }\n if (!this.shaderLoaderEl.classList.contains(\"hfp-visible\")) return;\n this.shaderLoaderEl.classList.add(\"hfp-hiding\");\n this.shaderLoaderEl.classList.remove(\"hfp-visible\");\n this._scheduleShaderLoaderHideCleanup();\n }\n\n private _scheduleShaderLoaderHideCleanup(): void {\n if (this.shaderLoaderHideTimeout) clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = setTimeout(() => {\n this.shaderLoaderEl.classList.remove(\"hfp-hiding\");\n this.shaderLoaderHideTimeout = null;\n }, 420);\n }\n\n private _resetShaderLoader(): void {\n if (this.shaderLoaderHideTimeout) {\n clearTimeout(this.shaderLoaderHideTimeout);\n this.shaderLoaderHideTimeout = null;\n }\n this.shaderLoaderEl.classList.remove(\"hfp-visible\", \"hfp-hiding\");\n this.shaderLoaderFillEl.style.transform = \"scaleX(0)\";\n this.shaderLoaderTransitionValueEl.textContent = \"\";\n this.shaderLoaderFrameValueEl.textContent = \"\";\n this.shaderLoaderFrameRowEl.style.visibility = \"hidden\";\n }\n\n private _updateShaderLoader(status: ShaderTransitionState): void {\n if (this._shaderLoadingMode() !== \"player\") {\n this._resetShaderLoader();\n return;\n }\n if (status.ready || !status.loading) {\n this._hideShaderLoader();\n return;\n }\n\n const progress =\n typeof status.progress === \"number\" && Number.isFinite(status.progress) ? status.progress : 0;\n const total =\n typeof status.total === \"number\" && Number.isFinite(status.total) ? status.total : 0;\n const ratio = total > 0 ? Math.min(1, Math.max(0, progress / total)) : 0;\n const phraseIndex = Math.min(\n SHADER_LOADING_PHRASES.length - 1,\n Math.floor(ratio * SHADER_LOADING_PHRASES.length),\n );\n this.shaderLoaderTitleEl.textContent =\n SHADER_LOADING_PHRASES[phraseIndex] || \"Preparing scene transitions\";\n this.shaderLoaderDetailEl.textContent =\n status.phase === \"cached\"\n ? \"Loading cached transition frames before playback.\"\n : status.phase === \"finalizing\"\n ? \"Uploading transition textures for smooth playback.\"\n : \"Rendering animated scene samples for shader transitions.\";\n this.shaderLoaderFillEl.style.transform = `scaleX(${ratio})`;\n\n this.shaderLoaderTransitionValueEl.textContent =\n status.currentTransition !== undefined && status.transitionTotal !== undefined\n ? `${status.currentTransition}/${status.transitionTotal}`\n : total > 0\n ? `${progress}/${total}`\n : \"\";\n\n const frameValue =\n status.transitionFrame !== undefined && status.transitionFrames !== undefined\n ? `${status.transitionFrame}/${status.transitionFrames}`\n : \"\";\n this.shaderLoaderFrameLabelEl.textContent =\n status.phase === \"cached\"\n ? \"cached transition frames\"\n : status.phase === \"finalizing\"\n ? \"finalizing transition frames\"\n : \"rendering transition frames\";\n this.shaderLoaderFrameValueEl.textContent = frameValue;\n this.shaderLoaderFrameRowEl.style.visibility = frameValue ? \"visible\" : \"hidden\";\n this.shaderLoaderEl.setAttribute(\"aria-valuenow\", String(Math.round(ratio * 100)));\n this._showShaderLoader();\n }\n\n /**\n * Reach into the runtime's `window.__player.seek` directly, skipping the\n * postMessage hop. Same-origin only — cross-origin embeds throw a\n * `SecurityError` on `contentWindow` property access, which we catch and\n * report as a no-op so the caller can transparently fall back to the\n * postMessage bridge. Returns `true` only when the runtime accepted the\n * call (`__player.seek` exists, is callable, and didn't throw).\n *\n * Studio has used this access path privately via `iframe.contentWindow.__player`\n * (see `useTimelinePlayer.ts`); this helper just formalizes the same\n * detection inside the player so external scrub UIs get the same\n * single-task latency. The runtime-side `seek` is the same wrapped\n * function the postMessage handler calls (`installRuntimeControlBridge`\n * routes through `player.seek`), so `markExplicitSeek()` and downstream\n * runtime state stay identical between the two paths.\n */\n private _trySyncSeek(timeInSeconds: number): boolean {\n try {\n const win = this.iframe.contentWindow as\n | (Window & { __player?: { seek?: (t: number) => void } })\n | null;\n const player = win?.__player;\n const seek = player?.seek;\n if (typeof seek !== \"function\") return false;\n seek.call(player, timeInSeconds);\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelineSeek(timeInSeconds: number): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.seek(timeInSeconds);\n // GSAP seek() preserves play state; the player seek() contract lands paused.\n timeline.pause();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelinePlay(): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.play();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _tryDirectTimelinePause(): boolean {\n const timeline = this._directTimelineAdapter || this._resolveDirectTimelineAdapter();\n if (!timeline) return false;\n try {\n timeline.pause();\n this._directTimelineAdapter = timeline;\n return true;\n } catch {\n return false;\n }\n }\n\n private _startDirectTimelineClock(): void {\n this._stopDirectTimelineClock();\n\n const tick = () => {\n const timeline = this._directTimelineAdapter;\n if (!timeline || this._paused) {\n this._directTimelineRaf = null;\n return;\n }\n\n let currentTime: number;\n try {\n currentTime = timeline.time();\n } catch {\n this._directTimelineRaf = null;\n return;\n }\n\n if (this._duration > 0) currentTime = Math.min(currentTime, this._duration);\n this._currentTime = currentTime;\n const completedPlayback = this._duration > 0 && currentTime >= this._duration;\n const now = performance.now();\n if (now - this._lastUpdateMs > 100 || completedPlayback) {\n this._lastUpdateMs = now;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n this.dispatchEvent(\n new CustomEvent(\"timeupdate\", { detail: { currentTime: this._currentTime } }),\n );\n }\n\n if (completedPlayback) {\n if (this.loop) {\n this.seek(0);\n this.play();\n return;\n }\n timeline.pause();\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n this._directTimelineRaf = null;\n return;\n }\n\n this._directTimelineRaf = requestAnimationFrame(tick);\n };\n\n this._directTimelineRaf = requestAnimationFrame(tick);\n }\n\n private _stopDirectTimelineClock(): void {\n if (this._directTimelineRaf === null) return;\n cancelAnimationFrame(this._directTimelineRaf);\n this._directTimelineRaf = null;\n }\n\n private _resolveDirectTimelineAdapter(): DirectTimelineAdapter | null {\n try {\n const win = this.iframe.contentWindow;\n if (!win) return null;\n return this._resolveDirectTimelineAdapterFromWindow(win);\n } catch {\n return null;\n }\n }\n\n private _resolveDirectTimelineAdapterFromWindow(win: Window): DirectTimelineAdapter | null {\n if (this._hasRuntimeBridge(win)) return null;\n\n const timelines = Reflect.get(win, \"__timelines\");\n if (!isObjectRecord(timelines)) return null;\n\n const keys = Object.keys(timelines);\n if (keys.length === 0) return null;\n\n const rootId = this.iframe.contentDocument\n ?.querySelector(\"[data-composition-id]\")\n ?.getAttribute(\"data-composition-id\");\n const key = rootId && rootId in timelines ? rootId : keys[keys.length - 1];\n const timeline = timelines[key];\n return isDirectTimelineAdapter(timeline) ? timeline : null;\n }\n\n private _hasRuntimeBridge(win: Window): boolean {\n return Reflect.get(win, \"__hf\") !== undefined || isObjectRecord(Reflect.get(win, \"__player\"));\n }\n\n private _resolvePlaybackDurationAdapter(win: Window): PlaybackDurationAdapter | null {\n const runtimePlayer = Reflect.get(win, \"__player\");\n if (isRuntimeDurationAdapter(runtimePlayer)) {\n return { kind: \"runtime\", getDuration: () => runtimePlayer.getDuration() };\n }\n\n const timeline = this._resolveDirectTimelineAdapterFromWindow(win);\n if (timeline) {\n return {\n kind: \"direct-timeline\",\n timeline,\n getDuration: () => timeline.duration(),\n };\n }\n\n return null;\n }\n\n private _isControlsClick(event: Event) {\n return event\n .composedPath()\n .some((target) => target instanceof HTMLElement && target.classList.contains(\"hfp-controls\"));\n }\n\n private _onMessage(e: MessageEvent) {\n if (e.source !== this.iframe.contentWindow) return;\n const data = e.data;\n if (!data || data.source !== \"hf-preview\") return;\n\n if (data.type === \"shader-transition-state\") {\n const state: ShaderTransitionState =\n data.state && typeof data.state === \"object\" ? data.state : {};\n this._updateShaderLoader(state);\n this.dispatchEvent(\n new CustomEvent(\"shadertransitionstate\", {\n detail: { compositionId: data.compositionId, state },\n }),\n );\n return;\n }\n\n if (data.type === \"state\") {\n const rawTime = (data.frame ?? 0) / DEFAULT_FPS;\n this._currentTime = this._duration > 0 ? Math.min(rawTime, this._duration) : rawTime;\n const wasPlaying = !this._paused;\n const nextPaused = !data.isPlaying;\n const completedPlayback =\n this._duration > 0 && this._currentTime >= this._duration && (wasPlaying || data.isPlaying);\n\n if (completedPlayback && this.loop) {\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = nextPaused;\n this.seek(0);\n this.play();\n return;\n }\n\n this._paused = nextPaused;\n\n // Under parent ownership the proxies are the audible output, so they\n // mirror the iframe's play/pause transitions (externally-driven pause\n // via `__player.pause()`, scrubber interactions, etc.) and their\n // currentTime is slaved to the iframe timeline. Under runtime ownership\n // the proxies stay paused and silent; nothing here should wake them.\n if (this._audioOwner === \"parent\") {\n if (wasPlaying && this._paused) {\n this._pauseParentMedia();\n } else if (!wasPlaying && !this._paused) {\n this._playParentMedia();\n }\n this._mirrorParentMediaTime(this._currentTime);\n }\n\n // Throttle UI updates and event dispatch to ~10fps to avoid excessive re-renders\n const now = performance.now();\n if (now - this._lastUpdateMs > 100 || this._paused !== wasPlaying) {\n this._lastUpdateMs = now;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n this.controlsApi?.updatePlaying(!this._paused);\n this.dispatchEvent(\n new CustomEvent(\"timeupdate\", { detail: { currentTime: this._currentTime } }),\n );\n }\n\n if (completedPlayback) {\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n }\n }\n\n if (data.type === \"media-autoplay-blocked\") {\n this._promoteToParentProxy();\n }\n\n if (data.type === \"timeline\" && data.durationInFrames > 0) {\n // Ignore Infinity duration from runtime (caused by loop-inflated timelines without data-duration)\n // The player already has duration from the initial probe, so keep that.\n if (Number.isFinite(data.durationInFrames)) {\n this._duration = data.durationInFrames / DEFAULT_FPS;\n this.controlsApi?.updateTime(this._currentTime, this._duration);\n }\n }\n\n if (data.type === \"stage-size\" && data.width > 0 && data.height > 0) {\n this._compositionWidth = data.width;\n this._compositionHeight = data.height;\n this._updateScale();\n }\n }\n\n private _runtimeInjected = false;\n\n private _onIframeLoad() {\n let attempts = 0;\n this._runtimeInjected = false;\n this._directTimelineAdapter = null;\n this._stopDirectTimelineClock();\n this._resetShaderLoader();\n // A fresh iframe means a fresh runtime — `mediaOutputMuted` and the\n // autoplay-blocked latch are both reset inside it. The web component's\n // `_audioOwner` must reset to match, otherwise a composition switch on\n // a previously-promoted player would leave the parent thinking it owns\n // audio against a runtime that's happily playing the iframe copy again\n // — briefly reintroducing the double-voice bug for one probe window.\n // The next `NotAllowedError` (if any) will re-promote.\n const wasPromoted = this._audioOwner === \"parent\";\n this._audioOwner = \"runtime\";\n this._playbackErrorPosted = false;\n this._pauseParentMedia();\n // The old iframe document is about to go away. Disconnect the\n // MutationObserver now so we don't hold a reference to it; a fresh\n // one will attach once the new document settles in `_setupParentMedia`.\n this._teardownMediaObserver();\n if (wasPromoted) {\n this.dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"runtime\", reason: \"iframe-reload\" },\n }),\n );\n }\n if (this._probeInterval) clearInterval(this._probeInterval);\n\n this._probeInterval = setInterval(() => {\n attempts++;\n try {\n const win = this.iframe.contentWindow as Window & {\n __player?: { getDuration: () => number };\n __timelines?: Record<string, { duration: () => number }>;\n __hf?: unknown;\n };\n if (!win) return;\n\n // Check if the runtime bridge is active (__hf or __player from the runtime)\n const hasRuntime = !!(win.__hf || win.__player);\n const hasTimelines = !!(win.__timelines && Object.keys(win.__timelines).length > 0);\n const hasNestedCompositions =\n !!this.iframe.contentDocument?.querySelector(\"[data-composition-src]\");\n\n if (\n shouldInjectRuntime({\n hasRuntime,\n hasTimelines,\n hasNestedCompositions,\n runtimeInjected: this._runtimeInjected,\n attempts,\n })\n ) {\n this._injectRuntime();\n return; // Wait for runtime to load and initialize\n }\n\n // Runtime was injected but hasn't loaded yet — keep waiting\n if (this._runtimeInjected && !hasRuntime) {\n return;\n }\n\n const adapter = this._resolvePlaybackDurationAdapter(win);\n if (adapter && adapter.getDuration() > 0) {\n clearInterval(this._probeInterval!);\n this._duration = adapter.getDuration();\n this._directTimelineAdapter =\n adapter.kind === \"direct-timeline\" ? adapter.timeline : null;\n this._ready = true;\n this.controlsApi?.updateTime(0, this._duration);\n this.dispatchEvent(new CustomEvent(\"ready\", { detail: { duration: this._duration } }));\n\n // Auto-detect dimensions from composition\n const doc = this.iframe.contentDocument;\n const root = doc?.querySelector(\"[data-composition-id]\");\n if (root) {\n const w = parseInt(root.getAttribute(\"data-width\") || \"0\", 10);\n const h = parseInt(root.getAttribute(\"data-height\") || \"0\", 10);\n if (w > 0 && h > 0) {\n this._compositionWidth = w;\n this._compositionHeight = h;\n this._updateScale();\n }\n }\n\n this._setupParentMedia();\n\n if (this.hasAttribute(\"autoplay\")) {\n this.play();\n }\n return;\n }\n } catch {\n /* cross-origin */\n }\n\n if (attempts >= 40) {\n clearInterval(this._probeInterval!);\n this.dispatchEvent(\n new CustomEvent(\"error\", {\n detail: { message: \"Composition timeline not found after 8s\" },\n }),\n );\n }\n }, 200);\n }\n\n /** Inject the HyperFrames runtime into the iframe if not already present. */\n private _injectRuntime() {\n this._runtimeInjected = true;\n try {\n const doc = this.iframe.contentDocument;\n if (!doc) return;\n const script = doc.createElement(\"script\");\n script.src = RUNTIME_CDN_URL;\n script.onload = () => {\n // Runtime loaded — the probe interval will pick up __hf on next tick\n };\n script.onerror = () => {\n // CDN failed — the probe will continue and eventually timeout\n };\n (doc.head || doc.documentElement).appendChild(script);\n } catch {\n /* cross-origin — can't inject */\n }\n }\n\n private _updateScale() {\n const rect = this.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const scale = Math.min(\n rect.width / this._compositionWidth,\n rect.height / this._compositionHeight,\n );\n this.iframe.style.width = `${this._compositionWidth}px`;\n this.iframe.style.height = `${this._compositionHeight}px`;\n this.iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;\n }\n\n private _setupControls() {\n if (this.controlsApi) return;\n const callbacks: ControlsCallbacks = {\n onPlay: () => this.play(),\n onPause: () => this.pause(),\n onSeek: (fraction) => this.seek(fraction * this._duration),\n onSpeedChange: (speed) => {\n this.playbackRate = speed;\n },\n onMuteToggle: () => {\n this.muted = !this.muted;\n },\n onVolumeChange: (volume) => {\n this.volume = volume;\n },\n };\n const presetsAttr = this.getAttribute(\"speed-presets\");\n const speedPresets = presetsAttr\n ? presetsAttr\n .split(\",\")\n .map(Number)\n .filter((n) => !isNaN(n) && n > 0)\n : undefined;\n this.controlsApi = createControls(this.shadow, callbacks, { speedPresets });\n this.controlsApi.updateMuted(this.muted);\n this.controlsApi.updateVolume(this._volume);\n }\n\n private _setupPoster() {\n const url = this.getAttribute(\"poster\");\n if (!url) {\n this.posterEl?.remove();\n this.posterEl = null;\n return;\n }\n if (!this.posterEl) {\n this.posterEl = document.createElement(\"img\");\n this.posterEl.className = \"hfp-poster\";\n this.shadow.appendChild(this.posterEl);\n }\n this.posterEl.src = url;\n }\n\n private _playParentMedia() {\n for (const m of this._parentMedia) {\n if (!m.el.src) continue;\n // Under parent ownership the proxy is the only audible pipeline. If\n // its `play()` rejects (rare — parent also lacks activation in some\n // programmatic embed flows), swallowing silently leaves the viewer\n // staring at motion with no audio and no signal. Surface it as a\n // `playbackerror` event — but only once per parent-ownership session;\n // see `_playbackErrorPosted` for why.\n m.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n\n private _reportPlaybackError(err: unknown) {\n if (this._playbackErrorPosted) return;\n this._playbackErrorPosted = true;\n this.dispatchEvent(\n new CustomEvent(\"playbackerror\", { detail: { source: \"parent-proxy\", error: err } }),\n );\n }\n\n private _pauseParentMedia() {\n for (const m of this._parentMedia) m.el.pause();\n }\n\n /**\n * Drag parent-proxy `currentTime` onto the iframe's timeline. Called on\n * every runtime state message under parent ownership. Threshold is 50 ms\n * — ITU-R BT.1359 puts A/V offset perceptibility at roughly ±45 ms, so\n * anything looser risks audible lip-sync drift on talking-head content\n * (a core use case). The re-seek cost at this tightness is a handful of\n * extra `currentTime` writes per second; the media element's own buffer\n * smooths them out without visible rebuffer on the mirror path.\n */\n private static readonly MIRROR_DRIFT_THRESHOLD_SECONDS = 0.05;\n\n /**\n * How many *consecutive* over-threshold steady-state samples we wait for\n * before issuing a `currentTime` write. A value of 2 means a single\n * spike (one slow bridge tick, one tab-throttled rAF batch, one GC pause)\n * is absorbed without a seek; sustained drift still corrects on the very\n * next tick after the threshold is crossed twice in a row.\n *\n * **Coupling with the timeline-control bridge** — read before changing:\n * worst_case_correction_latency_ms\n * ≈ MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES × bridgeMaxPostIntervalMs\n *\n * `bridgeMaxPostIntervalMs` (currently `80`) lives at\n * `packages/core/src/runtime/state.ts` (field on `RuntimeState`). At\n * today's values, worst-case is `2 × 80 ms = 160 ms` — still well under\n * the human shot-change tolerance for A/V re-sync. If you bump bridge\n * cadence (raising `bridgeMaxPostIntervalMs`) you may need to drop this\n * constant to `1` to keep the product under ~150 ms; if you tighten\n * cadence you can raise this to absorb more jitter without perceptual\n * cost. There is a back-reference in `state.ts` next to\n * `bridgeMaxPostIntervalMs` so a change to either side surfaces the\n * coupling.\n */\n private static readonly MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES = 2;\n\n /**\n * Mirror parent-proxy `currentTime` to the iframe timeline. Defaults to\n * the *coalesced* path: a single over-threshold sample is treated as\n * jitter and merely increments a per-proxy counter; the actual seek only\n * fires once `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES` consecutive\n * samples agree. Pass `{ force: true }` for one-shot alignment moments\n * (audio-ownership promotion, brand-new proxy initialization) where we\n * cannot tolerate even ~80 ms of misaligned audible playback.\n *\n * The counter is also reset on any in-threshold sample and on any\n * out-of-range timeline position, so a proxy that drops back into a\n * scene later starts fresh rather than carrying stale samples from the\n * last time it was active.\n */\n private _mirrorParentMediaTime(timelineSeconds: number, options?: { force?: boolean }) {\n const force = options?.force === true;\n const requiredSamples = HyperframesPlayer.MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES;\n const threshold = HyperframesPlayer.MIRROR_DRIFT_THRESHOLD_SECONDS;\n for (const m of this._parentMedia) {\n const relTime = timelineSeconds - m.start;\n if (relTime < 0 || relTime >= m.duration) {\n m.driftSamples = 0;\n continue;\n }\n if (Math.abs(m.el.currentTime - relTime) > threshold) {\n m.driftSamples += 1;\n if (force || m.driftSamples >= requiredSamples) {\n m.el.currentTime = relTime;\n m.driftSamples = 0;\n }\n } else {\n m.driftSamples = 0;\n }\n }\n }\n\n /**\n * Take ownership of audible playback. Fired in response to the runtime's\n * `media-autoplay-blocked` signal — the iframe has lost the autoplay lottery\n * and will never produce audio without a fresh gesture inside itself.\n *\n * Effects, in order:\n * 1. Ask the runtime to mute its own media output via the bridge. The\n * runtime then keeps advancing timed media for frame-accurate state\n * but produces no sound of its own, freeing us to be the single\n * audible source without racing a volume-reassert loop.\n * 2. Align every parent proxy's currentTime to the iframe's timeline so\n * the cut-over is imperceptible.\n * 3. If the player is currently playing, start the proxies.\n *\n * Idempotent: repeat calls are a no-op.\n */\n private _promoteToParentProxy() {\n if (this._audioOwner === \"parent\") return;\n this._audioOwner = \"parent\";\n // Synchronously mute iframe media to close the race window where a\n // user gesture could let the runtime's el.play() succeed before the\n // async bridge mute lands.\n try {\n const doc = this.iframe.contentDocument;\n if (doc) {\n for (const el of doc.querySelectorAll<HTMLMediaElement>(\"video, audio\")) {\n el.muted = true;\n }\n }\n } catch {\n /* cross-origin */\n }\n // `_sendControl` is async — the iframe won't see the mute for ~one\n // message-loop tick. In that narrow window the runtime's next\n // `syncRuntimeMedia` pass may still try `el.play()` on the iframe\n // copy; we rely on the autoplay gate (which got us here in the first\n // place) to keep rejecting until our mute lands. This is defensible\n // precisely because the scenario that triggered promotion is\n // \"autoplay blocked\" — the iframe can't make noise on its own.\n this._sendControl(\"set-media-output-muted\", { muted: true });\n // One-shot alignment: a brand-new proxy must pick up the iframe's exact\n // timeline position immediately to avoid an audible jump. Bypass the\n // jitter-coalescing gate.\n this._mirrorParentMediaTime(this._currentTime, { force: true });\n if (!this._paused) this._playParentMedia();\n this.dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"parent\", reason: \"autoplay-blocked\" },\n }),\n );\n }\n\n /**\n * Create a parent-frame media element, configure it, and start preloading.\n * Returns the newly-created proxy entry, or `null` if one already exists for\n * this src (dedup) — callers that need to act on the new element should\n * branch on the return value rather than inferring via `_parentMedia.length`.\n */\n private _createParentMedia(\n src: string,\n tag: \"audio\" | \"video\",\n start: number,\n duration: number,\n ): { el: HTMLMediaElement; start: number; duration: number; driftSamples: number } | null {\n // Deduplicate — browsers normalize URLs so we compare on the element after assignment\n if (this._parentMedia.some((m) => m.el.src === src)) return null;\n\n const el = tag === \"video\" ? document.createElement(\"video\") : new Audio();\n el.preload = \"auto\";\n el.src = src;\n el.load();\n el.muted = this.muted;\n el.volume = this._volume;\n if (this.playbackRate !== 1) el.playbackRate = this.playbackRate;\n\n const entry = { el, start, duration, driftSamples: 0 };\n this._parentMedia.push(entry);\n return entry;\n }\n\n /**\n * Set up a single parent-frame audio from an explicit URL (via `audio-src`).\n * Convenience for the common single-narration case — starts preloading\n * immediately without waiting for the iframe to load.\n */\n private _setupParentAudioFromUrl(audioSrc: string) {\n this._createParentMedia(audioSrc, \"audio\", 0, Infinity);\n }\n\n /**\n * Mirror every timed iframe media element (`audio[data-start]`,\n * `video[data-start]`) into a parent-frame proxy. The proxies preload at\n * iframe-ready time so the cut-over to parent ownership — should the\n * runtime's autoplay attempt later reject — is instantaneous.\n *\n * Under runtime ownership (the default) these proxies stay paused and\n * inert; the iframe is the audible source. Ownership flips only in\n * response to a real `media-autoplay-blocked` message from the runtime.\n *\n * Also installs a MutationObserver so that media added to the iframe\n * *after* the initial scan (sub-composition activation is the common\n * case) gets a proxy on the fly. Without this, under parent ownership\n * late-added `<audio data-start>` would be silenced by the runtime\n * (`outputMuted` sticks per-tick) but have no parent-frame counterpart\n * to play — a silent hole in the audio track.\n */\n private _setupParentMedia() {\n try {\n const doc = this.iframe.contentDocument;\n if (!doc) return;\n\n // Find all timed media — matches the runtime's media.ts selector\n const mediaEls = doc.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n for (const iframeEl of mediaEls) this._adoptIframeMedia(iframeEl);\n\n this._observeDynamicMedia(doc);\n } catch {\n // Cross-origin iframe — can't access DOM, fall back to iframe media\n }\n }\n\n /**\n * Create a parent-frame proxy mirroring a single iframe media element.\n * Extracted so both the initial scan and the MutationObserver path use\n * identical URL-resolution and attribute parsing.\n */\n private _adoptIframeMedia(iframeEl: HTMLMediaElement): void {\n // Respect the preloader's demotion: if the iframe element has been set to\n // metadata-only or none, creating a parent proxy with preload=\"auto\" would\n // bypass the lazy preloader and eagerly buffer the clip. Skip it — the\n // MutationObserver in _observeDynamicMedia watches for preload attribute\n // changes and will create the proxy just-in-time when the preloader\n // promotes the clip.\n if (iframeEl.preload === \"metadata\" || iframeEl.preload === \"none\") return;\n\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) return;\n\n // Resolve against the iframe's baseURI. The parent-frame <audio>/<video>\n // we create next lives in the host document, whose base URL differs from\n // the iframe's — without this, a relative src like \"assets/narration.wav\"\n // would resolve against the studio root and 404.\n const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\n\n const start = parseFloat(iframeEl.getAttribute(\"data-start\") || \"0\");\n const duration = parseFloat(iframeEl.getAttribute(\"data-duration\") || \"Infinity\");\n const tag = iframeEl.tagName === \"VIDEO\" ? (\"video\" as const) : (\"audio\" as const);\n\n const created = this._createParentMedia(src, tag, start, duration);\n // Iframe originals stay untouched — the runtime's `syncRuntimeMedia`\n // queries `audio[data-start]` for state and needs them addressable.\n // Their audible output is gated later by `set-media-output-muted` when\n // (and only when) parent ownership is promoted.\n\n // If we're already under parent ownership and the player is playing,\n // the new proxy needs to pick up where the timeline currently is and\n // start producing audio right away — otherwise it sits silent through\n // the next several hundred ms until the next runtime state message.\n if (created && this._audioOwner === \"parent\") {\n // One-shot alignment: a freshly-created proxy must catch up to the\n // current timeline position on the very first sample, so bypass the\n // jitter-coalescing gate.\n this._mirrorParentMediaTime(this._currentTime, { force: true });\n if (!this._paused && created.el.src) {\n created.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n }\n\n /**\n * Watch the iframe document for subtree additions of timed media so\n * sub-composition activation (late-attached `<audio data-start>`) grows\n * the parent-proxy set automatically. Disconnected on iframe reload via\n * `_teardownMediaObserver`.\n */\n private _observeDynamicMedia(doc: Document): void {\n this._teardownMediaObserver();\n if (typeof MutationObserver === \"undefined\" || !doc.body) return;\n const obs = new MutationObserver((mutations) => {\n for (const m of mutations) {\n // Attribute mutations: the preloader promotes a clip by changing its\n // preload attribute from \"metadata\" to \"auto\". When that happens, the\n // early-return guard in _adoptIframeMedia no longer blocks, so we can\n // create the parent proxy just-in-time.\n if (m.type === \"attributes\" && m.attributeName === \"preload\") {\n const target = m.target;\n if (\n target instanceof HTMLMediaElement &&\n target.matches(\"audio[data-start], video[data-start]\") &&\n target.preload === \"auto\"\n ) {\n this._adoptIframeMedia(target);\n }\n continue;\n }\n\n for (const added of m.addedNodes) {\n if (!(added instanceof Element)) continue;\n // Handle both the node itself and any timed media nested inside\n // (sub-compositions typically inject a fragment whose root is a\n // `<div data-composition-id=...>` with `<audio>` children).\n const candidates: HTMLMediaElement[] = [];\n if (added.matches?.(\"audio[data-start], video[data-start]\")) {\n candidates.push(added as HTMLMediaElement);\n }\n const inside = added.querySelectorAll?.<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (inside) for (const el of inside) candidates.push(el);\n for (const el of candidates) this._adoptIframeMedia(el);\n }\n for (const removed of m.removedNodes) {\n if (!(removed instanceof Element)) continue;\n // Symmetric detach: when a sub-composition unmounts, the iframe\n // media it owned is gone but our parent proxies would otherwise\n // linger — accumulating host-document <audio> elements and, under\n // parent ownership, still being played by `_playParentMedia` as\n // orphans. Match by resolved URL (same resolution as adoption).\n const dropped: HTMLMediaElement[] = [];\n if (removed.matches?.(\"audio[data-start], video[data-start]\")) {\n dropped.push(removed as HTMLMediaElement);\n }\n const inside = removed.querySelectorAll?.<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (inside) for (const el of inside) dropped.push(el);\n for (const el of dropped) this._detachIframeMedia(el);\n }\n }\n });\n const observeOpts: MutationObserverInit = {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"preload\"],\n };\n const hosts = doc.querySelectorAll(\"[data-composition-id]\");\n if (hosts.length > 0) {\n for (const host of hosts) {\n obs.observe(host, observeOpts);\n }\n } else {\n obs.observe(doc.body, observeOpts);\n }\n this._mediaObserver = obs;\n }\n\n private _teardownMediaObserver(): void {\n this._mediaObserver?.disconnect();\n this._mediaObserver = undefined;\n }\n\n /**\n * Inverse of `_adoptIframeMedia`: drop the parent proxy mirroring a removed\n * iframe media element. Resolves the src identically so matching is exact,\n * then pauses, clears the src (frees the decoder), and splices it out.\n */\n private _detachIframeMedia(iframeEl: HTMLMediaElement): void {\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) return;\n const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\n const idx = this._parentMedia.findIndex((m) => m.el.src === src);\n if (idx === -1) return;\n const entry = this._parentMedia[idx];\n entry.el.pause();\n entry.el.src = \"\";\n this._parentMedia.splice(idx, 1);\n }\n\n private _hidePoster() {\n this.posterEl?.remove();\n this.posterEl = null;\n }\n}\n\nif (!customElements.get(\"hyperframes-player\")) {\n customElements.define(\"hyperframes-player\", HyperframesPlayer);\n}\n\nexport { HyperframesPlayer };\nexport { formatTime, formatSpeed, SPEED_PRESETS } from \"./controls.js\";\nexport type { ControlsCallbacks, ControlsOptions } from \"./controls.js\";\n","export const PLAYER_STYLES = /* css */ `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n background: #000;\n contain: layout style;\n }\n\n .hfp-container {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n }\n\n\n .hfp-iframe {\n position: absolute;\n top: 50%;\n left: 50%;\n border: none;\n pointer-events: none;\n }\n\n .hfp-poster {\n position: absolute;\n inset: 0;\n object-fit: contain;\n z-index: 1;\n pointer-events: none;\n }\n\n .hfp-shader-loader {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: grid;\n place-items: center;\n visibility: hidden;\n opacity: 0;\n pointer-events: none;\n background: #030504;\n color: #f4f7fb;\n cursor: default;\n user-select: none;\n -webkit-user-select: none;\n transition: opacity 420ms ease-out, visibility 420ms ease-out;\n }\n\n .hfp-shader-loader.hfp-visible,\n .hfp-shader-loader.hfp-hiding {\n visibility: visible;\n }\n\n .hfp-shader-loader.hfp-visible {\n opacity: 1;\n pointer-events: auto;\n }\n\n .hfp-shader-loader.hfp-hiding {\n opacity: 0;\n pointer-events: none;\n }\n\n .hfp-shader-loader-panel {\n display: grid;\n grid-template-rows: 86px 40px 26px 12px 44px;\n justify-items: center;\n align-items: center;\n gap: 8px;\n width: min(620px, 82%);\n text-align: center;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n\n .hfp-shader-loader-mark {\n width: 86px;\n height: 86px;\n display: grid;\n place-items: center;\n overflow: visible;\n }\n\n .hfp-shader-loader-mark svg {\n display: block;\n overflow: visible;\n filter: drop-shadow(0 0 5px rgba(79, 219, 94, 0.16));\n pointer-events: none;\n }\n\n .hfp-shader-loader-title {\n width: 100%;\n height: 40px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 26px;\n line-height: 40px;\n font-weight: 700;\n letter-spacing: 0;\n }\n\n .hfp-shader-loader-title-text {\n color: transparent;\n background: linear-gradient(\n 90deg,\n rgba(244, 247, 251, 0.84) 0%,\n #ffffff 42%,\n #80efe4 52%,\n #ffffff 62%,\n rgba(244, 247, 251, 0.84) 100%\n );\n background-size: 220% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n animation: hfp-shader-loader-sheen 1.9s linear infinite;\n }\n\n .hfp-shader-loader-detail {\n width: 100%;\n height: 26px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: rgba(244, 247, 251, 0.62);\n font-size: 15px;\n line-height: 26px;\n font-weight: 500;\n }\n\n .hfp-shader-loader-track {\n width: min(360px, 100%);\n height: 8px;\n overflow: hidden;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.1);\n }\n\n .hfp-shader-loader-fill {\n width: 100%;\n height: 100%;\n border-radius: inherit;\n background: linear-gradient(90deg, #06e3fa, #4fdb5e);\n transform: scaleX(0);\n transform-origin: left center;\n transition: transform 160ms ease;\n }\n\n .hfp-shader-loader-progress {\n width: min(420px, 100%);\n height: 44px;\n display: grid;\n grid-template-rows: repeat(2, 22px);\n color: rgba(244, 247, 251, 0.48);\n font: 600 13px/22px \"IBM Plex Mono\", \"SF Mono\", \"Fira Code\", \"Courier New\", monospace;\n font-variant-numeric: tabular-nums;\n }\n\n .hfp-shader-loader-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 74px;\n align-items: center;\n column-gap: 20px;\n width: 100%;\n white-space: nowrap;\n }\n\n .hfp-shader-loader-label {\n min-width: 0;\n overflow: hidden;\n text-align: left;\n text-overflow: ellipsis;\n }\n\n .hfp-shader-loader-value {\n text-align: right;\n }\n\n @keyframes hfp-shader-loader-sheen {\n from {\n background-position: 140% 0;\n }\n to {\n background-position: -140% 0;\n }\n }\n\n /* ── Theming via CSS custom properties ──\n *\n * Override from outside the shadow DOM:\n * hyperframes-player {\n * --hfp-controls-bg: linear-gradient(transparent, rgba(0,0,0,0.9));\n * --hfp-accent: #ff6b6b;\n * --hfp-font: \"Inter\", sans-serif;\n * }\n */\n\n .hfp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n gap: var(--hfp-controls-gap, 12px);\n padding: var(--hfp-controls-padding, 8px 16px);\n background: var(--hfp-controls-bg, linear-gradient(transparent, rgba(0, 0, 0, 0.7)));\n color: var(--hfp-color, #fff);\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: var(--hfp-font-size, 13px);\n z-index: 10;\n pointer-events: auto;\n opacity: 1;\n transition: opacity 0.3s ease;\n user-select: none;\n }\n\n .hfp-controls.hfp-hidden {\n opacity: 0;\n pointer-events: none;\n }\n\n .hfp-play-btn {\n background: none;\n border: none;\n color: var(--hfp-color, #fff);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n z-index: 10;\n }\n\n .hfp-play-btn:hover {\n opacity: 0.8;\n }\n\n .hfp-play-btn svg,\n .hfp-play-btn svg * {\n pointer-events: none;\n }\n\n .hfp-scrubber {\n flex: 1;\n min-width: 0;\n height: var(--hfp-scrubber-height, 4px);\n background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));\n border-radius: var(--hfp-scrubber-radius, 2px);\n cursor: pointer;\n position: relative;\n overflow: hidden;\n }\n\n .hfp-scrubber:hover {\n height: var(--hfp-scrubber-height-hover, 6px);\n }\n\n .hfp-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--hfp-accent, #fff);\n pointer-events: none;\n }\n\n .hfp-time {\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n opacity: 0.9;\n }\n\n .hfp-speed-wrap {\n position: relative;\n flex-shrink: 0;\n }\n\n .hfp-speed-btn {\n background: var(--hfp-speed-btn-bg, rgba(255, 255, 255, 0.15));\n border: none;\n border-radius: var(--hfp-speed-btn-radius, 4px);\n color: var(--hfp-color, #fff);\n cursor: pointer;\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n padding: 4px 8px;\n min-width: 40px;\n text-align: center;\n transition: background 0.15s ease;\n }\n\n .hfp-speed-btn:hover {\n background: var(--hfp-speed-btn-bg-hover, rgba(255, 255, 255, 0.3));\n }\n\n .hfp-speed-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n background: var(--hfp-menu-bg, rgba(20, 20, 20, 0.95));\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border: 1px solid var(--hfp-menu-border, rgba(255, 255, 255, 0.1));\n border-radius: var(--hfp-menu-radius, 8px);\n padding: 4px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 80px;\n opacity: 0;\n visibility: hidden;\n transform: translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;\n box-shadow: var(--hfp-menu-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));\n }\n\n .hfp-speed-menu.hfp-open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n }\n\n .hfp-speed-option {\n background: none;\n border: none;\n border-radius: 4px;\n color: var(--hfp-menu-color, rgba(255, 255, 255, 0.7));\n cursor: pointer;\n font-family: var(--hfp-font, system-ui, -apple-system, sans-serif);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n padding: 6px 12px;\n text-align: left;\n transition: background 0.1s ease, color 0.1s ease;\n white-space: nowrap;\n }\n\n .hfp-speed-option:hover {\n background: var(--hfp-menu-hover-bg, rgba(255, 255, 255, 0.1));\n color: var(--hfp-color, #fff);\n }\n\n .hfp-speed-option.hfp-active {\n color: var(--hfp-accent, #fff);\n font-weight: 600;\n }\n\n .hfp-volume-wrap {\n position: relative;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 0;\n }\n\n .hfp-mute-btn {\n background: none;\n border: none;\n color: var(--hfp-color, #fff);\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n flex-shrink: 0;\n }\n\n .hfp-mute-btn:hover {\n opacity: 0.8;\n }\n\n .hfp-mute-btn svg,\n .hfp-mute-btn svg * {\n pointer-events: none;\n }\n\n .hfp-volume-slider-wrap {\n width: 0;\n overflow: hidden;\n transition: width 0.2s ease;\n display: flex;\n align-items: center;\n }\n\n .hfp-volume-wrap:hover .hfp-volume-slider-wrap {\n width: 64px;\n }\n\n .hfp-volume-slider {\n width: 56px;\n height: var(--hfp-scrubber-height, 4px);\n background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));\n border-radius: var(--hfp-scrubber-radius, 2px);\n cursor: pointer;\n position: relative;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n }\n\n .hfp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--hfp-accent, #fff);\n pointer-events: none;\n }\n`;\n\nexport const PLAY_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 18 18\" fill=\"currentColor\"><polygon points=\"4,2 16,9 4,16\"/></svg>`;\nexport const PAUSE_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 18 18\" fill=\"currentColor\"><rect x=\"3\" y=\"2\" width=\"4\" height=\"14\"/><rect x=\"11\" y=\"2\" width=\"4\" height=\"14\"/></svg>`;\nexport const VOLUME_HIGH_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/><path d=\"M14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>`;\nexport const VOLUME_LOW_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/></svg>`;\nexport const VOLUME_MUTED_ICON = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 9v6h4l5 5V4L7 9H3z\"/><path d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\" opacity=\"0.3\"/><line x1=\"18\" y1=\"7\" x2=\"14\" y2=\"17\" stroke=\"currentColor\" stroke-width=\"2\"/></svg>`;\n\n/**\n * Process-wide cache for the constructed PLAYER_STYLES sheet. Lazy so the\n * module stays SSR-safe (CSSStyleSheet is window-scoped) and so a single\n * sheet can be shared across every shadow root via `adoptedStyleSheets` —\n * the studio thumbnail grid renders dozens of players, and avoiding N\n * duplicate `<style>` parses + style-recalc invalidations is the win here.\n *\n * `null` after a failed construction attempt = \"fall back forever in this\n * process\" (the usual cause is a missing constructor in older runtimes;\n * retrying every call would just throw the same way).\n */\nlet sharedSheet: CSSStyleSheet | null | undefined;\n\n/**\n * Returns the shared player stylesheet, or `null` if constructable\n * stylesheets aren't available in this environment.\n *\n * The result is memoized for the life of the module — every shadow root\n * adopts the same `CSSStyleSheet` instance.\n */\nexport function getSharedPlayerStyleSheet(): CSSStyleSheet | null {\n if (sharedSheet !== undefined) return sharedSheet;\n\n if (typeof CSSStyleSheet === \"undefined\") {\n sharedSheet = null;\n return null;\n }\n\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(PLAYER_STYLES);\n sharedSheet = sheet;\n return sheet;\n } catch {\n sharedSheet = null;\n return null;\n }\n}\n\n/**\n * Internal hook for tests to clear the memoized sheet. Not part of the\n * public API.\n */\nexport function _resetSharedPlayerStyleSheet(): void {\n sharedSheet = undefined;\n}\n\n/**\n * Install PLAYER_STYLES into a player shadow root. Prefers the shared\n * constructable stylesheet (one parse, one rule tree, N adopters) and\n * falls back to a per-instance `<style>` element when the host runtime\n * lacks `adoptedStyleSheets` support.\n *\n * Idempotent: re-applying to a root that already adopts the shared sheet\n * is a no-op. Pre-existing adopted sheets are preserved (we append, never\n * replace), so callers further up the chain can keep their styles.\n */\nexport function applyPlayerStyles(shadow: ShadowRoot): void {\n const sheet = getSharedPlayerStyleSheet();\n const adopted = (shadow as ShadowRoot & { adoptedStyleSheets?: CSSStyleSheet[] })\n .adoptedStyleSheets;\n\n if (sheet && Array.isArray(adopted)) {\n if (!adopted.includes(sheet)) {\n (shadow as ShadowRoot & { adoptedStyleSheets: CSSStyleSheet[] }).adoptedStyleSheets = [\n ...adopted,\n sheet,\n ];\n }\n return;\n }\n\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n shadow.appendChild(style);\n}\n","import {\n PLAY_ICON,\n PAUSE_ICON,\n VOLUME_HIGH_ICON,\n VOLUME_LOW_ICON,\n VOLUME_MUTED_ICON,\n} from \"./styles.js\";\n\nexport interface ControlsCallbacks {\n onPlay: () => void;\n onPause: () => void;\n onSeek: (fraction: number) => void;\n onSpeedChange: (speed: number) => void;\n onMuteToggle: () => void;\n onVolumeChange: (volume: number) => void;\n}\n\n/** Default logarithmic speed presets — each step roughly doubles/halves. */\nexport const SPEED_PRESETS = [0.25, 0.5, 1, 1.5, 2, 4] as const;\n\nexport interface ControlsOptions {\n /** Speed presets shown in the menu. Defaults to SPEED_PRESETS. */\n speedPresets?: readonly number[];\n}\n\nexport function formatSpeed(speed: number): string {\n return Number.isInteger(speed) ? `${speed}x` : `${speed}x`;\n}\n\nexport function formatTime(seconds: number): string {\n // Handle non-finite values gracefully\n if (!Number.isFinite(seconds) || seconds < 0) {\n return \"0:00\";\n }\n const s = Math.floor(seconds);\n const m = Math.floor(s / 60);\n const sec = s % 60;\n return `${m}:${sec.toString().padStart(2, \"0\")}`;\n}\n\nexport function createControls(\n parent: ShadowRoot | HTMLElement,\n callbacks: ControlsCallbacks,\n options: ControlsOptions = {},\n): {\n updateTime: (current: number, duration: number) => void;\n updatePlaying: (playing: boolean) => void;\n updateSpeed: (speed: number) => void;\n updateMuted: (muted: boolean) => void;\n updateVolume: (volume: number) => void;\n show: () => void;\n hide: () => void;\n destroy: () => void;\n} {\n const presets = options.speedPresets ?? SPEED_PRESETS;\n\n const controls = document.createElement(\"div\");\n controls.className = \"hfp-controls\";\n // Keep overlay interactions from falling through to the host-level click toggle.\n controls.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n });\n\n const playBtn = document.createElement(\"button\");\n playBtn.className = \"hfp-play-btn\";\n playBtn.type = \"button\";\n playBtn.innerHTML = PLAY_ICON;\n playBtn.setAttribute(\"aria-label\", \"Play\");\n\n const scrubber = document.createElement(\"div\");\n scrubber.className = \"hfp-scrubber\";\n const progress = document.createElement(\"div\");\n progress.className = \"hfp-progress\";\n progress.style.width = \"0%\";\n scrubber.appendChild(progress);\n\n const time = document.createElement(\"span\");\n time.className = \"hfp-time\";\n time.textContent = \"0:00 / 0:00\";\n\n const speedWrap = document.createElement(\"div\");\n speedWrap.className = \"hfp-speed-wrap\";\n\n const speedBtn = document.createElement(\"button\");\n speedBtn.className = \"hfp-speed-btn\";\n speedBtn.type = \"button\";\n speedBtn.textContent = \"1x\";\n speedBtn.setAttribute(\"aria-label\", \"Playback speed\");\n\n const speedMenu = document.createElement(\"div\");\n speedMenu.className = \"hfp-speed-menu\";\n speedMenu.setAttribute(\"role\", \"menu\");\n for (const preset of presets) {\n const item = document.createElement(\"button\");\n item.className = \"hfp-speed-option\";\n item.type = \"button\";\n item.setAttribute(\"role\", \"menuitem\");\n item.dataset.speed = String(preset);\n item.textContent = formatSpeed(preset);\n if (preset === 1) item.classList.add(\"hfp-active\");\n speedMenu.appendChild(item);\n }\n\n speedWrap.appendChild(speedMenu);\n speedWrap.appendChild(speedBtn);\n\n const volumeWrap = document.createElement(\"div\");\n volumeWrap.className = \"hfp-volume-wrap\";\n\n const muteBtn = document.createElement(\"button\");\n muteBtn.className = \"hfp-mute-btn\";\n muteBtn.type = \"button\";\n muteBtn.innerHTML = VOLUME_HIGH_ICON;\n muteBtn.setAttribute(\"aria-label\", \"Mute\");\n\n const volumeSliderWrap = document.createElement(\"div\");\n volumeSliderWrap.className = \"hfp-volume-slider-wrap\";\n\n const volumeSlider = document.createElement(\"div\");\n volumeSlider.className = \"hfp-volume-slider\";\n volumeSlider.setAttribute(\"role\", \"slider\");\n volumeSlider.setAttribute(\"aria-label\", \"Volume\");\n volumeSlider.setAttribute(\"aria-valuemin\", \"0\");\n volumeSlider.setAttribute(\"aria-valuemax\", \"100\");\n volumeSlider.setAttribute(\"aria-valuenow\", \"100\");\n volumeSlider.tabIndex = 0;\n const volumeFill = document.createElement(\"div\");\n volumeFill.className = \"hfp-volume-fill\";\n volumeFill.style.width = \"100%\";\n volumeSlider.appendChild(volumeFill);\n volumeSliderWrap.appendChild(volumeSlider);\n\n volumeWrap.appendChild(volumeSliderWrap);\n volumeWrap.appendChild(muteBtn);\n\n controls.appendChild(playBtn);\n controls.appendChild(scrubber);\n controls.appendChild(time);\n controls.appendChild(volumeWrap);\n controls.appendChild(speedWrap);\n parent.appendChild(controls);\n\n let isPlaying = false;\n let isMuted = false;\n let currentVolume = 1;\n let hideTimeout: ReturnType<typeof setTimeout> | null = null;\n let speedIndex = presets.indexOf(1); // start at 1x\n if (speedIndex === -1) speedIndex = 0;\n\n const getVolumeIcon = (muted: boolean, volume: number): string => {\n if (muted) return VOLUME_MUTED_ICON;\n if (volume === 0) return VOLUME_LOW_ICON;\n if (volume < 0.5) return VOLUME_LOW_ICON;\n return VOLUME_HIGH_ICON;\n };\n\n playBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (isPlaying) callbacks.onPause();\n else callbacks.onPlay();\n });\n\n muteBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n callbacks.onMuteToggle();\n });\n\n let volumeScrubbing = false;\n\n const handleVolumeAt = (clientX: number) => {\n const rect = volumeSlider.getBoundingClientRect();\n const fraction = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n currentVolume = fraction;\n volumeFill.style.width = `${fraction * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(fraction * 100)));\n if (isMuted && fraction > 0) callbacks.onMuteToggle();\n muteBtn.innerHTML = getVolumeIcon(isMuted, fraction);\n callbacks.onVolumeChange(fraction);\n };\n\n volumeSlider.addEventListener(\"mousedown\", (e) => {\n e.stopPropagation();\n volumeScrubbing = true;\n handleVolumeAt(e.clientX);\n });\n const onVolumeMouseMove = (e: MouseEvent) => {\n if (volumeScrubbing) handleVolumeAt(e.clientX);\n };\n const onVolumeMouseUp = () => {\n volumeScrubbing = false;\n };\n document.addEventListener(\"mousemove\", onVolumeMouseMove);\n document.addEventListener(\"mouseup\", onVolumeMouseUp);\n\n volumeSlider.addEventListener(\n \"touchstart\",\n (e) => {\n volumeScrubbing = true;\n const touch = e.touches[0];\n if (touch) handleVolumeAt(touch.clientX);\n },\n { passive: true },\n );\n const onVolumeTouchMove = (e: TouchEvent) => {\n if (volumeScrubbing) {\n const touch = e.touches[0];\n if (touch) handleVolumeAt(touch.clientX);\n }\n };\n const onVolumeTouchEnd = () => {\n volumeScrubbing = false;\n };\n document.addEventListener(\"touchmove\", onVolumeTouchMove, { passive: true });\n document.addEventListener(\"touchend\", onVolumeTouchEnd);\n\n const VOLUME_STEP = 0.05;\n volumeSlider.addEventListener(\"keydown\", (e) => {\n let newVol = currentVolume;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") {\n newVol = Math.min(1, currentVolume + VOLUME_STEP);\n } else if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") {\n newVol = Math.max(0, currentVolume - VOLUME_STEP);\n } else {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n currentVolume = newVol;\n volumeFill.style.width = `${newVol * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(newVol * 100)));\n if (isMuted && newVol > 0) callbacks.onMuteToggle();\n muteBtn.innerHTML = getVolumeIcon(isMuted, newVol);\n callbacks.onVolumeChange(newVol);\n });\n\n const setActiveOption = (speed: number) => {\n for (const opt of speedMenu.querySelectorAll(\".hfp-speed-option\")) {\n opt.classList.toggle(\"hfp-active\", (opt as HTMLElement).dataset.speed === String(speed));\n }\n };\n\n speedBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const isOpen = speedMenu.classList.toggle(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", String(isOpen));\n });\n\n speedMenu.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const target = (e.target as HTMLElement).closest(\".hfp-speed-option\") as HTMLElement | null;\n if (!target) return;\n const newSpeed = parseFloat(target.dataset.speed!);\n speedIndex = presets.indexOf(newSpeed);\n speedBtn.textContent = formatSpeed(newSpeed);\n setActiveOption(newSpeed);\n speedMenu.classList.remove(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", \"false\");\n callbacks.onSpeedChange(newSpeed);\n });\n\n // Close menu when clicking outside\n const onDocClick = () => {\n speedMenu.classList.remove(\"hfp-open\");\n speedBtn.setAttribute(\"aria-expanded\", \"false\");\n };\n document.addEventListener(\"click\", onDocClick);\n\n const handleScrubAt = (clientX: number) => {\n const rect = scrubber.getBoundingClientRect();\n const fraction = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n callbacks.onSeek(fraction);\n };\n\n let scrubbing = false;\n\n scrubber.addEventListener(\"mousedown\", (e) => {\n e.stopPropagation();\n scrubbing = true;\n handleScrubAt(e.clientX);\n });\n const onMouseMove = (e: MouseEvent) => {\n if (scrubbing) handleScrubAt(e.clientX);\n };\n const onMouseUp = () => {\n scrubbing = false;\n };\n document.addEventListener(\"mousemove\", onMouseMove);\n document.addEventListener(\"mouseup\", onMouseUp);\n\n scrubber.addEventListener(\n \"touchstart\",\n (e) => {\n scrubbing = true;\n const touch = e.touches[0];\n if (touch) handleScrubAt(touch.clientX);\n },\n { passive: true },\n );\n const onTouchMove = (e: TouchEvent) => {\n if (scrubbing) {\n const touch = e.touches[0];\n if (touch) handleScrubAt(touch.clientX);\n }\n };\n const onTouchEnd = () => {\n scrubbing = false;\n };\n document.addEventListener(\"touchmove\", onTouchMove, { passive: true });\n document.addEventListener(\"touchend\", onTouchEnd);\n\n const startHideTimer = () => {\n if (hideTimeout) clearTimeout(hideTimeout);\n hideTimeout = setTimeout(() => {\n if (isPlaying) controls.classList.add(\"hfp-hidden\");\n }, 3000);\n };\n\n const host = parent instanceof ShadowRoot ? (parent.host as HTMLElement) : parent;\n host.addEventListener(\"mousemove\", () => {\n controls.classList.remove(\"hfp-hidden\");\n startHideTimer();\n });\n host.addEventListener(\"mouseleave\", () => {\n if (isPlaying) controls.classList.add(\"hfp-hidden\");\n });\n\n return {\n updateTime(current: number, duration: number) {\n // Defensive: source should already clamp, but guard here so the UI never overflows.\n const clampedCurrent = duration > 0 ? Math.min(current, duration) : current;\n const pct = duration > 0 ? (clampedCurrent / duration) * 100 : 0;\n progress.style.width = `${pct}%`;\n time.textContent = `${formatTime(clampedCurrent)} / ${formatTime(duration)}`;\n },\n updatePlaying(playing: boolean) {\n isPlaying = playing;\n playBtn.innerHTML = playing ? PAUSE_ICON : PLAY_ICON;\n playBtn.setAttribute(\"aria-label\", playing ? \"Pause\" : \"Play\");\n if (playing) startHideTimer();\n else controls.classList.remove(\"hfp-hidden\");\n },\n updateSpeed(speed: number) {\n const idx = presets.indexOf(speed);\n if (idx !== -1) speedIndex = idx;\n speedBtn.textContent = formatSpeed(speed);\n setActiveOption(speed);\n },\n updateMuted(muted: boolean) {\n isMuted = muted;\n muteBtn.innerHTML = getVolumeIcon(muted, currentVolume);\n muteBtn.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n },\n updateVolume(volume: number) {\n currentVolume = volume;\n volumeFill.style.width = `${volume * 100}%`;\n volumeSlider.setAttribute(\"aria-valuenow\", String(Math.round(volume * 100)));\n muteBtn.innerHTML = getVolumeIcon(isMuted, volume);\n },\n show() {\n controls.style.display = \"\";\n },\n hide() {\n controls.style.display = \"none\";\n },\n destroy() {\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"mouseup\", onMouseUp);\n document.removeEventListener(\"touchmove\", onTouchMove);\n document.removeEventListener(\"touchend\", onTouchEnd);\n document.removeEventListener(\"mousemove\", onVolumeMouseMove);\n document.removeEventListener(\"mouseup\", onVolumeMouseUp);\n document.removeEventListener(\"touchmove\", onVolumeTouchMove);\n document.removeEventListener(\"touchend\", onVolumeTouchEnd);\n document.removeEventListener(\"click\", onDocClick);\n if (hideTimeout) clearTimeout(hideTimeout);\n },\n };\n}\n","/**\n * Decide whether the player should inject the HyperFrames runtime on the\n * current probe tick.\n *\n * The player polls the loaded iframe every 200ms to discover either:\n * - a runtime bridge already installed (`window.__hf` / `window.__player`), or\n * - GSAP timelines registered at `window.__timelines`.\n *\n * Two classes of composition require different injection timing:\n *\n * Nested — the composition uses `data-composition-src` on child elements to\n * lazy-load sub-scenes. The runtime is what loads those children, so the\n * composition cannot possibly render on its own. We inject immediately; if\n * we waited, an inline pre-runtime `gsap.timeline` (common for authoring a\n * preview before the runtime rebuilds the master timeline) would register\n * at `__timelines[\"main\"]` with a partial duration, and the adapter path\n * would then lock the player into `ready` against that incomplete timeline.\n *\n * Self-contained — the composition has no nested scenes and ships all of\n * its animation inline (timelines registered under `__timelines`). These\n * don't strictly need the runtime; the adapter can drive them directly.\n * We give the adapter path first shot (a 5-tick grace period) and only\n * inject the runtime as a fallback if no adapter emerges.\n */\nexport interface ProbeState {\n hasRuntime: boolean;\n hasTimelines: boolean;\n hasNestedCompositions: boolean;\n runtimeInjected: boolean;\n attempts: number;\n}\n\nexport function shouldInjectRuntime(state: ProbeState): boolean {\n if (state.hasRuntime || state.runtimeInjected) return false;\n if (state.hasNestedCompositions) return true;\n if (state.hasTimelines && state.attempts >= 5) return true;\n return false;\n}\n"],"mappings":"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","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","__toCommonJS","PLAYER_STYLES","PLAY_ICON","PAUSE_ICON","VOLUME_HIGH_ICON","VOLUME_LOW_ICON","VOLUME_MUTED_ICON","SPEED_PRESETS","formatSpeed","speed","formatTime","seconds","s","m","sec","createControls","parent","callbacks","options","presets","controls","e","playBtn","PLAY_ICON","scrubber","progress","time","speedWrap","speedBtn","speedMenu","preset","item","volumeWrap","muteBtn","VOLUME_HIGH_ICON","volumeSliderWrap","volumeSlider","volumeFill","isPlaying","isMuted","currentVolume","hideTimeout","speedIndex","getVolumeIcon","muted","volume","VOLUME_MUTED_ICON","VOLUME_LOW_ICON","volumeScrubbing","handleVolumeAt","clientX","rect","fraction","onVolumeMouseMove","onVolumeMouseUp","touch","onVolumeTouchMove","onVolumeTouchEnd","VOLUME_STEP","newVol","setActiveOption","opt","isOpen","target","newSpeed","onDocClick","handleScrubAt","scrubbing","onMouseMove","onMouseUp","onTouchMove","onTouchEnd","startHideTimer","host","current","duration","clampedCurrent","pct","playing","PAUSE_ICON","idx","shouldInjectRuntime","state","sharedSheet","getSharedSheet","sheet","PLAYER_STYLES","DEFAULT_FPS","RUNTIME_CDN_URL","SHADER_CAPTURE_SCALE_ATTR","SHADER_LOADING_ATTR","SHADER_CAPTURE_SCALE_PARAM","SHADER_LOADING_PARAM","SHADER_LOADING_PHRASES","normalizeShaderCaptureScale","value","parsed","normalizeShaderLoadingMode","normalized","setQueryParam","params","key","withShaderQueryParams","src","scale","loadingMode","hashIndex","beforeHash","hash","queryIndex","path","query","nextQuery","isObjectRecord","isRuntimeDurationAdapter","isDirectTimelineAdapter","injectShaderOptionsIntoSrcdoc","html","lines","script","match","HyperframesPlayer","_HyperframesPlayer","style","shaderLoader","event","m","name","_old","val","rate","v","directTimelineStarted","timeInSeconds","frame","relTime","t","r","mode","l","action","extra","srcdoc","root","blockOverlayInteraction","eventName","panel","markFrame","title","titleText","detail","track","fill","progress","createProgressRow","labelText","row","label","transitionStatus","frameStatus","status","total","ratio","phraseIndex","frameValue","player","seek","timeline","tick","currentTime","completedPlayback","now","win","timelines","keys","rootId","runtimePlayer","target","data","state","rawTime","wasPlaying","nextPaused","attempts","wasPromoted","hasRuntime","hasTimelines","hasNestedCompositions","shouldInjectRuntime","adapter","w","h","doc","rect","callbacks","fraction","speed","volume","presetsAttr","speedPresets","n","createControls","url","err","timelineSeconds","options","force","requiredSamples","threshold","el","tag","start","duration","entry","audioSrc","mediaEls","iframeEl","rawSrc","created","obs","mutations","added","candidates","inside","removed","dropped","observeOpts","hosts","host","idx"]}
|