@hyperframes/player 0.4.1 → 0.4.3

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,4 +1,4 @@
1
- "use strict";var HyperframesPlayer=(()=>{var w=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var U=(o,e)=>{for(var t in e)w(o,t,{get:e[t],enumerable:!0})},W=(o,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of j(e))!z.call(o,i)&&i!==t&&w(o,i,{get:()=>e[i],enumerable:!(s=F(e,i))||s.enumerable});return o};var $=o=>W(w({},"__esModule",{value:!0}),o);var q={};U(q,{HyperframesPlayer:()=>x,SPEED_PRESETS:()=>k,formatSpeed:()=>v,formatTime:()=>E});var H=`
1
+ "use strict";var HyperframesPlayer=(()=>{var x=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var U=(o,e)=>{for(var t in e)x(o,t,{get:e[t],enumerable:!0})},W=(o,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of j(e))!z.call(o,s)&&s!==t&&x(o,s,{get:()=>e[s],enumerable:!(i=F(e,s))||i.enumerable});return o};var $=o=>W(x({},"__esModule",{value:!0}),o);var B={};U(B,{HyperframesPlayer:()=>w,SPEED_PRESETS:()=>k,formatSpeed:()=>v,formatTime:()=>E});var N=`
2
2
  :host {
3
3
  display: block;
4
4
  position: relative;
@@ -195,5 +195,5 @@
195
195
  color: var(--hfp-accent, #fff);
196
196
  font-weight: 600;
197
197
  }
198
- `,C='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',O='<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>';var k=[.25,.5,1,1.5,2,4];function v(o){return Number.isInteger(o)?`${o}x`:`${o}x`}function E(o){if(!Number.isFinite(o)||o<0)return"0:00";let e=Math.floor(o),t=Math.floor(e/60),s=e%60;return`${t}:${s.toString().padStart(2,"0")}`}function D(o,e,t={}){let s=t.speedPresets??k,i=document.createElement("div");i.className="hfp-controls",i.addEventListener("click",r=>{r.stopPropagation()});let a=document.createElement("button");a.className="hfp-play-btn",a.type="button",a.innerHTML=C,a.setAttribute("aria-label","Play");let d=document.createElement("div");d.className="hfp-scrubber";let l=document.createElement("div");l.className="hfp-progress",l.style.width="0%",d.appendChild(l);let c=document.createElement("span");c.className="hfp-time",c.textContent="0:00 / 0:00";let h=document.createElement("div");h.className="hfp-speed-wrap";let p=document.createElement("button");p.className="hfp-speed-btn",p.type="button",p.textContent="1x",p.setAttribute("aria-label","Playback speed");let u=document.createElement("div");u.className="hfp-speed-menu",u.setAttribute("role","menu");for(let r of s){let n=document.createElement("button");n.className="hfp-speed-option",n.type="button",n.setAttribute("role","menuitem"),n.dataset.speed=String(r),n.textContent=v(r),r===1&&n.classList.add("hfp-active"),u.appendChild(n)}h.appendChild(u),h.appendChild(p),i.appendChild(a),i.appendChild(d),i.appendChild(c),i.appendChild(h),o.appendChild(i);let g=!1,b=null,_=s.indexOf(1);_===-1&&(_=0),a.addEventListener("click",r=>{r.stopPropagation(),g?e.onPause():e.onPlay()});let M=r=>{for(let n of u.querySelectorAll(".hfp-speed-option"))n.classList.toggle("hfp-active",n.dataset.speed===String(r))};p.addEventListener("click",r=>{r.stopPropagation();let n=u.classList.toggle("hfp-open");p.setAttribute("aria-expanded",String(n))}),u.addEventListener("click",r=>{r.stopPropagation();let n=r.target.closest(".hfp-speed-option");if(!n)return;let m=parseFloat(n.dataset.speed);_=s.indexOf(m),p.textContent=v(m),M(m),u.classList.remove("hfp-open"),p.setAttribute("aria-expanded","false"),e.onSpeedChange(m)});let L=()=>{u.classList.remove("hfp-open"),p.setAttribute("aria-expanded","false")};document.addEventListener("click",L);let y=r=>{let n=d.getBoundingClientRect(),m=Math.max(0,Math.min(1,(r-n.left)/n.width));e.onSeek(m)},f=!1;d.addEventListener("mousedown",r=>{r.stopPropagation(),f=!0,y(r.clientX)});let P=r=>{f&&y(r.clientX)},T=()=>{f=!1};document.addEventListener("mousemove",P),document.addEventListener("mouseup",T),d.addEventListener("touchstart",r=>{f=!0;let n=r.touches[0];n&&y(n.clientX)},{passive:!0});let S=r=>{if(f){let n=r.touches[0];n&&y(n.clientX)}},I=()=>{f=!1};document.addEventListener("touchmove",S,{passive:!0}),document.addEventListener("touchend",I);let R=()=>{b&&clearTimeout(b),b=setTimeout(()=>{g&&i.classList.add("hfp-hidden")},3e3)},N=o instanceof ShadowRoot?o.host:o;return N.addEventListener("mousemove",()=>{i.classList.remove("hfp-hidden"),R()}),N.addEventListener("mouseleave",()=>{g&&i.classList.add("hfp-hidden")}),{updateTime(r,n){let m=n>0?r/n*100:0;l.style.width=`${m}%`,c.textContent=`${E(r)} / ${E(n)}`},updatePlaying(r){g=r,a.innerHTML=r?O:C,a.setAttribute("aria-label",r?"Pause":"Play"),r?R():i.classList.remove("hfp-hidden")},updateSpeed(r){let n=s.indexOf(r);n!==-1&&(_=n),p.textContent=v(r),M(r)},show(){i.style.display=""},hide(){i.style.display="none"},destroy(){document.removeEventListener("mousemove",P),document.removeEventListener("mouseup",T),document.removeEventListener("touchmove",S),document.removeEventListener("touchend",I),document.removeEventListener("click",L),b&&clearTimeout(b)}}}var A=30,Y="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",x=class extends HTMLElement{static get observedAttributes(){return["src","width","height","controls","muted","poster","playback-rate","audio-src"]}shadow;container;iframe;posterEl=null;controlsApi=null;resizeObserver;_ready=!1;_duration=0;_currentTime=0;_paused=!0;_compositionWidth=1920;_compositionHeight=1080;_probeInterval=null;_lastUpdateMs=0;_parentMedia=[];constructor(){super(),this.shadow=this.attachShadow({mode:"open"});let e=document.createElement("style");e.textContent=H,this.shadow.appendChild(e),this.container=document.createElement("div"),this.container.className="hfp-container",this.iframe=document.createElement("iframe"),this.iframe.className="hfp-iframe",this.iframe.sandbox.add("allow-scripts","allow-same-origin"),this.iframe.allow="autoplay; fullscreen",this.iframe.referrerPolicy="no-referrer",this.iframe.title="HyperFrames Composition",this.container.appendChild(this.iframe),this.shadow.appendChild(this.container),this.addEventListener("click",t=>{this._isControlsClick(t)||(this._paused?this.play():this.pause())}),this.resizeObserver=new ResizeObserver(()=>this._updateScale()),this._onMessage=this._onMessage.bind(this),this._onIframeLoad=this._onIframeLoad.bind(this)}connectedCallback(){this.resizeObserver.observe(this),window.addEventListener("message",this._onMessage),this.iframe.addEventListener("load",this._onIframeLoad),this.hasAttribute("controls")&&this._setupControls(),this.hasAttribute("poster")&&this._setupPoster(),this.hasAttribute("audio-src")&&this._setupParentAudioFromUrl(this.getAttribute("audio-src")),this.hasAttribute("src")&&(this.iframe.src=this.getAttribute("src"))}disconnectedCallback(){this.resizeObserver.disconnect(),window.removeEventListener("message",this._onMessage),this.iframe.removeEventListener("load",this._onIframeLoad),this._probeInterval&&clearInterval(this._probeInterval),this.controlsApi?.destroy();for(let e of this._parentMedia)e.el.pause(),e.el.src="";this._parentMedia=[]}attributeChangedCallback(e,t,s){switch(e){case"src":s&&(this._ready=!1,this.iframe.src=s);break;case"width":this._compositionWidth=parseInt(s||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(s||"1080",10),this._updateScale();break;case"controls":s!==null?this._setupControls():(this.controlsApi?.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{let i=parseFloat(s||"1");for(let a of this._parentMedia)a.el.playbackRate=i;this._sendControl("set-playback-rate",{playbackRate:i}),this.controlsApi?.updateSpeed(i),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(let i of this._parentMedia)i.el.muted=s!==null;this._sendControl("set-muted",{muted:s!==null});break;case"audio-src":s&&this._setupParentAudioFromUrl(s);break}}get iframeElement(){return this.iframe}play(){this._hidePoster(),this._playParentMedia(),this._sendControl("play"),this._paused=!1,this.controlsApi?.updatePlaying(!0),this.dispatchEvent(new Event("play"))}pause(){this._pauseParentMedia(),this._sendControl("pause"),this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("pause"))}seek(e){let t=Math.round(e*A);this._sendControl("seek",{frame:t}),this._currentTime=e;for(let s of this._parentMedia){let i=e-s.start;i>=0&&i<s.duration&&(s.el.currentTime=i)}this._paused=!0,this.controlsApi?.updatePlaying(!1),this.controlsApi?.updateTime(this._currentTime,this._duration)}get currentTime(){return this._currentTime}set currentTime(e){this.seek(e)}get duration(){return this._duration}get paused(){return this._paused}get ready(){return this._ready}get playbackRate(){return parseFloat(this.getAttribute("playback-rate")||"1")}set playbackRate(e){this.setAttribute("playback-rate",String(e))}get muted(){return this.hasAttribute("muted")}set muted(e){e?this.setAttribute("muted",""):this.removeAttribute("muted")}get loop(){return this.hasAttribute("loop")}set loop(e){e?this.setAttribute("loop",""):this.removeAttribute("loop")}_sendControl(e,t={}){try{this.iframe.contentWindow?.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){if(e.source!==this.iframe.contentWindow)return;let t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="state"){this._currentTime=(t.frame??0)/A;let s=!this._paused;this._paused=!t.isPlaying,s&&this._paused?this._pauseParentMedia():!s&&!this._paused&&this._playParentMedia();let i=performance.now();(i-this._lastUpdateMs>100||this._paused!==s)&&(this._lastUpdateMs=i,this.controlsApi?.updateTime(this._currentTime,this._duration),this.controlsApi?.updatePlaying(!this._paused),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{currentTime:this._currentTime}}))),this._currentTime>=this._duration&&!this._paused&&(this._pauseParentMedia(),this.loop?(this.seek(0),this.play()):(this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("ended"))))}t.type==="timeline"&&t.durationInFrames>0&&Number.isFinite(t.durationInFrames)&&(this._duration=t.durationInFrames/A,this.controlsApi?.updateTime(this._currentTime,this._duration)),t.type==="stage-size"&&t.width>0&&t.height>0&&(this._compositionWidth=t.width,this._compositionHeight=t.height,this._updateScale())}}_runtimeInjected=!1;_onIframeLoad(){let e=0;this._runtimeInjected=!1,this._probeInterval&&clearInterval(this._probeInterval),this._probeInterval=setInterval(()=>{e++;try{let t=this.iframe.contentWindow;if(!t)return;let s=!!(t.__hf||t.__player),i=!!(t.__timelines&&Object.keys(t.__timelines).length>0);if(!s&&i&&!this._runtimeInjected&&e>=5){this._injectRuntime();return}if(this._runtimeInjected&&!s)return;let d=(()=>{if(t.__player&&typeof t.__player.getDuration=="function")return t.__player;if(t.__timelines){let l=Object.keys(t.__timelines);if(l.length>0){let c=this.iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),h=c&&c in t.__timelines?c:l[l.length-1],p=t.__timelines[h];return{getDuration:()=>p.duration()}}}return null})();if(d&&d.getDuration()>0){clearInterval(this._probeInterval),this._duration=d.getDuration(),this._ready=!0,this.controlsApi?.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));let c=this.iframe.contentDocument?.querySelector("[data-composition-id]");if(c){let h=parseInt(c.getAttribute("data-width")||"0",10),p=parseInt(c.getAttribute("data-height")||"0",10);h>0&&p>0&&(this._compositionWidth=h,this._compositionHeight=p,this._updateScale())}this._setupParentMedia(),this.hasAttribute("autoplay")&&this.play();return}}catch{}e>=40&&(clearInterval(this._probeInterval),this.dispatchEvent(new CustomEvent("error",{detail:{message:"Composition timeline not found after 8s"}})))},200)}_injectRuntime(){this._runtimeInjected=!0;try{let e=this.iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=Y,t.onload=()=>{},t.onerror=()=>{},(e.head||e.documentElement).appendChild(t)}catch{}}_updateScale(){let e=this.getBoundingClientRect();if(e.width===0||e.height===0)return;let t=Math.min(e.width/this._compositionWidth,e.height/this._compositionHeight);this.iframe.style.width=`${this._compositionWidth}px`,this.iframe.style.height=`${this._compositionHeight}px`,this.iframe.style.transform=`translate(-50%, -50%) scale(${t})`}_setupControls(){if(this.controlsApi)return;let e={onPlay:()=>this.play(),onPause:()=>this.pause(),onSeek:i=>this.seek(i*this._duration),onSpeedChange:i=>{this.playbackRate=i}},t=this.getAttribute("speed-presets"),s=t?t.split(",").map(Number).filter(i=>!isNaN(i)&&i>0):void 0;this.controlsApi=D(this.shadow,e,{speedPresets:s})}_setupPoster(){let e=this.getAttribute("poster");if(!e){this.posterEl?.remove(),this.posterEl=null;return}this.posterEl||(this.posterEl=document.createElement("img"),this.posterEl.className="hfp-poster",this.shadow.appendChild(this.posterEl)),this.posterEl.src=e}_playParentMedia(){for(let e of this._parentMedia)e.el.src&&e.el.play().then(()=>{this._muteIframeMedia()}).catch(()=>{})}_muteIframeMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let s of t)s.volume=0}catch{}}_pauseParentMedia(){for(let e of this._parentMedia)e.el.pause()}_createParentMedia(e,t,s,i){if(this._parentMedia.some(d=>d.el.src===e))return;let a=t==="video"?document.createElement("video"):new Audio;a.preload="auto",a.src=e,a.load(),a.muted=this.muted,this.playbackRate!==1&&(a.playbackRate=this.playbackRate),this._parentMedia.push({el:a,start:s,duration:i})}_setupParentAudioFromUrl(e){this._createParentMedia(e,"audio",0,1/0)}_setupParentMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let s of t){let i=s.getAttribute("src")||s.querySelector("source")?.getAttribute("src");if(!i)continue;let a=new URL(i,s.ownerDocument.baseURI).href,d=parseFloat(s.getAttribute("data-start")||"0"),l=parseFloat(s.getAttribute("data-duration")||"Infinity"),c=s.tagName==="VIDEO"?"video":"audio";this._createParentMedia(a,c,d,l)}}catch{}}_hidePoster(){this.posterEl?.remove(),this.posterEl=null}};customElements.get("hyperframes-player")||customElements.define("hyperframes-player",x);return $(q);})();
198
+ `,C='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',H='<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>';var k=[.25,.5,1,1.5,2,4];function v(o){return Number.isInteger(o)?`${o}x`:`${o}x`}function E(o){if(!Number.isFinite(o)||o<0)return"0:00";let e=Math.floor(o),t=Math.floor(e/60),i=e%60;return`${t}:${i.toString().padStart(2,"0")}`}function D(o,e,t={}){let i=t.speedPresets??k,s=document.createElement("div");s.className="hfp-controls",s.addEventListener("click",r=>{r.stopPropagation()});let a=document.createElement("button");a.className="hfp-play-btn",a.type="button",a.innerHTML=C,a.setAttribute("aria-label","Play");let d=document.createElement("div");d.className="hfp-scrubber";let h=document.createElement("div");h.className="hfp-progress",h.style.width="0%",d.appendChild(h);let c=document.createElement("span");c.className="hfp-time",c.textContent="0:00 / 0:00";let l=document.createElement("div");l.className="hfp-speed-wrap";let p=document.createElement("button");p.className="hfp-speed-btn",p.type="button",p.textContent="1x",p.setAttribute("aria-label","Playback speed");let u=document.createElement("div");u.className="hfp-speed-menu",u.setAttribute("role","menu");for(let r of i){let n=document.createElement("button");n.className="hfp-speed-option",n.type="button",n.setAttribute("role","menuitem"),n.dataset.speed=String(r),n.textContent=v(r),r===1&&n.classList.add("hfp-active"),u.appendChild(n)}l.appendChild(u),l.appendChild(p),s.appendChild(a),s.appendChild(d),s.appendChild(c),s.appendChild(l),o.appendChild(s);let _=!1,b=null,g=i.indexOf(1);g===-1&&(g=0),a.addEventListener("click",r=>{r.stopPropagation(),_?e.onPause():e.onPlay()});let M=r=>{for(let n of u.querySelectorAll(".hfp-speed-option"))n.classList.toggle("hfp-active",n.dataset.speed===String(r))};p.addEventListener("click",r=>{r.stopPropagation();let n=u.classList.toggle("hfp-open");p.setAttribute("aria-expanded",String(n))}),u.addEventListener("click",r=>{r.stopPropagation();let n=r.target.closest(".hfp-speed-option");if(!n)return;let m=parseFloat(n.dataset.speed);g=i.indexOf(m),p.textContent=v(m),M(m),u.classList.remove("hfp-open"),p.setAttribute("aria-expanded","false"),e.onSpeedChange(m)});let A=()=>{u.classList.remove("hfp-open"),p.setAttribute("aria-expanded","false")};document.addEventListener("click",A);let y=r=>{let n=d.getBoundingClientRect(),m=Math.max(0,Math.min(1,(r-n.left)/n.width));e.onSeek(m)},f=!1;d.addEventListener("mousedown",r=>{r.stopPropagation(),f=!0,y(r.clientX)});let T=r=>{f&&y(r.clientX)},L=()=>{f=!1};document.addEventListener("mousemove",T),document.addEventListener("mouseup",L),d.addEventListener("touchstart",r=>{f=!0;let n=r.touches[0];n&&y(n.clientX)},{passive:!0});let S=r=>{if(f){let n=r.touches[0];n&&y(n.clientX)}},I=()=>{f=!1};document.addEventListener("touchmove",S,{passive:!0}),document.addEventListener("touchend",I);let R=()=>{b&&clearTimeout(b),b=setTimeout(()=>{_&&s.classList.add("hfp-hidden")},3e3)},O=o instanceof ShadowRoot?o.host:o;return O.addEventListener("mousemove",()=>{s.classList.remove("hfp-hidden"),R()}),O.addEventListener("mouseleave",()=>{_&&s.classList.add("hfp-hidden")}),{updateTime(r,n){let m=n>0?r/n*100:0;h.style.width=`${m}%`,c.textContent=`${E(r)} / ${E(n)}`},updatePlaying(r){_=r,a.innerHTML=r?H:C,a.setAttribute("aria-label",r?"Pause":"Play"),r?R():s.classList.remove("hfp-hidden")},updateSpeed(r){let n=i.indexOf(r);n!==-1&&(g=n),p.textContent=v(r),M(r)},show(){s.style.display=""},hide(){s.style.display="none"},destroy(){document.removeEventListener("mousemove",T),document.removeEventListener("mouseup",L),document.removeEventListener("touchmove",S),document.removeEventListener("touchend",I),document.removeEventListener("click",A),b&&clearTimeout(b)}}}var P=30,Y="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",w=class extends HTMLElement{static get observedAttributes(){return["src","width","height","controls","muted","poster","playback-rate","audio-src"]}shadow;container;iframe;posterEl=null;controlsApi=null;resizeObserver;_ready=!1;_duration=0;_currentTime=0;_paused=!0;_compositionWidth=1920;_compositionHeight=1080;_probeInterval=null;_lastUpdateMs=0;_parentMedia=[];_audioOwner="runtime";constructor(){super(),this.shadow=this.attachShadow({mode:"open"});let e=document.createElement("style");e.textContent=N,this.shadow.appendChild(e),this.container=document.createElement("div"),this.container.className="hfp-container",this.iframe=document.createElement("iframe"),this.iframe.className="hfp-iframe",this.iframe.sandbox.add("allow-scripts","allow-same-origin"),this.iframe.allow="autoplay; fullscreen",this.iframe.referrerPolicy="no-referrer",this.iframe.title="HyperFrames Composition",this.container.appendChild(this.iframe),this.shadow.appendChild(this.container),this.addEventListener("click",t=>{this._isControlsClick(t)||(this._paused?this.play():this.pause())}),this.resizeObserver=new ResizeObserver(()=>this._updateScale()),this._onMessage=this._onMessage.bind(this),this._onIframeLoad=this._onIframeLoad.bind(this)}connectedCallback(){this.resizeObserver.observe(this),window.addEventListener("message",this._onMessage),this.iframe.addEventListener("load",this._onIframeLoad),this.hasAttribute("controls")&&this._setupControls(),this.hasAttribute("poster")&&this._setupPoster(),this.hasAttribute("audio-src")&&this._setupParentAudioFromUrl(this.getAttribute("audio-src")),this.hasAttribute("src")&&(this.iframe.src=this.getAttribute("src"))}disconnectedCallback(){this.resizeObserver.disconnect(),window.removeEventListener("message",this._onMessage),this.iframe.removeEventListener("load",this._onIframeLoad),this._probeInterval&&clearInterval(this._probeInterval),this.controlsApi?.destroy();for(let e of this._parentMedia)e.el.pause(),e.el.src="";this._parentMedia=[]}attributeChangedCallback(e,t,i){switch(e){case"src":i&&(this._ready=!1,this.iframe.src=i);break;case"width":this._compositionWidth=parseInt(i||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(i||"1080",10),this._updateScale();break;case"controls":i!==null?this._setupControls():(this.controlsApi?.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{let s=parseFloat(i||"1");for(let a of this._parentMedia)a.el.playbackRate=s;this._sendControl("set-playback-rate",{playbackRate:s}),this.controlsApi?.updateSpeed(s),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(let s of this._parentMedia)s.el.muted=i!==null;this._sendControl("set-muted",{muted:i!==null});break;case"audio-src":i&&this._setupParentAudioFromUrl(i);break}}get iframeElement(){return this.iframe}play(){this._hidePoster(),this._sendControl("play"),this._audioOwner==="parent"&&this._playParentMedia(),this._paused=!1,this.controlsApi?.updatePlaying(!0),this.dispatchEvent(new Event("play"))}pause(){this._sendControl("pause"),this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("pause"))}seek(e){let t=Math.round(e*P);if(this._sendControl("seek",{frame:t}),this._currentTime=e,this._audioOwner==="parent")for(let i of this._parentMedia){let s=e-i.start;s>=0&&s<i.duration&&(i.el.currentTime=s)}this._paused=!0,this.controlsApi?.updatePlaying(!1),this.controlsApi?.updateTime(this._currentTime,this._duration)}get currentTime(){return this._currentTime}set currentTime(e){this.seek(e)}get duration(){return this._duration}get paused(){return this._paused}get ready(){return this._ready}get playbackRate(){return parseFloat(this.getAttribute("playback-rate")||"1")}set playbackRate(e){this.setAttribute("playback-rate",String(e))}get muted(){return this.hasAttribute("muted")}set muted(e){e?this.setAttribute("muted",""):this.removeAttribute("muted")}get loop(){return this.hasAttribute("loop")}set loop(e){e?this.setAttribute("loop",""):this.removeAttribute("loop")}_sendControl(e,t={}){try{this.iframe.contentWindow?.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){if(e.source!==this.iframe.contentWindow)return;let t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="state"){this._currentTime=(t.frame??0)/P;let i=!this._paused;this._paused=!t.isPlaying,this._audioOwner==="parent"&&(i&&this._paused?this._pauseParentMedia():!i&&!this._paused&&this._playParentMedia(),this._mirrorParentMediaTime(this._currentTime));let s=performance.now();(s-this._lastUpdateMs>100||this._paused!==i)&&(this._lastUpdateMs=s,this.controlsApi?.updateTime(this._currentTime,this._duration),this.controlsApi?.updatePlaying(!this._paused),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{currentTime:this._currentTime}}))),this._currentTime>=this._duration&&!this._paused&&(this._audioOwner==="parent"&&this._pauseParentMedia(),this.loop?(this.seek(0),this.play()):(this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("ended"))))}t.type==="media-autoplay-blocked"&&this._promoteToParentProxy(),t.type==="timeline"&&t.durationInFrames>0&&Number.isFinite(t.durationInFrames)&&(this._duration=t.durationInFrames/P,this.controlsApi?.updateTime(this._currentTime,this._duration)),t.type==="stage-size"&&t.width>0&&t.height>0&&(this._compositionWidth=t.width,this._compositionHeight=t.height,this._updateScale())}}_runtimeInjected=!1;_onIframeLoad(){let e=0;this._runtimeInjected=!1,this._probeInterval&&clearInterval(this._probeInterval),this._probeInterval=setInterval(()=>{e++;try{let t=this.iframe.contentWindow;if(!t)return;let i=!!(t.__hf||t.__player),s=!!(t.__timelines&&Object.keys(t.__timelines).length>0);if(!i&&s&&!this._runtimeInjected&&e>=5){this._injectRuntime();return}if(this._runtimeInjected&&!i)return;let d=(()=>{if(t.__player&&typeof t.__player.getDuration=="function")return t.__player;if(t.__timelines){let h=Object.keys(t.__timelines);if(h.length>0){let c=this.iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),l=c&&c in t.__timelines?c:h[h.length-1],p=t.__timelines[l];return{getDuration:()=>p.duration()}}}return null})();if(d&&d.getDuration()>0){clearInterval(this._probeInterval),this._duration=d.getDuration(),this._ready=!0,this.controlsApi?.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));let c=this.iframe.contentDocument?.querySelector("[data-composition-id]");if(c){let l=parseInt(c.getAttribute("data-width")||"0",10),p=parseInt(c.getAttribute("data-height")||"0",10);l>0&&p>0&&(this._compositionWidth=l,this._compositionHeight=p,this._updateScale())}this._setupParentMedia(),this.hasAttribute("autoplay")&&this.play();return}}catch{}e>=40&&(clearInterval(this._probeInterval),this.dispatchEvent(new CustomEvent("error",{detail:{message:"Composition timeline not found after 8s"}})))},200)}_injectRuntime(){this._runtimeInjected=!0;try{let e=this.iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=Y,t.onload=()=>{},t.onerror=()=>{},(e.head||e.documentElement).appendChild(t)}catch{}}_updateScale(){let e=this.getBoundingClientRect();if(e.width===0||e.height===0)return;let t=Math.min(e.width/this._compositionWidth,e.height/this._compositionHeight);this.iframe.style.width=`${this._compositionWidth}px`,this.iframe.style.height=`${this._compositionHeight}px`,this.iframe.style.transform=`translate(-50%, -50%) scale(${t})`}_setupControls(){if(this.controlsApi)return;let e={onPlay:()=>this.play(),onPause:()=>this.pause(),onSeek:s=>this.seek(s*this._duration),onSpeedChange:s=>{this.playbackRate=s}},t=this.getAttribute("speed-presets"),i=t?t.split(",").map(Number).filter(s=>!isNaN(s)&&s>0):void 0;this.controlsApi=D(this.shadow,e,{speedPresets:i})}_setupPoster(){let e=this.getAttribute("poster");if(!e){this.posterEl?.remove(),this.posterEl=null;return}this.posterEl||(this.posterEl=document.createElement("img"),this.posterEl.className="hfp-poster",this.shadow.appendChild(this.posterEl)),this.posterEl.src=e}_playParentMedia(){for(let e of this._parentMedia)e.el.src&&e.el.play().catch(()=>{})}_pauseParentMedia(){for(let e of this._parentMedia)e.el.pause()}_mirrorParentMediaTime(e){for(let t of this._parentMedia){let i=e-t.start;i<0||i>=t.duration||Math.abs(t.el.currentTime-i)>.15&&(t.el.currentTime=i)}}_promoteToParentProxy(){this._audioOwner!=="parent"&&(this._audioOwner="parent",this._sendControl("set-media-output-muted",{muted:!0}),this._mirrorParentMediaTime(this._currentTime),this._paused||this._playParentMedia())}_createParentMedia(e,t,i,s){if(this._parentMedia.some(d=>d.el.src===e))return;let a=t==="video"?document.createElement("video"):new Audio;a.preload="auto",a.src=e,a.load(),a.muted=this.muted,this.playbackRate!==1&&(a.playbackRate=this.playbackRate),this._parentMedia.push({el:a,start:i,duration:s})}_setupParentAudioFromUrl(e){this._createParentMedia(e,"audio",0,1/0)}_setupParentMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let i of t){let s=i.getAttribute("src")||i.querySelector("source")?.getAttribute("src");if(!s)continue;let a=new URL(s,i.ownerDocument.baseURI).href,d=parseFloat(i.getAttribute("data-start")||"0"),h=parseFloat(i.getAttribute("data-duration")||"Infinity"),c=i.tagName==="VIDEO"?"video":"audio";this._createParentMedia(a,c,d,h)}}catch{}}_hidePoster(){this.posterEl?.remove(),this.posterEl=null}};customElements.get("hyperframes-player")||customElements.define("hyperframes-player",w);return $(B);})();
199
199
  //# sourceMappingURL=hyperframes-player.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hyperframes-player.ts","../src/styles.ts","../src/controls.ts"],"sourcesContent":["import { createControls, SPEED_PRESETS, type ControlsCallbacks } from \"./controls.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\n\nconst DEFAULT_FPS = 30;\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\n\nclass HyperframesPlayer extends HTMLElement {\n static get observedAttributes() {\n return [\"src\", \"width\", \"height\", \"controls\", \"muted\", \"poster\", \"playback-rate\", \"audio-src\"];\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\n private _ready = false;\n private _duration = 0;\n private _currentTime = 0;\n private _paused = true;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _probeInterval: ReturnType<typeof setInterval> | null = null;\n private _lastUpdateMs = 0;\n\n /**\n * Parent-frame media elements for mobile playback.\n *\n * Mobile browsers block media.play() inside iframes when the user gesture\n * happened in the parent frame — postMessage doesn't transfer user activation\n * (per the User Activation v2 spec). We extract ALL media sources from the\n * iframe's timed elements (audio/video with data-start), play them in the\n * parent frame (where the gesture lives), and disable the iframe copies.\n */\n private _parentMedia: Array<{\n el: HTMLMediaElement;\n start: number;\n duration: number;\n }> = [];\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n this.shadow.appendChild(style);\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\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 if (this.hasAttribute(\"src\")) this.iframe.src = 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.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 = val;\n }\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 break;\n case \"audio-src\":\n if (val) this._setupParentAudioFromUrl(val);\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 this._playParentMedia();\n this._sendControl(\"play\");\n this._paused = false;\n this.controlsApi?.updatePlaying(true);\n this.dispatchEvent(new Event(\"play\"));\n }\n\n pause() {\n this._pauseParentMedia();\n this._sendControl(\"pause\");\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"pause\"));\n }\n\n seek(timeInSeconds: number) {\n const frame = Math.round(timeInSeconds * DEFAULT_FPS);\n this._sendControl(\"seek\", { frame });\n this._currentTime = timeInSeconds;\n\n // Sync parent media positions (accounting for each element's start offset)\n for (const m of this._parentMedia) {\n const relTime = timeInSeconds - m.start;\n if (relTime >= 0 && relTime < m.duration) {\n 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 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 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 _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 === \"state\") {\n this._currentTime = (data.frame ?? 0) / DEFAULT_FPS;\n const wasPlaying = !this._paused;\n this._paused = !data.isPlaying;\n\n // Sync parent media on runtime play/pause transitions (e.g. browser\n // throttling, visibility change, or scrubber interaction in the iframe).\n if (wasPlaying && this._paused) {\n this._pauseParentMedia();\n } else if (!wasPlaying && !this._paused) {\n this._playParentMedia();\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 (this._currentTime >= this._duration && !this._paused) {\n this._pauseParentMedia();\n if (this.loop) {\n this.seek(0);\n this.play();\n } else {\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n }\n }\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 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\n // Auto-inject runtime if GSAP timelines exist but no runtime bridge\n if (!hasRuntime && hasTimelines && !this._runtimeInjected && attempts >= 5) {\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 getAdapter = () => {\n if (win.__player && typeof win.__player.getDuration === \"function\") return win.__player;\n if (win.__timelines) {\n const keys = Object.keys(win.__timelines);\n if (keys.length > 0) {\n // Resolve the root composition id from the DOM — the outermost\n // `[data-composition-id]` element is the master. Bundled previews\n // register the root composition alongside sub-compositions, and\n // without this lookup Object.keys() order would make a\n // sub-composition's duration hijack the overall video length.\n const rootId = this.iframe.contentDocument\n ?.querySelector(\"[data-composition-id]\")\n ?.getAttribute(\"data-composition-id\");\n const key = rootId && rootId in win.__timelines ? rootId : keys[keys.length - 1];\n const tl = win.__timelines[key];\n return { getDuration: () => tl.duration() };\n }\n }\n return null;\n };\n\n const adapter = getAdapter();\n if (adapter && adapter.getDuration() > 0) {\n clearInterval(this._probeInterval!);\n this._duration = adapter.getDuration();\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 };\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 }\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) {\n m.el\n .play()\n .then(() => {\n // Parent play succeeded — mute the iframe copy to prevent double audio.\n // This runs asynchronously, so the runtime may briefly play both copies,\n // but the overlap is inaudible (same audio at the same position).\n this._muteIframeMedia();\n })\n .catch(() => {});\n }\n }\n }\n\n private _muteIframeMedia() {\n try {\n const doc = this.iframe.contentDocument;\n if (!doc) return;\n const mediaEls = doc.querySelectorAll<HTMLMediaElement>(\n \"audio[data-start], video[data-start]\",\n );\n for (const el of mediaEls) el.volume = 0;\n } catch {\n // cross-origin\n }\n }\n\n private _pauseParentMedia() {\n for (const m of this._parentMedia) m.el.pause();\n }\n\n /** Create a parent-frame media element, configure it, and start preloading. */\n private _createParentMedia(src: string, tag: \"audio\" | \"video\", start: number, duration: number) {\n // Deduplicate — browsers normalize URLs so we compare on the element after assignment\n if (this._parentMedia.some((m) => m.el.src === src)) return;\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 if (this.playbackRate !== 1) el.playbackRate = this.playbackRate;\n\n this._parentMedia.push({ el, start, duration });\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 * Extract ALL timed media (audio/video with data-start) from the iframe's\n * DOM and create parent-frame copies. Disables the iframe originals so the\n * runtime doesn't try to play them (which would fail on mobile and cause\n * double playback on desktop).\n *\n * If `audio-src` was already set, this just disables the iframe media.\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\n for (const iframeEl of mediaEls) {\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) continue;\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 this._createParentMedia(src, tag, start, duration);\n\n // DO NOT strip data-start, data-duration, or src from the iframe elements.\n // The runtime's syncRuntimeMedia queries audio[data-start] — removing these\n // attributes makes the runtime unable to find, sync, or play media clips.\n // The iframe copies remain fully functional for the runtime. On mobile,\n // parent copies provide the audible output via the component's play() method.\n // On desktop and in the studio (which calls __player.play() directly),\n // the runtime's own media sync handles playback.\n }\n } catch {\n // Cross-origin iframe — can't access DOM, fall back to iframe media\n }\n }\n\n private _hidePoster() {\n this.posterEl?.remove();\n this.posterEl = null;\n }\n}\n\nif (!customElements.get(\"hyperframes-player\")) {\n customElements.define(\"hyperframes-player\", HyperframesPlayer);\n}\n\nexport { HyperframesPlayer };\nexport { formatTime, formatSpeed, SPEED_PRESETS } from \"./controls.js\";\nexport type { ControlsCallbacks, ControlsOptions } from \"./controls.js\";\n","export const PLAYER_STYLES = /* css */ `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n background: #000;\n contain: layout style;\n }\n\n .hfp-container {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n }\n\n\n .hfp-iframe {\n position: absolute;\n top: 50%;\n left: 50%;\n border: none;\n pointer-events: none;\n }\n\n .hfp-poster {\n position: absolute;\n inset: 0;\n object-fit: contain;\n z-index: 1;\n pointer-events: none;\n }\n\n /* ── 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 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 }\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 border-radius: var(--hfp-scrubber-radius, 2px);\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\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>`;\n","import { PLAY_ICON, PAUSE_ICON } from \"./styles.js\";\n\nexport interface ControlsCallbacks {\n onPlay: () => void;\n onPause: () => void;\n onSeek: (fraction: number) => void;\n onSpeedChange: (speed: 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 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 controls.appendChild(playBtn);\n controls.appendChild(scrubber);\n controls.appendChild(time);\n controls.appendChild(speedWrap);\n parent.appendChild(controls);\n\n let isPlaying = false;\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 playBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (isPlaying) callbacks.onPause();\n else callbacks.onPlay();\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 const pct = duration > 0 ? (current / duration) * 100 : 0;\n progress.style.width = `${pct}%`;\n time.textContent = `${formatTime(current)} / ${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 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(\"click\", onDocClick);\n if (hideTimeout) clearTimeout(hideTimeout);\n },\n };\n}\n"],"mappings":"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","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","PLAYER_STYLES","PLAY_ICON","PAUSE_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","isPlaying","hideTimeout","speedIndex","setActiveOption","opt","isOpen","target","newSpeed","onDocClick","handleScrubAt","clientX","rect","fraction","scrubbing","onMouseMove","onMouseUp","touch","onTouchMove","onTouchEnd","startHideTimer","host","current","duration","pct","playing","PAUSE_ICON","idx","DEFAULT_FPS","RUNTIME_CDN_URL","HyperframesPlayer","style","PLAYER_STYLES","event","m","name","_old","val","rate","timeInSeconds","frame","relTime","t","r","l","action","extra","target","data","wasPlaying","now","attempts","win","hasRuntime","hasTimelines","adapter","keys","rootId","key","tl","root","w","h","doc","script","rect","scale","callbacks","fraction","speed","presetsAttr","speedPresets","n","createControls","url","mediaEls","el","src","tag","start","duration","audioSrc","iframeEl","rawSrc"]}
