@hyperframes/player 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hyperframes-player.ts","../src/shouldInjectRuntime.ts","../src/timeline-adapters.ts","../src/composition-probe.ts","../src/styles.ts","../src/controls.ts","../src/controls-setup.ts","../src/iframe-dom.ts","../src/direct-timeline-clock.ts","../src/mediaObserverScope.ts","../src/parent-media.ts","../src/playback-state.ts","../src/runtime-message-handler.ts","../src/shader-options.ts","../src/shader-loader-element.ts","../src/shader-loader-state.ts"],"sourcesContent":["import { CompositionProbe, type ProbeResult } from \"./composition-probe.js\";\nimport { isControlsClick, setupControls, setupPoster } from \"./controls-setup.js\";\nimport { adoptShadowStyles, createCompositionIframe, scaleIframeToFit } from \"./iframe-dom.js\";\nimport { DirectTimelineClock } from \"./direct-timeline-clock.js\";\nimport { ParentMediaManager } from \"./parent-media.js\";\nimport { handleRuntimeMessage } from \"./runtime-message-handler.js\";\nimport {\n SHADER_CAPTURE_SCALE_ATTR,\n SHADER_LOADING_ATTR,\n type ShaderLoadingMode,\n getShaderCaptureScaleFromElement,\n getShaderModeFromElement,\n prepareSrcForElement,\n prepareSrcdocForElement,\n} from \"./shader-options.js\";\nimport { createShaderLoader } from \"./shader-loader-element.js\";\nimport { ShaderLoaderState } from \"./shader-loader-state.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\nimport { type DirectTimelineAdapter } from \"./timeline-adapters.js\";\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 setupControls> | null = null;\n private resizeObserver: ResizeObserver;\n private shaderLoader: ShaderLoaderState;\n private probe: CompositionProbe;\n\n private _ready = false;\n private _currentTime = 0;\n private _duration = 0;\n private _paused = true;\n private _lastUpdateMs = 0;\n private _volume = 1;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _directTimelineAdapter: DirectTimelineAdapter | null = null;\n private _directTimelineClock: DirectTimelineClock;\n private _media: ParentMediaManager;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n adoptShadowStyles(this.shadow, PLAYER_STYLES);\n ({ container: this.container, iframe: this.iframe } = createCompositionIframe());\n this.shadow.appendChild(this.container);\n\n const loaderElements = createShaderLoader();\n this.shadow.appendChild(loaderElements.root);\n this.shaderLoader = new ShaderLoaderState(loaderElements);\n\n this._media = new ParentMediaManager({\n dispatchEvent: (e) => this.dispatchEvent(e),\n getMuted: () => this.muted,\n getVolume: () => this._volume,\n getPlaybackRate: () => this.playbackRate,\n getCurrentTime: () => this._currentTime,\n isPaused: () => this._paused,\n });\n\n this._directTimelineClock = new DirectTimelineClock({\n onTimeUpdate: (currentTime, duration) => {\n this._currentTime = currentTime;\n this.controlsApi?.updateTime(currentTime, duration);\n this.dispatchEvent(new CustomEvent(\"timeupdate\", { detail: { currentTime } }));\n },\n getLoop: () => this.loop,\n restart: () => {\n this.seek(0);\n this.play();\n },\n onPaused: () => {\n if (this._media.audioOwner === \"parent\") this._media.pauseAll();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n },\n onEnded: () => this.loop,\n });\n\n this.probe = new CompositionProbe(this.iframe, {\n onReady: (result) => this._onProbeReady(result),\n onError: (message) => this.dispatchEvent(new CustomEvent(\"error\", { detail: { message } })),\n });\n\n this.addEventListener(\"click\", (event) => {\n if (isControlsClick(event)) return;\n if (this._paused) this.play();\n else this.pause();\n });\n\n this.resizeObserver = new ResizeObserver(() => this._rescale());\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 if (this.hasAttribute(\"controls\")) this._setupControls();\n if (this.hasAttribute(\"poster\"))\n this.posterEl = setupPoster(this.shadow, this.getAttribute(\"poster\"), this.posterEl);\n if (this.hasAttribute(\"audio-src\")) this._media.setupFromUrl(this.getAttribute(\"audio-src\")!);\n if (this.hasAttribute(\"srcdoc\"))\n this.iframe.srcdoc = prepareSrcdocForElement(this, this.getAttribute(\"srcdoc\")!);\n if (this.hasAttribute(\"src\"))\n this.iframe.src = prepareSrcForElement(this, 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 this.probe.stop();\n this._directTimelineClock.stop();\n this._directTimelineAdapter = null;\n this.shaderLoader.destroy();\n this._media.destroy();\n this.controlsApi?.destroy();\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 = prepareSrcForElement(this, val);\n }\n break;\n case \"srcdoc\":\n this._ready = false;\n if (val !== null) this.iframe.srcdoc = prepareSrcdocForElement(this, val);\n else this.iframe.removeAttribute(\"srcdoc\");\n break;\n case \"width\":\n this._compositionWidth = parseInt(val || \"1920\", 10);\n this._rescale();\n break;\n case \"height\":\n this._compositionHeight = parseInt(val || \"1080\", 10);\n this._rescale();\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.posterEl = setupPoster(this.shadow, val, this.posterEl);\n break;\n case \"playback-rate\": {\n const rate = parseFloat(val || \"1\");\n this._media.updatePlaybackRate(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 this._media.updateMuted(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 this._media.updateVolume(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._media.setupFromUrl(val);\n break;\n case SHADER_CAPTURE_SCALE_ATTR:\n case SHADER_LOADING_ATTR:\n this._reloadShaderOptions();\n break;\n }\n }\n\n /**\n * The inner `<iframe>` rendering the composition. Use this when integrating\n * with tools that need `contentWindow` — `.contentWindow` on the\n * `<hyperframes-player>` element itself returns `null` (Shadow DOM).\n */\n get iframeElement(): HTMLIFrameElement {\n return this.iframe;\n }\n\n play() {\n this.posterEl?.remove();\n this.posterEl = null;\n if (this._duration > 0 && this._currentTime >= this._duration) this.seek(0);\n const directTimelineStarted = this._tryDirectTimelinePlay();\n if (!directTimelineStarted) this._sendControl(\"play\");\n if (this._media.audioOwner === \"parent\") this._media.playAll();\n this._paused = false;\n this.controlsApi?.updatePlaying(true);\n this.dispatchEvent(new Event(\"play\"));\n if (directTimelineStarted && this._directTimelineAdapter) {\n this._directTimelineClock.start(\n this._directTimelineAdapter,\n () => this._currentTime,\n () => this._duration,\n () => this._paused,\n );\n }\n }\n\n pause() {\n if (!this._tryDirectTimelinePause()) this._sendControl(\"pause\");\n this._directTimelineClock.stop();\n if (this._media.audioOwner === \"parent\") this._media.pauseAll();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"pause\"));\n }\n\n seek(timeInSeconds: number) {\n if (!this._trySyncSeek(timeInSeconds) && !this._tryDirectTimelineSeek(timeInSeconds)) {\n this._sendControl(\"seek\", { frame: Math.round(timeInSeconds * 30) });\n }\n this._directTimelineClock.stop();\n this._currentTime = timeInSeconds;\n if (this._media.audioOwner === \"parent\") this._media.seekAll(timeInSeconds);\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 getShaderCaptureScaleFromElement(this);\n }\n set shaderCaptureScale(scale: number) {\n this.setAttribute(SHADER_CAPTURE_SCALE_ATTR, String(scale));\n }\n\n get shaderLoading() {\n return getShaderModeFromElement(this);\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 _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 _reloadShaderOptions(): void {\n if (getShaderModeFromElement(this) !== \"player\") this.shaderLoader.reset();\n if (this.hasAttribute(\"srcdoc\")) {\n this.iframe.srcdoc = prepareSrcdocForElement(this, this.getAttribute(\"srcdoc\") || \"\");\n return;\n }\n if (this.hasAttribute(\"src\")) {\n this.iframe.src = prepareSrcForElement(this, this.getAttribute(\"src\") || \"\");\n }\n }\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 if (typeof player?.seek !== \"function\") return false;\n player.seek.call(player, timeInSeconds);\n return true;\n } catch {\n return false;\n }\n }\n\n private _withDirectTimeline(fn: (tl: DirectTimelineAdapter) => void): boolean {\n const tl = this._directTimelineAdapter || this.probe.resolveDirectTimelineAdapter();\n if (!tl) return false;\n try {\n fn(tl);\n this._directTimelineAdapter = tl;\n return true;\n } catch {\n return false;\n }\n }\n\n // GSAP seek() preserves play state; player seek() contract lands paused.\n private _tryDirectTimelineSeek(t: number): boolean {\n return this._withDirectTimeline((tl) => {\n tl.seek(t);\n tl.pause();\n });\n }\n private _tryDirectTimelinePlay(): boolean {\n return this._withDirectTimeline((tl) => void tl.play());\n }\n private _tryDirectTimelinePause(): boolean {\n return this._withDirectTimeline((tl) => void tl.pause());\n }\n\n private _onMessage(e: MessageEvent) {\n handleRuntimeMessage(e, this.iframe.contentWindow, {\n getPlaybackState: () => ({\n currentTime: this._currentTime,\n duration: this._duration,\n paused: this._paused,\n lastUpdateMs: this._lastUpdateMs,\n }),\n setPlaybackState: ({ currentTime, duration, paused, lastUpdateMs }) => {\n this._currentTime = currentTime;\n this._duration = duration;\n this._paused = paused;\n this._lastUpdateMs = lastUpdateMs;\n },\n getShaderLoadingMode: () => getShaderModeFromElement(this),\n shaderLoader: this.shaderLoader,\n setCompositionSize: (w, h) => {\n this._compositionWidth = w;\n this._compositionHeight = h;\n this._rescale();\n },\n sendControl: (action, extra) => this._sendControl(action, extra),\n getIframeDoc: () => this.iframe.contentDocument,\n updateControlsTime: (t, d) => this.controlsApi?.updateTime(t, d),\n updateControlsPlaying: (p) => this.controlsApi?.updatePlaying(p),\n dispatchEvent: (ev) => this.dispatchEvent(ev),\n seek: (t) => this.seek(t),\n play: () => this.play(),\n getLoop: () => this.loop,\n media: this._media,\n });\n }\n\n private _onProbeReady({ duration, adapter, compositionSize }: ProbeResult) {\n this._duration = duration;\n this._directTimelineAdapter = adapter.kind === \"direct-timeline\" ? adapter.timeline : null;\n this._ready = true;\n this.controlsApi?.updateTime(0, duration);\n this.dispatchEvent(new CustomEvent(\"ready\", { detail: { duration } }));\n if (compositionSize) {\n this._compositionWidth = compositionSize.width;\n this._compositionHeight = compositionSize.height;\n this._rescale();\n }\n try {\n const doc = this.iframe.contentDocument;\n if (doc) this._media.setupFromIframe(doc);\n } catch {\n /* cross-origin */\n }\n if (this.hasAttribute(\"autoplay\")) this.play();\n }\n\n private _rescale() {\n scaleIframeToFit(this, this.iframe, this._compositionWidth, this._compositionHeight);\n }\n\n private _onIframeLoad() {\n this._directTimelineAdapter = null;\n this._directTimelineClock.stop();\n this.shaderLoader.reset();\n this._media.resetForIframeLoad();\n this.probe.start();\n }\n\n private _setupControls() {\n if (this.controlsApi) return;\n this.controlsApi = setupControls(\n this.shadow,\n this.muted,\n this._volume,\n this.getAttribute(\"speed-presets\"),\n {\n onPlay: () => this.play(),\n onPause: () => this.pause(),\n onSeek: (f) => this.seek(f * this._duration),\n onSpeedChange: (s) => void (this.playbackRate = s),\n onMuteToggle: () => void (this.muted = !this.muted),\n onVolumeChange: (v) => void (this.volume = v),\n },\n );\n }\n\n // Test-instrumentation pass-throughs (match original field names).\n get _audioOwner() {\n return this._media.audioOwner;\n }\n get _parentMedia() {\n return this._media.entries;\n }\n _mirrorParentMediaTime(t: number, opts?: { force?: boolean }) {\n this._media.mirrorTime(t, opts);\n }\n _promoteToParentProxy() {\n let d: Document | null = null;\n try {\n d = this.iframe.contentDocument;\n } catch {\n /* x-origin */\n }\n this._media.promoteToParentProxy(d, (t, o) => this._mirrorParentMediaTime(t, o));\n this._sendControl(\"set-media-output-muted\", { muted: true });\n }\n _observeDynamicMedia(doc: Document) {\n this._media.setupFromIframe(doc);\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\";\nexport type { ShaderLoadingMode } from \"./shader-options.js\";\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","/**\n * Types and type-guards for the two playback adapter paths the player supports:\n *\n * - `RuntimeDurationAdapter` — the HyperFrames runtime exposes `window.__player`\n * with a `getDuration()` method. This is the standard path for compositions\n * served through the runtime bridge.\n *\n * - `DirectTimelineAdapter` — same-origin standalone compositions can expose\n * their GSAP master timeline at `window.__timelines` without installing the\n * full runtime. The player drives play/pause/seek directly against the\n * timeline object, bypassing the postMessage bridge.\n *\n * `PlaybackDurationAdapter` is the discriminated union the probe interval\n * returns after deciding which path is available.\n */\n\nexport interface RuntimeDurationAdapter {\n getDuration: () => number;\n}\n\nexport interface DirectTimelineAdapter {\n duration: () => number;\n time: () => number;\n seek: (timeInSeconds: number) => unknown;\n play: () => unknown;\n pause: () => unknown;\n}\n\nexport type PlaybackDurationAdapter =\n | { kind: \"runtime\"; getDuration: () => number }\n | { kind: \"direct-timeline\"; timeline: DirectTimelineAdapter; getDuration: () => number };\n\nexport function isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isRuntimeDurationAdapter(value: unknown): value is RuntimeDurationAdapter {\n return isObjectRecord(value) && typeof value.getDuration === \"function\";\n}\n\nexport function 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","/**\n * Probes an iframe document to discover the composition's playback adapter\n * and detect whether the HyperFrames runtime needs to be injected.\n *\n * The probe interval polls every 200 ms until one of:\n * - A `PlaybackDurationAdapter` resolves with a positive duration, or\n * - 40 attempts (~8 s) expire without a result.\n *\n * The `CompositionProbe` class owns the interval; the caller must call\n * `stop()` on disconnect or src change.\n */\n\nimport { shouldInjectRuntime } from \"./shouldInjectRuntime.js\";\nimport {\n type DirectTimelineAdapter,\n type PlaybackDurationAdapter,\n isDirectTimelineAdapter,\n isObjectRecord,\n isRuntimeDurationAdapter,\n} from \"./timeline-adapters.js\";\n\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\n\nexport interface ProbeResult {\n duration: number;\n adapter: PlaybackDurationAdapter;\n /** Resolved composition dimensions, if present in the document. */\n compositionSize: { width: number; height: number } | null;\n}\n\nexport interface ProbeCallbacks {\n onReady: (result: ProbeResult) => void;\n onError: (message: string) => void;\n /** Called when runtime is successfully injected (informational). */\n onRuntimeInjected?: () => void;\n}\n\nexport class CompositionProbe {\n private _interval: ReturnType<typeof setInterval> | null = null;\n private _runtimeInjected = false;\n\n constructor(\n private readonly _iframe: HTMLIFrameElement,\n private readonly _callbacks: ProbeCallbacks,\n ) {}\n\n get runtimeInjected(): boolean {\n return this._runtimeInjected;\n }\n\n /** Start (or restart) the probe. Stops any previously running probe first. */\n start(): void {\n this.stop();\n this._runtimeInjected = false;\n let attempts = 0;\n\n this._interval = 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 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;\n }\n\n if (this._runtimeInjected && !hasRuntime) return;\n\n const adapter = this._resolvePlaybackDurationAdapter(win);\n if (adapter && adapter.getDuration() > 0) {\n this.stop();\n\n const doc = this._iframe.contentDocument;\n let compositionSize: { width: number; height: number } | null = null;\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) compositionSize = { width: w, height: h };\n }\n\n this._callbacks.onReady({\n duration: adapter.getDuration(),\n adapter,\n compositionSize,\n });\n return;\n }\n } catch {\n /* cross-origin */\n }\n\n if (attempts >= 40) {\n this.stop();\n this._callbacks.onError(\"Composition timeline not found after 8s\");\n }\n }, 200);\n }\n\n stop(): void {\n if (this._interval !== null) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n\n // ── Adapter resolution (same-origin only) ────────────────────────────────\n\n 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 resolveDirectTimelineAdapterFromWindow(win: Window): DirectTimelineAdapter | null {\n return this._resolveDirectTimelineAdapterFromWindow(win);\n }\n\n hasRuntimeBridge(win: Window): boolean {\n return Reflect.get(win, \"__hf\") !== undefined || isObjectRecord(Reflect.get(win, \"__player\"));\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n private _injectRuntime(): void {\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 (doc.head || doc.documentElement).appendChild(script);\n this._callbacks.onRuntimeInjected?.();\n } catch {\n /* cross-origin — can't inject */\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 _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","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 * Helpers for wiring the player's optional UI elements: the playback controls\n * bar, the poster image overlay, and input-filtering utilities.\n *\n * Extracted from the web component so the setup logic doesn't inflate the\n * class body. These are pure imperative DOM operations; they carry no\n * persistent state beyond what the caller tracks.\n */\n\nimport { createControls, type ControlsCallbacks, type ControlsOptions } from \"./controls.js\";\n\n/**\n * Create the playback controls overlay and attach it to `parent`.\n * Returns the controls API object. A no-op guard (returns the existing API)\n * must be enforced by the caller — this function always constructs.\n */\nexport function setupControls(\n parent: ShadowRoot,\n muted: boolean,\n volume: number,\n speedPresetsAttr: string | null,\n callbacks: ControlsCallbacks,\n): ReturnType<typeof createControls> {\n const speedPresets = speedPresetsAttr\n ? speedPresetsAttr\n .split(\",\")\n .map(Number)\n .filter((n) => !isNaN(n) && n > 0)\n : undefined;\n const options: ControlsOptions = speedPresets ? { speedPresets } : {};\n const api = createControls(parent, callbacks, options);\n api.updateMuted(muted);\n api.updateVolume(volume);\n return api;\n}\n\n/**\n * Set up or remove the poster image element in `parent`.\n *\n * - When `posterUrl` is non-empty, creates the `<img>` if needed and sets src.\n * - When `posterUrl` is null/empty, removes the existing element (if any).\n *\n * Returns the current poster element (possibly newly created) or `null`.\n */\nexport function setupPoster(\n parent: ShadowRoot,\n posterUrl: string | null,\n existing: HTMLImageElement | null,\n): HTMLImageElement | null {\n if (!posterUrl) {\n existing?.remove();\n return null;\n }\n if (!existing) {\n existing = document.createElement(\"img\");\n existing.className = \"hfp-poster\";\n parent.appendChild(existing);\n }\n existing.src = posterUrl;\n return existing;\n}\n\n/**\n * Returns `true` when `event` originated inside an `hfp-controls` element.\n * Used to prevent the bare-player-surface click handler from double-firing\n * when the user clicks an overlay control button.\n */\nexport function isControlsClick(event: Event): boolean {\n return event\n .composedPath()\n .some((t) => t instanceof HTMLElement && t.classList.contains(\"hfp-controls\"));\n}\n","/**\n * DOM setup helpers for the player's shadow root.\n *\n * Keeps constructor boilerplate out of the web component class body.\n */\n\n// Cached Constructable Stylesheet shared across all player instances.\nlet _sharedSheet: CSSStyleSheet | null = null;\n\n/**\n * Adopt `cssText` into `shadow` via a shared Constructable Stylesheet when the\n * browser supports it, falling back to a `<style>` element injection. The sheet\n * is cached on first creation and reused across all player instances.\n */\nexport function adoptShadowStyles(shadow: ShadowRoot, cssText: string): void {\n if (typeof CSSStyleSheet !== \"undefined\") {\n try {\n if (!_sharedSheet) {\n _sharedSheet = new CSSStyleSheet();\n _sharedSheet.replaceSync(cssText);\n }\n shadow.adoptedStyleSheets = [_sharedSheet];\n return;\n } catch {\n /* fallthrough */\n }\n }\n const style = document.createElement(\"style\");\n style.textContent = cssText;\n shadow.appendChild(style);\n}\n\n/**\n * Creates and configures the iframe element that hosts the composition, plus\n * the wrapper container div. Returns handles to both so the constructor can\n * attach them to the shadow root and track references without inlining the\n * boilerplate.\n */\nexport function createCompositionIframe(): {\n container: HTMLDivElement;\n iframe: HTMLIFrameElement;\n} {\n const container = document.createElement(\"div\");\n container.className = \"hfp-container\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.className = \"hfp-iframe\";\n iframe.sandbox.add(\"allow-scripts\", \"allow-same-origin\");\n iframe.allow = \"autoplay; fullscreen\";\n iframe.referrerPolicy = \"no-referrer\";\n iframe.title = \"HyperFrames Composition\";\n\n container.appendChild(iframe);\n return { container, iframe };\n}\n\n/**\n * Scale the iframe so the composition fits inside the player element while\n * preserving aspect ratio. No-ops when the player has no painted size yet.\n */\nexport function scaleIframeToFit(\n playerElement: HTMLElement,\n iframe: HTMLIFrameElement,\n compositionWidth: number,\n compositionHeight: number,\n): void {\n const rect = playerElement.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const scale = Math.min(rect.width / compositionWidth, rect.height / compositionHeight);\n iframe.style.width = `${compositionWidth}px`;\n iframe.style.height = `${compositionHeight}px`;\n iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;\n}\n","/**\n * rAF-based clock that polls a `DirectTimelineAdapter` for current time and\n * drives the player's time/playback-ended callbacks.\n *\n * Used for same-origin standalone GSAP compositions that expose\n * `window.__timelines` but have no runtime bridge — the player drives them\n * directly through the adapter instead of going through postMessage.\n */\n\nimport type { DirectTimelineAdapter } from \"./timeline-adapters.js\";\n\nconst UI_UPDATE_INTERVAL_MS = 100;\n\nexport interface ClockCallbacks {\n /** Called every ~100ms and on completion with the current time. */\n onTimeUpdate: (currentTime: number, duration: number) => void;\n /** Called when playback reaches the end. Return true to loop (seek+play). */\n onEnded: () => boolean;\n /** Get the current loop flag. */\n getLoop: () => boolean;\n /** Trigger a seek-then-play loop restart. */\n restart: () => void;\n /** Notify that playback has paused (from the timeline side). */\n onPaused: () => void;\n}\n\nexport class DirectTimelineClock {\n private _raf: number | null = null;\n private _lastUpdateMs = 0;\n\n constructor(private readonly _callbacks: ClockCallbacks) {}\n\n start(\n timeline: DirectTimelineAdapter,\n getCurrentTime: () => number,\n getDuration: () => number,\n isPaused: () => boolean,\n ): void {\n this.stop();\n\n const tick = () => {\n if (isPaused()) {\n this._raf = null;\n return;\n }\n\n let currentTime: number;\n try {\n currentTime = timeline.time();\n } catch {\n this._raf = null;\n return;\n }\n\n const duration = getDuration();\n if (duration > 0) currentTime = Math.min(currentTime, duration);\n\n const completedPlayback = duration > 0 && currentTime >= duration;\n const now = performance.now();\n\n if (now - this._lastUpdateMs > UI_UPDATE_INTERVAL_MS || completedPlayback) {\n this._lastUpdateMs = now;\n this._callbacks.onTimeUpdate(currentTime, duration);\n }\n\n if (completedPlayback) {\n if (this._callbacks.getLoop()) {\n this._callbacks.restart();\n return;\n }\n try {\n timeline.pause();\n } catch {\n /* ignore */\n }\n this._callbacks.onPaused();\n this._raf = null;\n return;\n }\n\n this._raf = requestAnimationFrame(tick);\n };\n\n this._raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n if (this._raf === null) return;\n cancelAnimationFrame(this._raf);\n this._raf = null;\n }\n\n get isRunning(): boolean {\n return this._raf !== null;\n }\n}\n","/**\n * Internal helper for scoping the player's media MutationObserver to the\n * composition tree inside the iframe.\n *\n * Not part of the package's public API — kept in its own module so the\n * decision logic can be exercised by unit tests without exposing it through\n * the player entry point.\n */\n\n/**\n * Pick the elements inside `doc` that the media MutationObserver should\n * attach to.\n *\n * Compositions mount inside `[data-composition-id]` host elements — the\n * runtime root and any sub-composition hosts that `compositionLoader` writes\n * into them. Watching only those hosts (with `subtree: true`) catches every\n * late-arriving timed media element from sub-composition activation, while\n * filtering out churn from analytics tags, runtime telemetry markers, and\n * other out-of-host nodes that the runtime appends straight to `<body>`\n * during bootstrap.\n *\n * Nested hosts are filtered out — they're already covered by their nearest\n * host ancestor's subtree observation, so observing them too would deliver\n * each callback twice and double-count adoption work.\n *\n * Falls back to `[doc.body]` when no composition hosts are present, which\n * preserves the previous behavior for documents that aren't yet (or never\n * will be) composition-structured. Returns an empty array when neither a\n * host nor a body is available — the caller should treat that as \"nothing\n * to observe\".\n *\n * When the scoped path is taken but the body still carries timed media\n * outside every host, a `console.warn` fires once per call as a forensic\n * signal: the new scope skips that media, so any `<audio data-start>` /\n * `<video data-start>` injected at body level will silently never get a\n * parent-frame proxy. Today every runtime path appends inside a host so\n * this branch shouldn't trip; if it does, the warn surfaces the drift\n * immediately rather than presenting as a missing-audio bug downstream.\n */\nexport function selectMediaObserverTargets(doc: Document): Element[] {\n const all = Array.from(doc.querySelectorAll<Element>(\"[data-composition-id]\"));\n if (all.length === 0) {\n return doc.body ? [doc.body] : [];\n }\n\n const topLevel: Element[] = [];\n for (const el of all) {\n if (!hasCompositionAncestor(el)) {\n topLevel.push(el);\n }\n }\n\n warnOnUnscopedTimedMedia(doc);\n return topLevel;\n}\n\n/**\n * Forensic guard: with composition hosts present the observer attaches only\n * to those subtrees, so any timed media sitting at body level (or under a\n * non-host wrapper) is invisible to the adoption pipeline. Walk the body for\n * `[data-start]` audio/video that has no `[data-composition-id]` ancestor\n * and emit a single `console.warn` listing the orphans. The walk is cheap\n * (one `querySelectorAll` over a typed selector + a `closest` per match)\n * and only runs on the scoped path, so the no-host fallback retains its\n * legacy behavior with zero extra work.\n */\nfunction warnOnUnscopedTimedMedia(doc: Document): void {\n const body = doc.body;\n if (!body) return;\n if (typeof console === \"undefined\" || typeof console.warn !== \"function\") return;\n\n const candidates = body.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (candidates.length === 0) return;\n\n const orphans: HTMLMediaElement[] = [];\n for (const el of candidates) {\n if (!el.closest(\"[data-composition-id]\")) orphans.push(el);\n }\n if (orphans.length === 0) return;\n\n console.warn(\n \"[hyperframes-player] selectMediaObserverTargets: composition hosts are present, \" +\n `but ${orphans.length} body-level timed media element(s) sit outside every ` +\n \"[data-composition-id] subtree and will not be observed. Move them inside a \" +\n \"composition host or the parent-frame proxy will never adopt them.\",\n orphans,\n );\n}\n\nfunction hasCompositionAncestor(el: Element): boolean {\n let cursor = el.parentElement;\n while (cursor) {\n if (cursor.hasAttribute(\"data-composition-id\")) return true;\n cursor = cursor.parentElement;\n }\n return false;\n}\n","/**\n * Parent-frame media proxy subsystem.\n *\n * Maintains mirror copies of the iframe's timed `<audio>`/`<video>` elements\n * in the parent frame so that mobile browsers — which gate `el.play()` on user\n * activation in the *same* frame — can still produce audible output via proxies\n * the parent controls directly.\n *\n * See the class-level JSDoc on `HyperframesPlayer` for the full ownership model.\n */\n\nimport { selectMediaObserverTargets } from \"./mediaObserverScope.js\";\n\n/** Minimum absolute drift before a currentTime correction is attempted. */\nconst MIRROR_DRIFT_THRESHOLD_SECONDS = 0.05;\n\n/**\n * How many *consecutive* over-threshold samples are required before issuing a\n * `currentTime` write. Absorbs single-sample jitter (GC pause, slow bridge\n * tick) without thrashing. Forced calls bypass this gate.\n *\n * Worst-case correction latency ≈ this × bridgeMaxPostIntervalMs (80 ms in\n * core/runtime/state.ts) = 160 ms — well under human A/V re-sync tolerance.\n */\nconst MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES = 2;\n\nexport interface ProxyEntry {\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. A write is only issued once this\n * reaches `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES`, absorbing\n * single-sample jitter without thrashing.\n */\n driftSamples: number;\n}\n\nexport class ParentMediaManager {\n private _entries: ProxyEntry[] = [];\n private _mediaObserver?: MutationObserver;\n private _playbackErrorPosted = false;\n private _audioOwner: \"runtime\" | \"parent\" = \"runtime\";\n\n private readonly _dispatchEvent: (event: Event) => void;\n private readonly _getMuted: () => boolean;\n private readonly _getVolume: () => number;\n private readonly _getPlaybackRate: () => number;\n private readonly _getCurrentTime: () => number;\n private readonly _isPaused: () => boolean;\n\n constructor(opts: {\n dispatchEvent: (event: Event) => void;\n getMuted: () => boolean;\n getVolume: () => number;\n getPlaybackRate: () => number;\n getCurrentTime: () => number;\n isPaused: () => boolean;\n }) {\n this._dispatchEvent = opts.dispatchEvent;\n this._getMuted = opts.getMuted;\n this._getVolume = opts.getVolume;\n this._getPlaybackRate = opts.getPlaybackRate;\n this._getCurrentTime = opts.getCurrentTime;\n this._isPaused = opts.isPaused;\n }\n\n get audioOwner(): \"runtime\" | \"parent\" {\n return this._audioOwner;\n }\n\n /** Exposed for test instrumentation only — do not use in production code. */\n get entries(): ProxyEntry[] {\n return this._entries;\n }\n\n get playbackErrorPosted(): boolean {\n return this._playbackErrorPosted;\n }\n\n resetForIframeLoad(): void {\n this._playbackErrorPosted = false;\n const wasPromoted = this._audioOwner === \"parent\";\n this._audioOwner = \"runtime\";\n this.pauseAll();\n this.teardownObserver();\n if (wasPromoted) {\n this._dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"runtime\", reason: \"iframe-reload\" },\n }),\n );\n }\n }\n\n destroy(): void {\n this.teardownObserver();\n for (const m of this._entries) {\n m.el.pause();\n m.el.src = \"\";\n }\n this._entries = [];\n }\n\n updateMuted(muted: boolean): void {\n for (const m of this._entries) m.el.muted = muted;\n }\n\n updateVolume(volume: number): void {\n for (const m of this._entries) m.el.volume = volume;\n }\n\n updatePlaybackRate(rate: number): void {\n for (const m of this._entries) m.el.playbackRate = rate;\n }\n\n playAll(): void {\n for (const m of this._entries) {\n if (!m.el.src) continue;\n m.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n\n pauseAll(): void {\n for (const m of this._entries) m.el.pause();\n }\n\n seekAll(timeInSeconds: number): void {\n for (const m of this._entries) {\n const relTime = timeInSeconds - m.start;\n if (relTime >= 0 && relTime < m.duration) m.el.currentTime = relTime;\n }\n }\n\n /**\n * Mirror parent-proxy `currentTime` to the iframe timeline, with optional\n * jitter-coalescing. Pass `{ force: true }` for alignment moments (ownership\n * promotion, new proxy initialization) where drift must be corrected\n * immediately.\n */\n mirrorTime(timelineSeconds: number, options?: { force?: boolean }): void {\n const force = options?.force === true;\n for (const m of this._entries) {\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) > MIRROR_DRIFT_THRESHOLD_SECONDS) {\n m.driftSamples += 1;\n if (force || m.driftSamples >= MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES) {\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 in response to the runtime's\n * `media-autoplay-blocked` signal. Idempotent.\n *\n * The caller is responsible for muting the iframe's own media output via the\n * postMessage bridge (`set-media-output-muted`) after calling this.\n */\n /**\n * Take ownership of audible playback. Idempotent. The `onMirror` callback\n * is called with the current timeline time and `{ force: true }` so the\n * caller's mirror implementation runs (enabling test spies on the player\n * to fire). If omitted, `mirrorTime` is called directly.\n */\n promoteToParentProxy(\n iframeDoc: Document | null,\n onMirror?: (t: number, opts: { force: boolean }) => void,\n ): void {\n if (this._audioOwner === \"parent\") return;\n this._audioOwner = \"parent\";\n\n // Synchronously mute iframe media to close the race window.\n if (iframeDoc) {\n for (const el of iframeDoc.querySelectorAll<HTMLMediaElement>(\"video, audio\")) {\n el.muted = true;\n }\n }\n\n // One-shot alignment — bypass jitter-coalescing gate.\n const t = this._getCurrentTime();\n if (onMirror) onMirror(t, { force: true });\n else this.mirrorTime(t, { force: true });\n if (!this._isPaused()) this.playAll();\n\n this._dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"parent\", reason: \"autoplay-blocked\" },\n }),\n );\n }\n\n /**\n * Set up proxies for all timed media currently in the iframe document, then\n * install a MutationObserver for media added later (sub-composition activation).\n */\n setupFromIframe(iframeDoc: Document): void {\n const mediaEls = iframeDoc.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n for (const iframeEl of mediaEls) this._adoptIframeMedia(iframeEl);\n this._observeDynamicMedia(iframeDoc);\n }\n\n /** Set up a single proxy from an explicit URL (the `audio-src` attribute path). */\n setupFromUrl(audioSrc: string): void {\n this._createEntry(audioSrc, \"audio\", 0, Infinity);\n }\n\n teardownObserver(): void {\n this._mediaObserver?.disconnect();\n this._mediaObserver = undefined;\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n private _reportPlaybackError(err: unknown): void {\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 /**\n * Create a parent-frame media element and start preloading it. Returns the\n * new entry, or `null` if a proxy for this src already exists (dedup).\n */\n private _createEntry(\n src: string,\n tag: \"audio\" | \"video\",\n start: number,\n duration: number,\n ): ProxyEntry | null {\n if (this._entries.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._getMuted();\n el.volume = this._getVolume();\n const rate = this._getPlaybackRate();\n if (rate !== 1) el.playbackRate = rate;\n\n const entry: ProxyEntry = { el, start, duration, driftSamples: 0 };\n this._entries.push(entry);\n return entry;\n }\n\n private _adoptIframeMedia(iframeEl: HTMLMediaElement): void {\n // Skip elements the preloader has demoted — the observer will re-trigger\n // when the preload attribute is promoted to \"auto\".\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 const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\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._createEntry(src, tag, start, duration);\n\n // If already under parent ownership and playing, the new proxy must catch\n // up immediately — bypass the jitter-coalescing gate.\n if (created && this._audioOwner === \"parent\") {\n this.mirrorTime(this._getCurrentTime(), { force: true });\n if (!this._isPaused() && created.el.src) {\n created.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n }\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._entries.findIndex((m) => m.el.src === src);\n if (idx === -1) return;\n const entry = this._entries[idx];\n entry.el.pause();\n entry.el.src = \"\";\n this._entries.splice(idx, 1);\n }\n\n private _observeDynamicMedia(doc: Document): void {\n this.teardownObserver();\n if (typeof MutationObserver === \"undefined\" || !doc.body) return;\n\n const obs = new MutationObserver((mutations) => {\n for (const m of mutations) {\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 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\n for (const removed of m.removedNodes) {\n if (!(removed instanceof Element)) continue;\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\n const observeOpts: MutationObserverInit = {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"preload\"],\n };\n\n const targets = selectMediaObserverTargets(doc);\n for (const target of targets) {\n obs.observe(target, observeOpts);\n }\n this._mediaObserver = obs;\n }\n}\n","/**\n * Pure playback-state update logic for the `state` message from the runtime.\n *\n * Extracted from the web component so the state-transition rules — loop\n * handling, play/pause mirroring, completion detection — can be read and\n * exercised independently.\n */\n\nimport type { ParentMediaManager } from \"./parent-media.js\";\n\nconst UI_UPDATE_INTERVAL_MS = 100;\n\nexport interface PlaybackState {\n currentTime: number;\n duration: number;\n paused: boolean;\n lastUpdateMs: number;\n}\n\nexport interface PlaybackStateCallbacks {\n updateControlsTime: (current: number, duration: number) => void;\n updateControlsPlaying: (playing: boolean) => void;\n dispatchEvent: (event: Event) => void;\n seek: (t: number) => void;\n play: () => void;\n getLoop: () => boolean;\n media: ParentMediaManager;\n}\n\n/**\n * Process a `state` message from the runtime and return the next state.\n * Side effects (controls updates, events, media mirroring) are fired through\n * `callbacks`. The caller must commit the returned state object.\n */\nexport function applyRuntimeStateMessage(\n data: { frame: number; isPlaying: boolean },\n fps: number,\n current: PlaybackState,\n callbacks: PlaybackStateCallbacks,\n): PlaybackState {\n const rawTime = (data.frame ?? 0) / fps;\n const currentTime = current.duration > 0 ? Math.min(rawTime, current.duration) : rawTime;\n const wasPlaying = !current.paused;\n const nextPaused = !data.isPlaying;\n const completedPlayback =\n current.duration > 0 && currentTime >= current.duration && (wasPlaying || data.isPlaying);\n\n if (completedPlayback && callbacks.getLoop()) {\n if (callbacks.media.audioOwner === \"parent\") callbacks.media.pauseAll();\n callbacks.seek(0);\n callbacks.play();\n // play() sets paused=false; reflect that in the returned state so the\n // caller's destructure doesn't overwrite it with the stale nextPaused value.\n return { ...current, currentTime, paused: false };\n }\n\n const next: PlaybackState = { ...current, currentTime, paused: nextPaused };\n\n if (callbacks.media.audioOwner === \"parent\") {\n if (wasPlaying && nextPaused) {\n callbacks.media.pauseAll();\n } else if (!wasPlaying && !nextPaused) {\n callbacks.media.playAll();\n }\n callbacks.media.mirrorTime(currentTime);\n }\n\n const now = performance.now();\n const playStateChanged = nextPaused !== current.paused;\n if (now - current.lastUpdateMs > UI_UPDATE_INTERVAL_MS || playStateChanged) {\n next.lastUpdateMs = now;\n callbacks.updateControlsTime(currentTime, current.duration);\n callbacks.updateControlsPlaying(!nextPaused);\n callbacks.dispatchEvent(new CustomEvent(\"timeupdate\", { detail: { currentTime } }));\n }\n\n if (completedPlayback) {\n if (callbacks.media.audioOwner === \"parent\") callbacks.media.pauseAll();\n next.paused = true;\n callbacks.updateControlsPlaying(false);\n callbacks.dispatchEvent(new Event(\"ended\"));\n }\n\n return next;\n}\n","/**\n * Routes postMessages from the composition iframe to the appropriate handlers.\n *\n * Accepts the raw MessageEvent and delegates through typed callbacks so the\n * web component keeps its state fields private and this module stays stateless.\n */\n\nimport {\n applyRuntimeStateMessage,\n type PlaybackState,\n type PlaybackStateCallbacks,\n} from \"./playback-state.js\";\nimport type { ShaderLoaderState } from \"./shader-loader-state.js\";\nimport type { ShaderTransitionState } from \"./shader-options.js\";\n\nconst FPS = 30;\n\nexport interface MessageHandlerCallbacks extends PlaybackStateCallbacks {\n getPlaybackState: () => PlaybackState;\n setPlaybackState: (next: PlaybackState) => void;\n getShaderLoadingMode: () => string;\n shaderLoader: ShaderLoaderState;\n setCompositionSize: (width: number, height: number) => void;\n sendControl: (action: string, extra?: Record<string, unknown>) => void;\n getIframeDoc: () => Document | null;\n}\n\nexport function handleRuntimeMessage(\n event: MessageEvent,\n frameWindow: Window | null,\n callbacks: MessageHandlerCallbacks,\n): void {\n if (event.source !== frameWindow) return;\n const data = event.data as Record<string, unknown> | undefined;\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\"\n ? (data[\"state\"] as ShaderTransitionState)\n : {};\n callbacks.shaderLoader.update(state, callbacks.getShaderLoadingMode());\n callbacks.dispatchEvent(\n new CustomEvent(\"shadertransitionstate\", {\n detail: { compositionId: data[\"compositionId\"], state },\n }),\n );\n return;\n }\n\n if (data[\"type\"] === \"state\") {\n callbacks.setPlaybackState(\n applyRuntimeStateMessage(\n { frame: (data[\"frame\"] as number) ?? 0, isPlaying: !!data[\"isPlaying\"] },\n FPS,\n callbacks.getPlaybackState(),\n callbacks,\n ),\n );\n return;\n }\n\n if (data[\"type\"] === \"media-autoplay-blocked\") {\n let iframeDoc: Document | null = null;\n try {\n iframeDoc = callbacks.getIframeDoc();\n } catch {\n /* cross-origin */\n }\n callbacks.media.promoteToParentProxy(iframeDoc, (t, opts) =>\n callbacks.media.mirrorTime(t, opts),\n );\n callbacks.sendControl(\"set-media-output-muted\", { muted: true });\n return;\n }\n\n if (data[\"type\"] === \"timeline\" && (data[\"durationInFrames\"] as number) > 0) {\n if (Number.isFinite(data[\"durationInFrames\"])) {\n const pb = callbacks.getPlaybackState();\n const duration = (data[\"durationInFrames\"] as number) / FPS;\n callbacks.setPlaybackState({ ...pb, duration });\n callbacks.updateControlsTime(pb.currentTime, duration);\n }\n return;\n }\n\n if (\n data[\"type\"] === \"stage-size\" &&\n (data[\"width\"] as number) > 0 &&\n (data[\"height\"] as number) > 0\n ) {\n callbacks.setCompositionSize(data[\"width\"] as number, data[\"height\"] as number);\n }\n}\n","/**\n * Shader transition option types, constants, and pure helper functions for\n * injecting shader capture scale and loading mode parameters into composition\n * URLs and srcdoc HTML.\n */\n\nexport const SHADER_CAPTURE_SCALE_ATTR = \"shader-capture-scale\";\nexport const SHADER_LOADING_ATTR = \"shader-loading\";\nexport const SHADER_CAPTURE_SCALE_PARAM = \"__hf_shader_capture_scale\";\nexport const SHADER_LOADING_PARAM = \"__hf_shader_loading\";\n\nexport const 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\nexport type ShaderLoadingMode = \"composition\" | \"player\" | \"none\";\n\nexport interface 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\nexport function 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\nexport function 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\nexport function 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\nexport function 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\n/**\n * Convenience wrappers that read shader attributes directly from an element,\n * avoiding boilerplate in the web component class body.\n */\n\nexport function getShaderModeFromElement(el: Element): ShaderLoadingMode {\n return normalizeShaderLoadingMode(el.getAttribute(SHADER_LOADING_ATTR));\n}\n\nexport function getShaderCaptureScaleFromElement(el: Element): number {\n return Number(normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)) ?? \"1\");\n}\n\nexport function prepareSrcForElement(el: Element, src: string): string {\n return withShaderQueryParams(\n src,\n normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)),\n getShaderModeFromElement(el),\n );\n}\n\nexport function prepareSrcdocForElement(el: Element, srcdoc: string): string {\n return injectShaderOptionsIntoSrcdoc(\n srcdoc,\n normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)),\n getShaderModeFromElement(el),\n );\n}\n","/**\n * Factory for the shader-transition loading overlay DOM tree.\n *\n * Kept in its own module so the ~100-line DOM construction stays out of the\n * web component class body. The returned `ShaderLoaderElements` bag gives the\n * component direct handles to the nodes it needs to update without querying\n * the shadow DOM on every state change.\n */\n\nimport { SHADER_LOADING_PHRASES } from \"./shader-options.js\";\n\nexport interface 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\nexport function 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 titleContainer = document.createElement(\"div\");\n titleContainer.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 titleContainer.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(titleContainer);\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 * Runtime state controller for the shader-transition loading overlay.\n *\n * Manages show/hide transitions (with a CSS fade-out delay) and updates\n * the progress bar, phrase text, and detail rows from `ShaderTransitionState`\n * messages received from the iframe.\n *\n * Holds direct references to the DOM nodes created by `createShaderLoader`\n * so state updates never touch the shadow-DOM query API at runtime.\n */\n\nimport { SHADER_LOADING_PHRASES, type ShaderTransitionState } from \"./shader-options.js\";\nimport type { ShaderLoaderElements } from \"./shader-loader-element.js\";\n\nconst HIDE_TRANSITION_MS = 420;\n\nexport class ShaderLoaderState {\n private readonly _el: ShaderLoaderElements;\n private _hideTimeout: ReturnType<typeof setTimeout> | null = null;\n\n constructor(elements: ShaderLoaderElements) {\n this._el = elements;\n }\n\n show(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n this._el.root.classList.remove(\"hfp-hiding\");\n this._el.root.classList.add(\"hfp-visible\");\n }\n\n hide(): void {\n if (this._el.root.classList.contains(\"hfp-hiding\")) {\n if (!this._hideTimeout) this._scheduleCleanup();\n return;\n }\n if (!this._el.root.classList.contains(\"hfp-visible\")) return;\n this._el.root.classList.add(\"hfp-hiding\");\n this._el.root.classList.remove(\"hfp-visible\");\n this._scheduleCleanup();\n }\n\n reset(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n this._el.root.classList.remove(\"hfp-visible\", \"hfp-hiding\");\n this._el.fill.style.transform = \"scaleX(0)\";\n this._el.transitionValue.textContent = \"\";\n this._el.frameValue.textContent = \"\";\n this._el.frameRow.style.visibility = \"hidden\";\n }\n\n update(status: ShaderTransitionState, loadingMode: string): void {\n if (loadingMode !== \"player\") {\n this.reset();\n return;\n }\n if (status.ready || !status.loading) {\n this.hide();\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\n const phraseIndex = Math.min(\n SHADER_LOADING_PHRASES.length - 1,\n Math.floor(ratio * SHADER_LOADING_PHRASES.length),\n );\n this._el.title.textContent =\n SHADER_LOADING_PHRASES[phraseIndex] || \"Preparing scene transitions\";\n\n this._el.detail.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\n this._el.fill.style.transform = `scaleX(${ratio})`;\n\n this._el.transitionValue.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\n this._el.frameLabel.textContent =\n status.phase === \"cached\"\n ? \"cached transition frames\"\n : status.phase === \"finalizing\"\n ? \"finalizing transition frames\"\n : \"rendering transition frames\";\n\n this._el.frameValue.textContent = frameValue;\n this._el.frameRow.style.visibility = frameValue ? \"visible\" : \"hidden\";\n this._el.root.setAttribute(\"aria-valuenow\", String(Math.round(ratio * 100)));\n this.show();\n }\n\n get hideTimeout(): ReturnType<typeof setTimeout> | null {\n return this._hideTimeout;\n }\n\n destroy(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n }\n\n private _scheduleCleanup(): void {\n if (this._hideTimeout) clearTimeout(this._hideTimeout);\n this._hideTimeout = setTimeout(() => {\n this._el.root.classList.remove(\"hfp-hiding\");\n this._hideTimeout = null;\n }, HIDE_TRANSITION_MS);\n }\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAma1BC,EAAY,8GACZC,GAAa,gKACbC,EAAmB,6SACnBC,EAAkB,6LAClBC,GAAoB,wRCrZ1B,IAAMC,EAAgB,CAAC,IAAM,GAAK,EAAG,IAAK,EAAG,CAAC,EAO9C,SAASC,EAAYC,EAAuB,CACjD,OAAO,OAAO,UAAUA,CAAK,EAAI,GAAGA,CAAK,IAAM,GAAGA,CAAK,GACzD,CAEO,SAASC,EAAWC,EAAyB,CAElD,GAAI,CAAC,OAAO,SAASA,CAAO,GAAKA,EAAU,EACzC,MAAO,OAET,IAAMC,EAAI,KAAK,MAAMD,CAAO,EACtBE,EAAI,KAAK,MAAMD,EAAI,EAAE,EACrBE,EAAMF,EAAI,GAChB,MAAO,GAAGC,CAAC,IAAIC,EAAI,SAAS,EAAE,SAAS,EAAG,GAAG,CAAC,EAChD,CAEO,SAASC,GACdC,EACAC,EACAC,EAA2B,CAAC,EAU5B,CACA,IAAMC,EAAUD,EAAQ,cAAgBX,EAElCa,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,eAErBA,EAAS,iBAAiB,QAAUC,GAAM,CACxCA,EAAE,gBAAgB,CACpB,CAAC,EAED,IAAMC,EAAU,SAAS,cAAc,QAAQ,EAC/CA,EAAQ,UAAY,eACpBA,EAAQ,KAAO,SACfA,EAAQ,UAAYC,EACpBD,EAAQ,aAAa,aAAc,MAAM,EAEzC,IAAME,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,eACrB,IAAMC,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,eACrBA,EAAS,MAAM,MAAQ,KACvBD,EAAS,YAAYC,CAAQ,EAE7B,IAAMC,EAAO,SAAS,cAAc,MAAM,EAC1CA,EAAK,UAAY,WACjBA,EAAK,YAAc,cAEnB,IAAMC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,iBAEtB,IAAMC,EAAW,SAAS,cAAc,QAAQ,EAChDA,EAAS,UAAY,gBACrBA,EAAS,KAAO,SAChBA,EAAS,YAAc,KACvBA,EAAS,aAAa,aAAc,gBAAgB,EAEpD,IAAMC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,iBACtBA,EAAU,aAAa,OAAQ,MAAM,EACrC,QAAWC,KAAUX,EAAS,CAC5B,IAAMY,EAAO,SAAS,cAAc,QAAQ,EAC5CA,EAAK,UAAY,mBACjBA,EAAK,KAAO,SACZA,EAAK,aAAa,OAAQ,UAAU,EACpCA,EAAK,QAAQ,MAAQ,OAAOD,CAAM,EAClCC,EAAK,YAAcvB,EAAYsB,CAAM,EACjCA,IAAW,GAAGC,EAAK,UAAU,IAAI,YAAY,EACjDF,EAAU,YAAYE,CAAI,CAC5B,CAEAJ,EAAU,YAAYE,CAAS,EAC/BF,EAAU,YAAYC,CAAQ,EAE9B,IAAMI,EAAa,SAAS,cAAc,KAAK,EAC/CA,EAAW,UAAY,kBAEvB,IAAMC,EAAU,SAAS,cAAc,QAAQ,EAC/CA,EAAQ,UAAY,eACpBA,EAAQ,KAAO,SACfA,EAAQ,UAAYC,EACpBD,EAAQ,aAAa,aAAc,MAAM,EAEzC,IAAME,EAAmB,SAAS,cAAc,KAAK,EACrDA,EAAiB,UAAY,yBAE7B,IAAMC,EAAe,SAAS,cAAc,KAAK,EACjDA,EAAa,UAAY,oBACzBA,EAAa,aAAa,OAAQ,QAAQ,EAC1CA,EAAa,aAAa,aAAc,QAAQ,EAChDA,EAAa,aAAa,gBAAiB,GAAG,EAC9CA,EAAa,aAAa,gBAAiB,KAAK,EAChDA,EAAa,aAAa,gBAAiB,KAAK,EAChDA,EAAa,SAAW,EACxB,IAAMC,EAAa,SAAS,cAAc,KAAK,EAC/CA,EAAW,UAAY,kBACvBA,EAAW,MAAM,MAAQ,OACzBD,EAAa,YAAYC,CAAU,EACnCF,EAAiB,YAAYC,CAAY,EAEzCJ,EAAW,YAAYG,CAAgB,EACvCH,EAAW,YAAYC,CAAO,EAE9Bb,EAAS,YAAYE,CAAO,EAC5BF,EAAS,YAAYI,CAAQ,EAC7BJ,EAAS,YAAYM,CAAI,EACzBN,EAAS,YAAYY,CAAU,EAC/BZ,EAAS,YAAYO,CAAS,EAC9BX,EAAO,YAAYI,CAAQ,EAE3B,IAAIkB,EAAY,GACZC,EAAU,GACVC,EAAgB,EAChBC,EAAoD,KACpDC,EAAavB,EAAQ,QAAQ,CAAC,EAC9BuB,IAAe,KAAIA,EAAa,GAEpC,IAAMC,EAAgB,CAACC,EAAgBC,IACjCD,EAAcE,GACdD,IAAW,EAAUE,EACrBF,EAAS,GAAYE,EAClBb,EAGTZ,EAAQ,iBAAiB,QAAUD,GAAM,CACvCA,EAAE,gBAAgB,EACdiB,EAAWrB,EAAU,QAAQ,EAC5BA,EAAU,OAAO,CACxB,CAAC,EAEDgB,EAAQ,iBAAiB,QAAUZ,GAAM,CACvCA,EAAE,gBAAgB,EAClBJ,EAAU,aAAa,CACzB,CAAC,EAED,IAAI+B,EAAkB,GAEhBC,EAAkBC,GAAoB,CAC1C,IAAMC,EAAOf,EAAa,sBAAsB,EAC1CgB,EAAW,KAAK,IAAI,EAAG,KAAK,IAAI,GAAIF,EAAUC,EAAK,MAAQA,EAAK,KAAK,CAAC,EAC5EX,EAAgBY,EAChBf,EAAW,MAAM,MAAQ,GAAGe,EAAW,GAAG,IAC1ChB,EAAa,aAAa,gBAAiB,OAAO,KAAK,MAAMgB,EAAW,GAAG,CAAC,CAAC,EACzEb,GAAWa,EAAW,GAAGnC,EAAU,aAAa,EACpDgB,EAAQ,UAAYU,EAAcJ,EAASa,CAAQ,EACnDnC,EAAU,eAAemC,CAAQ,CACnC,EAEAhB,EAAa,iBAAiB,YAAcf,GAAM,CAChDA,EAAE,gBAAgB,EAClB2B,EAAkB,GAClBC,EAAe5B,EAAE,OAAO,CAC1B,CAAC,EACD,IAAMgC,EAAqBhC,GAAkB,CACvC2B,GAAiBC,EAAe5B,EAAE,OAAO,CAC/C,EACMiC,EAAkB,IAAM,CAC5BN,EAAkB,EACpB,EACA,SAAS,iBAAiB,YAAaK,CAAiB,EACxD,SAAS,iBAAiB,UAAWC,CAAe,EAEpDlB,EAAa,iBACX,aACCf,GAAM,CACL2B,EAAkB,GAClB,IAAMO,EAAQlC,EAAE,QAAQ,CAAC,EACrBkC,GAAON,EAAeM,EAAM,OAAO,CACzC,EACA,CAAE,QAAS,EAAK,CAClB,EACA,IAAMC,GAAqBnC,GAAkB,CAC3C,GAAI2B,EAAiB,CACnB,IAAMO,EAAQlC,EAAE,QAAQ,CAAC,EACrBkC,GAAON,EAAeM,EAAM,OAAO,CACzC,CACF,EACME,GAAmB,IAAM,CAC7BT,EAAkB,EACpB,EACA,SAAS,iBAAiB,YAAaQ,GAAmB,CAAE,QAAS,EAAK,CAAC,EAC3E,SAAS,iBAAiB,WAAYC,EAAgB,EAEtD,IAAMC,GAAc,IACpBtB,EAAa,iBAAiB,UAAYf,GAAM,CAC9C,IAAIsC,EAASnB,EACb,GAAInB,EAAE,MAAQ,cAAgBA,EAAE,MAAQ,UACtCsC,EAAS,KAAK,IAAI,EAAGnB,EAAgBkB,EAAW,UACvCrC,EAAE,MAAQ,aAAeA,EAAE,MAAQ,YAC5CsC,EAAS,KAAK,IAAI,EAAGnB,EAAgBkB,EAAW,MAEhD,QAEFrC,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAClBmB,EAAgBmB,EAChBtB,EAAW,MAAM,MAAQ,GAAGsB,EAAS,GAAG,IACxCvB,EAAa,aAAa,gBAAiB,OAAO,KAAK,MAAMuB,EAAS,GAAG,CAAC,CAAC,EACvEpB,GAAWoB,EAAS,GAAG1C,EAAU,aAAa,EAClDgB,EAAQ,UAAYU,EAAcJ,EAASoB,CAAM,EACjD1C,EAAU,eAAe0C,CAAM,CACjC,CAAC,EAED,IAAMC,GAAmBnD,GAAkB,CACzC,QAAWoD,KAAOhC,EAAU,iBAAiB,mBAAmB,EAC9DgC,EAAI,UAAU,OAAO,aAAeA,EAAoB,QAAQ,QAAU,OAAOpD,CAAK,CAAC,CAE3F,EAEAmB,EAAS,iBAAiB,QAAUP,GAAM,CACxCA,EAAE,gBAAgB,EAClB,IAAMyC,EAASjC,EAAU,UAAU,OAAO,UAAU,EACpDD,EAAS,aAAa,gBAAiB,OAAOkC,CAAM,CAAC,CACvD,CAAC,EAEDjC,EAAU,iBAAiB,QAAUR,GAAM,CACzCA,EAAE,gBAAgB,EAClB,IAAM0C,EAAU1C,EAAE,OAAuB,QAAQ,mBAAmB,EACpE,GAAI,CAAC0C,EAAQ,OACb,IAAMC,EAAW,WAAWD,EAAO,QAAQ,KAAM,EACjDrB,EAAavB,EAAQ,QAAQ6C,CAAQ,EACrCpC,EAAS,YAAcpB,EAAYwD,CAAQ,EAC3CJ,GAAgBI,CAAQ,EACxBnC,EAAU,UAAU,OAAO,UAAU,EACrCD,EAAS,aAAa,gBAAiB,OAAO,EAC9CX,EAAU,cAAc+C,CAAQ,CAClC,CAAC,EAGD,IAAMC,GAAa,IAAM,CACvBpC,EAAU,UAAU,OAAO,UAAU,EACrCD,EAAS,aAAa,gBAAiB,OAAO,CAChD,EACA,SAAS,iBAAiB,QAASqC,EAAU,EAE7C,IAAMC,EAAiBhB,GAAoB,CACzC,IAAMC,EAAO3B,EAAS,sBAAsB,EACtC4B,EAAW,KAAK,IAAI,EAAG,KAAK,IAAI,GAAIF,EAAUC,EAAK,MAAQA,EAAK,KAAK,CAAC,EAC5ElC,EAAU,OAAOmC,CAAQ,CAC3B,EAEIe,EAAY,GAEhB3C,EAAS,iBAAiB,YAAcH,GAAM,CAC5CA,EAAE,gBAAgB,EAClB8C,EAAY,GACZD,EAAc7C,EAAE,OAAO,CACzB,CAAC,EACD,IAAM+C,GAAe/C,GAAkB,CACjC8C,GAAWD,EAAc7C,EAAE,OAAO,CACxC,EACMgD,GAAY,IAAM,CACtBF,EAAY,EACd,EACA,SAAS,iBAAiB,YAAaC,EAAW,EAClD,SAAS,iBAAiB,UAAWC,EAAS,EAE9C7C,EAAS,iBACP,aACCH,GAAM,CACL8C,EAAY,GACZ,IAAMZ,EAAQlC,EAAE,QAAQ,CAAC,EACrBkC,GAAOW,EAAcX,EAAM,OAAO,CACxC,EACA,CAAE,QAAS,EAAK,CAClB,EACA,IAAMe,GAAejD,GAAkB,CACrC,GAAI8C,EAAW,CACb,IAAMZ,EAAQlC,EAAE,QAAQ,CAAC,EACrBkC,GAAOW,EAAcX,EAAM,OAAO,CACxC,CACF,EACMgB,GAAa,IAAM,CACvBJ,EAAY,EACd,EACA,SAAS,iBAAiB,YAAaG,GAAa,CAAE,QAAS,EAAK,CAAC,EACrE,SAAS,iBAAiB,WAAYC,EAAU,EAEhD,IAAMC,GAAiB,IAAM,CACvB/B,GAAa,aAAaA,CAAW,EACzCA,EAAc,WAAW,IAAM,CACzBH,GAAWlB,EAAS,UAAU,IAAI,YAAY,CACpD,EAAG,GAAI,CACT,EAEMqD,GAAOzD,aAAkB,WAAcA,EAAO,KAAuBA,EAC3E,OAAAyD,GAAK,iBAAiB,YAAa,IAAM,CACvCrD,EAAS,UAAU,OAAO,YAAY,EACtCoD,GAAe,CACjB,CAAC,EACDC,GAAK,iBAAiB,aAAc,IAAM,CACpCnC,GAAWlB,EAAS,UAAU,IAAI,YAAY,CACpD,CAAC,EAEM,CACL,WAAWsD,EAAiBC,EAAkB,CAE5C,IAAMC,EAAiBD,EAAW,EAAI,KAAK,IAAID,EAASC,CAAQ,EAAID,EAC9DG,GAAMF,EAAW,EAAKC,EAAiBD,EAAY,IAAM,EAC/DlD,EAAS,MAAM,MAAQ,GAAGoD,EAAG,IAC7BnD,EAAK,YAAc,GAAGhB,EAAWkE,CAAc,CAAC,MAAMlE,EAAWiE,CAAQ,CAAC,EAC5E,EACA,cAAcG,EAAkB,CAC9BxC,EAAYwC,EACZxD,EAAQ,UAAYwD,EAAUC,GAAaxD,EAC3CD,EAAQ,aAAa,aAAcwD,EAAU,QAAU,MAAM,EACzDA,EAASN,GAAe,EACvBpD,EAAS,UAAU,OAAO,YAAY,CAC7C,EACA,YAAYX,EAAe,CACzB,IAAMuE,EAAM7D,EAAQ,QAAQV,CAAK,EAC7BuE,IAAQ,KAAItC,EAAasC,GAC7BpD,EAAS,YAAcpB,EAAYC,CAAK,EACxCmD,GAAgBnD,CAAK,CACvB,EACA,YAAYmC,EAAgB,CAC1BL,EAAUK,EACVX,EAAQ,UAAYU,EAAcC,EAAOJ,CAAa,EACtDP,EAAQ,aAAa,aAAcW,EAAQ,SAAW,MAAM,CAC9D,EACA,aAAaC,EAAgB,CAC3BL,EAAgBK,EAChBR,EAAW,MAAM,MAAQ,GAAGQ,EAAS,GAAG,IACxCT,EAAa,aAAa,gBAAiB,OAAO,KAAK,MAAMS,EAAS,GAAG,CAAC,CAAC,EAC3EZ,EAAQ,UAAYU,EAAcJ,EAASM,CAAM,CACnD,EACA,MAAO,CACLzB,EAAS,MAAM,QAAU,EAC3B,EACA,MAAO,CACLA,EAAS,MAAM,QAAU,MAC3B,EACA,SAAU,CACR,SAAS,oBAAoB,YAAagD,EAAW,EACrD,SAAS,oBAAoB,UAAWC,EAAS,EACjD,SAAS,oBAAoB,YAAaC,EAAW,EACrD,SAAS,oBAAoB,WAAYC,EAAU,EACnD,SAAS,oBAAoB,YAAalB,CAAiB,EAC3D,SAAS,oBAAoB,UAAWC,CAAe,EACvD,SAAS,oBAAoB,YAAaE,EAAiB,EAC3D,SAAS,oBAAoB,WAAYC,EAAgB,EACzD,SAAS,oBAAoB,QAASQ,EAAU,EAC5CxB,GAAa,aAAaA,CAAW,CAC3C,CACF,CACF,CCzWO,SAASwC,GACdC,EACAC,EACAC,EACAC,EACAC,EACmC,CACnC,IAAMC,EAAeF,EACjBA,EACG,MAAM,GAAG,EACT,IAAI,MAAM,EACV,OAAQG,GAAM,CAAC,MAAMA,CAAC,GAAKA,EAAI,CAAC,EACnC,OAEEC,EAAMC,GAAeR,EAAQI,EADFC,EAAe,CAAE,aAAAA,CAAa,EAAI,CAAC,CACf,EACrD,OAAAE,EAAI,YAAYN,CAAK,EACrBM,EAAI,aAAaL,CAAM,EAChBK,CACT,CAUO,SAASE,EACdT,EACAU,EACAC,EACyB,CACzB,OAAKD,GAIAC,IACHA,EAAW,SAAS,cAAc,KAAK,EACvCA,EAAS,UAAY,aACrBX,EAAO,YAAYW,CAAQ,GAE7BA,EAAS,IAAMD,EACRC,IATLA,GAAU,OAAO,EACV,KASX,CAOO,SAASC,GAAgBC,EAAuB,CACrD,OAAOA,EACJ,aAAa,EACb,KAAMC,GAAMA,aAAa,aAAeA,EAAE,UAAU,SAAS,cAAc,CAAC,CACjF,CChEA,IAAIC,EAAqC,KAOlC,SAASC,GAAkBC,EAAoBC,EAAuB,CAC3E,GAAI,OAAO,cAAkB,IAC3B,GAAI,CACGH,IACHA,EAAe,IAAI,cACnBA,EAAa,YAAYG,CAAO,GAElCD,EAAO,mBAAqB,CAACF,CAAY,EACzC,MACF,MAAQ,CAER,CAEF,IAAMI,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAcD,EACpBD,EAAO,YAAYE,CAAK,CAC1B,CAQO,SAASC,IAGd,CACA,IAAMC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,gBAEtB,IAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9C,OAAAA,EAAO,UAAY,aACnBA,EAAO,QAAQ,IAAI,gBAAiB,mBAAmB,EACvDA,EAAO,MAAQ,uBACfA,EAAO,eAAiB,cACxBA,EAAO,MAAQ,0BAEfD,EAAU,YAAYC,CAAM,EACrB,CAAE,UAAAD,EAAW,OAAAC,CAAO,CAC7B,CAMO,SAASC,GACdC,EACAF,EACAG,EACAC,EACM,CACN,IAAMC,EAAOH,EAAc,sBAAsB,EACjD,GAAIG,EAAK,QAAU,GAAKA,EAAK,SAAW,EAAG,OAC3C,IAAMC,EAAQ,KAAK,IAAID,EAAK,MAAQF,EAAkBE,EAAK,OAASD,CAAiB,EACrFJ,EAAO,MAAM,MAAQ,GAAGG,CAAgB,KACxCH,EAAO,MAAM,OAAS,GAAGI,CAAiB,KAC1CJ,EAAO,MAAM,UAAY,+BAA+BM,CAAK,GAC/D,CC9CO,IAAMC,EAAN,KAA0B,CAI/B,YAA6BC,EAA4B,CAA5B,gBAAAA,CAA6B,CAA7B,WAHrB,KAAsB,KACtB,cAAgB,EAIxB,MACEC,EACAC,EACAC,EACAC,EACM,CACN,KAAK,KAAK,EAEV,IAAMC,EAAO,IAAM,CACjB,GAAID,EAAS,EAAG,CACd,KAAK,KAAO,KACZ,MACF,CAEA,IAAIE,EACJ,GAAI,CACFA,EAAcL,EAAS,KAAK,CAC9B,MAAQ,CACN,KAAK,KAAO,KACZ,MACF,CAEA,IAAMM,EAAWJ,EAAY,EACzBI,EAAW,IAAGD,EAAc,KAAK,IAAIA,EAAaC,CAAQ,GAE9D,IAAMC,EAAoBD,EAAW,GAAKD,GAAeC,EACnDE,EAAM,YAAY,IAAI,EAO5B,IALIA,EAAM,KAAK,cAAgB,KAAyBD,KACtD,KAAK,cAAgBC,EACrB,KAAK,WAAW,aAAaH,EAAaC,CAAQ,GAGhDC,EAAmB,CACrB,GAAI,KAAK,WAAW,QAAQ,EAAG,CAC7B,KAAK,WAAW,QAAQ,EACxB,MACF,CACA,GAAI,CACFP,EAAS,MAAM,CACjB,MAAQ,CAER,CACA,KAAK,WAAW,SAAS,EACzB,KAAK,KAAO,KACZ,MACF,CAEA,KAAK,KAAO,sBAAsBI,CAAI,CACxC,EAEA,KAAK,KAAO,sBAAsBA,CAAI,CACxC,CAEA,MAAa,CACP,KAAK,OAAS,OAClB,qBAAqB,KAAK,IAAI,EAC9B,KAAK,KAAO,KACd,CAEA,IAAI,WAAqB,CACvB,OAAO,KAAK,OAAS,IACvB,CACF,ECxDO,SAASK,GAA2BC,EAA0B,CACnE,IAAMC,EAAM,MAAM,KAAKD,EAAI,iBAA0B,uBAAuB,CAAC,EAC7E,GAAIC,EAAI,SAAW,EACjB,OAAOD,EAAI,KAAO,CAACA,EAAI,IAAI,EAAI,CAAC,EAGlC,IAAME,EAAsB,CAAC,EAC7B,QAAWC,KAAMF,EACVG,GAAuBD,CAAE,GAC5BD,EAAS,KAAKC,CAAE,EAIpB,OAAAE,GAAyBL,CAAG,EACrBE,CACT,CAYA,SAASG,GAAyBL,EAAqB,CACrD,IAAMM,EAAON,EAAI,KAEjB,GADI,CAACM,GACD,OAAO,QAAY,KAAe,OAAO,QAAQ,MAAS,WAAY,OAE1E,IAAMC,EAAaD,EAAK,iBACtB,sCACF,EACA,GAAIC,EAAW,SAAW,EAAG,OAE7B,IAAMC,EAA8B,CAAC,EACrC,QAAWL,KAAMI,EACVJ,EAAG,QAAQ,uBAAuB,GAAGK,EAAQ,KAAKL,CAAE,EAEvDK,EAAQ,SAAW,GAEvB,QAAQ,KACN,uFACSA,EAAQ,MAAM,oMAGvBA,CACF,CACF,CAEA,SAASJ,GAAuBD,EAAsB,CACpD,IAAIM,EAASN,EAAG,cAChB,KAAOM,GAAQ,CACb,GAAIA,EAAO,aAAa,qBAAqB,EAAG,MAAO,GACvDA,EAASA,EAAO,aAClB,CACA,MAAO,EACT,CCpFA,IAAMC,GAAiC,IAUjCC,GAA4C,EAgBrCC,EAAN,KAAyB,CACtB,SAAyB,CAAC,EAC1B,eACA,qBAAuB,GACvB,YAAoC,UAE3B,eACA,UACA,WACA,iBACA,gBACA,UAEjB,YAAYC,EAOT,CACD,KAAK,eAAiBA,EAAK,cAC3B,KAAK,UAAYA,EAAK,SACtB,KAAK,WAAaA,EAAK,UACvB,KAAK,iBAAmBA,EAAK,gBAC7B,KAAK,gBAAkBA,EAAK,eAC5B,KAAK,UAAYA,EAAK,QACxB,CAEA,IAAI,YAAmC,CACrC,OAAO,KAAK,WACd,CAGA,IAAI,SAAwB,CAC1B,OAAO,KAAK,QACd,CAEA,IAAI,qBAA+B,CACjC,OAAO,KAAK,oBACd,CAEA,oBAA2B,CACzB,KAAK,qBAAuB,GAC5B,IAAMC,EAAc,KAAK,cAAgB,SACzC,KAAK,YAAc,UACnB,KAAK,SAAS,EACd,KAAK,iBAAiB,EAClBA,GACF,KAAK,eACH,IAAI,YAAY,uBAAwB,CACtC,OAAQ,CAAE,MAAO,UAAW,OAAQ,eAAgB,CACtD,CAAC,CACH,CAEJ,CAEA,SAAgB,CACd,KAAK,iBAAiB,EACtB,QAAWC,KAAK,KAAK,SACnBA,EAAE,GAAG,MAAM,EACXA,EAAE,GAAG,IAAM,GAEb,KAAK,SAAW,CAAC,CACnB,CAEA,YAAYC,EAAsB,CAChC,QAAWD,KAAK,KAAK,SAAUA,EAAE,GAAG,MAAQC,CAC9C,CAEA,aAAaC,EAAsB,CACjC,QAAWF,KAAK,KAAK,SAAUA,EAAE,GAAG,OAASE,CAC/C,CAEA,mBAAmBC,EAAoB,CACrC,QAAWH,KAAK,KAAK,SAAUA,EAAE,GAAG,aAAeG,CACrD,CAEA,SAAgB,CACd,QAAWH,KAAK,KAAK,SACdA,EAAE,GAAG,KACVA,EAAE,GAAG,KAAK,EAAE,MAAOI,GAAiB,KAAK,qBAAqBA,CAAG,CAAC,CAEtE,CAEA,UAAiB,CACf,QAAWJ,KAAK,KAAK,SAAUA,EAAE,GAAG,MAAM,CAC5C,CAEA,QAAQK,EAA6B,CACnC,QAAWL,KAAK,KAAK,SAAU,CAC7B,IAAMM,EAAUD,EAAgBL,EAAE,MAC9BM,GAAW,GAAKA,EAAUN,EAAE,WAAUA,EAAE,GAAG,YAAcM,EAC/D,CACF,CAQA,WAAWC,EAAyBC,EAAqC,CACvE,IAAMC,EAAQD,GAAS,QAAU,GACjC,QAAWR,KAAK,KAAK,SAAU,CAC7B,IAAMM,EAAUC,EAAkBP,EAAE,MACpC,GAAIM,EAAU,GAAKA,GAAWN,EAAE,SAAU,CACxCA,EAAE,aAAe,EACjB,QACF,CACI,KAAK,IAAIA,EAAE,GAAG,YAAcM,CAAO,EAAIX,IACzCK,EAAE,cAAgB,GACdS,GAAST,EAAE,cAAgBJ,MAC7BI,EAAE,GAAG,YAAcM,EACnBN,EAAE,aAAe,IAGnBA,EAAE,aAAe,CAErB,CACF,CAeA,qBACEU,EACAC,EACM,CACN,GAAI,KAAK,cAAgB,SAAU,OAInC,GAHA,KAAK,YAAc,SAGfD,EACF,QAAWE,KAAMF,EAAU,iBAAmC,cAAc,EAC1EE,EAAG,MAAQ,GAKf,IAAMC,EAAI,KAAK,gBAAgB,EAC3BF,EAAUA,EAASE,EAAG,CAAE,MAAO,EAAK,CAAC,EACpC,KAAK,WAAWA,EAAG,CAAE,MAAO,EAAK,CAAC,EAClC,KAAK,UAAU,GAAG,KAAK,QAAQ,EAEpC,KAAK,eACH,IAAI,YAAY,uBAAwB,CACtC,OAAQ,CAAE,MAAO,SAAU,OAAQ,kBAAmB,CACxD,CAAC,CACH,CACF,CAMA,gBAAgBH,EAA2B,CACzC,IAAMI,EAAWJ,EAAU,iBACzB,sCACF,EACA,QAAWK,KAAYD,EAAU,KAAK,kBAAkBC,CAAQ,EAChE,KAAK,qBAAqBL,CAAS,CACrC,CAGA,aAAaM,EAAwB,CACnC,KAAK,aAAaA,EAAU,QAAS,EAAG,GAAQ,CAClD,CAEA,kBAAyB,CACvB,KAAK,gBAAgB,WAAW,EAChC,KAAK,eAAiB,MACxB,CAIQ,qBAAqBZ,EAAoB,CAC3C,KAAK,uBACT,KAAK,qBAAuB,GAC5B,KAAK,eACH,IAAI,YAAY,gBAAiB,CAAE,OAAQ,CAAE,OAAQ,eAAgB,MAAOA,CAAI,CAAE,CAAC,CACrF,EACF,CAMQ,aACNa,EACAC,EACAC,EACAC,EACmB,CACnB,GAAI,KAAK,SAAS,KAAMpB,GAAMA,EAAE,GAAG,MAAQiB,CAAG,EAAG,OAAO,KAExD,IAAML,EAAKM,IAAQ,QAAU,SAAS,cAAc,OAAO,EAAI,IAAI,MACnEN,EAAG,QAAU,OACbA,EAAG,IAAMK,EACTL,EAAG,KAAK,EACRA,EAAG,MAAQ,KAAK,UAAU,EAC1BA,EAAG,OAAS,KAAK,WAAW,EAC5B,IAAMT,EAAO,KAAK,iBAAiB,EAC/BA,IAAS,IAAGS,EAAG,aAAeT,GAElC,IAAMkB,EAAoB,CAAE,GAAAT,EAAI,MAAAO,EAAO,SAAAC,EAAU,aAAc,CAAE,EACjE,YAAK,SAAS,KAAKC,CAAK,EACjBA,CACT,CAEQ,kBAAkBN,EAAkC,CAG1D,GAAIA,EAAS,UAAY,YAAcA,EAAS,UAAY,OAAQ,OAEpE,IAAMO,EACJP,EAAS,aAAa,KAAK,GAAKA,EAAS,cAAc,QAAQ,GAAG,aAAa,KAAK,EACtF,GAAI,CAACO,EAAQ,OAEb,IAAML,EAAM,IAAI,IAAIK,EAAQP,EAAS,cAAc,OAAO,EAAE,KACtDI,EAAQ,WAAWJ,EAAS,aAAa,YAAY,GAAK,GAAG,EAC7DK,EAAW,WAAWL,EAAS,aAAa,eAAe,GAAK,UAAU,EAC1EG,EAAMH,EAAS,UAAY,QAAW,QAAqB,QAE3DQ,EAAU,KAAK,aAAaN,EAAKC,EAAKC,EAAOC,CAAQ,EAIvDG,GAAW,KAAK,cAAgB,WAClC,KAAK,WAAW,KAAK,gBAAgB,EAAG,CAAE,MAAO,EAAK,CAAC,EACnD,CAAC,KAAK,UAAU,GAAKA,EAAQ,GAAG,KAClCA,EAAQ,GAAG,KAAK,EAAE,MAAOnB,GAAiB,KAAK,qBAAqBA,CAAG,CAAC,EAG9E,CAEQ,mBAAmBW,EAAkC,CAC3D,IAAMO,EACJP,EAAS,aAAa,KAAK,GAAKA,EAAS,cAAc,QAAQ,GAAG,aAAa,KAAK,EACtF,GAAI,CAACO,EAAQ,OACb,IAAML,EAAM,IAAI,IAAIK,EAAQP,EAAS,cAAc,OAAO,EAAE,KACtDS,EAAM,KAAK,SAAS,UAAWxB,GAAMA,EAAE,GAAG,MAAQiB,CAAG,EAC3D,GAAIO,IAAQ,GAAI,OAChB,IAAMH,EAAQ,KAAK,SAASG,CAAG,EAC/BH,EAAM,GAAG,MAAM,EACfA,EAAM,GAAG,IAAM,GACf,KAAK,SAAS,OAAOG,EAAK,CAAC,CAC7B,CAEQ,qBAAqBC,EAAqB,CAEhD,GADA,KAAK,iBAAiB,EAClB,OAAO,iBAAqB,KAAe,CAACA,EAAI,KAAM,OAE1D,IAAMC,EAAM,IAAI,iBAAkBC,GAAc,CAC9C,QAAW3B,KAAK2B,EAAW,CACzB,GAAI3B,EAAE,OAAS,cAAgBA,EAAE,gBAAkB,UAAW,CAC5D,IAAM4B,EAAS5B,EAAE,OAEf4B,aAAkB,kBAClBA,EAAO,QAAQ,sCAAsC,GACrDA,EAAO,UAAY,QAEnB,KAAK,kBAAkBA,CAAM,EAE/B,QACF,CAEA,QAAWC,KAAS7B,EAAE,WAAY,CAChC,GAAI,EAAE6B,aAAiB,SAAU,SACjC,IAAMC,EAAiC,CAAC,EACpCD,EAAM,UAAU,sCAAsC,GACxDC,EAAW,KAAKD,CAAyB,EAE3C,IAAME,EAASF,EAAM,mBACnB,sCACF,EACA,GAAIE,EAAQ,QAAWnB,KAAMmB,EAAQD,EAAW,KAAKlB,CAAE,EACvD,QAAWA,KAAMkB,EAAY,KAAK,kBAAkBlB,CAAE,CACxD,CAEA,QAAWoB,KAAWhC,EAAE,aAAc,CACpC,GAAI,EAAEgC,aAAmB,SAAU,SACnC,IAAMC,EAA8B,CAAC,EACjCD,EAAQ,UAAU,sCAAsC,GAC1DC,EAAQ,KAAKD,CAA2B,EAE1C,IAAMD,EAASC,EAAQ,mBACrB,sCACF,EACA,GAAID,EAAQ,QAAWnB,KAAMmB,EAAQE,EAAQ,KAAKrB,CAAE,EACpD,QAAWA,KAAMqB,EAAS,KAAK,mBAAmBrB,CAAE,CACtD,CACF,CACF,CAAC,EAEKsB,EAAoC,CACxC,UAAW,GACX,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,SAAS,CAC7B,EAEMC,EAAUC,GAA2BX,CAAG,EAC9C,QAAWG,KAAUO,EACnBT,EAAI,QAAQE,EAAQM,CAAW,EAEjC,KAAK,eAAiBR,CACxB,CACF,ECpUO,SAASW,GACdC,EACAC,EACAC,EACAC,EACe,CACf,IAAMC,GAAWJ,EAAK,OAAS,GAAKC,EAC9BI,EAAcH,EAAQ,SAAW,EAAI,KAAK,IAAIE,EAASF,EAAQ,QAAQ,EAAIE,EAC3EE,EAAa,CAACJ,EAAQ,OACtBK,EAAa,CAACP,EAAK,UACnBQ,EACJN,EAAQ,SAAW,GAAKG,GAAeH,EAAQ,WAAaI,GAAcN,EAAK,WAEjF,GAAIQ,GAAqBL,EAAU,QAAQ,EACzC,OAAIA,EAAU,MAAM,aAAe,UAAUA,EAAU,MAAM,SAAS,EACtEA,EAAU,KAAK,CAAC,EAChBA,EAAU,KAAK,EAGR,CAAE,GAAGD,EAAS,YAAAG,EAAa,OAAQ,EAAM,EAGlD,IAAMI,EAAsB,CAAE,GAAGP,EAAS,YAAAG,EAAa,OAAQE,CAAW,EAEtEJ,EAAU,MAAM,aAAe,WAC7BG,GAAcC,EAChBJ,EAAU,MAAM,SAAS,EAChB,CAACG,GAAc,CAACC,GACzBJ,EAAU,MAAM,QAAQ,EAE1BA,EAAU,MAAM,WAAWE,CAAW,GAGxC,IAAMK,EAAM,YAAY,IAAI,EACtBC,EAAmBJ,IAAeL,EAAQ,OAChD,OAAIQ,EAAMR,EAAQ,aAAe,KAAyBS,KACxDF,EAAK,aAAeC,EACpBP,EAAU,mBAAmBE,EAAaH,EAAQ,QAAQ,EAC1DC,EAAU,sBAAsB,CAACI,CAAU,EAC3CJ,EAAU,cAAc,IAAI,YAAY,aAAc,CAAE,OAAQ,CAAE,YAAAE,CAAY,CAAE,CAAC,CAAC,GAGhFG,IACEL,EAAU,MAAM,aAAe,UAAUA,EAAU,MAAM,SAAS,EACtEM,EAAK,OAAS,GACdN,EAAU,sBAAsB,EAAK,EACrCA,EAAU,cAAc,IAAI,MAAM,OAAO,CAAC,GAGrCM,CACT,CCrEA,IAAMG,GAAM,GAYL,SAASC,GACdC,EACAC,EACAC,EACM,CACN,GAAIF,EAAM,SAAWC,EAAa,OAClC,IAAME,EAAOH,EAAM,KACnB,GAAI,GAACG,GAAQA,EAAK,SAAc,cAEhC,IAAIA,EAAK,OAAY,0BAA2B,CAC9C,IAAMC,EACJD,EAAK,OAAY,OAAOA,EAAK,OAAa,SACrCA,EAAK,MACN,CAAC,EACPD,EAAU,aAAa,OAAOE,EAAOF,EAAU,qBAAqB,CAAC,EACrEA,EAAU,cACR,IAAI,YAAY,wBAAyB,CACvC,OAAQ,CAAE,cAAeC,EAAK,cAAkB,MAAAC,CAAM,CACxD,CAAC,CACH,EACA,MACF,CAEA,GAAID,EAAK,OAAY,QAAS,CAC5BD,EAAU,iBACRG,GACE,CAAE,MAAQF,EAAK,OAAuB,EAAG,UAAW,CAAC,CAACA,EAAK,SAAa,EACxEL,GACAI,EAAU,iBAAiB,EAC3BA,CACF,CACF,EACA,MACF,CAEA,GAAIC,EAAK,OAAY,yBAA0B,CAC7C,IAAIG,EAA6B,KACjC,GAAI,CACFA,EAAYJ,EAAU,aAAa,CACrC,MAAQ,CAER,CACAA,EAAU,MAAM,qBAAqBI,EAAW,CAACC,EAAGC,IAClDN,EAAU,MAAM,WAAWK,EAAGC,CAAI,CACpC,EACAN,EAAU,YAAY,yBAA0B,CAAE,MAAO,EAAK,CAAC,EAC/D,MACF,CAEA,GAAIC,EAAK,OAAY,YAAeA,EAAK,iBAAiC,EAAG,CAC3E,GAAI,OAAO,SAASA,EAAK,gBAAmB,EAAG,CAC7C,IAAMM,EAAKP,EAAU,iBAAiB,EAChCQ,EAAYP,EAAK,iBAAiCL,GACxDI,EAAU,iBAAiB,CAAE,GAAGO,EAAI,SAAAC,CAAS,CAAC,EAC9CR,EAAU,mBAAmBO,EAAG,YAAaC,CAAQ,CACvD,CACA,MACF,CAGEP,EAAK,OAAY,cAChBA,EAAK,MAAsB,GAC3BA,EAAK,OAAuB,GAE7BD,EAAU,mBAAmBC,EAAK,MAAoBA,EAAK,MAAmB,EAElF,CCvFO,IAAMQ,EAA4B,uBAC5BC,EAAsB,iBACtBC,GAA6B,4BAC7BC,GAAuB,sBAEvBC,EAAyB,CACpC,8BACA,iCACA,iCACA,4BACA,+BACF,EAgBO,SAASC,EAA4BC,EAAqC,CAC/E,GAAIA,IAAU,KAAM,OAAO,KAC3B,IAAMC,EAAS,OAAOD,CAAK,EAC3B,MAAI,CAAC,OAAO,SAASC,CAAM,GAAKA,GAAU,EAAU,KAC7C,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,IAAMA,CAAM,CAAC,CAAC,CACnD,CAEO,SAASC,GAA2BF,EAAyC,CAClF,GAAIA,IAAU,MAAQA,EAAM,KAAK,IAAM,GAAI,MAAO,cAClD,IAAMG,EAAaH,EAAM,KAAK,EAAE,YAAY,EAC5C,OACEG,IAAe,QACfA,IAAe,SACfA,IAAe,KACfA,IAAe,MAER,OAGPA,IAAe,UACfA,IAAe,QACfA,IAAe,KACfA,IAAe,KAER,SAEF,aACT,CAEA,SAASC,GAAcC,EAAyBC,EAAaN,EAA4B,CACnFA,IAAU,KAAMK,EAAO,OAAOC,CAAG,EAChCD,EAAO,IAAIC,EAAKN,CAAK,CAC5B,CAEO,SAASO,GACdC,EACAC,EACAC,EACQ,CACR,IAAMC,EAAYH,EAAI,QAAQ,GAAG,EAC3BI,EAAaD,GAAa,EAAIH,EAAI,MAAM,EAAGG,CAAS,EAAIH,EACxDK,EAAOF,GAAa,EAAIH,EAAI,MAAMG,CAAS,EAAI,GAC/CG,EAAaF,EAAW,QAAQ,GAAG,EACnCG,EAAOD,GAAc,EAAIF,EAAW,MAAM,EAAGE,CAAU,EAAIF,EAC3DI,EAAQF,GAAc,EAAIF,EAAW,MAAME,EAAa,CAAC,EAAI,GAC7DT,EAAS,IAAI,gBAAgBW,CAAK,EACxCZ,GAAcC,EAAQT,GAA4Ba,CAAK,EACvDL,GAAcC,EAAQR,GAAsBa,IAAgB,cAAgB,KAAOA,CAAW,EAC9F,IAAMO,EAAYZ,EAAO,SAAS,EAClC,MAAO,GAAGU,CAAI,GAAGE,EAAY,IAAIA,CAAS,GAAK,EAAE,GAAGJ,CAAI,EAC1D,CAEO,SAASK,GACdC,EACAV,EACAC,EACQ,CACR,GAAID,IAAU,MAAQC,IAAgB,cAAe,OAAOS,EAC5D,IAAMC,EAAkB,CAAC,EACrBX,IAAU,MAAMW,EAAM,KAAK,oCAAoC,KAAK,UAAUX,CAAK,CAAC,GAAG,EACvFC,IAAgB,eAClBU,EAAM,KAAK,8BAA8B,KAAK,UAAUV,CAAW,CAAC,GAAG,EAEzE,IAAMW,EAAS,kDAAkDD,EAAM,KAAK,EAAE,CAAC,YAC/E,MAAI,iBAAiB,KAAKD,CAAI,EACrBA,EAAK,QAAQ,iBAAmBG,GAAU,GAAGA,CAAK,GAAGD,CAAM,EAAE,EAClE,iBAAiB,KAAKF,CAAI,EACrBA,EAAK,QAAQ,iBAAmBG,GAAU,GAAGA,CAAK,GAAGD,CAAM,EAAE,EAC/D,GAAGA,CAAM,GAAGF,CAAI,EACzB,CAOO,SAASI,EAAyBC,EAAgC,CACvE,OAAOtB,GAA2BsB,EAAG,aAAa7B,CAAmB,CAAC,CACxE,CAEO,SAAS8B,GAAiCD,EAAqB,CACpE,OAAO,OAAOzB,EAA4ByB,EAAG,aAAa9B,CAAyB,CAAC,GAAK,GAAG,CAC9F,CAEO,SAASgC,EAAqBF,EAAahB,EAAqB,CACrE,OAAOD,GACLC,EACAT,EAA4ByB,EAAG,aAAa9B,CAAyB,CAAC,EACtE6B,EAAyBC,CAAE,CAC7B,CACF,CAEO,SAASG,EAAwBH,EAAaI,EAAwB,CAC3E,OAAOV,GACLU,EACA7B,EAA4ByB,EAAG,aAAa9B,CAAyB,CAAC,EACtE6B,EAAyBC,CAAE,CAC7B,CACF,CC7GO,SAASK,IAA2C,CACzD,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,oBACjBA,EAAK,aAAa,OAAQ,QAAQ,EAClCA,EAAK,aAAa,YAAa,QAAQ,EACvCA,EAAK,aAAa,aAAc,6BAA6B,EAC7DA,EAAK,aAAa,0BAA2B,EAAE,EAC/CA,EAAK,UAAY,GAEjB,IAAMC,EAA2BC,GAAiB,CAChDA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,CACxB,EACA,QAAWC,IAAa,CACtB,cACA,YACA,cACA,YACA,QACA,WACA,cACA,YACF,EACEH,EAAK,iBAAiBG,EAAWF,EAAyB,CAAE,QAAS,EAAK,CAAC,EAG7E,IAAMG,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,0BAClBA,EAAM,UAAY,GAElB,IAAMC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,yBACtBA,EAAU,UAAY,GACtBA,EAAU,UAAY,CACpB,sGACA,wQACA,0QACA,SACA,kIACA,+BACA,0CACA,oBACA,wIACA,+BACA,0CACA,oBACA,UACA,QACF,EAAE,KAAK,EAAE,EAET,IAAMC,EAAiB,SAAS,cAAc,KAAK,EACnDA,EAAe,UAAY,0BAC3B,IAAMC,EAAY,SAAS,cAAc,MAAM,EAC/CA,EAAU,UAAY,+BACtBA,EAAU,YAAcC,EAAuB,CAAC,GAAK,8BACrDF,EAAe,YAAYC,CAAS,EAEpC,IAAME,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,2BACnBA,EAAO,YAAc,2DAErB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,0BAClBA,EAAM,aAAa,cAAe,MAAM,EACxC,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,yBACjBD,EAAM,YAAYC,CAAI,EAEtB,IAAMC,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,6BACrB,IAAMC,EAAqBC,GAAsB,CAC/C,IAAMC,EAAM,SAAS,cAAc,KAAK,EACxCA,EAAI,UAAY,wBAChB,IAAMC,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,0BAClBA,EAAM,YAAcF,EACpB,IAAMG,EAAQ,SAAS,cAAc,MAAM,EAC3C,OAAAA,EAAM,UAAY,0BAClBF,EAAI,YAAYC,CAAK,EACrBD,EAAI,YAAYE,CAAK,EACrBL,EAAS,YAAYG,CAAG,EACjB,CAAE,IAAAA,EAAK,MAAAC,EAAO,MAAAC,CAAM,CAC7B,EACMC,EAAmBL,EAAkB,YAAY,EACjDM,EAAcN,EAAkB,kBAAkB,EAExD,OAAAT,EAAM,YAAYC,CAAS,EAC3BD,EAAM,YAAYE,CAAc,EAChCF,EAAM,YAAYK,CAAM,EACxBL,EAAM,YAAYM,CAAK,EACvBN,EAAM,YAAYQ,CAAQ,EAC1BZ,EAAK,YAAYI,CAAK,EAEf,CACL,KAAAJ,EACA,KAAAW,EACA,MAAOJ,EACP,OAAAE,EACA,gBAAiBS,EAAiB,MAClC,WAAYC,EAAY,MACxB,WAAYA,EAAY,MACxB,SAAUA,EAAY,GACxB,CACF,CC/GA,IAAMC,GAAqB,IAEdC,EAAN,KAAwB,CACZ,IACT,aAAqD,KAE7D,YAAYC,EAAgC,CAC1C,KAAK,IAAMA,CACb,CAEA,MAAa,CACP,KAAK,eACP,aAAa,KAAK,YAAY,EAC9B,KAAK,aAAe,MAEtB,KAAK,IAAI,KAAK,UAAU,OAAO,YAAY,EAC3C,KAAK,IAAI,KAAK,UAAU,IAAI,aAAa,CAC3C,CAEA,MAAa,CACX,GAAI,KAAK,IAAI,KAAK,UAAU,SAAS,YAAY,EAAG,CAC7C,KAAK,cAAc,KAAK,iBAAiB,EAC9C,MACF,CACK,KAAK,IAAI,KAAK,UAAU,SAAS,aAAa,IACnD,KAAK,IAAI,KAAK,UAAU,IAAI,YAAY,EACxC,KAAK,IAAI,KAAK,UAAU,OAAO,aAAa,EAC5C,KAAK,iBAAiB,EACxB,CAEA,OAAc,CACR,KAAK,eACP,aAAa,KAAK,YAAY,EAC9B,KAAK,aAAe,MAEtB,KAAK,IAAI,KAAK,UAAU,OAAO,cAAe,YAAY,EAC1D,KAAK,IAAI,KAAK,MAAM,UAAY,YAChC,KAAK,IAAI,gBAAgB,YAAc,GACvC,KAAK,IAAI,WAAW,YAAc,GAClC,KAAK,IAAI,SAAS,MAAM,WAAa,QACvC,CAEA,OAAOC,EAA+BC,EAA2B,CAC/D,GAAIA,IAAgB,SAAU,CAC5B,KAAK,MAAM,EACX,MACF,CACA,GAAID,EAAO,OAAS,CAACA,EAAO,QAAS,CACnC,KAAK,KAAK,EACV,MACF,CAEA,IAAME,EACJ,OAAOF,EAAO,UAAa,UAAY,OAAO,SAASA,EAAO,QAAQ,EAAIA,EAAO,SAAW,EACxFG,EACJ,OAAOH,EAAO,OAAU,UAAY,OAAO,SAASA,EAAO,KAAK,EAAIA,EAAO,MAAQ,EAC/EI,EAAQD,EAAQ,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGD,EAAWC,CAAK,CAAC,EAAI,EAEjEE,EAAc,KAAK,IACvBC,EAAuB,OAAS,EAChC,KAAK,MAAMF,EAAQE,EAAuB,MAAM,CAClD,EACA,KAAK,IAAI,MAAM,YACbA,EAAuBD,CAAW,GAAK,8BAEzC,KAAK,IAAI,OAAO,YACdL,EAAO,QAAU,SACb,oDACAA,EAAO,QAAU,aACf,qDACA,2DAER,KAAK,IAAI,KAAK,MAAM,UAAY,UAAUI,CAAK,IAE/C,KAAK,IAAI,gBAAgB,YACvBJ,EAAO,oBAAsB,QAAaA,EAAO,kBAAoB,OACjE,GAAGA,EAAO,iBAAiB,IAAIA,EAAO,eAAe,GACrDG,EAAQ,EACN,GAAGD,CAAQ,IAAIC,CAAK,GACpB,GAER,IAAMI,EACJP,EAAO,kBAAoB,QAAaA,EAAO,mBAAqB,OAChE,GAAGA,EAAO,eAAe,IAAIA,EAAO,gBAAgB,GACpD,GAEN,KAAK,IAAI,WAAW,YAClBA,EAAO,QAAU,SACb,2BACAA,EAAO,QAAU,aACf,+BACA,8BAER,KAAK,IAAI,WAAW,YAAcO,EAClC,KAAK,IAAI,SAAS,MAAM,WAAaA,EAAa,UAAY,SAC9D,KAAK,IAAI,KAAK,aAAa,gBAAiB,OAAO,KAAK,MAAMH,EAAQ,GAAG,CAAC,CAAC,EAC3E,KAAK,KAAK,CACZ,CAEA,IAAI,aAAoD,CACtD,OAAO,KAAK,YACd,CAEA,SAAgB,CACV,KAAK,eACP,aAAa,KAAK,YAAY,EAC9B,KAAK,aAAe,KAExB,CAEQ,kBAAyB,CAC3B,KAAK,cAAc,aAAa,KAAK,YAAY,EACrD,KAAK,aAAe,WAAW,IAAM,CACnC,KAAK,IAAI,KAAK,UAAU,OAAO,YAAY,EAC3C,KAAK,aAAe,IACtB,EAAGP,EAAkB,CACvB,CACF,Ef/GA,IAAMW,EAAN,cAAgC,WAAY,CAC1C,WAAW,oBAAqB,CAC9B,MAAO,CACL,MACA,SACA,QACA,SACA,WACA,QACA,SACA,SACA,gBACA,YACAC,EACAC,CACF,CACF,CAEQ,OACA,UACA,OACA,SAAoC,KACpC,YAAuD,KACvD,eACA,aACA,MAEA,OAAS,GACT,aAAe,EACf,UAAY,EACZ,QAAU,GACV,cAAgB,EAChB,QAAU,EACV,kBAAoB,KACpB,mBAAqB,KACrB,uBAAuD,KACvD,qBACA,OAER,aAAc,CACZ,MAAM,EACN,KAAK,OAAS,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,EAEhDC,GAAkB,KAAK,OAAQC,EAAa,EAC3C,CAAE,UAAW,KAAK,UAAW,OAAQ,KAAK,MAAO,EAAIC,GAAwB,EAC9E,KAAK,OAAO,YAAY,KAAK,SAAS,EAEtC,IAAMC,EAAiBC,GAAmB,EAC1C,KAAK,OAAO,YAAYD,EAAe,IAAI,EAC3C,KAAK,aAAe,IAAIE,EAAkBF,CAAc,EAExD,KAAK,OAAS,IAAIG,EAAmB,CACnC,cAAgBC,GAAM,KAAK,cAAcA,CAAC,EAC1C,SAAU,IAAM,KAAK,MACrB,UAAW,IAAM,KAAK,QACtB,gBAAiB,IAAM,KAAK,aAC5B,eAAgB,IAAM,KAAK,aAC3B,SAAU,IAAM,KAAK,OACvB,CAAC,EAED,KAAK,qBAAuB,IAAIC,EAAoB,CAClD,aAAc,CAACC,EAAaC,IAAa,CACvC,KAAK,aAAeD,EACpB,KAAK,aAAa,WAAWA,EAAaC,CAAQ,EAClD,KAAK,cAAc,IAAI,YAAY,aAAc,CAAE,OAAQ,CAAE,YAAAD,CAAY,CAAE,CAAC,CAAC,CAC/E,EACA,QAAS,IAAM,KAAK,KACpB,QAAS,IAAM,CACb,KAAK,KAAK,CAAC,EACX,KAAK,KAAK,CACZ,EACA,SAAU,IAAM,CACV,KAAK,OAAO,aAAe,UAAU,KAAK,OAAO,SAAS,EAC9D,KAAK,QAAU,GACf,KAAK,aAAa,cAAc,EAAK,EACrC,KAAK,cAAc,IAAI,MAAM,OAAO,CAAC,CACvC,EACA,QAAS,IAAM,KAAK,IACtB,CAAC,EAED,KAAK,MAAQ,IAAIE,EAAiB,KAAK,OAAQ,CAC7C,QAAUC,GAAW,KAAK,cAAcA,CAAM,EAC9C,QAAUC,GAAY,KAAK,cAAc,IAAI,YAAY,QAAS,CAAE,OAAQ,CAAE,QAAAA,CAAQ,CAAE,CAAC,CAAC,CAC5F,CAAC,EAED,KAAK,iBAAiB,QAAUC,GAAU,CACpCC,GAAgBD,CAAK,IACrB,KAAK,QAAS,KAAK,KAAK,EACvB,KAAK,MAAM,EAClB,CAAC,EAED,KAAK,eAAiB,IAAI,eAAe,IAAM,KAAK,SAAS,CAAC,EAC9D,KAAK,WAAa,KAAK,WAAW,KAAK,IAAI,EAC3C,KAAK,cAAgB,KAAK,cAAc,KAAK,IAAI,CACnD,CAEA,mBAAoB,CAClB,KAAK,eAAe,QAAQ,IAAI,EAChC,OAAO,iBAAiB,UAAW,KAAK,UAAU,EAClD,KAAK,OAAO,iBAAiB,OAAQ,KAAK,aAAa,EACnD,KAAK,aAAa,UAAU,GAAG,KAAK,eAAe,EACnD,KAAK,aAAa,QAAQ,IAC5B,KAAK,SAAWE,EAAY,KAAK,OAAQ,KAAK,aAAa,QAAQ,EAAG,KAAK,QAAQ,GACjF,KAAK,aAAa,WAAW,GAAG,KAAK,OAAO,aAAa,KAAK,aAAa,WAAW,CAAE,EACxF,KAAK,aAAa,QAAQ,IAC5B,KAAK,OAAO,OAASC,EAAwB,KAAM,KAAK,aAAa,QAAQ,CAAE,GAC7E,KAAK,aAAa,KAAK,IACzB,KAAK,OAAO,IAAMC,EAAqB,KAAM,KAAK,aAAa,KAAK,CAAE,EAC1E,CAEA,sBAAuB,CACrB,KAAK,eAAe,WAAW,EAC/B,OAAO,oBAAoB,UAAW,KAAK,UAAU,EACrD,KAAK,OAAO,oBAAoB,OAAQ,KAAK,aAAa,EAC1D,KAAK,MAAM,KAAK,EAChB,KAAK,qBAAqB,KAAK,EAC/B,KAAK,uBAAyB,KAC9B,KAAK,aAAa,QAAQ,EAC1B,KAAK,OAAO,QAAQ,EACpB,KAAK,aAAa,QAAQ,CAC5B,CAEA,yBAAyBC,EAAcC,EAAqBC,EAAoB,CAC9E,OAAQF,EAAM,CACZ,IAAK,MACCE,IACF,KAAK,OAAS,GACd,KAAK,OAAO,IAAMH,EAAqB,KAAMG,CAAG,GAElD,MACF,IAAK,SACH,KAAK,OAAS,GACVA,IAAQ,KAAM,KAAK,OAAO,OAASJ,EAAwB,KAAMI,CAAG,EACnE,KAAK,OAAO,gBAAgB,QAAQ,EACzC,MACF,IAAK,QACH,KAAK,kBAAoB,SAASA,GAAO,OAAQ,EAAE,EACnD,KAAK,SAAS,EACd,MACF,IAAK,SACH,KAAK,mBAAqB,SAASA,GAAO,OAAQ,EAAE,EACpD,KAAK,SAAS,EACd,MACF,IAAK,WACCA,IAAQ,KAAM,KAAK,eAAe,GAEpC,KAAK,aAAa,QAAQ,EAC1B,KAAK,YAAc,MAErB,MACF,IAAK,SACH,KAAK,SAAWL,EAAY,KAAK,OAAQK,EAAK,KAAK,QAAQ,EAC3D,MACF,IAAK,gBAAiB,CACpB,IAAMC,EAAO,WAAWD,GAAO,GAAG,EAClC,KAAK,OAAO,mBAAmBC,CAAI,EACnC,KAAK,aAAa,oBAAqB,CAAE,aAAcA,CAAK,CAAC,EAC7D,KAAK,aAAa,YAAYA,CAAI,EAClC,KAAK,cAAc,IAAI,MAAM,YAAY,CAAC,EAC1C,KACF,CACA,IAAK,QACH,KAAK,OAAO,YAAYD,IAAQ,IAAI,EACpC,KAAK,aAAa,YAAa,CAAE,MAAOA,IAAQ,IAAK,CAAC,EACtD,KAAK,aAAa,YAAYA,IAAQ,IAAI,EAC1C,KAAK,cAAc,IAAI,MAAM,cAAc,CAAC,EAC5C,MACF,IAAK,SAAU,CACb,IAAME,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,WAAWF,GAAO,GAAG,CAAC,CAAC,EACzD,KAAK,QAAUE,EACf,KAAK,OAAO,aAAaA,CAAC,EAC1B,KAAK,aAAa,aAAc,CAAE,OAAQA,CAAE,CAAC,EAC7C,KAAK,aAAa,aAAaA,CAAC,EAChC,KAAK,cAAc,IAAI,MAAM,cAAc,CAAC,EAC5C,KACF,CACA,IAAK,YACCF,GAAK,KAAK,OAAO,aAAaA,CAAG,EACrC,MACF,KAAKvB,EACL,KAAKC,EACH,KAAK,qBAAqB,EAC1B,KACJ,CACF,CAOA,IAAI,eAAmC,CACrC,OAAO,KAAK,MACd,CAEA,MAAO,CACL,KAAK,UAAU,OAAO,EACtB,KAAK,SAAW,KACZ,KAAK,UAAY,GAAK,KAAK,cAAgB,KAAK,WAAW,KAAK,KAAK,CAAC,EAC1E,IAAMyB,EAAwB,KAAK,uBAAuB,EACrDA,GAAuB,KAAK,aAAa,MAAM,EAChD,KAAK,OAAO,aAAe,UAAU,KAAK,OAAO,QAAQ,EAC7D,KAAK,QAAU,GACf,KAAK,aAAa,cAAc,EAAI,EACpC,KAAK,cAAc,IAAI,MAAM,MAAM,CAAC,EAChCA,GAAyB,KAAK,wBAChC,KAAK,qBAAqB,MACxB,KAAK,uBACL,IAAM,KAAK,aACX,IAAM,KAAK,UACX,IAAM,KAAK,OACb,CAEJ,CAEA,OAAQ,CACD,KAAK,wBAAwB,GAAG,KAAK,aAAa,OAAO,EAC9D,KAAK,qBAAqB,KAAK,EAC3B,KAAK,OAAO,aAAe,UAAU,KAAK,OAAO,SAAS,EAC9D,KAAK,QAAU,GACf,KAAK,aAAa,cAAc,EAAK,EACrC,KAAK,cAAc,IAAI,MAAM,OAAO,CAAC,CACvC,CAEA,KAAKC,EAAuB,CACtB,CAAC,KAAK,aAAaA,CAAa,GAAK,CAAC,KAAK,uBAAuBA,CAAa,GACjF,KAAK,aAAa,OAAQ,CAAE,MAAO,KAAK,MAAMA,EAAgB,EAAE,CAAE,CAAC,EAErE,KAAK,qBAAqB,KAAK,EAC/B,KAAK,aAAeA,EAChB,KAAK,OAAO,aAAe,UAAU,KAAK,OAAO,QAAQA,CAAa,EAC1E,KAAK,QAAU,GACf,KAAK,aAAa,cAAc,EAAK,EACrC,KAAK,aAAa,WAAW,KAAK,aAAc,KAAK,SAAS,CAChE,CAEA,IAAI,aAAc,CAChB,OAAO,KAAK,YACd,CACA,IAAI,YAAYC,EAAW,CACzB,KAAK,KAAKA,CAAC,CACb,CAEA,IAAI,UAAW,CACb,OAAO,KAAK,SACd,CACA,IAAI,QAAS,CACX,OAAO,KAAK,OACd,CACA,IAAI,OAAQ,CACV,OAAO,KAAK,MACd,CAEA,IAAI,cAAe,CACjB,OAAO,WAAW,KAAK,aAAa,eAAe,GAAK,GAAG,CAC7D,CACA,IAAI,aAAaC,EAAW,CAC1B,KAAK,aAAa,gBAAiB,OAAOA,CAAC,CAAC,CAC9C,CAEA,IAAI,oBAAqB,CACvB,OAAOC,GAAiC,IAAI,CAC9C,CACA,IAAI,mBAAmBC,EAAe,CACpC,KAAK,aAAa/B,EAA2B,OAAO+B,CAAK,CAAC,CAC5D,CAEA,IAAI,eAAgB,CAClB,OAAOC,EAAyB,IAAI,CACtC,CACA,IAAI,cAAcC,EAAyB,CACrCA,IAAS,cAAe,KAAK,gBAAgBhC,CAAmB,EAC/D,KAAK,aAAaA,EAAqBgC,CAAI,CAClD,CAEA,IAAI,OAAQ,CACV,OAAO,KAAK,aAAa,OAAO,CAClC,CACA,IAAI,MAAMC,EAAY,CAChBA,EAAG,KAAK,aAAa,QAAS,EAAE,EAC/B,KAAK,gBAAgB,OAAO,CACnC,CAEA,IAAI,QAAS,CACX,OAAO,KAAK,OACd,CACA,IAAI,OAAOT,EAAW,CACpB,KAAK,aAAa,SAAU,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGA,CAAC,CAAC,CAAC,CAAC,CACjE,CAEA,IAAI,MAAO,CACT,OAAO,KAAK,aAAa,MAAM,CACjC,CACA,IAAI,KAAKU,EAAY,CACfA,EAAG,KAAK,aAAa,OAAQ,EAAE,EAC9B,KAAK,gBAAgB,MAAM,CAClC,CAEQ,aAAaC,EAAgBC,EAAiC,CAAC,EAAG,CACxE,GAAI,CACF,KAAK,OAAO,eAAe,YACzB,CAAE,OAAQ,YAAa,KAAM,UAAW,OAAAD,EAAQ,GAAGC,CAAM,EACzD,GACF,CACF,MAAQ,CAER,CACF,CAEQ,sBAA6B,CAEnC,GADIL,EAAyB,IAAI,IAAM,UAAU,KAAK,aAAa,MAAM,EACrE,KAAK,aAAa,QAAQ,EAAG,CAC/B,KAAK,OAAO,OAASb,EAAwB,KAAM,KAAK,aAAa,QAAQ,GAAK,EAAE,EACpF,MACF,CACI,KAAK,aAAa,KAAK,IACzB,KAAK,OAAO,IAAMC,EAAqB,KAAM,KAAK,aAAa,KAAK,GAAK,EAAE,EAE/E,CAEQ,aAAaO,EAAgC,CACnD,GAAI,CAIF,IAAMW,EAHM,KAAK,OAAO,eAGJ,SACpB,OAAI,OAAOA,GAAQ,MAAS,WAAmB,IAC/CA,EAAO,KAAK,KAAKA,EAAQX,CAAa,EAC/B,GACT,MAAQ,CACN,MAAO,EACT,CACF,CAEQ,oBAAoBY,EAAkD,CAC5E,IAAMC,EAAK,KAAK,wBAA0B,KAAK,MAAM,6BAA6B,EAClF,GAAI,CAACA,EAAI,MAAO,GAChB,GAAI,CACF,OAAAD,EAAGC,CAAE,EACL,KAAK,uBAAyBA,EACvB,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAGQ,uBAAuBZ,EAAoB,CACjD,OAAO,KAAK,oBAAqBY,GAAO,CACtCA,EAAG,KAAKZ,CAAC,EACTY,EAAG,MAAM,CACX,CAAC,CACH,CACQ,wBAAkC,CACxC,OAAO,KAAK,oBAAqBA,GAAI,CAAQA,EAAG,KAAK,EAAC,CACxD,CACQ,yBAAmC,CACzC,OAAO,KAAK,oBAAqBA,GAAI,CAAQA,EAAG,MAAM,EAAC,CACzD,CAEQ,WAAW,EAAiB,CAClCC,GAAqB,EAAG,KAAK,OAAO,cAAe,CACjD,iBAAkB,KAAO,CACvB,YAAa,KAAK,aAClB,SAAU,KAAK,UACf,OAAQ,KAAK,QACb,aAAc,KAAK,aACrB,GACA,iBAAkB,CAAC,CAAE,YAAA9B,EAAa,SAAAC,EAAU,OAAA8B,EAAQ,aAAAC,CAAa,IAAM,CACrE,KAAK,aAAehC,EACpB,KAAK,UAAYC,EACjB,KAAK,QAAU8B,EACf,KAAK,cAAgBC,CACvB,EACA,qBAAsB,IAAMX,EAAyB,IAAI,EACzD,aAAc,KAAK,aACnB,mBAAoB,CAACY,EAAGC,IAAM,CAC5B,KAAK,kBAAoBD,EACzB,KAAK,mBAAqBC,EAC1B,KAAK,SAAS,CAChB,EACA,YAAa,CAACT,EAAQC,IAAU,KAAK,aAAaD,EAAQC,CAAK,EAC/D,aAAc,IAAM,KAAK,OAAO,gBAChC,mBAAoB,CAAC,EAAGS,IAAM,KAAK,aAAa,WAAW,EAAGA,CAAC,EAC/D,sBAAwBC,GAAM,KAAK,aAAa,cAAcA,CAAC,EAC/D,cAAgBC,GAAO,KAAK,cAAcA,CAAE,EAC5C,KAAO,GAAM,KAAK,KAAK,CAAC,EACxB,KAAM,IAAM,KAAK,KAAK,EACtB,QAAS,IAAM,KAAK,KACpB,MAAO,KAAK,MACd,CAAC,CACH,CAEQ,cAAc,CAAE,SAAApC,EAAU,QAAAqC,EAAS,gBAAAC,CAAgB,EAAgB,CACzE,KAAK,UAAYtC,EACjB,KAAK,uBAAyBqC,EAAQ,OAAS,kBAAoBA,EAAQ,SAAW,KACtF,KAAK,OAAS,GACd,KAAK,aAAa,WAAW,EAAGrC,CAAQ,EACxC,KAAK,cAAc,IAAI,YAAY,QAAS,CAAE,OAAQ,CAAE,SAAAA,CAAS,CAAE,CAAC,CAAC,EACjEsC,IACF,KAAK,kBAAoBA,EAAgB,MACzC,KAAK,mBAAqBA,EAAgB,OAC1C,KAAK,SAAS,GAEhB,GAAI,CACF,IAAMC,EAAM,KAAK,OAAO,gBACpBA,GAAK,KAAK,OAAO,gBAAgBA,CAAG,CAC1C,MAAQ,CAER,CACI,KAAK,aAAa,UAAU,GAAG,KAAK,KAAK,CAC/C,CAEQ,UAAW,CACjBC,GAAiB,KAAM,KAAK,OAAQ,KAAK,kBAAmB,KAAK,kBAAkB,CACrF,CAEQ,eAAgB,CACtB,KAAK,uBAAyB,KAC9B,KAAK,qBAAqB,KAAK,EAC/B,KAAK,aAAa,MAAM,EACxB,KAAK,OAAO,mBAAmB,EAC/B,KAAK,MAAM,MAAM,CACnB,CAEQ,gBAAiB,CACnB,KAAK,cACT,KAAK,YAAcC,GACjB,KAAK,OACL,KAAK,MACL,KAAK,QACL,KAAK,aAAa,eAAe,EACjC,CACE,OAAQ,IAAM,KAAK,KAAK,EACxB,QAAS,IAAM,KAAK,MAAM,EAC1B,OAASC,GAAM,KAAK,KAAKA,EAAI,KAAK,SAAS,EAC3C,cAAgBC,GAAG,CAAS,KAAK,aAAeA,GAChD,aAAc,IAAG,CAAS,KAAK,MAAQ,CAAC,KAAK,OAC7C,eAAiB9B,GAAG,CAAS,KAAK,OAASA,EAC7C,CACF,EACF,CAGA,IAAI,aAAc,CAChB,OAAO,KAAK,OAAO,UACrB,CACA,IAAI,cAAe,CACjB,OAAO,KAAK,OAAO,OACrB,CACA,uBAAuBG,EAAW4B,EAA4B,CAC5D,KAAK,OAAO,WAAW5B,EAAG4B,CAAI,CAChC,CACA,uBAAwB,CACtB,IAAIV,EAAqB,KACzB,GAAI,CACFA,EAAI,KAAK,OAAO,eAClB,MAAQ,CAER,CACA,KAAK,OAAO,qBAAqBA,EAAG,CAAC,EAAGW,IAAM,KAAK,uBAAuB,EAAGA,CAAC,CAAC,EAC/E,KAAK,aAAa,yBAA0B,CAAE,MAAO,EAAK,CAAC,CAC7D,CACA,qBAAqBN,EAAe,CAClC,KAAK,OAAO,gBAAgBA,CAAG,CACjC,CACF,EAEK,eAAe,IAAI,oBAAoB,GAC1C,eAAe,OAAO,qBAAsBpD,CAAiB","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","__toCommonJS","shouldInjectRuntime","state","isObjectRecord","value","isRuntimeDurationAdapter","isDirectTimelineAdapter","RUNTIME_CDN_URL","CompositionProbe","_iframe","_callbacks","attempts","win","hasRuntime","hasTimelines","hasNestedCompositions","shouldInjectRuntime","adapter","doc","compositionSize","root","w","h","isObjectRecord","script","timelines","keys","rootId","key","timeline","isDirectTimelineAdapter","runtimePlayer","isRuntimeDurationAdapter","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","setupControls","parent","muted","volume","speedPresetsAttr","callbacks","speedPresets","n","api","createControls","setupPoster","posterUrl","existing","isControlsClick","event","t","_sharedSheet","adoptShadowStyles","shadow","cssText","style","createCompositionIframe","container","iframe","scaleIframeToFit","playerElement","compositionWidth","compositionHeight","rect","scale","DirectTimelineClock","_callbacks","timeline","getCurrentTime","getDuration","isPaused","tick","currentTime","duration","completedPlayback","now","selectMediaObserverTargets","doc","all","topLevel","el","hasCompositionAncestor","warnOnUnscopedTimedMedia","body","candidates","orphans","cursor","MIRROR_DRIFT_THRESHOLD_SECONDS","MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES","ParentMediaManager","opts","wasPromoted","m","muted","volume","rate","err","timeInSeconds","relTime","timelineSeconds","options","force","iframeDoc","onMirror","el","t","mediaEls","iframeEl","audioSrc","src","tag","start","duration","entry","rawSrc","created","idx","doc","obs","mutations","target","added","candidates","inside","removed","dropped","observeOpts","targets","selectMediaObserverTargets","applyRuntimeStateMessage","data","fps","current","callbacks","rawTime","currentTime","wasPlaying","nextPaused","completedPlayback","next","now","playStateChanged","FPS","handleRuntimeMessage","event","frameWindow","callbacks","data","state","applyRuntimeStateMessage","iframeDoc","t","opts","pb","duration","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","injectShaderOptionsIntoSrcdoc","html","lines","script","match","getShaderModeFromElement","el","getShaderCaptureScaleFromElement","prepareSrcForElement","prepareSrcdocForElement","srcdoc","createShaderLoader","root","blockOverlayInteraction","event","eventName","panel","markFrame","titleContainer","titleText","SHADER_LOADING_PHRASES","detail","track","fill","progress","createProgressRow","labelText","row","label","value","transitionStatus","frameStatus","HIDE_TRANSITION_MS","ShaderLoaderState","elements","status","loadingMode","progress","total","ratio","phraseIndex","SHADER_LOADING_PHRASES","frameValue","HyperframesPlayer","SHADER_CAPTURE_SCALE_ATTR","SHADER_LOADING_ATTR","adoptShadowStyles","PLAYER_STYLES","createCompositionIframe","loaderElements","createShaderLoader","ShaderLoaderState","ParentMediaManager","e","DirectTimelineClock","currentTime","duration","CompositionProbe","result","message","event","isControlsClick","setupPoster","prepareSrcdocForElement","prepareSrcForElement","name","_old","val","rate","v","directTimelineStarted","timeInSeconds","t","r","getShaderCaptureScaleFromElement","scale","getShaderModeFromElement","mode","m","l","action","extra","player","fn","tl","handleRuntimeMessage","paused","lastUpdateMs","w","h","d","p","ev","adapter","compositionSize","doc","scaleIframeToFit","setupControls","f","s","opts","o"]}
1
+ {"version":3,"sources":["../src/hyperframes-player.ts","../src/shouldInjectRuntime.ts","../src/timeline-adapters.ts","../src/composition-probe.ts","../src/styles.ts","../src/controls.ts","../src/controls-setup.ts","../src/iframe-dom.ts","../src/direct-timeline-clock.ts","../src/mediaObserverScope.ts","../src/parent-media.ts","../src/playback-state.ts","../src/runtime-message-handler.ts","../src/shader-options.ts","../src/shader-loader-element.ts","../src/shader-loader-state.ts"],"sourcesContent":["import { CompositionProbe, type ProbeResult } from \"./composition-probe.js\";\nimport { isControlsClick, setupControls, setupPoster } from \"./controls-setup.js\";\nimport { adoptShadowStyles, createCompositionIframe, scaleIframeToFit } from \"./iframe-dom.js\";\nimport { DirectTimelineClock } from \"./direct-timeline-clock.js\";\nimport { ParentMediaManager } from \"./parent-media.js\";\nimport { handleRuntimeMessage } from \"./runtime-message-handler.js\";\nimport {\n SHADER_CAPTURE_SCALE_ATTR,\n SHADER_LOADING_ATTR,\n type ShaderLoadingMode,\n getShaderCaptureScaleFromElement,\n getShaderModeFromElement,\n prepareSrcForElement,\n prepareSrcdocForElement,\n} from \"./shader-options.js\";\nimport { createShaderLoader } from \"./shader-loader-element.js\";\nimport { ShaderLoaderState } from \"./shader-loader-state.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\nimport { type DirectTimelineAdapter } from \"./timeline-adapters.js\";\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 setupControls> | null = null;\n private resizeObserver: ResizeObserver;\n private shaderLoader: ShaderLoaderState;\n private probe: CompositionProbe;\n\n private _ready = false;\n private _currentTime = 0;\n private _duration = 0;\n private _paused = true;\n private _lastUpdateMs = 0;\n private _volume = 1;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _directTimelineAdapter: DirectTimelineAdapter | null = null;\n private _directTimelineClock: DirectTimelineClock;\n private _parentTickRaf: number | null = null;\n private _media: ParentMediaManager;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n adoptShadowStyles(this.shadow, PLAYER_STYLES);\n ({ container: this.container, iframe: this.iframe } = createCompositionIframe());\n this.shadow.appendChild(this.container);\n\n const loaderElements = createShaderLoader();\n this.shadow.appendChild(loaderElements.root);\n this.shaderLoader = new ShaderLoaderState(loaderElements);\n\n this._media = new ParentMediaManager({\n dispatchEvent: (e) => this.dispatchEvent(e),\n getMuted: () => this.muted,\n getVolume: () => this._volume,\n getPlaybackRate: () => this.playbackRate,\n getCurrentTime: () => this._currentTime,\n isPaused: () => this._paused,\n });\n\n this._directTimelineClock = new DirectTimelineClock({\n onTimeUpdate: (currentTime, duration) => {\n this._currentTime = currentTime;\n this.controlsApi?.updateTime(currentTime, duration);\n this.dispatchEvent(new CustomEvent(\"timeupdate\", { detail: { currentTime } }));\n },\n getLoop: () => this.loop,\n restart: () => {\n this.seek(0);\n this.play();\n },\n onPaused: () => {\n if (this._media.audioOwner === \"parent\") this._media.pauseAll();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n },\n onEnded: () => this.loop,\n });\n\n this.probe = new CompositionProbe(this.iframe, {\n onReady: (result) => this._onProbeReady(result),\n onError: (message) => this.dispatchEvent(new CustomEvent(\"error\", { detail: { message } })),\n });\n\n this.addEventListener(\"click\", (event) => {\n if (isControlsClick(event)) return;\n if (this._paused) this.play();\n else this.pause();\n });\n\n this.resizeObserver = new ResizeObserver(() => this._rescale());\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 if (this.hasAttribute(\"controls\")) this._setupControls();\n if (this.hasAttribute(\"poster\"))\n this.posterEl = setupPoster(this.shadow, this.getAttribute(\"poster\"), this.posterEl);\n if (this.hasAttribute(\"audio-src\")) this._media.setupFromUrl(this.getAttribute(\"audio-src\")!);\n if (this.hasAttribute(\"srcdoc\"))\n this.iframe.srcdoc = prepareSrcdocForElement(this, this.getAttribute(\"srcdoc\")!);\n if (this.hasAttribute(\"src\"))\n this.iframe.src = prepareSrcForElement(this, 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 this.probe.stop();\n this._directTimelineClock.stop();\n this._stopParentTickClock();\n this._directTimelineAdapter = null;\n this.shaderLoader.destroy();\n this._media.destroy();\n this.controlsApi?.destroy();\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 = prepareSrcForElement(this, val);\n }\n break;\n case \"srcdoc\":\n this._ready = false;\n if (val !== null) this.iframe.srcdoc = prepareSrcdocForElement(this, val);\n else this.iframe.removeAttribute(\"srcdoc\");\n break;\n case \"width\":\n this._compositionWidth = parseInt(val || \"1920\", 10);\n this._rescale();\n break;\n case \"height\":\n this._compositionHeight = parseInt(val || \"1080\", 10);\n this._rescale();\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.posterEl = setupPoster(this.shadow, val, this.posterEl);\n break;\n case \"playback-rate\": {\n const rate = parseFloat(val || \"1\");\n this._media.updatePlaybackRate(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 this._media.updateMuted(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 this._media.updateVolume(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._media.setupFromUrl(val);\n break;\n case SHADER_CAPTURE_SCALE_ATTR:\n case SHADER_LOADING_ATTR:\n this._reloadShaderOptions();\n break;\n }\n }\n\n /**\n * The inner `<iframe>` rendering the composition. Use this when integrating\n * with tools that need `contentWindow` — `.contentWindow` on the\n * `<hyperframes-player>` element itself returns `null` (Shadow DOM).\n */\n get iframeElement(): HTMLIFrameElement {\n return this.iframe;\n }\n\n play() {\n this.posterEl?.remove();\n this.posterEl = null;\n if (this._duration > 0 && this._currentTime >= this._duration) this.seek(0);\n // Must be set before _startParentTickClock so the RAF loop's `_paused`\n // check doesn't immediately self-terminate on the first callback.\n this._paused = false;\n const directTimelineStarted = this._tryDirectTimelinePlay();\n if (!directTimelineStarted) {\n this._sendControl(\"play\");\n // Only start the parent tick clock once the composition is ready and\n // confirmed on the runtime bridge path (not the direct-timeline path).\n // Guards against firing ticks into an uninitialized iframe when play()\n // is called before the probe has resolved.\n if (this._ready && !this._directTimelineAdapter) {\n this._startParentTickClock();\n }\n }\n if (this._media.audioOwner === \"parent\") this._media.playAll();\n this.controlsApi?.updatePlaying(true);\n this.dispatchEvent(new Event(\"play\"));\n if (directTimelineStarted && this._directTimelineAdapter) {\n this._directTimelineClock.start(\n this._directTimelineAdapter,\n () => this._currentTime,\n () => this._duration,\n () => this._paused,\n );\n }\n }\n\n pause() {\n if (!this._tryDirectTimelinePause()) this._sendControl(\"pause\");\n this._directTimelineClock.stop();\n this._stopParentTickClock();\n if (this._media.audioOwner === \"parent\") this._media.pauseAll();\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"pause\"));\n }\n\n seek(timeInSeconds: number) {\n if (!this._trySyncSeek(timeInSeconds) && !this._tryDirectTimelineSeek(timeInSeconds)) {\n this._sendControl(\"seek\", { frame: Math.round(timeInSeconds * 30) });\n }\n this._directTimelineClock.stop();\n this._stopParentTickClock();\n this._currentTime = timeInSeconds;\n if (this._media.audioOwner === \"parent\") this._media.seekAll(timeInSeconds);\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 getShaderCaptureScaleFromElement(this);\n }\n set shaderCaptureScale(scale: number) {\n this.setAttribute(SHADER_CAPTURE_SCALE_ATTR, String(scale));\n }\n\n get shaderLoading() {\n return getShaderModeFromElement(this);\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 _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 _reloadShaderOptions(): void {\n if (getShaderModeFromElement(this) !== \"player\") this.shaderLoader.reset();\n if (this.hasAttribute(\"srcdoc\")) {\n this.iframe.srcdoc = prepareSrcdocForElement(this, this.getAttribute(\"srcdoc\") || \"\");\n return;\n }\n if (this.hasAttribute(\"src\")) {\n this.iframe.src = prepareSrcForElement(this, this.getAttribute(\"src\") || \"\");\n }\n }\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 if (typeof player?.seek !== \"function\") return false;\n player.seek.call(player, timeInSeconds);\n return true;\n } catch {\n return false;\n }\n }\n\n private _withDirectTimeline(fn: (tl: DirectTimelineAdapter) => void): boolean {\n const tl = this._directTimelineAdapter || this.probe.resolveDirectTimelineAdapter();\n if (!tl) return false;\n try {\n fn(tl);\n this._directTimelineAdapter = tl;\n return true;\n } catch {\n return false;\n }\n }\n\n // GSAP seek() preserves play state; player seek() contract lands paused.\n private _tryDirectTimelineSeek(t: number): boolean {\n return this._withDirectTimeline((tl) => {\n tl.seek(t);\n tl.pause();\n });\n }\n private _tryDirectTimelinePlay(): boolean {\n return this._withDirectTimeline((tl) => void tl.play());\n }\n private _tryDirectTimelinePause(): boolean {\n return this._withDirectTimeline((tl) => void tl.pause());\n }\n\n /**\n * Widget-frame RAF loop that sends \"tick\" postMessages to the composition\n * iframe on every frame. Used for the runtime bridge path so that animation\n * advances even when the composition iframe's own rAF is throttled by\n * Chromium (e.g. deeply nested cross-origin iframes in Electron / Claude desktop).\n * The runtime's own rAF loop still runs — ticking GSAP twice per frame is\n * harmless because seekTimelineAndAdapters is idempotent.\n */\n private _startParentTickClock(): void {\n this._stopParentTickClock();\n const tick = () => {\n if (this._paused) {\n this._parentTickRaf = null;\n return;\n }\n this._sendControl(\"tick\");\n this._parentTickRaf = requestAnimationFrame(tick);\n };\n this._parentTickRaf = requestAnimationFrame(tick);\n }\n\n private _stopParentTickClock(): void {\n if (this._parentTickRaf === null) return;\n cancelAnimationFrame(this._parentTickRaf);\n this._parentTickRaf = null;\n }\n\n private _onMessage(e: MessageEvent) {\n handleRuntimeMessage(e, this.iframe.contentWindow, {\n getPlaybackState: () => ({\n currentTime: this._currentTime,\n duration: this._duration,\n paused: this._paused,\n lastUpdateMs: this._lastUpdateMs,\n }),\n setPlaybackState: ({ currentTime, duration, paused, lastUpdateMs }) => {\n this._currentTime = currentTime;\n this._duration = duration;\n this._paused = paused;\n this._lastUpdateMs = lastUpdateMs;\n },\n getShaderLoadingMode: () => getShaderModeFromElement(this),\n shaderLoader: this.shaderLoader,\n setCompositionSize: (w, h) => {\n this._compositionWidth = w;\n this._compositionHeight = h;\n this._rescale();\n },\n sendControl: (action, extra) => this._sendControl(action, extra),\n getIframeDoc: () => this.iframe.contentDocument,\n updateControlsTime: (t, d) => this.controlsApi?.updateTime(t, d),\n updateControlsPlaying: (p) => this.controlsApi?.updatePlaying(p),\n dispatchEvent: (ev) => this.dispatchEvent(ev),\n seek: (t) => this.seek(t),\n play: () => this.play(),\n getLoop: () => this.loop,\n media: this._media,\n });\n }\n\n private _onProbeReady({ duration, adapter, compositionSize }: ProbeResult) {\n this._duration = duration;\n this._directTimelineAdapter = adapter.kind === \"direct-timeline\" ? adapter.timeline : null;\n this._ready = true;\n this.controlsApi?.updateTime(0, duration);\n this.dispatchEvent(new CustomEvent(\"ready\", { detail: { duration } }));\n if (compositionSize) {\n this._compositionWidth = compositionSize.width;\n this._compositionHeight = compositionSize.height;\n this._rescale();\n }\n try {\n const doc = this.iframe.contentDocument;\n if (doc) this._media.setupFromIframe(doc);\n } catch {\n /* cross-origin */\n }\n if (this.hasAttribute(\"autoplay\")) this.play();\n }\n\n private _rescale() {\n scaleIframeToFit(this, this.iframe, this._compositionWidth, this._compositionHeight);\n }\n\n private _onIframeLoad() {\n this._directTimelineAdapter = null;\n this._directTimelineClock.stop();\n this._stopParentTickClock();\n this.shaderLoader.reset();\n this._media.resetForIframeLoad();\n this.probe.start();\n }\n\n private _setupControls() {\n if (this.controlsApi) return;\n this.controlsApi = setupControls(\n this.shadow,\n this.muted,\n this._volume,\n this.getAttribute(\"speed-presets\"),\n {\n onPlay: () => this.play(),\n onPause: () => this.pause(),\n onSeek: (f) => this.seek(f * this._duration),\n onSpeedChange: (s) => void (this.playbackRate = s),\n onMuteToggle: () => void (this.muted = !this.muted),\n onVolumeChange: (v) => void (this.volume = v),\n },\n );\n }\n\n // Test-instrumentation pass-throughs (match original field names).\n get _audioOwner() {\n return this._media.audioOwner;\n }\n get _parentMedia() {\n return this._media.entries;\n }\n _mirrorParentMediaTime(t: number, opts?: { force?: boolean }) {\n this._media.mirrorTime(t, opts);\n }\n _promoteToParentProxy() {\n let d: Document | null = null;\n try {\n d = this.iframe.contentDocument;\n } catch {\n /* x-origin */\n }\n this._media.promoteToParentProxy(d, (t, o) => this._mirrorParentMediaTime(t, o));\n this._sendControl(\"set-media-output-muted\", { muted: true });\n }\n _observeDynamicMedia(doc: Document) {\n this._media.setupFromIframe(doc);\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\";\nexport type { ShaderLoadingMode } from \"./shader-options.js\";\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","/**\n * Types and type-guards for the two playback adapter paths the player supports:\n *\n * - `RuntimeDurationAdapter` — the HyperFrames runtime exposes `window.__player`\n * with a `getDuration()` method. This is the standard path for compositions\n * served through the runtime bridge.\n *\n * - `DirectTimelineAdapter` — same-origin standalone compositions can expose\n * their GSAP master timeline at `window.__timelines` without installing the\n * full runtime. The player drives play/pause/seek directly against the\n * timeline object, bypassing the postMessage bridge.\n *\n * `PlaybackDurationAdapter` is the discriminated union the probe interval\n * returns after deciding which path is available.\n */\n\nexport interface RuntimeDurationAdapter {\n getDuration: () => number;\n}\n\nexport interface DirectTimelineAdapter {\n duration: () => number;\n time: () => number;\n seek: (timeInSeconds: number) => unknown;\n play: () => unknown;\n pause: () => unknown;\n}\n\nexport type PlaybackDurationAdapter =\n | { kind: \"runtime\"; getDuration: () => number }\n | { kind: \"direct-timeline\"; timeline: DirectTimelineAdapter; getDuration: () => number };\n\nexport function isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isRuntimeDurationAdapter(value: unknown): value is RuntimeDurationAdapter {\n return isObjectRecord(value) && typeof value.getDuration === \"function\";\n}\n\nexport function 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","/**\n * Probes an iframe document to discover the composition's playback adapter\n * and detect whether the HyperFrames runtime needs to be injected.\n *\n * The probe interval polls every 200 ms until one of:\n * - A `PlaybackDurationAdapter` resolves with a positive duration, or\n * - 40 attempts (~8 s) expire without a result.\n *\n * The `CompositionProbe` class owns the interval; the caller must call\n * `stop()` on disconnect or src change.\n */\n\nimport { shouldInjectRuntime } from \"./shouldInjectRuntime.js\";\nimport {\n type DirectTimelineAdapter,\n type PlaybackDurationAdapter,\n isDirectTimelineAdapter,\n isObjectRecord,\n isRuntimeDurationAdapter,\n} from \"./timeline-adapters.js\";\n\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\n\nexport interface ProbeResult {\n duration: number;\n adapter: PlaybackDurationAdapter;\n /** Resolved composition dimensions, if present in the document. */\n compositionSize: { width: number; height: number } | null;\n}\n\nexport interface ProbeCallbacks {\n onReady: (result: ProbeResult) => void;\n onError: (message: string) => void;\n /** Called when runtime is successfully injected (informational). */\n onRuntimeInjected?: () => void;\n}\n\nexport class CompositionProbe {\n private _interval: ReturnType<typeof setInterval> | null = null;\n private _runtimeInjected = false;\n\n constructor(\n private readonly _iframe: HTMLIFrameElement,\n private readonly _callbacks: ProbeCallbacks,\n ) {}\n\n get runtimeInjected(): boolean {\n return this._runtimeInjected;\n }\n\n /** Start (or restart) the probe. Stops any previously running probe first. */\n start(): void {\n this.stop();\n this._runtimeInjected = false;\n let attempts = 0;\n\n this._interval = 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 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;\n }\n\n if (this._runtimeInjected && !hasRuntime) return;\n\n const adapter = this._resolvePlaybackDurationAdapter(win);\n if (adapter && adapter.getDuration() > 0) {\n this.stop();\n\n const doc = this._iframe.contentDocument;\n let compositionSize: { width: number; height: number } | null = null;\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) compositionSize = { width: w, height: h };\n }\n\n this._callbacks.onReady({\n duration: adapter.getDuration(),\n adapter,\n compositionSize,\n });\n return;\n }\n } catch {\n /* cross-origin */\n }\n\n if (attempts >= 40) {\n this.stop();\n this._callbacks.onError(\"Composition timeline not found after 8s\");\n }\n }, 200);\n }\n\n stop(): void {\n if (this._interval !== null) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n\n // ── Adapter resolution (same-origin only) ────────────────────────────────\n\n 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 resolveDirectTimelineAdapterFromWindow(win: Window): DirectTimelineAdapter | null {\n return this._resolveDirectTimelineAdapterFromWindow(win);\n }\n\n hasRuntimeBridge(win: Window): boolean {\n return Reflect.get(win, \"__hf\") !== undefined || isObjectRecord(Reflect.get(win, \"__player\"));\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n private _injectRuntime(): void {\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 (doc.head || doc.documentElement).appendChild(script);\n this._callbacks.onRuntimeInjected?.();\n } catch {\n /* cross-origin — can't inject */\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 _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","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 * Helpers for wiring the player's optional UI elements: the playback controls\n * bar, the poster image overlay, and input-filtering utilities.\n *\n * Extracted from the web component so the setup logic doesn't inflate the\n * class body. These are pure imperative DOM operations; they carry no\n * persistent state beyond what the caller tracks.\n */\n\nimport { createControls, type ControlsCallbacks, type ControlsOptions } from \"./controls.js\";\n\n/**\n * Create the playback controls overlay and attach it to `parent`.\n * Returns the controls API object. A no-op guard (returns the existing API)\n * must be enforced by the caller — this function always constructs.\n */\nexport function setupControls(\n parent: ShadowRoot,\n muted: boolean,\n volume: number,\n speedPresetsAttr: string | null,\n callbacks: ControlsCallbacks,\n): ReturnType<typeof createControls> {\n const speedPresets = speedPresetsAttr\n ? speedPresetsAttr\n .split(\",\")\n .map(Number)\n .filter((n) => !isNaN(n) && n > 0)\n : undefined;\n const options: ControlsOptions = speedPresets ? { speedPresets } : {};\n const api = createControls(parent, callbacks, options);\n api.updateMuted(muted);\n api.updateVolume(volume);\n return api;\n}\n\n/**\n * Set up or remove the poster image element in `parent`.\n *\n * - When `posterUrl` is non-empty, creates the `<img>` if needed and sets src.\n * - When `posterUrl` is null/empty, removes the existing element (if any).\n *\n * Returns the current poster element (possibly newly created) or `null`.\n */\nexport function setupPoster(\n parent: ShadowRoot,\n posterUrl: string | null,\n existing: HTMLImageElement | null,\n): HTMLImageElement | null {\n if (!posterUrl) {\n existing?.remove();\n return null;\n }\n if (!existing) {\n existing = document.createElement(\"img\");\n existing.className = \"hfp-poster\";\n parent.appendChild(existing);\n }\n existing.src = posterUrl;\n return existing;\n}\n\n/**\n * Returns `true` when `event` originated inside an `hfp-controls` element.\n * Used to prevent the bare-player-surface click handler from double-firing\n * when the user clicks an overlay control button.\n */\nexport function isControlsClick(event: Event): boolean {\n return event\n .composedPath()\n .some((t) => t instanceof HTMLElement && t.classList.contains(\"hfp-controls\"));\n}\n","/**\n * DOM setup helpers for the player's shadow root.\n *\n * Keeps constructor boilerplate out of the web component class body.\n */\n\n// Cached Constructable Stylesheet shared across all player instances.\nlet _sharedSheet: CSSStyleSheet | null = null;\n\n/**\n * Adopt `cssText` into `shadow` via a shared Constructable Stylesheet when the\n * browser supports it, falling back to a `<style>` element injection. The sheet\n * is cached on first creation and reused across all player instances.\n */\nexport function adoptShadowStyles(shadow: ShadowRoot, cssText: string): void {\n if (typeof CSSStyleSheet !== \"undefined\") {\n try {\n if (!_sharedSheet) {\n _sharedSheet = new CSSStyleSheet();\n _sharedSheet.replaceSync(cssText);\n }\n shadow.adoptedStyleSheets = [_sharedSheet];\n return;\n } catch {\n /* fallthrough */\n }\n }\n const style = document.createElement(\"style\");\n style.textContent = cssText;\n shadow.appendChild(style);\n}\n\n/**\n * Creates and configures the iframe element that hosts the composition, plus\n * the wrapper container div. Returns handles to both so the constructor can\n * attach them to the shadow root and track references without inlining the\n * boilerplate.\n */\nexport function createCompositionIframe(): {\n container: HTMLDivElement;\n iframe: HTMLIFrameElement;\n} {\n const container = document.createElement(\"div\");\n container.className = \"hfp-container\";\n\n const iframe = document.createElement(\"iframe\");\n iframe.className = \"hfp-iframe\";\n iframe.sandbox.add(\"allow-scripts\", \"allow-same-origin\");\n iframe.allow = \"autoplay; fullscreen\";\n iframe.referrerPolicy = \"no-referrer\";\n iframe.title = \"HyperFrames Composition\";\n\n container.appendChild(iframe);\n return { container, iframe };\n}\n\n/**\n * Scale the iframe so the composition fits inside the player element while\n * preserving aspect ratio. No-ops when the player has no painted size yet.\n */\nexport function scaleIframeToFit(\n playerElement: HTMLElement,\n iframe: HTMLIFrameElement,\n compositionWidth: number,\n compositionHeight: number,\n): void {\n const rect = playerElement.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const scale = Math.min(rect.width / compositionWidth, rect.height / compositionHeight);\n iframe.style.width = `${compositionWidth}px`;\n iframe.style.height = `${compositionHeight}px`;\n iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;\n}\n","/**\n * rAF-based clock that polls a `DirectTimelineAdapter` for current time and\n * drives the player's time/playback-ended callbacks.\n *\n * Used for same-origin standalone GSAP compositions that expose\n * `window.__timelines` but have no runtime bridge — the player drives them\n * directly through the adapter instead of going through postMessage.\n */\n\nimport type { DirectTimelineAdapter } from \"./timeline-adapters.js\";\n\nconst UI_UPDATE_INTERVAL_MS = 100;\n\nexport interface ClockCallbacks {\n /** Called every ~100ms and on completion with the current time. */\n onTimeUpdate: (currentTime: number, duration: number) => void;\n /** Called when playback reaches the end. Return true to loop (seek+play). */\n onEnded: () => boolean;\n /** Get the current loop flag. */\n getLoop: () => boolean;\n /** Trigger a seek-then-play loop restart. */\n restart: () => void;\n /** Notify that playback has paused (from the timeline side). */\n onPaused: () => void;\n}\n\nexport class DirectTimelineClock {\n private _raf: number | null = null;\n private _lastUpdateMs = 0;\n\n constructor(private readonly _callbacks: ClockCallbacks) {}\n\n start(\n timeline: DirectTimelineAdapter,\n getCurrentTime: () => number,\n getDuration: () => number,\n isPaused: () => boolean,\n ): void {\n this.stop();\n\n const tick = () => {\n if (isPaused()) {\n this._raf = null;\n return;\n }\n\n let currentTime: number;\n try {\n currentTime = timeline.time();\n } catch {\n this._raf = null;\n return;\n }\n\n const duration = getDuration();\n if (duration > 0) currentTime = Math.min(currentTime, duration);\n\n const completedPlayback = duration > 0 && currentTime >= duration;\n const now = performance.now();\n\n if (now - this._lastUpdateMs > UI_UPDATE_INTERVAL_MS || completedPlayback) {\n this._lastUpdateMs = now;\n this._callbacks.onTimeUpdate(currentTime, duration);\n }\n\n if (completedPlayback) {\n if (this._callbacks.getLoop()) {\n this._callbacks.restart();\n return;\n }\n try {\n timeline.pause();\n } catch {\n /* ignore */\n }\n this._callbacks.onPaused();\n this._raf = null;\n return;\n }\n\n this._raf = requestAnimationFrame(tick);\n };\n\n this._raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n if (this._raf === null) return;\n cancelAnimationFrame(this._raf);\n this._raf = null;\n }\n\n get isRunning(): boolean {\n return this._raf !== null;\n }\n}\n","/**\n * Internal helper for scoping the player's media MutationObserver to the\n * composition tree inside the iframe.\n *\n * Not part of the package's public API — kept in its own module so the\n * decision logic can be exercised by unit tests without exposing it through\n * the player entry point.\n */\n\n/**\n * Pick the elements inside `doc` that the media MutationObserver should\n * attach to.\n *\n * Compositions mount inside `[data-composition-id]` host elements — the\n * runtime root and any sub-composition hosts that `compositionLoader` writes\n * into them. Watching only those hosts (with `subtree: true`) catches every\n * late-arriving timed media element from sub-composition activation, while\n * filtering out churn from analytics tags, runtime telemetry markers, and\n * other out-of-host nodes that the runtime appends straight to `<body>`\n * during bootstrap.\n *\n * Nested hosts are filtered out — they're already covered by their nearest\n * host ancestor's subtree observation, so observing them too would deliver\n * each callback twice and double-count adoption work.\n *\n * Falls back to `[doc.body]` when no composition hosts are present, which\n * preserves the previous behavior for documents that aren't yet (or never\n * will be) composition-structured. Returns an empty array when neither a\n * host nor a body is available — the caller should treat that as \"nothing\n * to observe\".\n *\n * When the scoped path is taken but the body still carries timed media\n * outside every host, a `console.warn` fires once per call as a forensic\n * signal: the new scope skips that media, so any `<audio data-start>` /\n * `<video data-start>` injected at body level will silently never get a\n * parent-frame proxy. Today every runtime path appends inside a host so\n * this branch shouldn't trip; if it does, the warn surfaces the drift\n * immediately rather than presenting as a missing-audio bug downstream.\n */\nexport function selectMediaObserverTargets(doc: Document): Element[] {\n const all = Array.from(doc.querySelectorAll<Element>(\"[data-composition-id]\"));\n if (all.length === 0) {\n return doc.body ? [doc.body] : [];\n }\n\n const topLevel: Element[] = [];\n for (const el of all) {\n if (!hasCompositionAncestor(el)) {\n topLevel.push(el);\n }\n }\n\n warnOnUnscopedTimedMedia(doc);\n return topLevel;\n}\n\n/**\n * Forensic guard: with composition hosts present the observer attaches only\n * to those subtrees, so any timed media sitting at body level (or under a\n * non-host wrapper) is invisible to the adoption pipeline. Walk the body for\n * `[data-start]` audio/video that has no `[data-composition-id]` ancestor\n * and emit a single `console.warn` listing the orphans. The walk is cheap\n * (one `querySelectorAll` over a typed selector + a `closest` per match)\n * and only runs on the scoped path, so the no-host fallback retains its\n * legacy behavior with zero extra work.\n */\nfunction warnOnUnscopedTimedMedia(doc: Document): void {\n const body = doc.body;\n if (!body) return;\n if (typeof console === \"undefined\" || typeof console.warn !== \"function\") return;\n\n const candidates = body.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n if (candidates.length === 0) return;\n\n const orphans: HTMLMediaElement[] = [];\n for (const el of candidates) {\n if (!el.closest(\"[data-composition-id]\")) orphans.push(el);\n }\n if (orphans.length === 0) return;\n\n console.warn(\n \"[hyperframes-player] selectMediaObserverTargets: composition hosts are present, \" +\n `but ${orphans.length} body-level timed media element(s) sit outside every ` +\n \"[data-composition-id] subtree and will not be observed. Move them inside a \" +\n \"composition host or the parent-frame proxy will never adopt them.\",\n orphans,\n );\n}\n\nfunction hasCompositionAncestor(el: Element): boolean {\n let cursor = el.parentElement;\n while (cursor) {\n if (cursor.hasAttribute(\"data-composition-id\")) return true;\n cursor = cursor.parentElement;\n }\n return false;\n}\n","/**\n * Parent-frame media proxy subsystem.\n *\n * Maintains mirror copies of the iframe's timed `<audio>`/`<video>` elements\n * in the parent frame so that mobile browsers — which gate `el.play()` on user\n * activation in the *same* frame — can still produce audible output via proxies\n * the parent controls directly.\n *\n * See the class-level JSDoc on `HyperframesPlayer` for the full ownership model.\n */\n\nimport { selectMediaObserverTargets } from \"./mediaObserverScope.js\";\n\n/** Minimum absolute drift before a currentTime correction is attempted. */\nconst MIRROR_DRIFT_THRESHOLD_SECONDS = 0.05;\n\n/**\n * How many *consecutive* over-threshold samples are required before issuing a\n * `currentTime` write. Absorbs single-sample jitter (GC pause, slow bridge\n * tick) without thrashing. Forced calls bypass this gate.\n *\n * Worst-case correction latency ≈ this × bridgeMaxPostIntervalMs (80 ms in\n * core/runtime/state.ts) = 160 ms — well under human A/V re-sync tolerance.\n */\nconst MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES = 2;\n\nexport interface ProxyEntry {\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. A write is only issued once this\n * reaches `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES`, absorbing\n * single-sample jitter without thrashing.\n */\n driftSamples: number;\n}\n\nexport class ParentMediaManager {\n private _entries: ProxyEntry[] = [];\n private _mediaObserver?: MutationObserver;\n private _playbackErrorPosted = false;\n private _audioOwner: \"runtime\" | \"parent\" = \"runtime\";\n\n private readonly _dispatchEvent: (event: Event) => void;\n private readonly _getMuted: () => boolean;\n private readonly _getVolume: () => number;\n private readonly _getPlaybackRate: () => number;\n private readonly _getCurrentTime: () => number;\n private readonly _isPaused: () => boolean;\n\n constructor(opts: {\n dispatchEvent: (event: Event) => void;\n getMuted: () => boolean;\n getVolume: () => number;\n getPlaybackRate: () => number;\n getCurrentTime: () => number;\n isPaused: () => boolean;\n }) {\n this._dispatchEvent = opts.dispatchEvent;\n this._getMuted = opts.getMuted;\n this._getVolume = opts.getVolume;\n this._getPlaybackRate = opts.getPlaybackRate;\n this._getCurrentTime = opts.getCurrentTime;\n this._isPaused = opts.isPaused;\n }\n\n get audioOwner(): \"runtime\" | \"parent\" {\n return this._audioOwner;\n }\n\n /** Exposed for test instrumentation only — do not use in production code. */\n get entries(): ProxyEntry[] {\n return this._entries;\n }\n\n get playbackErrorPosted(): boolean {\n return this._playbackErrorPosted;\n }\n\n resetForIframeLoad(): void {\n this._playbackErrorPosted = false;\n const wasPromoted = this._audioOwner === \"parent\";\n this._audioOwner = \"runtime\";\n this.pauseAll();\n this.teardownObserver();\n if (wasPromoted) {\n this._dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"runtime\", reason: \"iframe-reload\" },\n }),\n );\n }\n }\n\n destroy(): void {\n this.teardownObserver();\n for (const m of this._entries) {\n m.el.pause();\n m.el.src = \"\";\n }\n this._entries = [];\n }\n\n updateMuted(muted: boolean): void {\n for (const m of this._entries) m.el.muted = muted;\n }\n\n updateVolume(volume: number): void {\n for (const m of this._entries) m.el.volume = volume;\n }\n\n updatePlaybackRate(rate: number): void {\n for (const m of this._entries) m.el.playbackRate = rate;\n }\n\n playAll(): void {\n for (const m of this._entries) {\n if (!m.el.src) continue;\n m.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n\n pauseAll(): void {\n for (const m of this._entries) m.el.pause();\n }\n\n seekAll(timeInSeconds: number): void {\n for (const m of this._entries) {\n const relTime = timeInSeconds - m.start;\n if (relTime >= 0 && relTime < m.duration) m.el.currentTime = relTime;\n }\n }\n\n /**\n * Mirror parent-proxy `currentTime` to the iframe timeline, with optional\n * jitter-coalescing. Pass `{ force: true }` for alignment moments (ownership\n * promotion, new proxy initialization) where drift must be corrected\n * immediately.\n */\n mirrorTime(timelineSeconds: number, options?: { force?: boolean }): void {\n const force = options?.force === true;\n for (const m of this._entries) {\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) > MIRROR_DRIFT_THRESHOLD_SECONDS) {\n m.driftSamples += 1;\n if (force || m.driftSamples >= MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES) {\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 in response to the runtime's\n * `media-autoplay-blocked` signal. Idempotent.\n *\n * The caller is responsible for muting the iframe's own media output via the\n * postMessage bridge (`set-media-output-muted`) after calling this.\n */\n /**\n * Take ownership of audible playback. Idempotent. The `onMirror` callback\n * is called with the current timeline time and `{ force: true }` so the\n * caller's mirror implementation runs (enabling test spies on the player\n * to fire). If omitted, `mirrorTime` is called directly.\n */\n promoteToParentProxy(\n iframeDoc: Document | null,\n onMirror?: (t: number, opts: { force: boolean }) => void,\n ): void {\n if (this._audioOwner === \"parent\") return;\n this._audioOwner = \"parent\";\n\n // Synchronously mute iframe media to close the race window.\n if (iframeDoc) {\n for (const el of iframeDoc.querySelectorAll<HTMLMediaElement>(\"video, audio\")) {\n el.muted = true;\n }\n }\n\n // One-shot alignment — bypass jitter-coalescing gate.\n const t = this._getCurrentTime();\n if (onMirror) onMirror(t, { force: true });\n else this.mirrorTime(t, { force: true });\n if (!this._isPaused()) this.playAll();\n\n this._dispatchEvent(\n new CustomEvent(\"audioownershipchange\", {\n detail: { owner: \"parent\", reason: \"autoplay-blocked\" },\n }),\n );\n }\n\n /**\n * Set up proxies for all timed media currently in the iframe document, then\n * install a MutationObserver for media added later (sub-composition activation).\n */\n setupFromIframe(iframeDoc: Document): void {\n const mediaEls = iframeDoc.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n for (const iframeEl of mediaEls) this._adoptIframeMedia(iframeEl);\n this._observeDynamicMedia(iframeDoc);\n }\n\n /** Set up a single proxy from an explicit URL (the `audio-src` attribute path). */\n setupFromUrl(audioSrc: string): void {\n this._createEntry(audioSrc, \"audio\", 0, Infinity);\n }\n\n teardownObserver(): void {\n this._mediaObserver?.disconnect();\n this._mediaObserver = undefined;\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n private _reportPlaybackError(err: unknown): void {\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 /**\n * Create a parent-frame media element and start preloading it. Returns the\n * new entry, or `null` if a proxy for this src already exists (dedup).\n */\n private _createEntry(\n src: string,\n tag: \"audio\" | \"video\",\n start: number,\n duration: number,\n ): ProxyEntry | null {\n if (this._entries.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._getMuted();\n el.volume = this._getVolume();\n const rate = this._getPlaybackRate();\n if (rate !== 1) el.playbackRate = rate;\n\n const entry: ProxyEntry = { el, start, duration, driftSamples: 0 };\n this._entries.push(entry);\n return entry;\n }\n\n private _adoptIframeMedia(iframeEl: HTMLMediaElement): void {\n // Skip elements the preloader has demoted — the observer will re-trigger\n // when the preload attribute is promoted to \"auto\".\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 const src = new URL(rawSrc, iframeEl.ownerDocument.baseURI).href;\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._createEntry(src, tag, start, duration);\n\n // If already under parent ownership and playing, the new proxy must catch\n // up immediately — bypass the jitter-coalescing gate.\n if (created && this._audioOwner === \"parent\") {\n this.mirrorTime(this._getCurrentTime(), { force: true });\n if (!this._isPaused() && created.el.src) {\n created.el.play().catch((err: unknown) => this._reportPlaybackError(err));\n }\n }\n }\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._entries.findIndex((m) => m.el.src === src);\n if (idx === -1) return;\n const entry = this._entries[idx];\n entry.el.pause();\n entry.el.src = \"\";\n this._entries.splice(idx, 1);\n }\n\n private _observeDynamicMedia(doc: Document): void {\n this.teardownObserver();\n if (typeof MutationObserver === \"undefined\" || !doc.body) return;\n\n const obs = new MutationObserver((mutations) => {\n for (const m of mutations) {\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 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\n for (const removed of m.removedNodes) {\n if (!(removed instanceof Element)) continue;\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\n const observeOpts: MutationObserverInit = {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"preload\"],\n };\n\n const targets = selectMediaObserverTargets(doc);\n for (const target of targets) {\n obs.observe(target, observeOpts);\n }\n this._mediaObserver = obs;\n }\n}\n","/**\n * Pure playback-state update logic for the `state` message from the runtime.\n *\n * Extracted from the web component so the state-transition rules — loop\n * handling, play/pause mirroring, completion detection — can be read and\n * exercised independently.\n */\n\nimport type { ParentMediaManager } from \"./parent-media.js\";\n\nconst UI_UPDATE_INTERVAL_MS = 100;\n\nexport interface PlaybackState {\n currentTime: number;\n duration: number;\n paused: boolean;\n lastUpdateMs: number;\n}\n\nexport interface PlaybackStateCallbacks {\n updateControlsTime: (current: number, duration: number) => void;\n updateControlsPlaying: (playing: boolean) => void;\n dispatchEvent: (event: Event) => void;\n seek: (t: number) => void;\n play: () => void;\n getLoop: () => boolean;\n media: ParentMediaManager;\n}\n\n/**\n * Process a `state` message from the runtime and return the next state.\n * Side effects (controls updates, events, media mirroring) are fired through\n * `callbacks`. The caller must commit the returned state object.\n */\nexport function applyRuntimeStateMessage(\n data: { frame: number; isPlaying: boolean },\n fps: number,\n current: PlaybackState,\n callbacks: PlaybackStateCallbacks,\n): PlaybackState {\n const rawTime = (data.frame ?? 0) / fps;\n const currentTime = current.duration > 0 ? Math.min(rawTime, current.duration) : rawTime;\n const wasPlaying = !current.paused;\n const nextPaused = !data.isPlaying;\n const completedPlayback =\n current.duration > 0 && currentTime >= current.duration && (wasPlaying || data.isPlaying);\n\n if (completedPlayback && callbacks.getLoop()) {\n if (callbacks.media.audioOwner === \"parent\") callbacks.media.pauseAll();\n callbacks.seek(0);\n callbacks.play();\n // play() sets paused=false; reflect that in the returned state so the\n // caller's destructure doesn't overwrite it with the stale nextPaused value.\n return { ...current, currentTime, paused: false };\n }\n\n const next: PlaybackState = { ...current, currentTime, paused: nextPaused };\n\n if (callbacks.media.audioOwner === \"parent\") {\n if (wasPlaying && nextPaused) {\n callbacks.media.pauseAll();\n } else if (!wasPlaying && !nextPaused) {\n callbacks.media.playAll();\n }\n callbacks.media.mirrorTime(currentTime);\n }\n\n const now = performance.now();\n const playStateChanged = nextPaused !== current.paused;\n if (now - current.lastUpdateMs > UI_UPDATE_INTERVAL_MS || playStateChanged) {\n next.lastUpdateMs = now;\n callbacks.updateControlsTime(currentTime, current.duration);\n callbacks.updateControlsPlaying(!nextPaused);\n callbacks.dispatchEvent(new CustomEvent(\"timeupdate\", { detail: { currentTime } }));\n }\n\n if (completedPlayback) {\n if (callbacks.media.audioOwner === \"parent\") callbacks.media.pauseAll();\n next.paused = true;\n callbacks.updateControlsPlaying(false);\n callbacks.dispatchEvent(new Event(\"ended\"));\n }\n\n return next;\n}\n","/**\n * Routes postMessages from the composition iframe to the appropriate handlers.\n *\n * Accepts the raw MessageEvent and delegates through typed callbacks so the\n * web component keeps its state fields private and this module stays stateless.\n */\n\nimport {\n applyRuntimeStateMessage,\n type PlaybackState,\n type PlaybackStateCallbacks,\n} from \"./playback-state.js\";\nimport type { ShaderLoaderState } from \"./shader-loader-state.js\";\nimport type { ShaderTransitionState } from \"./shader-options.js\";\n\nconst FPS = 30;\n\nexport interface MessageHandlerCallbacks extends PlaybackStateCallbacks {\n getPlaybackState: () => PlaybackState;\n setPlaybackState: (next: PlaybackState) => void;\n getShaderLoadingMode: () => string;\n shaderLoader: ShaderLoaderState;\n setCompositionSize: (width: number, height: number) => void;\n sendControl: (action: string, extra?: Record<string, unknown>) => void;\n getIframeDoc: () => Document | null;\n}\n\nexport function handleRuntimeMessage(\n event: MessageEvent,\n frameWindow: Window | null,\n callbacks: MessageHandlerCallbacks,\n): void {\n if (event.source !== frameWindow) return;\n const data = event.data as Record<string, unknown> | undefined;\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\"\n ? (data[\"state\"] as ShaderTransitionState)\n : {};\n callbacks.shaderLoader.update(state, callbacks.getShaderLoadingMode());\n callbacks.dispatchEvent(\n new CustomEvent(\"shadertransitionstate\", {\n detail: { compositionId: data[\"compositionId\"], state },\n }),\n );\n return;\n }\n\n if (data[\"type\"] === \"state\") {\n callbacks.setPlaybackState(\n applyRuntimeStateMessage(\n { frame: (data[\"frame\"] as number) ?? 0, isPlaying: !!data[\"isPlaying\"] },\n FPS,\n callbacks.getPlaybackState(),\n callbacks,\n ),\n );\n return;\n }\n\n if (data[\"type\"] === \"media-autoplay-blocked\") {\n let iframeDoc: Document | null = null;\n try {\n iframeDoc = callbacks.getIframeDoc();\n } catch {\n /* cross-origin */\n }\n callbacks.media.promoteToParentProxy(iframeDoc, (t, opts) =>\n callbacks.media.mirrorTime(t, opts),\n );\n callbacks.sendControl(\"set-media-output-muted\", { muted: true });\n return;\n }\n\n if (data[\"type\"] === \"timeline\" && (data[\"durationInFrames\"] as number) > 0) {\n if (Number.isFinite(data[\"durationInFrames\"])) {\n const pb = callbacks.getPlaybackState();\n const duration = (data[\"durationInFrames\"] as number) / FPS;\n callbacks.setPlaybackState({ ...pb, duration });\n callbacks.updateControlsTime(pb.currentTime, duration);\n }\n return;\n }\n\n if (\n data[\"type\"] === \"stage-size\" &&\n (data[\"width\"] as number) > 0 &&\n (data[\"height\"] as number) > 0\n ) {\n callbacks.setCompositionSize(data[\"width\"] as number, data[\"height\"] as number);\n }\n}\n","/**\n * Shader transition option types, constants, and pure helper functions for\n * injecting shader capture scale and loading mode parameters into composition\n * URLs and srcdoc HTML.\n */\n\nexport const SHADER_CAPTURE_SCALE_ATTR = \"shader-capture-scale\";\nexport const SHADER_LOADING_ATTR = \"shader-loading\";\nexport const SHADER_CAPTURE_SCALE_PARAM = \"__hf_shader_capture_scale\";\nexport const SHADER_LOADING_PARAM = \"__hf_shader_loading\";\n\nexport const 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\nexport type ShaderLoadingMode = \"composition\" | \"player\" | \"none\";\n\nexport interface 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\nexport function 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\nexport function 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\nexport function 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\nexport function 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\n/**\n * Convenience wrappers that read shader attributes directly from an element,\n * avoiding boilerplate in the web component class body.\n */\n\nexport function getShaderModeFromElement(el: Element): ShaderLoadingMode {\n return normalizeShaderLoadingMode(el.getAttribute(SHADER_LOADING_ATTR));\n}\n\nexport function getShaderCaptureScaleFromElement(el: Element): number {\n return Number(normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)) ?? \"1\");\n}\n\nexport function prepareSrcForElement(el: Element, src: string): string {\n return withShaderQueryParams(\n src,\n normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)),\n getShaderModeFromElement(el),\n );\n}\n\nexport function prepareSrcdocForElement(el: Element, srcdoc: string): string {\n return injectShaderOptionsIntoSrcdoc(\n srcdoc,\n normalizeShaderCaptureScale(el.getAttribute(SHADER_CAPTURE_SCALE_ATTR)),\n getShaderModeFromElement(el),\n );\n}\n","/**\n * Factory for the shader-transition loading overlay DOM tree.\n *\n * Kept in its own module so the ~100-line DOM construction stays out of the\n * web component class body. The returned `ShaderLoaderElements` bag gives the\n * component direct handles to the nodes it needs to update without querying\n * the shadow DOM on every state change.\n */\n\nimport { SHADER_LOADING_PHRASES } from \"./shader-options.js\";\n\nexport interface 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\nexport function 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 titleContainer = document.createElement(\"div\");\n titleContainer.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 titleContainer.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(titleContainer);\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 * Runtime state controller for the shader-transition loading overlay.\n *\n * Manages show/hide transitions (with a CSS fade-out delay) and updates\n * the progress bar, phrase text, and detail rows from `ShaderTransitionState`\n * messages received from the iframe.\n *\n * Holds direct references to the DOM nodes created by `createShaderLoader`\n * so state updates never touch the shadow-DOM query API at runtime.\n */\n\nimport { SHADER_LOADING_PHRASES, type ShaderTransitionState } from \"./shader-options.js\";\nimport type { ShaderLoaderElements } from \"./shader-loader-element.js\";\n\nconst HIDE_TRANSITION_MS = 420;\n\nexport class ShaderLoaderState {\n private readonly _el: ShaderLoaderElements;\n private _hideTimeout: ReturnType<typeof setTimeout> | null = null;\n\n constructor(elements: ShaderLoaderElements) {\n this._el = elements;\n }\n\n show(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n this._el.root.classList.remove(\"hfp-hiding\");\n this._el.root.classList.add(\"hfp-visible\");\n }\n\n hide(): void {\n if (this._el.root.classList.contains(\"hfp-hiding\")) {\n if (!this._hideTimeout) this._scheduleCleanup();\n return;\n }\n if (!this._el.root.classList.contains(\"hfp-visible\")) return;\n this._el.root.classList.add(\"hfp-hiding\");\n this._el.root.classList.remove(\"hfp-visible\");\n this._scheduleCleanup();\n }\n\n reset(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n this._el.root.classList.remove(\"hfp-visible\", \"hfp-hiding\");\n this._el.fill.style.transform = \"scaleX(0)\";\n this._el.transitionValue.textContent = \"\";\n this._el.frameValue.textContent = \"\";\n this._el.frameRow.style.visibility = \"hidden\";\n }\n\n update(status: ShaderTransitionState, loadingMode: string): void {\n if (loadingMode !== \"player\") {\n this.reset();\n return;\n }\n if (status.ready || !status.loading) {\n this.hide();\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\n const phraseIndex = Math.min(\n SHADER_LOADING_PHRASES.length - 1,\n Math.floor(ratio * SHADER_LOADING_PHRASES.length),\n );\n this._el.title.textContent =\n SHADER_LOADING_PHRASES[phraseIndex] || \"Preparing scene transitions\";\n\n this._el.detail.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\n this._el.fill.style.transform = `scaleX(${ratio})`;\n\n this._el.transitionValue.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\n this._el.frameLabel.textContent =\n status.phase === \"cached\"\n ? \"cached transition frames\"\n : status.phase === \"finalizing\"\n ? \"finalizing transition frames\"\n : \"rendering transition frames\";\n\n this._el.frameValue.textContent = frameValue;\n this._el.frameRow.style.visibility = frameValue ? \"visible\" : \"hidden\";\n this._el.root.setAttribute(\"aria-valuenow\", String(Math.round(ratio * 100)));\n this.show();\n }\n\n get hideTimeout(): ReturnType<typeof setTimeout> | null {\n return this._hideTimeout;\n }\n\n destroy(): void {\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n }\n\n private _scheduleCleanup(): void {\n if (this._hideTimeout) clearTimeout(this._hideTimeout);\n this._hideTimeout = setTimeout(() => {\n this._el.root.classList.remove(\"hfp-hiding\");\n this._hideTimeout = null;\n }, HIDE_TRANSITION_MS);\n }\n}\n"],"mappings":"mbAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,uBAAAE,EAAA,kBAAAC,EAAA,gBAAAC,EAAA,eAAAC,IAAA,eAAAC,GAAAN,ICgCO,SAASO,GAAoBC,EAA4B,CAC9D,OAAIA,EAAM,YAAcA,EAAM,gBAAwB,GAClD,GAAAA,EAAM,uBACNA,EAAM,cAAgBA,EAAM,UAAY,EAE9C,CCLO,SAASC,EAAeC,EAAkD,CAC/E,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAEO,SAASC,GAAyBD,EAAiD,CACxF,OAAOD,EAAeC,CAAK,GAAK,OAAOA,EAAM,aAAgB,UAC/D,CAEO,SAASE,GAAwBF,EAAgD,CACtF,OACED,EAAeC,CAAK,GACpB,OAAOA,EAAM,UAAa,YAC1B,OAAOA,EAAM,MAAS,YACtB,OAAOA,EAAM,MAAS,YACtB,OAAOA,EAAM,MAAS,YACtB,OAAOA,EAAM,OAAU,UAE3B,CC5BA,IAAMG,GACJ,iFAgBWC,EAAN,KAAuB,CAI5B,YACmBC,EACAC,EACjB,CAFiB,aAAAD,EACA,gBAAAC,CAChB,CAFgB,QACA,WALX,UAAmD,KACnD,iBAAmB,GAO3B,IAAI,iBAA2B,CAC7B,OAAO,KAAK,gBACd,CAGA,OAAc,CACZ,KAAK,KAAK,EACV,KAAK,iBAAmB,GACxB,IAAIC,EAAW,EAEf,KAAK,UAAY,YAAY,IAAM,CACjCA,IACA,GAAI,CACF,IAAMC,EAAM,KAAK,QAAQ,cAKzB,GAAI,CAACA,EAAK,OAEV,IAAMC,EAAa,CAAC,EAAED,EAAI,MAAQA,EAAI,UAChCE,EAAe,CAAC,EAAEF,EAAI,aAAe,OAAO,KAAKA,EAAI,WAAW,EAAE,OAAS,GAC3EG,EACJ,CAAC,CAAC,KAAK,QAAQ,iBAAiB,cAAc,wBAAwB,EAExE,GACEC,GAAoB,CAClB,WAAAH,EACA,aAAAC,EACA,sBAAAC,EACA,gBAAiB,KAAK,iBACtB,SAAAJ,CACF,CAAC,EACD,CACA,KAAK,eAAe,EACpB,MACF,CAEA,GAAI,KAAK,kBAAoB,CAACE,EAAY,OAE1C,IAAMI,EAAU,KAAK,gCAAgCL,CAAG,EACxD,GAAIK,GAAWA,EAAQ,YAAY,EAAI,EAAG,CACxC,KAAK,KAAK,EAEV,IAAMC,EAAM,KAAK,QAAQ,gBACrBC,EAA4D,KAC1DC,EAAOF,GAAK,cAAc,uBAAuB,EACvD,GAAIE,EAAM,CACR,IAAMC,EAAI,SAASD,EAAK,aAAa,YAAY,GAAK,IAAK,EAAE,EACvDE,EAAI,SAASF,EAAK,aAAa,aAAa,GAAK,IAAK,EAAE,EAC1DC,EAAI,GAAKC,EAAI,IAAGH,EAAkB,CAAE,MAAOE,EAAG,OAAQC,CAAE,EAC9D,CAEA,KAAK,WAAW,QAAQ,CACtB,SAAUL,EAAQ,YAAY,EAC9B,QAAAA,EACA,gBAAAE,CACF,CAAC,EACD,MACF,CACF,MAAQ,CAER,CAEIR,GAAY,KACd,KAAK,KAAK,EACV,KAAK,WAAW,QAAQ,yCAAyC,EAErE,EAAG,GAAG,CACR,CAEA,MAAa,CACP,KAAK,YAAc,OACrB,cAAc,KAAK,SAAS,EAC5B,KAAK,UAAY,KAErB,CAIA,8BAA6D,CAC3D,GAAI,CACF,IAAMC,EAAM,KAAK,QAAQ,cACzB,OAAKA,EACE,KAAK,wCAAwCA,CAAG,EADtC,IAEnB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,uCAAuCA,EAA2C,CAChF,OAAO,KAAK,wCAAwCA,CAAG,CACzD,CAEA,iBAAiBA,EAAsB,CACrC,OAAO,QAAQ,IAAIA,EAAK,MAAM,IAAM,QAAaW,EAAe,QAAQ,IAAIX,EAAK,UAAU,CAAC,CAC9F,CAIQ,gBAAuB,CAC7B,KAAK,iBAAmB,GACxB,GAAI,CACF,IAAMM,EAAM,KAAK,QAAQ,gBACzB,GAAI,CAACA,EAAK,OACV,IAAMM,EAASN,EAAI,cAAc,QAAQ,EACzCM,EAAO,IAAMjB,IACZW,EAAI,MAAQA,EAAI,iBAAiB,YAAYM,CAAM,EACpD,KAAK,WAAW,oBAAoB,CACtC,MAAQ,CAER,CACF,CAEQ,wCAAwCZ,EAA2C,CACzF,GAAI,KAAK,iBAAiBA,CAAG,EAAG,OAAO,KAEvC,IAAMa,EAAY,QAAQ,IAAIb,EAAK,aAAa,EAChD,GAAI,CAACW,EAAeE,CAAS,EAAG,OAAO,KAEvC,IAAMC,EAAO,OAAO,KAAKD,CAAS,EAClC,GAAIC,EAAK,SAAW,EAAG,OAAO,KAE9B,IAAMC,EAAS,KAAK,QAAQ,iBACxB,cAAc,uBAAuB,GACrC,aAAa,qBAAqB,EAChCC,EAAMD,GAAUA,KAAUF,EAAYE,EAASD,EAAKA,EAAK,OAAS,CAAC,EACnEG,EAAWJ,EAAUG,CAAG,EAC9B,OAAOE,GAAwBD,CAAQ,EAAIA,EAAW,IACxD,CAEQ,gCAAgCjB,EAA6C,CACnF,IAAMmB,EAAgB,QAAQ,IAAInB,EAAK,UAAU,EACjD,GAAIoB,GAAyBD,CAAa,EACxC,MAAO,CAAE,KAAM,UAAW,YAAa,IAAMA,EAAc,YAAY,CAAE,EAG3E,IAAMF,EAAW,KAAK,wCAAwCjB,CAAG,EACjE,OAAIiB,EACK,CACL,KAAM,kBACN,SAAAA,EACA,YAAa,IAAMA,EAAS,SAAS,CACvC,EAGK,IACT,CACF,ECnMO,IAAMI,GAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","__toCommonJS","shouldInjectRuntime","state","isObjectRecord","value","isRuntimeDurationAdapter","isDirectTimelineAdapter","RUNTIME_CDN_URL","CompositionProbe","_iframe","_callbacks","attempts","win","hasRuntime","hasTimelines","hasNestedCompositions","shouldInjectRuntime","adapter","doc","compositionSize","root","w","h","isObjectRecord","script","timelines","keys","rootId","key","timeline","isDirectTimelineAdapter","runtimePlayer","isRuntimeDurationAdapter","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","setupControls","parent","muted","volume","speedPresetsAttr","callbacks","speedPresets","n","api","createControls","setupPoster","posterUrl","existing","isControlsClick","event","t","_sharedSheet","adoptShadowStyles","shadow","cssText","style","createCompositionIframe","container","iframe","scaleIframeToFit","playerElement","compositionWidth","compositionHeight","rect","scale","DirectTimelineClock","_callbacks","timeline","getCurrentTime","getDuration","isPaused","tick","currentTime","duration","completedPlayback","now","selectMediaObserverTargets","doc","all","topLevel","el","hasCompositionAncestor","warnOnUnscopedTimedMedia","body","candidates","orphans","cursor","MIRROR_DRIFT_THRESHOLD_SECONDS","MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES","ParentMediaManager","opts","wasPromoted","m","muted","volume","rate","err","timeInSeconds","relTime","timelineSeconds","options","force","iframeDoc","onMirror","el","t","mediaEls","iframeEl","audioSrc","src","tag","start","duration","entry","rawSrc","created","idx","doc","obs","mutations","target","added","candidates","inside","removed","dropped","observeOpts","targets","selectMediaObserverTargets","applyRuntimeStateMessage","data","fps","current","callbacks","rawTime","currentTime","wasPlaying","nextPaused","completedPlayback","next","now","playStateChanged","FPS","handleRuntimeMessage","event","frameWindow","callbacks","data","state","applyRuntimeStateMessage","iframeDoc","t","opts","pb","duration","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","injectShaderOptionsIntoSrcdoc","html","lines","script","match","getShaderModeFromElement","el","getShaderCaptureScaleFromElement","prepareSrcForElement","prepareSrcdocForElement","srcdoc","createShaderLoader","root","blockOverlayInteraction","event","eventName","panel","markFrame","titleContainer","titleText","SHADER_LOADING_PHRASES","detail","track","fill","progress","createProgressRow","labelText","row","label","value","transitionStatus","frameStatus","HIDE_TRANSITION_MS","ShaderLoaderState","elements","status","loadingMode","progress","total","ratio","phraseIndex","SHADER_LOADING_PHRASES","frameValue","HyperframesPlayer","SHADER_CAPTURE_SCALE_ATTR","SHADER_LOADING_ATTR","adoptShadowStyles","PLAYER_STYLES","createCompositionIframe","loaderElements","createShaderLoader","ShaderLoaderState","ParentMediaManager","e","DirectTimelineClock","currentTime","duration","CompositionProbe","result","message","event","isControlsClick","setupPoster","prepareSrcdocForElement","prepareSrcForElement","name","_old","val","rate","v","directTimelineStarted","timeInSeconds","t","r","getShaderCaptureScaleFromElement","scale","getShaderModeFromElement","mode","m","l","action","extra","player","fn","tl","tick","handleRuntimeMessage","paused","lastUpdateMs","w","h","d","p","ev","adapter","compositionSize","doc","scaleIframeToFit","setupControls","f","s","opts","o"]}