@hyperframes/player 0.6.0 → 0.6.1

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