1
+ {"version":3,"sources":["../src/hyperframes-player.ts","../src/styles.ts","../src/controls.ts"],"sourcesContent":["import { createControls, SPEED_PRESETS, type ControlsCallbacks } from \"./controls.js\";\nimport { PLAYER_STYLES } from \"./styles.js\";\n\nconst DEFAULT_FPS = 30;\nconst RUNTIME_CDN_URL =\n \"https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js\";\n\nclass HyperframesPlayer extends HTMLElement {\n static get observedAttributes() {\n return [\"src\", \"width\", \"height\", \"controls\", \"muted\", \"poster\", \"playback-rate\", \"audio-src\"];\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\n private _ready = false;\n private _duration = 0;\n private _currentTime = 0;\n private _paused = true;\n private _compositionWidth = 1920;\n private _compositionHeight = 1080;\n private _probeInterval: ReturnType<typeof setInterval> | null = null;\n private _lastUpdateMs = 0;\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\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 constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n\n const style = document.createElement(\"style\");\n style.textContent = PLAYER_STYLES;\n this.shadow.appendChild(style);\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\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 if (this.hasAttribute(\"src\")) this.iframe.src = 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.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 = val;\n }\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 break;\n case \"audio-src\":\n if (val) this._setupParentAudioFromUrl(val);\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 // Always drive the iframe runtime — it's the single source of timeline\n // truth regardless of who owns audible output. When we own audio, the\n // proxies join; when the runtime owns, they stay silent.\n 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 }\n\n pause() {\n this._sendControl(\"pause\");\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 seek(timeInSeconds: number) {\n const frame = Math.round(timeInSeconds * DEFAULT_FPS);\n this._sendControl(\"seek\", { frame });\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 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 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 _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 === \"state\") {\n this._currentTime = (data.frame ?? 0) / DEFAULT_FPS;\n const wasPlaying = !this._paused;\n this._paused = !data.isPlaying;\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 (this._currentTime >= this._duration && !this._paused) {\n if (this._audioOwner === \"parent\") this._pauseParentMedia();\n if (this.loop) {\n this.seek(0);\n this.play();\n } else {\n this._paused = true;\n this.controlsApi?.updatePlaying(false);\n this.dispatchEvent(new Event(\"ended\"));\n }\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 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\n // Auto-inject runtime if GSAP timelines exist but no runtime bridge\n if (!hasRuntime && hasTimelines && !this._runtimeInjected && attempts >= 5) {\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 getAdapter = () => {\n if (win.__player && typeof win.__player.getDuration === \"function\") return win.__player;\n if (win.__timelines) {\n const keys = Object.keys(win.__timelines);\n if (keys.length > 0) {\n // Resolve the root composition id from the DOM — the outermost\n // `[data-composition-id]` element is the master. Bundled previews\n // register the root composition alongside sub-compositions, and\n // without this lookup Object.keys() order would make a\n // sub-composition's duration hijack the overall video length.\n const rootId = this.iframe.contentDocument\n ?.querySelector(\"[data-composition-id]\")\n ?.getAttribute(\"data-composition-id\");\n const key = rootId && rootId in win.__timelines ? rootId : keys[keys.length - 1];\n const tl = win.__timelines[key];\n return { getDuration: () => tl.duration() };\n }\n }\n return null;\n };\n\n const adapter = getAdapter();\n if (adapter && adapter.getDuration() > 0) {\n clearInterval(this._probeInterval!);\n this._duration = adapter.getDuration();\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 };\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 }\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 // Best-effort: if the parent itself has no user activation, this will\n // also reject — the caller has already decided parent ownership is\n // warranted, and there's nothing better to fall back to from here.\n m.el.play().catch(() => {});\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. Only re-seeks when\n * drift exceeds 150 ms so we don't trigger a re-buffer on every tick —\n * native HTMLMediaElement playback rate drift stays well inside that.\n */\n private _mirrorParentMediaTime(timelineSeconds: number) {\n for (const m of this._parentMedia) {\n const relTime = timelineSeconds - m.start;\n if (relTime < 0 || relTime >= m.duration) continue;\n if (Math.abs(m.el.currentTime - relTime) > 0.15) m.el.currentTime = relTime;\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 this._sendControl(\"set-media-output-muted\", { muted: true });\n this._mirrorParentMediaTime(this._currentTime);\n if (!this._paused) this._playParentMedia();\n }\n\n /** Create a parent-frame media element, configure it, and start preloading. */\n private _createParentMedia(src: string, tag: \"audio\" | \"video\", start: number, duration: number) {\n // Deduplicate — browsers normalize URLs so we compare on the element after assignment\n if (this._parentMedia.some((m) => m.el.src === src)) return;\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 if (this.playbackRate !== 1) el.playbackRate = this.playbackRate;\n\n this._parentMedia.push({ el, start, duration });\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 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\n for (const iframeEl of mediaEls) {\n const rawSrc =\n iframeEl.getAttribute(\"src\") || iframeEl.querySelector(\"source\")?.getAttribute(\"src\");\n if (!rawSrc) continue;\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 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`\n // when (and only when) parent ownership is promoted.\n }\n } catch {\n // Cross-origin iframe — can't access DOM, fall back to iframe media\n }\n }\n\n private _hidePoster() {\n this.posterEl?.remove();\n this.posterEl = null;\n }\n}\n\nif (!customElements.get(\"hyperframes-player\")) {\n customElements.define(\"hyperframes-player\", HyperframesPlayer);\n}\n\nexport { HyperframesPlayer };\nexport { formatTime, formatSpeed, SPEED_PRESETS } from \"./controls.js\";\nexport type { ControlsCallbacks, ControlsOptions } from \"./controls.js\";\n","export const PLAYER_STYLES = /* css */ `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n background: #000;\n contain: layout style;\n }\n\n .hfp-container {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n }\n\n\n .hfp-iframe {\n position: absolute;\n top: 50%;\n left: 50%;\n border: none;\n pointer-events: none;\n }\n\n .hfp-poster {\n position: absolute;\n inset: 0;\n object-fit: contain;\n z-index: 1;\n pointer-events: none;\n }\n\n /* ── 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 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 }\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 border-radius: var(--hfp-scrubber-radius, 2px);\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\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>`;\n","import { PLAY_ICON, PAUSE_ICON } from \"./styles.js\";\n\nexport interface ControlsCallbacks {\n onPlay: () => void;\n onPause: () => void;\n onSeek: (fraction: number) => void;\n onSpeedChange: (speed: 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 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 controls.appendChild(playBtn);\n controls.appendChild(scrubber);\n controls.appendChild(time);\n controls.appendChild(speedWrap);\n parent.appendChild(controls);\n\n let isPlaying = false;\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 playBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (isPlaying) callbacks.onPause();\n else callbacks.onPlay();\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 const pct = duration > 0 ? (current / duration) * 100 : 0;\n progress.style.width = `${pct}%`;\n time.textContent = `${formatTime(current)} / ${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 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(\"click\", onDocClick);\n if (hideTimeout) clearTimeout(hideTimeout);\n },\n };\n}\n"],"mappings":"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","names":["hyperframes_player_exports","__export","HyperframesPlayer","SPEED_PRESETS","formatSpeed","formatTime","PLAYER_STYLES","PLAY_ICON","PAUSE_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","isPlaying","hideTimeout","speedIndex","setActiveOption","opt","isOpen","target","newSpeed","onDocClick","handleScrubAt","clientX","rect","fraction","scrubbing","onMouseMove","onMouseUp","touch","onTouchMove","onTouchEnd","startHideTimer","host","current","duration","pct","playing","PAUSE_ICON","idx","DEFAULT_FPS","RUNTIME_CDN_URL","HyperframesPlayer","style","PLAYER_STYLES","event","m","name","_old","val","rate","timeInSeconds","frame","relTime","t","r","l","action","extra","target","data","wasPlaying","now","attempts","win","hasRuntime","hasTimelines","adapter","keys","rootId","key","tl","root","w","h","doc","script","rect","scale","callbacks","fraction","speed","presetsAttr","speedPresets","n","createControls","url","timelineSeconds","src","tag","start","duration","el","audioSrc","mediaEls","iframeEl","rawSrc"]}
@@ -195,5 +195,5 @@ var R=`
195
195
  color: var(--hfp-accent, #fff);
196
196
  font-weight: 600;
197
197
  }
198
- `,E='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',N='<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>';var H=[.25,.5,1,1.5,2,4];function y(c){return Number.isInteger(c)?`${c}x`:`${c}x`}function x(c){if(!Number.isFinite(c)||c<0)return"0:00";let e=Math.floor(c),t=Math.floor(e/60),i=e%60;return`${t}:${i.toString().padStart(2,"0")}`}function O(c,e,t={}){let i=t.speedPresets??H,s=document.createElement("div");s.className="hfp-controls",s.addEventListener("click",r=>{r.stopPropagation()});let o=document.createElement("button");o.className="hfp-play-btn",o.type="button",o.innerHTML=E,o.setAttribute("aria-label","Play");let p=document.createElement("div");p.className="hfp-scrubber";let l=document.createElement("div");l.className="hfp-progress",l.style.width="0%",p.appendChild(l);let d=document.createElement("span");d.className="hfp-time",d.textContent="0:00 / 0:00";let h=document.createElement("div");h.className="hfp-speed-wrap";let a=document.createElement("button");a.className="hfp-speed-btn",a.type="button",a.textContent="1x",a.setAttribute("aria-label","Playback speed");let u=document.createElement("div");u.className="hfp-speed-menu",u.setAttribute("role","menu");for(let r of i){let n=document.createElement("button");n.className="hfp-speed-option",n.type="button",n.setAttribute("role","menuitem"),n.dataset.speed=String(r),n.textContent=y(r),r===1&&n.classList.add("hfp-active"),u.appendChild(n)}h.appendChild(u),h.appendChild(a),s.appendChild(o),s.appendChild(p),s.appendChild(d),s.appendChild(h),c.appendChild(s);let v=!1,b=null,g=i.indexOf(1);g===-1&&(g=0),o.addEventListener("click",r=>{r.stopPropagation(),v?e.onPause():e.onPlay()});let k=r=>{for(let n of u.querySelectorAll(".hfp-speed-option"))n.classList.toggle("hfp-active",n.dataset.speed===String(r))};a.addEventListener("click",r=>{r.stopPropagation();let n=u.classList.toggle("hfp-open");a.setAttribute("aria-expanded",String(n))}),u.addEventListener("click",r=>{r.stopPropagation();let n=r.target.closest(".hfp-speed-option");if(!n)return;let m=parseFloat(n.dataset.speed);g=i.indexOf(m),a.textContent=y(m),k(m),u.classList.remove("hfp-open"),a.setAttribute("aria-expanded","false"),e.onSpeedChange(m)});let A=()=>{u.classList.remove("hfp-open"),a.setAttribute("aria-expanded","false")};document.addEventListener("click",A);let _=r=>{let n=p.getBoundingClientRect(),m=Math.max(0,Math.min(1,(r-n.left)/n.width));e.onSeek(m)},f=!1;p.addEventListener("mousedown",r=>{r.stopPropagation(),f=!0,_(r.clientX)});let M=r=>{f&&_(r.clientX)},L=()=>{f=!1};document.addEventListener("mousemove",M),document.addEventListener("mouseup",L),p.addEventListener("touchstart",r=>{f=!0;let n=r.touches[0];n&&_(n.clientX)},{passive:!0});let P=r=>{if(f){let n=r.touches[0];n&&_(n.clientX)}},T=()=>{f=!1};document.addEventListener("touchmove",P,{passive:!0}),document.addEventListener("touchend",T);let S=()=>{b&&clearTimeout(b),b=setTimeout(()=>{v&&s.classList.add("hfp-hidden")},3e3)},I=c instanceof ShadowRoot?c.host:c;return I.addEventListener("mousemove",()=>{s.classList.remove("hfp-hidden"),S()}),I.addEventListener("mouseleave",()=>{v&&s.classList.add("hfp-hidden")}),{updateTime(r,n){let m=n>0?r/n*100:0;l.style.width=`${m}%`,d.textContent=`${x(r)} / ${x(n)}`},updatePlaying(r){v=r,o.innerHTML=r?N:E,o.setAttribute("aria-label",r?"Pause":"Play"),r?S():s.classList.remove("hfp-hidden")},updateSpeed(r){let n=i.indexOf(r);n!==-1&&(g=n),a.textContent=y(r),k(r)},show(){s.style.display=""},hide(){s.style.display="none"},destroy(){document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",L),document.removeEventListener("touchmove",P),document.removeEventListener("touchend",T),document.removeEventListener("click",A),b&&clearTimeout(b)}}}var w=30,D="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",C=class extends HTMLElement{static get observedAttributes(){return["src","width","height","controls","muted","poster","playback-rate","audio-src"]}shadow;container;iframe;posterEl=null;controlsApi=null;resizeObserver;_ready=!1;_duration=0;_currentTime=0;_paused=!0;_compositionWidth=1920;_compositionHeight=1080;_probeInterval=null;_lastUpdateMs=0;_parentMedia=[];constructor(){super(),this.shadow=this.attachShadow({mode:"open"});let e=document.createElement("style");e.textContent=R,this.shadow.appendChild(e),this.container=document.createElement("div"),this.container.className="hfp-container",this.iframe=document.createElement("iframe"),this.iframe.className="hfp-iframe",this.iframe.sandbox.add("allow-scripts","allow-same-origin"),this.iframe.allow="autoplay; fullscreen",this.iframe.referrerPolicy="no-referrer",this.iframe.title="HyperFrames Composition",this.container.appendChild(this.iframe),this.shadow.appendChild(this.container),this.addEventListener("click",t=>{this._isControlsClick(t)||(this._paused?this.play():this.pause())}),this.resizeObserver=new ResizeObserver(()=>this._updateScale()),this._onMessage=this._onMessage.bind(this),this._onIframeLoad=this._onIframeLoad.bind(this)}connectedCallback(){this.resizeObserver.observe(this),window.addEventListener("message",this._onMessage),this.iframe.addEventListener("load",this._onIframeLoad),this.hasAttribute("controls")&&this._setupControls(),this.hasAttribute("poster")&&this._setupPoster(),this.hasAttribute("audio-src")&&this._setupParentAudioFromUrl(this.getAttribute("audio-src")),this.hasAttribute("src")&&(this.iframe.src=this.getAttribute("src"))}disconnectedCallback(){this.resizeObserver.disconnect(),window.removeEventListener("message",this._onMessage),this.iframe.removeEventListener("load",this._onIframeLoad),this._probeInterval&&clearInterval(this._probeInterval),this.controlsApi?.destroy();for(let e of this._parentMedia)e.el.pause(),e.el.src="";this._parentMedia=[]}attributeChangedCallback(e,t,i){switch(e){case"src":i&&(this._ready=!1,this.iframe.src=i);break;case"width":this._compositionWidth=parseInt(i||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(i||"1080",10),this._updateScale();break;case"controls":i!==null?this._setupControls():(this.controlsApi?.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{let s=parseFloat(i||"1");for(let o of this._parentMedia)o.el.playbackRate=s;this._sendControl("set-playback-rate",{playbackRate:s}),this.controlsApi?.updateSpeed(s),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(let s of this._parentMedia)s.el.muted=i!==null;this._sendControl("set-muted",{muted:i!==null});break;case"audio-src":i&&this._setupParentAudioFromUrl(i);break}}get iframeElement(){return this.iframe}play(){this._hidePoster(),this._playParentMedia(),this._sendControl("play"),this._paused=!1,this.controlsApi?.updatePlaying(!0),this.dispatchEvent(new Event("play"))}pause(){this._pauseParentMedia(),this._sendControl("pause"),this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("pause"))}seek(e){let t=Math.round(e*w);this._sendControl("seek",{frame:t}),this._currentTime=e;for(let i of this._parentMedia){let s=e-i.start;s>=0&&s<i.duration&&(i.el.currentTime=s)}this._paused=!0,this.controlsApi?.updatePlaying(!1),this.controlsApi?.updateTime(this._currentTime,this._duration)}get currentTime(){return this._currentTime}set currentTime(e){this.seek(e)}get duration(){return this._duration}get paused(){return this._paused}get ready(){return this._ready}get playbackRate(){return parseFloat(this.getAttribute("playback-rate")||"1")}set playbackRate(e){this.setAttribute("playback-rate",String(e))}get muted(){return this.hasAttribute("muted")}set muted(e){e?this.setAttribute("muted",""):this.removeAttribute("muted")}get loop(){return this.hasAttribute("loop")}set loop(e){e?this.setAttribute("loop",""):this.removeAttribute("loop")}_sendControl(e,t={}){try{this.iframe.contentWindow?.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){if(e.source!==this.iframe.contentWindow)return;let t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="state"){this._currentTime=(t.frame??0)/w;let i=!this._paused;this._paused=!t.isPlaying,i&&this._paused?this._pauseParentMedia():!i&&!this._paused&&this._playParentMedia();let s=performance.now();(s-this._lastUpdateMs>100||this._paused!==i)&&(this._lastUpdateMs=s,this.controlsApi?.updateTime(this._currentTime,this._duration),this.controlsApi?.updatePlaying(!this._paused),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{currentTime:this._currentTime}}))),this._currentTime>=this._duration&&!this._paused&&(this._pauseParentMedia(),this.loop?(this.seek(0),this.play()):(this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("ended"))))}t.type==="timeline"&&t.durationInFrames>0&&Number.isFinite(t.durationInFrames)&&(this._duration=t.durationInFrames/w,this.controlsApi?.updateTime(this._currentTime,this._duration)),t.type==="stage-size"&&t.width>0&&t.height>0&&(this._compositionWidth=t.width,this._compositionHeight=t.height,this._updateScale())}}_runtimeInjected=!1;_onIframeLoad(){let e=0;this._runtimeInjected=!1,this._probeInterval&&clearInterval(this._probeInterval),this._probeInterval=setInterval(()=>{e++;try{let t=this.iframe.contentWindow;if(!t)return;let i=!!(t.__hf||t.__player),s=!!(t.__timelines&&Object.keys(t.__timelines).length>0);if(!i&&s&&!this._runtimeInjected&&e>=5){this._injectRuntime();return}if(this._runtimeInjected&&!i)return;let p=(()=>{if(t.__player&&typeof t.__player.getDuration=="function")return t.__player;if(t.__timelines){let l=Object.keys(t.__timelines);if(l.length>0){let d=this.iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),h=d&&d in t.__timelines?d:l[l.length-1],a=t.__timelines[h];return{getDuration:()=>a.duration()}}}return null})();if(p&&p.getDuration()>0){clearInterval(this._probeInterval),this._duration=p.getDuration(),this._ready=!0,this.controlsApi?.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));let d=this.iframe.contentDocument?.querySelector("[data-composition-id]");if(d){let h=parseInt(d.getAttribute("data-width")||"0",10),a=parseInt(d.getAttribute("data-height")||"0",10);h>0&&a>0&&(this._compositionWidth=h,this._compositionHeight=a,this._updateScale())}this._setupParentMedia(),this.hasAttribute("autoplay")&&this.play();return}}catch{}e>=40&&(clearInterval(this._probeInterval),this.dispatchEvent(new CustomEvent("error",{detail:{message:"Composition timeline not found after 8s"}})))},200)}_injectRuntime(){this._runtimeInjected=!0;try{let e=this.iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=D,t.onload=()=>{},t.onerror=()=>{},(e.head||e.documentElement).appendChild(t)}catch{}}_updateScale(){let e=this.getBoundingClientRect();if(e.width===0||e.height===0)return;let t=Math.min(e.width/this._compositionWidth,e.height/this._compositionHeight);this.iframe.style.width=`${this._compositionWidth}px`,this.iframe.style.height=`${this._compositionHeight}px`,this.iframe.style.transform=`translate(-50%, -50%) scale(${t})`}_setupControls(){if(this.controlsApi)return;let e={onPlay:()=>this.play(),onPause:()=>this.pause(),onSeek:s=>this.seek(s*this._duration),onSpeedChange:s=>{this.playbackRate=s}},t=this.getAttribute("speed-presets"),i=t?t.split(",").map(Number).filter(s=>!isNaN(s)&&s>0):void 0;this.controlsApi=O(this.shadow,e,{speedPresets:i})}_setupPoster(){let e=this.getAttribute("poster");if(!e){this.posterEl?.remove(),this.posterEl=null;return}this.posterEl||(this.posterEl=document.createElement("img"),this.posterEl.className="hfp-poster",this.shadow.appendChild(this.posterEl)),this.posterEl.src=e}_playParentMedia(){for(let e of this._parentMedia)e.el.src&&e.el.play().then(()=>{this._muteIframeMedia()}).catch(()=>{})}_muteIframeMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let i of t)i.volume=0}catch{}}_pauseParentMedia(){for(let e of this._parentMedia)e.el.pause()}_createParentMedia(e,t,i,s){if(this._parentMedia.some(p=>p.el.src===e))return;let o=t==="video"?document.createElement("video"):new Audio;o.preload="auto",o.src=e,o.load(),o.muted=this.muted,this.playbackRate!==1&&(o.playbackRate=this.playbackRate),this._parentMedia.push({el:o,start:i,duration:s})}_setupParentAudioFromUrl(e){this._createParentMedia(e,"audio",0,1/0)}_setupParentMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let i of t){let s=i.getAttribute("src")||i.querySelector("source")?.getAttribute("src");if(!s)continue;let o=new URL(s,i.ownerDocument.baseURI).href,p=parseFloat(i.getAttribute("data-start")||"0"),l=parseFloat(i.getAttribute("data-duration")||"Infinity"),d=i.tagName==="VIDEO"?"video":"audio";this._createParentMedia(o,d,p,l)}}catch{}}_hidePoster(){this.posterEl?.remove(),this.posterEl=null}};customElements.get("hyperframes-player")||customElements.define("hyperframes-player",C);export{C as HyperframesPlayer,H as SPEED_PRESETS,y as formatSpeed,x as formatTime};
198
+ `,E='<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>',O='<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>';var N=[.25,.5,1,1.5,2,4];function y(c){return Number.isInteger(c)?`${c}x`:`${c}x`}function w(c){if(!Number.isFinite(c)||c<0)return"0:00";let e=Math.floor(c),t=Math.floor(e/60),i=e%60;return`${t}:${i.toString().padStart(2,"0")}`}function H(c,e,t={}){let i=t.speedPresets??N,s=document.createElement("div");s.className="hfp-controls",s.addEventListener("click",r=>{r.stopPropagation()});let o=document.createElement("button");o.className="hfp-play-btn",o.type="button",o.innerHTML=E,o.setAttribute("aria-label","Play");let p=document.createElement("div");p.className="hfp-scrubber";let h=document.createElement("div");h.className="hfp-progress",h.style.width="0%",p.appendChild(h);let d=document.createElement("span");d.className="hfp-time",d.textContent="0:00 / 0:00";let l=document.createElement("div");l.className="hfp-speed-wrap";let a=document.createElement("button");a.className="hfp-speed-btn",a.type="button",a.textContent="1x",a.setAttribute("aria-label","Playback speed");let u=document.createElement("div");u.className="hfp-speed-menu",u.setAttribute("role","menu");for(let r of i){let n=document.createElement("button");n.className="hfp-speed-option",n.type="button",n.setAttribute("role","menuitem"),n.dataset.speed=String(r),n.textContent=y(r),r===1&&n.classList.add("hfp-active"),u.appendChild(n)}l.appendChild(u),l.appendChild(a),s.appendChild(o),s.appendChild(p),s.appendChild(d),s.appendChild(l),c.appendChild(s);let v=!1,b=null,_=i.indexOf(1);_===-1&&(_=0),o.addEventListener("click",r=>{r.stopPropagation(),v?e.onPause():e.onPlay()});let k=r=>{for(let n of u.querySelectorAll(".hfp-speed-option"))n.classList.toggle("hfp-active",n.dataset.speed===String(r))};a.addEventListener("click",r=>{r.stopPropagation();let n=u.classList.toggle("hfp-open");a.setAttribute("aria-expanded",String(n))}),u.addEventListener("click",r=>{r.stopPropagation();let n=r.target.closest(".hfp-speed-option");if(!n)return;let m=parseFloat(n.dataset.speed);_=i.indexOf(m),a.textContent=y(m),k(m),u.classList.remove("hfp-open"),a.setAttribute("aria-expanded","false"),e.onSpeedChange(m)});let P=()=>{u.classList.remove("hfp-open"),a.setAttribute("aria-expanded","false")};document.addEventListener("click",P);let g=r=>{let n=p.getBoundingClientRect(),m=Math.max(0,Math.min(1,(r-n.left)/n.width));e.onSeek(m)},f=!1;p.addEventListener("mousedown",r=>{r.stopPropagation(),f=!0,g(r.clientX)});let M=r=>{f&&g(r.clientX)},A=()=>{f=!1};document.addEventListener("mousemove",M),document.addEventListener("mouseup",A),p.addEventListener("touchstart",r=>{f=!0;let n=r.touches[0];n&&g(n.clientX)},{passive:!0});let T=r=>{if(f){let n=r.touches[0];n&&g(n.clientX)}},L=()=>{f=!1};document.addEventListener("touchmove",T,{passive:!0}),document.addEventListener("touchend",L);let S=()=>{b&&clearTimeout(b),b=setTimeout(()=>{v&&s.classList.add("hfp-hidden")},3e3)},I=c instanceof ShadowRoot?c.host:c;return I.addEventListener("mousemove",()=>{s.classList.remove("hfp-hidden"),S()}),I.addEventListener("mouseleave",()=>{v&&s.classList.add("hfp-hidden")}),{updateTime(r,n){let m=n>0?r/n*100:0;h.style.width=`${m}%`,d.textContent=`${w(r)} / ${w(n)}`},updatePlaying(r){v=r,o.innerHTML=r?O:E,o.setAttribute("aria-label",r?"Pause":"Play"),r?S():s.classList.remove("hfp-hidden")},updateSpeed(r){let n=i.indexOf(r);n!==-1&&(_=n),a.textContent=y(r),k(r)},show(){s.style.display=""},hide(){s.style.display="none"},destroy(){document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",A),document.removeEventListener("touchmove",T),document.removeEventListener("touchend",L),document.removeEventListener("click",P),b&&clearTimeout(b)}}}var x=30,D="https://cdn.jsdelivr.net/npm/@hyperframes/core/dist/hyperframe.runtime.iife.js",C=class extends HTMLElement{static get observedAttributes(){return["src","width","height","controls","muted","poster","playback-rate","audio-src"]}shadow;container;iframe;posterEl=null;controlsApi=null;resizeObserver;_ready=!1;_duration=0;_currentTime=0;_paused=!0;_compositionWidth=1920;_compositionHeight=1080;_probeInterval=null;_lastUpdateMs=0;_parentMedia=[];_audioOwner="runtime";constructor(){super(),this.shadow=this.attachShadow({mode:"open"});let e=document.createElement("style");e.textContent=R,this.shadow.appendChild(e),this.container=document.createElement("div"),this.container.className="hfp-container",this.iframe=document.createElement("iframe"),this.iframe.className="hfp-iframe",this.iframe.sandbox.add("allow-scripts","allow-same-origin"),this.iframe.allow="autoplay; fullscreen",this.iframe.referrerPolicy="no-referrer",this.iframe.title="HyperFrames Composition",this.container.appendChild(this.iframe),this.shadow.appendChild(this.container),this.addEventListener("click",t=>{this._isControlsClick(t)||(this._paused?this.play():this.pause())}),this.resizeObserver=new ResizeObserver(()=>this._updateScale()),this._onMessage=this._onMessage.bind(this),this._onIframeLoad=this._onIframeLoad.bind(this)}connectedCallback(){this.resizeObserver.observe(this),window.addEventListener("message",this._onMessage),this.iframe.addEventListener("load",this._onIframeLoad),this.hasAttribute("controls")&&this._setupControls(),this.hasAttribute("poster")&&this._setupPoster(),this.hasAttribute("audio-src")&&this._setupParentAudioFromUrl(this.getAttribute("audio-src")),this.hasAttribute("src")&&(this.iframe.src=this.getAttribute("src"))}disconnectedCallback(){this.resizeObserver.disconnect(),window.removeEventListener("message",this._onMessage),this.iframe.removeEventListener("load",this._onIframeLoad),this._probeInterval&&clearInterval(this._probeInterval),this.controlsApi?.destroy();for(let e of this._parentMedia)e.el.pause(),e.el.src="";this._parentMedia=[]}attributeChangedCallback(e,t,i){switch(e){case"src":i&&(this._ready=!1,this.iframe.src=i);break;case"width":this._compositionWidth=parseInt(i||"1920",10),this._updateScale();break;case"height":this._compositionHeight=parseInt(i||"1080",10),this._updateScale();break;case"controls":i!==null?this._setupControls():(this.controlsApi?.destroy(),this.controlsApi=null);break;case"poster":this._setupPoster();break;case"playback-rate":{let s=parseFloat(i||"1");for(let o of this._parentMedia)o.el.playbackRate=s;this._sendControl("set-playback-rate",{playbackRate:s}),this.controlsApi?.updateSpeed(s),this.dispatchEvent(new Event("ratechange"));break}case"muted":for(let s of this._parentMedia)s.el.muted=i!==null;this._sendControl("set-muted",{muted:i!==null});break;case"audio-src":i&&this._setupParentAudioFromUrl(i);break}}get iframeElement(){return this.iframe}play(){this._hidePoster(),this._sendControl("play"),this._audioOwner==="parent"&&this._playParentMedia(),this._paused=!1,this.controlsApi?.updatePlaying(!0),this.dispatchEvent(new Event("play"))}pause(){this._sendControl("pause"),this._audioOwner==="parent"&&this._pauseParentMedia(),this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("pause"))}seek(e){let t=Math.round(e*x);if(this._sendControl("seek",{frame:t}),this._currentTime=e,this._audioOwner==="parent")for(let i of this._parentMedia){let s=e-i.start;s>=0&&s<i.duration&&(i.el.currentTime=s)}this._paused=!0,this.controlsApi?.updatePlaying(!1),this.controlsApi?.updateTime(this._currentTime,this._duration)}get currentTime(){return this._currentTime}set currentTime(e){this.seek(e)}get duration(){return this._duration}get paused(){return this._paused}get ready(){return this._ready}get playbackRate(){return parseFloat(this.getAttribute("playback-rate")||"1")}set playbackRate(e){this.setAttribute("playback-rate",String(e))}get muted(){return this.hasAttribute("muted")}set muted(e){e?this.setAttribute("muted",""):this.removeAttribute("muted")}get loop(){return this.hasAttribute("loop")}set loop(e){e?this.setAttribute("loop",""):this.removeAttribute("loop")}_sendControl(e,t={}){try{this.iframe.contentWindow?.postMessage({source:"hf-parent",type:"control",action:e,...t},"*")}catch{}}_isControlsClick(e){return e.composedPath().some(t=>t instanceof HTMLElement&&t.classList.contains("hfp-controls"))}_onMessage(e){if(e.source!==this.iframe.contentWindow)return;let t=e.data;if(!(!t||t.source!=="hf-preview")){if(t.type==="state"){this._currentTime=(t.frame??0)/x;let i=!this._paused;this._paused=!t.isPlaying,this._audioOwner==="parent"&&(i&&this._paused?this._pauseParentMedia():!i&&!this._paused&&this._playParentMedia(),this._mirrorParentMediaTime(this._currentTime));let s=performance.now();(s-this._lastUpdateMs>100||this._paused!==i)&&(this._lastUpdateMs=s,this.controlsApi?.updateTime(this._currentTime,this._duration),this.controlsApi?.updatePlaying(!this._paused),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{currentTime:this._currentTime}}))),this._currentTime>=this._duration&&!this._paused&&(this._audioOwner==="parent"&&this._pauseParentMedia(),this.loop?(this.seek(0),this.play()):(this._paused=!0,this.controlsApi?.updatePlaying(!1),this.dispatchEvent(new Event("ended"))))}t.type==="media-autoplay-blocked"&&this._promoteToParentProxy(),t.type==="timeline"&&t.durationInFrames>0&&Number.isFinite(t.durationInFrames)&&(this._duration=t.durationInFrames/x,this.controlsApi?.updateTime(this._currentTime,this._duration)),t.type==="stage-size"&&t.width>0&&t.height>0&&(this._compositionWidth=t.width,this._compositionHeight=t.height,this._updateScale())}}_runtimeInjected=!1;_onIframeLoad(){let e=0;this._runtimeInjected=!1,this._probeInterval&&clearInterval(this._probeInterval),this._probeInterval=setInterval(()=>{e++;try{let t=this.iframe.contentWindow;if(!t)return;let i=!!(t.__hf||t.__player),s=!!(t.__timelines&&Object.keys(t.__timelines).length>0);if(!i&&s&&!this._runtimeInjected&&e>=5){this._injectRuntime();return}if(this._runtimeInjected&&!i)return;let p=(()=>{if(t.__player&&typeof t.__player.getDuration=="function")return t.__player;if(t.__timelines){let h=Object.keys(t.__timelines);if(h.length>0){let d=this.iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),l=d&&d in t.__timelines?d:h[h.length-1],a=t.__timelines[l];return{getDuration:()=>a.duration()}}}return null})();if(p&&p.getDuration()>0){clearInterval(this._probeInterval),this._duration=p.getDuration(),this._ready=!0,this.controlsApi?.updateTime(0,this._duration),this.dispatchEvent(new CustomEvent("ready",{detail:{duration:this._duration}}));let d=this.iframe.contentDocument?.querySelector("[data-composition-id]");if(d){let l=parseInt(d.getAttribute("data-width")||"0",10),a=parseInt(d.getAttribute("data-height")||"0",10);l>0&&a>0&&(this._compositionWidth=l,this._compositionHeight=a,this._updateScale())}this._setupParentMedia(),this.hasAttribute("autoplay")&&this.play();return}}catch{}e>=40&&(clearInterval(this._probeInterval),this.dispatchEvent(new CustomEvent("error",{detail:{message:"Composition timeline not found after 8s"}})))},200)}_injectRuntime(){this._runtimeInjected=!0;try{let e=this.iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=D,t.onload=()=>{},t.onerror=()=>{},(e.head||e.documentElement).appendChild(t)}catch{}}_updateScale(){let e=this.getBoundingClientRect();if(e.width===0||e.height===0)return;let t=Math.min(e.width/this._compositionWidth,e.height/this._compositionHeight);this.iframe.style.width=`${this._compositionWidth}px`,this.iframe.style.height=`${this._compositionHeight}px`,this.iframe.style.transform=`translate(-50%, -50%) scale(${t})`}_setupControls(){if(this.controlsApi)return;let e={onPlay:()=>this.play(),onPause:()=>this.pause(),onSeek:s=>this.seek(s*this._duration),onSpeedChange:s=>{this.playbackRate=s}},t=this.getAttribute("speed-presets"),i=t?t.split(",").map(Number).filter(s=>!isNaN(s)&&s>0):void 0;this.controlsApi=H(this.shadow,e,{speedPresets:i})}_setupPoster(){let e=this.getAttribute("poster");if(!e){this.posterEl?.remove(),this.posterEl=null;return}this.posterEl||(this.posterEl=document.createElement("img"),this.posterEl.className="hfp-poster",this.shadow.appendChild(this.posterEl)),this.posterEl.src=e}_playParentMedia(){for(let e of this._parentMedia)e.el.src&&e.el.play().catch(()=>{})}_pauseParentMedia(){for(let e of this._parentMedia)e.el.pause()}_mirrorParentMediaTime(e){for(let t of this._parentMedia){let i=e-t.start;i<0||i>=t.duration||Math.abs(t.el.currentTime-i)>.15&&(t.el.currentTime=i)}}_promoteToParentProxy(){this._audioOwner!=="parent"&&(this._audioOwner="parent",this._sendControl("set-media-output-muted",{muted:!0}),this._mirrorParentMediaTime(this._currentTime),this._paused||this._playParentMedia())}_createParentMedia(e,t,i,s){if(this._parentMedia.some(p=>p.el.src===e))return;let o=t==="video"?document.createElement("video"):new Audio;o.preload="auto",o.src=e,o.load(),o.muted=this.muted,this.playbackRate!==1&&(o.playbackRate=this.playbackRate),this._parentMedia.push({el:o,start:i,duration:s})}_setupParentAudioFromUrl(e){this._createParentMedia(e,"audio",0,1/0)}_setupParentMedia(){try{let e=this.iframe.contentDocument;if(!e)return;let t=e.querySelectorAll("audio[data-start], video[data-start]");for(let i of t){let s=i.getAttribute("src")||i.querySelector("source")?.getAttribute("src");if(!s)continue;let o=new URL(s,i.ownerDocument.baseURI).href,p=parseFloat(i.getAttribute("data-start")||"0"),h=parseFloat(i.getAttribute("data-duration")||"Infinity"),d=i.tagName==="VIDEO"?"video":"audio";this._createParentMedia(o,d,p,h)}}catch{}}_hidePoster(){this.posterEl?.remove(),this.posterEl=null}};customElements.get("hyperframes-player")||customElements.define("hyperframes-player",C);export{C as HyperframesPlayer,N as SPEED_PRESETS,y as formatSpeed,w as formatTime};
199
199
  //# sourceMappingURL=hyperframes-player.js.map