@streamoji/aitwin 0.2.2 → 0.2.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.
package/dist/aitwin.cjs CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const nt=require("react/jsx-runtime"),w=require("react"),Rn=require("@pipecat-ai/client-js"),Xt=require("@pipecat-ai/websocket-transport");var rt=typeof document<"u"?document.currentScript:null;const Dn="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev",Zt={publicBase:Dn},Cn="0.2.2",Mn={version:Cn},xn={},ve="https://ai.streamoji.com",en=`${Zt.publicBase}/custom-faces`,Pn=`${Zt.publicBase}/aitwin-workers/v${Mn.version}/visemeDiffPreview.worker.js`;function Ln(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:rt&&rt.tagName.toUpperCase()==="SCRIPT"&&rt.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?xn?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():Pn}const On="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",ht="eng_a7f2b9c1",je="eng_d4e8f3a2",Ee="eng_c9b1e6d4",pe=Ee,Bn=new Set([je,Ee]),tn={[ht]:"oculus_direct",[Ee]:"oculus_direct",[je]:"inworld_word"},Pt={google:ht,inworld:je,cartesia:Ee};function Fn(e){return tn[e]}function Un(e){return e===Ee}function Nn(e){if(e.ttsEngineId&&e.ttsEngineId in tn)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in Pt?Pt[t]:pe}class We extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function nn(e,t=On){const n=new URL(t);n.searchParams.set("id",e);const r=await fetch(n.toString());if(r.status===404)throw new We;if(!r.ok)throw new Error(`getAiTwin failed (${r.status})`);const s=await r.json();if(!s.success)throw new We(s.error??"AI twin not found");const{tts:i,...o}=s.data;return{...o,ttsEngineId:Nn(s.data)}}const Vn="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",$n="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Wn="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Hn="6TNvp3SCs4Og0a1ijm9TommXLql1",qn="Swaraj Mali";async function $e(){const e=await fetch(Vn,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":$n,"Client-Secret":Wn},body:JSON.stringify({userId:Hn,userName:qn})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),n=t.authToken??t.token??"";if(!n.trim())throw new Error("getAuthToken returned no token");return n}async function Kn(e,t=ve){const n=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!n.ok)throw new Error(`/api/session-value failed (${n.status})`);const r=await n.json();if(!r.value)throw new Error("Session value response missing value");return r}function jn(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function Fe(e){return Uint8Array.from(e)}function Gn(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function zn(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=Fe(n.subarray(0,12)),s=Fe(n.subarray(12,28)),i=Fe(n.subarray(28)),o=Fe(Gn(i,s)),a=await crypto.subtle.importKey("raw",jn(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},a,o)}let it=null;const ot=new Set;function Ue(e){return e.includes("/custom-faces/")}function He(e){const t=e.binBase.trim().replace(/\/$/,""),n=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||Ue(t)||Ue(n)))return{twinBase:n,binBase:t,encrypted:!1};const s=Ue(t)?t:Ue(n)?n:t;return{twinBase:s,binBase:s,encrypted:!0}}function be(e){return He(e).encrypted}function at(e,t=en){const n=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:n,binBase:n,encrypted:!0}}function rn(e,t){return(e??t)?.trim()||void 0}function sn(e){it=He(e);for(const t of ot)t()}function Yn(e,t){const n=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!n)return He(e);const r=s=>{try{return new URL(s,n).href.replace(/\/$/,"")}catch{return s}};return He({twinBase:r(e.twinBase),binBase:r(e.binBase),encrypted:e.encrypted})}function Se(){if(!it)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return it}function Jn(e){return ot.add(e),()=>ot.delete(e)}function Lt(){const e=Se();return be(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Qn(){const e=Se();return be(e)?`${e.binBase}/idle.bin`:`${e.twinBase}/idle.mp4`}function Xn(){const e=Se();return be(e)?e.binBase:e.twinBase}function Ge(){return be(Se())}function Zn(){const e=Se();return be(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function er(){const e=Se();return be(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function tr(e){const t=e.split(/\r?\n/);let n="",r="";for(const i of t)i.startsWith("event:")?n=i.slice(6).trim():i.startsWith("data:")&&(r=i.slice(5).trim());if(!n)return null;let s={};if(r)try{s=JSON.parse(r)}catch{s={raw:r}}return{event:n,data:s}}const on=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],nr=new Map(on.map(e=>[e.toLowerCase(),e]));function an(e){const t=(e??"").trim();return t?nr.get(t.toLowerCase())??"sil":"sil"}const rr={aei:["aa","E","I"],o:["O","U"],ee:["I"],bmp:["PP"],fv:["FF"],l:["nn"],r:["RR"],th:["TH"],qw:["U","O"],cdgknstxyz:["DD","SS","kk","CH"]};function sr(e){if(!e)return["sil"];const t=e.toLowerCase();return rr[t]??["sil"]}function ir(e,t){const n=e.word.length;if(n<=0)return 0;const r=e.wduration;if(r<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/r));return Math.min(n-1,Math.floor(s*n))}function ze(e,t){const n=Math.max(0,Math.min(e.length-1,t));return e[n]??""}function or(e,t){const n=e.toLowerCase(),r=ze(n,t),s=n.slice(Math.max(0,t-1),t+2);return r==="i"||r==="y"?"I":s.includes("ee")||r==="e"||n.includes("ea")?"E":"aa"}function ar(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));if(/oo|ou|uw/.test(r))return"U";const s=ze(n,t);return s==="u"||s==="w"?"U":"O"}function cr(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-2),t+1);if(/qu/.test(r))return"U";const s=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));return/wo|wh/.test(s)||ze(n,t)==="o"?"O":"U"}const Ne={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function ur(e,t){const n=e.toLowerCase(),r=ze(n,t);if(Ne[r])return Ne[r];for(let s=0;s<n.length;s++){const i=n[s];if(Ne[i])return Ne[i]}return"DD"}function lr(e,t,n){const r=sr(e);if(r.length===1)return r[0];const s=t.trim();if(!s)return r[0];const i=(e??"").toLowerCase(),o=Math.max(0,n);switch(i){case"aei":return or(s,o);case"o":return ar(s,o);case"qw":return cr(s,o);case"cdgknstxyz":return ur(s,o);default:return r[0]}}const fr=24e3;function dr(e){let t=e.trim();const n=t.indexOf(",");t.startsWith("data:")&&n>=0&&(t=t.slice(n+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const r=t.length%4;return r!==0&&(t+="=".repeat(4-r)),t}function hr(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function Ot(e,t,n=fr){const r=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(r.buffer,r.byteOffset,r.byteLength/2),i=new Float32Array(s.length);for(let a=0;a<s.length;a++)i[a]=s[a]>=32768?-(65536-s[a])/32768:s[a]/32767;const o=e.createBuffer(1,i.length,n);return o.copyToChannel(i,0),o}function mr(e){const t=dr(e),n=window.atob(t),r=new Uint8Array(n.length);for(let s=0;s<n.length;s++)r[s]=n.charCodeAt(s);return r}async function _r(e,t){const n=mr(t);if(hr(n)){const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(r)}if(n.length>=2&&n.length%2===0)return Ot(e,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(r)}catch{return Ot(e,n)}}function Bt(e){let t=0;for(const n of e)t=Math.max(t,n.vtime+n.vduration);return t}function wr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function cn(e,t){const n=[],r=[];let s=null,i=!1,o=!1,a=!1,f=!1,l=0,_=0,p=0,m=0,T=null,k=1,u=t,y=!1;const d=()=>{T!=null&&(clearTimeout(T),T=null)},R=()=>{for(const g of r)try{g.stop()}catch{}r.length=0},S=()=>{for(const g of r)g.playbackRate.value=k},D=()=>{!f||i||r.length>0||n.length>0||(o=!1,d(),e.onSpeakingDone(),e.onNotify())},H=g=>{const x=Bt(g);if(x<=0){e.onSpeakingDone(),e.onNotify();return}o=!0,l=performance.now(),e.onSpeakingStart(),e.onNotify(),d();const b=k>0?x/k:x;T=setTimeout(()=>{y||(o=!1,T=null,e.onSpeakingDone(),e.onNotify())},b)},V=(g,x,b,C)=>{x.length!==0&&(C&&(p=b),x.forEach(I=>{const O=String(I.word??"").trim();if(!O)return;const $=Math.round((I.start??0)*1e3),U=I.duration!=null?Math.round(I.duration*1e3):I.end!=null?Math.max(1,Math.round((I.end-(I.start??0))*1e3)):0;g.push({word:O,wtime:p+$,wduration:U,queueIndex:g.length,gesture:I.gesture??null,emphasis:I.emphasis===!0})}))},W=(g,x,b,C,I,L)=>{b.length!==0&&(I&&(p=C),b.forEach(O=>{const $=String(O.symbol??O.viseme??"").trim();if(!$)return;const U=Math.round((O.start??0)*1e3),X=Math.max(1,Math.round((O.duration??0)*1e3)),se=p+U;let Z;if(Fn(L)==="oculus_direct")Z=an($);else{const ae=wr(x,se),J=ae?ir(ae,se):0;Z=lr($,ae?.word??"",J)}g.push({viseme:Z,weight:1,vtime:se,vduration:X})}),e.onNotify())},j=(g,x,b,C,I,L,O)=>{V(x,C,I,L),W(g,x,b,I,L,O)},E=async(g,x,b,C,I,L,O)=>{if(y||!b?.trim()){(C.length>0||I.length>0)&&!a&&j(g,x,C,I,p,L,u);return}if(i){n.push({audio:b,visemes:C,words:I,isNewSegment:L});return}i=!0;try{const $=window.AudioContext||window.webkitAudioContext;s||(s=new $);const U=s;U.state==="suspended"&&await U.resume();const X=await _r(U,b);a=!0;const se=U.currentTime;let Z=m;const ae=!o;Z<se&&(Z=se+.1),m=Z+X.duration;const J=U.createBufferSource();if(J.buffer=X,J.playbackRate.value=k,J.connect(U.destination),r.push(J),y){r.pop();return}if(ae){o=!0,e.onSpeakingStart(),_=Z,p=0,g.length=0,x.length=0,O?.();const v=(Z-se)*1e3;l=performance.now()+v}J.onended=()=>{const v=r.indexOf(J);v>=0&&r.splice(v,1),D(),e.onNotify()},J.start(Z);const c=(Z-_)*1e3;j(g,x,C,I,c,L,u)}catch($){console.error("[avatarChunkPlayback] audio chunk failed:",$)}finally{i=!1;const $=n.shift();$?await E(g,x,$.audio,$.visemes,$.words,$.isNewSegment,O):D()}};return{setStreamTtsEngineId:g=>{u=g},setPlaybackSpeed:g=>{k=Math.max(.1,Math.min(1,g)),S()},getPlaybackSpeed:()=>k,resetForNewStream:g=>{y=!1,f=!1,u=g},primeAudioContext:async()=>{const g=window.AudioContext||window.webkitAudioContext;s||(s=new g),s.state==="suspended"&&await s.resume()},stop:()=>{y=!0,d(),R(),n.length=0,i=!1,o=!1,a=!1,f=!1,p=0,m=0,_=0,l=0},markStreamComplete:g=>{if(f=!0,!a&&g.length>0){H(g);return}D()},playAudioChunk:E,queueTimingsOnly:(g,x,b,C,I)=>{let L=p;I&&g.length>0&&(L=Bt(g)),j(g,x,b,C,L,I,u)},getPlaybackElapsedMs:()=>o?a&&s&&_>0?Math.max(0,(s.currentTime-_)*1e3):Math.max(0,(performance.now()-l)*k):0,isSpeaking:()=>o,getPlaybackStartPerf:()=>l,bargeIn:()=>{y=!0,R(),n.length=0,i=!1,o=!1,a=!1,m=0,_=0,l=0,d()},interrupt:()=>{R(),n.length=0,i=!1,o=!1,a=!1,m=0,_=0,l=0,d()}}}function Ye(e,t){let n=null;for(const r of e)t<r.vtime||t>=r.vtime+r.vduration||(!n||r.vtime>=n.vtime)&&(n=r);return n?n.viseme:"sil"}function gr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}const mt=.5,_t=1.5,wt="Olivia",un="f786b574-daa5-4673-aa0c-cbe3e8534c02",Je=.85;function ct(e,t=ve){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],i=[];let o="neutral",a="",f=pe;const l=new Set,_=()=>l.forEach(k=>k()),p=cn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:_},pe),m=()=>{p.stop(),r.length=0,s.length=0,i.length=0,o="neutral",e("idle"),_()};return{setDeveloperToken:k=>{a=k},setTtsEngineId:k=>{f=k},getTtsEngineId:()=>f,speak:async(k,u)=>{m(),e("loading");const y=a.trim();if(!y)throw new Error("Developer token required for avatar_ttsWithPoses");const d=u?.ttsEngineId??f??pe;p.resetForNewStream(d),p.setStreamTtsEngineId(d);const R=u?.voiceId??wt,S=u?.speakingRate??Je,D=Math.max(mt,Math.min(_t,S)),H={user_query:k,ttsEngineId:d,speakingRate:D,tenant:"aiTwin"};Bn.has(d)&&(H.voice_id=R);try{const V=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${y}`},body:JSON.stringify(H)});if(!V.ok)throw new Error(`avatar_ttsWithPoses failed (${V.status})`);const W=V.body;if(!W)throw new Error("No response body");const j=W.getReader(),E=new TextDecoder;let A="";const g=async x=>{const b=tr(x);if(b){if(b.event==="audio"){const C=b.data.chunk,I=b.data.visemes??[],L=b.data.words??[],O=b.data.is_new_segment??!1;C?await p.playAudioChunk(r,s,C,I,L,O):(I.length>0||L.length>0)&&p.queueTimingsOnly(r,s,I,L,O)}else if(b.event==="metadata"){const C=b.data.mood;typeof C=="string"&&C.trim()&&(o=C.trim());const I=b.data.sentence_emotions;if(Array.isArray(I)){i.length=0;for(const L of I){if(!L||typeof L!="object")continue;const O=L;i.push({sentence_index:Number(O.sentence_index??0),text:String(O.text??""),sentiment:typeof O.sentiment=="string"?O.sentiment:void 0,emotion:String(O.emotion??o),start_word:Number(O.start_word??0),end_word:Number(O.end_word??0)})}}_()}else if(b.event==="error")throw new Error(String(b.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:x,value:b}=await j.read();b&&(A+=E.decode(b,{stream:!0}));const C=A.split(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const nt=require("react/jsx-runtime"),w=require("react"),Rn=require("@pipecat-ai/client-js"),Xt=require("@pipecat-ai/websocket-transport");var rt=typeof document<"u"?document.currentScript:null;const Dn="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev",Zt={publicBase:Dn},Cn="0.2.3",Mn={version:Cn},xn={},ve="https://ai.streamoji.com",en=`${Zt.publicBase}/custom-faces`,Pn=`${Zt.publicBase}/aitwin-workers/v${Mn.version}/visemeDiffPreview.worker.js`;function Ln(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:rt&&rt.tagName.toUpperCase()==="SCRIPT"&&rt.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?xn?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():Pn}const On="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",ht="eng_a7f2b9c1",je="eng_d4e8f3a2",Ee="eng_c9b1e6d4",pe=Ee,Bn=new Set([je,Ee]),tn={[ht]:"oculus_direct",[Ee]:"oculus_direct",[je]:"inworld_word"},Pt={google:ht,inworld:je,cartesia:Ee};function Fn(e){return tn[e]}function Un(e){return e===Ee}function Nn(e){if(e.ttsEngineId&&e.ttsEngineId in tn)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in Pt?Pt[t]:pe}class We extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function nn(e,t=On){const n=new URL(t);n.searchParams.set("id",e);const r=await fetch(n.toString());if(r.status===404)throw new We;if(!r.ok)throw new Error(`getAiTwin failed (${r.status})`);const s=await r.json();if(!s.success)throw new We(s.error??"AI twin not found");const{tts:i,...o}=s.data;return{...o,ttsEngineId:Nn(s.data)}}const Vn="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",$n="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Wn="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Hn="6TNvp3SCs4Og0a1ijm9TommXLql1",qn="Swaraj Mali";async function $e(){const e=await fetch(Vn,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":$n,"Client-Secret":Wn},body:JSON.stringify({userId:Hn,userName:qn})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),n=t.authToken??t.token??"";if(!n.trim())throw new Error("getAuthToken returned no token");return n}async function Kn(e,t=ve){const n=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!n.ok)throw new Error(`/api/session-value failed (${n.status})`);const r=await n.json();if(!r.value)throw new Error("Session value response missing value");return r}function jn(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function Fe(e){return Uint8Array.from(e)}function Gn(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function zn(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=Fe(n.subarray(0,12)),s=Fe(n.subarray(12,28)),i=Fe(n.subarray(28)),o=Fe(Gn(i,s)),a=await crypto.subtle.importKey("raw",jn(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},a,o)}let it=null;const ot=new Set;function Ue(e){return e.includes("/custom-faces/")}function He(e){const t=e.binBase.trim().replace(/\/$/,""),n=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||Ue(t)||Ue(n)))return{twinBase:n,binBase:t,encrypted:!1};const s=Ue(t)?t:Ue(n)?n:t;return{twinBase:s,binBase:s,encrypted:!0}}function be(e){return He(e).encrypted}function at(e,t=en){const n=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:n,binBase:n,encrypted:!0}}function rn(e,t){return(e??t)?.trim()||void 0}function sn(e){it=He(e);for(const t of ot)t()}function Yn(e,t){const n=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!n)return He(e);const r=s=>{try{return new URL(s,n).href.replace(/\/$/,"")}catch{return s}};return He({twinBase:r(e.twinBase),binBase:r(e.binBase),encrypted:e.encrypted})}function Se(){if(!it)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return it}function Jn(e){return ot.add(e),()=>ot.delete(e)}function Lt(){const e=Se();return be(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Qn(){const e=Se();return be(e)?`${e.binBase}/idle.bin`:`${e.twinBase}/idle.mp4`}function Xn(){const e=Se();return be(e)?e.binBase:e.twinBase}function Ge(){return be(Se())}function Zn(){const e=Se();return be(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function er(){const e=Se();return be(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function tr(e){const t=e.split(/\r?\n/);let n="",r="";for(const i of t)i.startsWith("event:")?n=i.slice(6).trim():i.startsWith("data:")&&(r=i.slice(5).trim());if(!n)return null;let s={};if(r)try{s=JSON.parse(r)}catch{s={raw:r}}return{event:n,data:s}}const on=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],nr=new Map(on.map(e=>[e.toLowerCase(),e]));function an(e){const t=(e??"").trim();return t?nr.get(t.toLowerCase())??"sil":"sil"}const rr={aei:["aa","E","I"],o:["O","U"],ee:["I"],bmp:["PP"],fv:["FF"],l:["nn"],r:["RR"],th:["TH"],qw:["U","O"],cdgknstxyz:["DD","SS","kk","CH"]};function sr(e){if(!e)return["sil"];const t=e.toLowerCase();return rr[t]??["sil"]}function ir(e,t){const n=e.word.length;if(n<=0)return 0;const r=e.wduration;if(r<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/r));return Math.min(n-1,Math.floor(s*n))}function ze(e,t){const n=Math.max(0,Math.min(e.length-1,t));return e[n]??""}function or(e,t){const n=e.toLowerCase(),r=ze(n,t),s=n.slice(Math.max(0,t-1),t+2);return r==="i"||r==="y"?"I":s.includes("ee")||r==="e"||n.includes("ea")?"E":"aa"}function ar(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));if(/oo|ou|uw/.test(r))return"U";const s=ze(n,t);return s==="u"||s==="w"?"U":"O"}function cr(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-2),t+1);if(/qu/.test(r))return"U";const s=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));return/wo|wh/.test(s)||ze(n,t)==="o"?"O":"U"}const Ne={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function ur(e,t){const n=e.toLowerCase(),r=ze(n,t);if(Ne[r])return Ne[r];for(let s=0;s<n.length;s++){const i=n[s];if(Ne[i])return Ne[i]}return"DD"}function lr(e,t,n){const r=sr(e);if(r.length===1)return r[0];const s=t.trim();if(!s)return r[0];const i=(e??"").toLowerCase(),o=Math.max(0,n);switch(i){case"aei":return or(s,o);case"o":return ar(s,o);case"qw":return cr(s,o);case"cdgknstxyz":return ur(s,o);default:return r[0]}}const fr=24e3;function dr(e){let t=e.trim();const n=t.indexOf(",");t.startsWith("data:")&&n>=0&&(t=t.slice(n+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const r=t.length%4;return r!==0&&(t+="=".repeat(4-r)),t}function hr(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function Ot(e,t,n=fr){const r=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(r.buffer,r.byteOffset,r.byteLength/2),i=new Float32Array(s.length);for(let a=0;a<s.length;a++)i[a]=s[a]>=32768?-(65536-s[a])/32768:s[a]/32767;const o=e.createBuffer(1,i.length,n);return o.copyToChannel(i,0),o}function mr(e){const t=dr(e),n=window.atob(t),r=new Uint8Array(n.length);for(let s=0;s<n.length;s++)r[s]=n.charCodeAt(s);return r}async function _r(e,t){const n=mr(t);if(hr(n)){const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(r)}if(n.length>=2&&n.length%2===0)return Ot(e,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(r)}catch{return Ot(e,n)}}function Bt(e){let t=0;for(const n of e)t=Math.max(t,n.vtime+n.vduration);return t}function wr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function cn(e,t){const n=[],r=[];let s=null,i=!1,o=!1,a=!1,f=!1,l=0,_=0,p=0,m=0,T=null,k=1,u=t,y=!1;const d=()=>{T!=null&&(clearTimeout(T),T=null)},R=()=>{for(const g of r)try{g.stop()}catch{}r.length=0},S=()=>{for(const g of r)g.playbackRate.value=k},D=()=>{!f||i||r.length>0||n.length>0||(o=!1,d(),e.onSpeakingDone(),e.onNotify())},H=g=>{const x=Bt(g);if(x<=0){e.onSpeakingDone(),e.onNotify();return}o=!0,l=performance.now(),e.onSpeakingStart(),e.onNotify(),d();const b=k>0?x/k:x;T=setTimeout(()=>{y||(o=!1,T=null,e.onSpeakingDone(),e.onNotify())},b)},V=(g,x,b,C)=>{x.length!==0&&(C&&(p=b),x.forEach(I=>{const O=String(I.word??"").trim();if(!O)return;const $=Math.round((I.start??0)*1e3),U=I.duration!=null?Math.round(I.duration*1e3):I.end!=null?Math.max(1,Math.round((I.end-(I.start??0))*1e3)):0;g.push({word:O,wtime:p+$,wduration:U,queueIndex:g.length,gesture:I.gesture??null,emphasis:I.emphasis===!0})}))},W=(g,x,b,C,I,L)=>{b.length!==0&&(I&&(p=C),b.forEach(O=>{const $=String(O.symbol??O.viseme??"").trim();if(!$)return;const U=Math.round((O.start??0)*1e3),X=Math.max(1,Math.round((O.duration??0)*1e3)),se=p+U;let Z;if(Fn(L)==="oculus_direct")Z=an($);else{const ae=wr(x,se),J=ae?ir(ae,se):0;Z=lr($,ae?.word??"",J)}g.push({viseme:Z,weight:1,vtime:se,vduration:X})}),e.onNotify())},j=(g,x,b,C,I,L,O)=>{V(x,C,I,L),W(g,x,b,I,L,O)},E=async(g,x,b,C,I,L,O)=>{if(y||!b?.trim()){(C.length>0||I.length>0)&&!a&&j(g,x,C,I,p,L,u);return}if(i){n.push({audio:b,visemes:C,words:I,isNewSegment:L});return}i=!0;try{const $=window.AudioContext||window.webkitAudioContext;s||(s=new $);const U=s;U.state==="suspended"&&await U.resume();const X=await _r(U,b);a=!0;const se=U.currentTime;let Z=m;const ae=!o;Z<se&&(Z=se+.1),m=Z+X.duration;const J=U.createBufferSource();if(J.buffer=X,J.playbackRate.value=k,J.connect(U.destination),r.push(J),y){r.pop();return}if(ae){o=!0,e.onSpeakingStart(),_=Z,p=0,g.length=0,x.length=0,O?.();const v=(Z-se)*1e3;l=performance.now()+v}J.onended=()=>{const v=r.indexOf(J);v>=0&&r.splice(v,1),D(),e.onNotify()},J.start(Z);const c=(Z-_)*1e3;j(g,x,C,I,c,L,u)}catch($){console.error("[avatarChunkPlayback] audio chunk failed:",$)}finally{i=!1;const $=n.shift();$?await E(g,x,$.audio,$.visemes,$.words,$.isNewSegment,O):D()}};return{setStreamTtsEngineId:g=>{u=g},setPlaybackSpeed:g=>{k=Math.max(.1,Math.min(1,g)),S()},getPlaybackSpeed:()=>k,resetForNewStream:g=>{y=!1,f=!1,u=g},primeAudioContext:async()=>{const g=window.AudioContext||window.webkitAudioContext;s||(s=new g),s.state==="suspended"&&await s.resume()},stop:()=>{y=!0,d(),R(),n.length=0,i=!1,o=!1,a=!1,f=!1,p=0,m=0,_=0,l=0},markStreamComplete:g=>{if(f=!0,!a&&g.length>0){H(g);return}D()},playAudioChunk:E,queueTimingsOnly:(g,x,b,C,I)=>{let L=p;I&&g.length>0&&(L=Bt(g)),j(g,x,b,C,L,I,u)},getPlaybackElapsedMs:()=>o?a&&s&&_>0?Math.max(0,(s.currentTime-_)*1e3):Math.max(0,(performance.now()-l)*k):0,isSpeaking:()=>o,getPlaybackStartPerf:()=>l,bargeIn:()=>{y=!0,R(),n.length=0,i=!1,o=!1,a=!1,m=0,_=0,l=0,d()},interrupt:()=>{R(),n.length=0,i=!1,o=!1,a=!1,m=0,_=0,l=0,d()}}}function Ye(e,t){let n=null;for(const r of e)t<r.vtime||t>=r.vtime+r.vduration||(!n||r.vtime>=n.vtime)&&(n=r);return n?n.viseme:"sil"}function gr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}const mt=.5,_t=1.5,wt="Olivia",un="f786b574-daa5-4673-aa0c-cbe3e8534c02",Je=.85;function ct(e,t=ve){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],i=[];let o="neutral",a="",f=pe;const l=new Set,_=()=>l.forEach(k=>k()),p=cn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:_},pe),m=()=>{p.stop(),r.length=0,s.length=0,i.length=0,o="neutral",e("idle"),_()};return{setDeveloperToken:k=>{a=k},setTtsEngineId:k=>{f=k},getTtsEngineId:()=>f,speak:async(k,u)=>{m(),e("loading");const y=a.trim();if(!y)throw new Error("Developer token required for avatar_ttsWithPoses");const d=u?.ttsEngineId??f??pe;p.resetForNewStream(d),p.setStreamTtsEngineId(d);const R=u?.voiceId??wt,S=u?.speakingRate??Je,D=Math.max(mt,Math.min(_t,S)),H={user_query:k,ttsEngineId:d,speakingRate:D,tenant:"aiTwin"};Bn.has(d)&&(H.voice_id=R);try{const V=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${y}`},body:JSON.stringify(H)});if(!V.ok)throw new Error(`avatar_ttsWithPoses failed (${V.status})`);const W=V.body;if(!W)throw new Error("No response body");const j=W.getReader(),E=new TextDecoder;let A="";const g=async x=>{const b=tr(x);if(b){if(b.event==="audio"){const C=b.data.chunk,I=b.data.visemes??[],L=b.data.words??[],O=b.data.is_new_segment??!1;C?await p.playAudioChunk(r,s,C,I,L,O):(I.length>0||L.length>0)&&p.queueTimingsOnly(r,s,I,L,O)}else if(b.event==="metadata"){const C=b.data.mood;typeof C=="string"&&C.trim()&&(o=C.trim());const I=b.data.sentence_emotions;if(Array.isArray(I)){i.length=0;for(const L of I){if(!L||typeof L!="object")continue;const O=L;i.push({sentence_index:Number(O.sentence_index??0),text:String(O.text??""),sentiment:typeof O.sentiment=="string"?O.sentiment:void 0,emotion:String(O.emotion??o),start_word:Number(O.start_word??0),end_word:Number(O.end_word??0)})}}_()}else if(b.event==="error")throw new Error(String(b.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:x,value:b}=await j.read();b&&(A+=E.decode(b,{stream:!0}));const C=A.split(`
2
2
 
3
- `);A=C.pop()??"";for(const I of C)await g(I);if(x){A.trim()&&await g(A.trim()),p.markStreamComplete(r);break}}}catch(V){throw console.error("[avatarTtsLipsync]",V),m(),e("error"),V}},stop:m,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>i,getStreamMood:()=>o,getPlaybackElapsedMs:()=>p.getPlaybackElapsedMs(),isSpeaking:()=>p.isSpeaking(),setPlaybackSpeed:k=>{p.setPlaybackSpeed(k)},getPlaybackSpeed:()=>p.getPlaybackSpeed(),subscribe:k=>(l.add(k),()=>l.delete(k))}}const gt=16e3,pt=24e3;class pr extends Xt.DailyMediaManager{constructor(){super(!0,!0,void 0,void 0,512,gt,pt)}bufferBotAudio(t,n){}}const yr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Er(e){return yr.test(e.trim())}function Sr(e){const t=e.replace(/\/$/,"");return t.startsWith("https://")?`wss://${t.slice(8)}`:t.startsWith("http://")?`ws://${t.slice(7)}`:t}function ln(e){if(e.wsUrl?.trim())return e.wsUrl.trim();const t=e.authToken?.trim();if(!t)throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");const n=e.apiBase??ve,r=new URL(`${Sr(n)}/ws/voice`);r.searchParams.set("authToken",t);const s=e.voiceId?.trim();s&&Er(s)&&r.searchParams.set("voiceId",s);const i=e.speakingRate??Je;return r.searchParams.set("speaking_rate",String(Math.max(mt,Math.min(_t,i)))),e.ttsStream===!1?r.searchParams.set("tts_stream","false"):r.searchParams.set("tts_stream","true"),r.toString()}function Ft(e,t){if(!Array.isArray(e))return[];const n=[];for(const r of e)!r||typeof r!="object"||n.push({sentence_index:Number(r.sentence_index??0),text:String(r.text??""),sentiment:typeof r.sentiment=="string"?r.sentiment:void 0,emotion:String(r.emotion??t),start_word:Number(r.start_word??0),end_word:Number(r.end_word??0)});return n}function kr(e){if(!e||typeof e!="object")return null;const t=e,n=t.data;return n&&typeof n=="object"?n:t}function fn(e,t={}){const n=[],r=[],s=[],i=[];let o="",a="neutral",f=Ee,l=!1,_=null,p=null,m={...t},T=0,k=null;const u=new Set,y=[];let d=!1,R=null;const S=()=>u.forEach(c=>c()),D=cn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:S},f),H=()=>{if(R)return R;const c=new MessageChannel;return c.port1.onmessage=()=>{const v=y.shift();v!==void 0&&$(v),y.length>0?c.port2.postMessage(null):d=!1},R=c,c},V=c=>{y.push(c),!d&&(d=!0,H().port2.postMessage(null))},W=()=>D.getPlaybackElapsedMs(),j=(c,v=o)=>{if(!m.onLipsyncDebug)return;const B=W(),z=n;m.onLipsyncDebug({event:c,spokenText:v,mood:a,playbackAnchorPerf:D.getPlaybackStartPerf(),playbackElapsedMs:B,queueLength:z.length,activeViseme:Ye(z,B),segments:i.map(ne=>({...ne,visemes:[...ne.visemes]}))})},E=(c,v)=>{m.onLipsyncDebug&&(k={event:c,spokenText:v},T===0&&(T=requestAnimationFrame(()=>{T=0;const B=k;k=null,B&&j(B.event,B.spokenText)})))},A=()=>{n.length=0,r.length=0,i.length=0,o="",a="neutral",s.length=0},g=()=>{D.stop(),A(),E("reset"),S()},x=()=>{g(),e("idle"),S()},b=(c,v)=>{typeof c.mood=="string"&&c.mood.trim()&&(a=c.mood.trim()),Array.isArray(c.sentence_emotions)&&c.sentence_emotions.length>0&&(v&&(s.length=0),s.push(...Ft(c.sentence_emotions,a)))},C=c=>{const v=String(c.spoken_text??c.text??"").trim();v&&(o=v),D.resetForNewStream(f),typeof c.mood=="string"&&c.mood.trim()&&(a=c.mood.trim()),s.length=0,s.push(...Ft(c.sentence_emotions,a)),Array.isArray(c.gestures)&&c.gestures,E("metadata",v),S()},I=(c,v,B,z)=>{if(!m.onLipsyncDebug)return;const ne=n.slice(B),Ae=z.filter(G=>String(G.symbol??G.viseme??"").trim());i.push({spokenText:v,audioOffsetMs:ne[0]?.vtime??0,isNewSegment:!!c.is_new_segment,chunkDurationMs:c.chunk_duration_ms!=null?Number(c.chunk_duration_ms):null,receivedAtMs:performance.now(),visemes:ne.map((G,ke)=>{const Y=Ae[ke];return{rawSymbol:String(Y?.symbol??Y?.viseme??G.viseme).trim(),viseme:G.viseme,startSec:Number(Y?.start??0),durationSec:Number(Y?.duration??0),vtimeMs:G.vtime,vdurationMs:G.vduration}})})},L=c=>{const v=String(c.spoken_text??c.text??"").trim(),B=!!c.is_new_segment||Number(c.chunk_index??-1)===0,z=c.visemes??[],ne=c.words??[],Ae=String(c.chunk??"").trim();v&&(o=v),b(c,B),B&&D.resetForNewStream(f);const G=n.length;D.playAudioChunk(n,r,Ae,z,ne,B,()=>{i.length=0}).then(()=>{I(c,v,G,z),E("audio_chunk",v)})},O=c=>{console.warn("[realtimeVoice] avatar_lipsync is deprecated — expect avatar_audio_chunk");const v=String(c.spoken_text??c.text??"").trim();v&&(o=v),b(c,!!c.is_new_segment);const B=n.length;D.queueTimingsOnly(n,r,c.visemes??[],c.words??[],!!c.is_new_segment),I(c,v,B,c.visemes??[]),E("lipsync",v),S()},$=c=>{const v=kr(c);if(!v){console.warn("[realtimeVoice] server message with no payload",c);return}const B=String(v.type??"unknown");switch(B!=="avatar_metadata"&&B!=="avatar_audio_chunk"&&B!=="avatar_lipsync"&&B!=="error"&&console.debug("[realtimeVoice] server message",B,v),B){case"avatar_metadata":C(v);break;case"avatar_audio_chunk":L(v);break;case"avatar_lipsync":O(v);break;case"error":{const z=String(v.message??"Voice session error");console.error("[realtimeVoice] server error:",z),e("error"),S();break}case"avatar_speak":console.warn("[realtimeVoice] avatar_speak received; tts_stream=false Mode B is not wired yet:",v.text);break}},U=()=>_||(p=new pr,_=new Rn.PipecatClient({transport:new Xt.WebSocketTransport({mediaManager:p,recorderSampleRate:gt,playerSampleRate:pt}),enableCam:!1,enableMic:!0,callbacks:{onBotReady:()=>{l=!0,e("idle"),S()},onDisconnected:()=>{l=!1,x()},onError:c=>{const v=typeof c=="object"&&c!==null&&"data"in c?String(c.data??c):String(c);console.error("[realtimeVoice]",v,c),e("error"),S()},onTransportStateChanged:c=>{c==="error"&&(e("error"),S())},onServerMessage:c=>{V(c)},onUserStartedSpeaking:()=>{D.bargeIn(),A(),e("idle"),E("user_started"),S()},onBotStartedSpeaking:()=>{E("bot_started")},onBotStoppedSpeaking:()=>{D.markStreamComplete(n),E("bot_stopped")},onUserTranscript:c=>{m.onUserTranscript?.(c.text,!!c.final)},onBotOutput:c=>{m.onBotOutput?.(c.text)}}}),_);return{setDeveloperToken:c=>{},setTtsEngineId:c=>{f=c,D.setStreamTtsEngineId(c)},getTtsEngineId:()=>f,speak:async(c,v)=>{const B=c.trim();if(B){if(!_||!l)throw new Error("Realtime voice is not connected. Call connect() first.");g(),await _.sendText(B)}},stop:()=>{g(),e("idle"),S()},connect:async c=>{if(c&&(m={...m,...c}),!m.authToken?.trim())throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");e("loading");const v=U();await v.initDevices();const B=ln(m);console.info("[realtimeVoice] connecting (avatar_audio_chunk RTVI):",B);try{await v.connect({wsUrl:B})}catch(z){l=!1,e("error"),S();const ne=z instanceof Error?z.message:"Realtime voice connect failed (WebSocket 101 only means upgrade OK — check authToken/origin/RTVI handshake)";throw new Error(`${ne} — ${B}`)}l=!0,e("idle"),S()},disconnect:async()=>{if(y.length=0,d=!1,_)try{await _.disconnect()}catch(c){console.error("[realtimeVoice] disconnect failed:",c)}l=!1,x()},isConnected:()=>l,unlockAudio:async()=>{await D.primeAudioContext()},getVisemeQueue:()=>n,getWordQueue:()=>r,getSentenceEmotions:()=>s,getStreamMood:()=>a,getPlaybackElapsedMs:W,isSpeaking:()=>D.isSpeaking(),setPlaybackSpeed:c=>{D.setPlaybackSpeed(c)},getPlaybackSpeed:()=>D.getPlaybackSpeed(),subscribe:c=>(u.add(c),()=>u.delete(c))}}function Tr(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function vr(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function br(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function Qe(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return zn(r,t)}async function Ar(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await Qe(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function Ir(e){const t=new Blob([e],{type:"image/webp"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(n),r(i)},i.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted image"))},i.src=n})}async function Rr(e){const t=new Blob([e],{type:"image/png"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(n),r(i)},i.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted PNG"))},i.src=n})}function Dr(e){const t=new Blob([e],{type:"video/mp4"});return URL.createObjectURL(t)}async function Cr(e){const t=Qn();if(!Ge())return t;if(!e)throw new Error("Missing asset key for encrypted idle video");const n=await Qe(t,e);return Dr(n)}function yt(e,t){return`${e}/${t}_minus_sil.png`}const dn=5;function hn(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const n=e?.crop?.content_threshold;return typeof n=="number"&&n>0?n:dn}const Mr=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],ut=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],xr=ut.length,Ut=new Set(["aa__kk","aa__nn","aa__sil","CH__aa","CH__DD","CH__E","CH__FF","CH__I","CH__kk","CH__nn","CH__O","CH__PP","CH__RR","CH__sil","CH__SS","CH__TH","CH__U","DD__aa","DD__E","DD__FF","DD__I","DD__kk","DD__nn","DD__O","DD__PP","DD__RR","DD__sil","DD__SS","DD__TH","DD__U","E__aa","E__FF","E__I","E__kk","E__nn","E__O","E__PP","E__RR","E__sil","E__SS","E__TH","E__U","FF__aa","FF__I","FF__kk","FF__nn","FF__O","FF__PP","FF__RR","FF__sil","FF__SS","FF__TH","FF__U","I__aa","I__kk","I__nn","I__O","I__PP","I__RR","I__sil","I__SS","I__TH","I__U","kk__nn","kk__sil","nn__sil","O__aa","O__kk","O__nn","O__PP","O__RR","O__sil","O__SS","O__TH","O__U","PP__aa","PP__kk","PP__nn","PP__RR","PP__sil","PP__SS","PP__TH","PP__U","RR__aa","RR__kk","RR__nn","RR__sil","RR__SS","RR__TH","RR__U","SS__aa","SS__kk","SS__nn","SS__sil","SS__TH","SS__U","TH__aa","TH__kk","TH__nn","TH__sil","TH__U","U__aa","U__kk","U__nn","U__sil"]);function Nt(e,t){return`${e}__${t}`}function Vt(e,t){return`pairs/${e}/${t}_minus_sil.png`}function qe(e){return`${e}_minus_sil.png`}function Pr(e,t){if(t.has(e))return e;const n=/^pairs\/([^/]+)\//.exec(e);if(!n)return null;const r=n[1].split("__")[1];if(!r||r==="sil")return null;const s=qe(r);return t.has(s)?s:null}function mn(e,t){return e.filter(n=>t.has(n))}function Lr(e,t,n){const r=Et(e,t);return mn(r,n).length>0}function Et(e,t){if(e===t)return[];const n=[],r=Nt(e,t);if(Ut.has(r)){for(const i of ut)n.push(Vt(r,i));return n}const s=Nt(t,e);if(Ut.has(s)){for(let i=xr-1;i>=0;i--)n.push(Vt(s,ut[i]));return n}return[]}function Or(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function Br(e,t){return Or(t).map(r=>e[r]).filter(r=>r!=null)}function Fr(e,t,n,r){if(r<=0)return 0;const i=Math.max(1,n-t)/r,o=e-t,a=Math.floor(o/i);return Math.min(r-1,Math.max(0,a))}function Ur(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}const Nr=[.32,.24,.16,.08,0],Vr=Nr.map(e=>`eyes_${e.toFixed(2)}`),$r=["eyes_0.32","eyes_0.16","eyes_0.00"],Wr=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],$t=2,Hr=-30,lt=20,qr={fear:-30,anger:-20,sad:-10,neutral:0,love:10,happy:0,disgust:20};function St(e){return`brow_${e}`}function kt(e){return qr[e]}function ye(e){return Math.max(Hr,Math.min(lt,e))}function Kr(e,t){if(e===t)return[];const n=e<t?$t:-$t,r=[];let s=e;for(;s!==t;)s+=n,r.push(s);return r}const jr={fear:"fear",afraid:"fear",anxiety:"fear",anger:"anger",angry:"anger",mad:"anger",sad:"sad",sadness:"sad",sorrow:"sad",neutral:"neutral",calm:"neutral",love:"love",loving:"love",affection:"love",happy:"happy",happiness:"happy",joy:"happy",joyful:"happy",excited:"happy",disgust:"disgust",disgusted:"disgust"};function Te(e,t="neutral"){const n=String(e??"").trim().toLowerCase();return n?jr[n]??t:t}function _n(e){return e===0?null:yt(St(e),"eyes_standard")}function wn(e){return _n(kt(e))}function Wt(e,t){const n=St(kt(e));return!t||t==="eyes_standard"?wn(e):e==="neutral"&&t==="eyes_standard"?null:yt(n,t)}function Gr(e,t){const n=[];for(const r of t)n.push(Wt(e,r));for(let r=t.length-2;r>=0;r--)n.push(Wt(e,t[r]));return n.push(wn(e)),n}function zr(e){return yt(St(e),"eyes_standard")}function Yr(e,t){return Kr(e,t).map(zr)}const Jr=6;function Qr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function Xr(e){const t=ye(e);return t>=lt?lt:ye(t+Jr)}function Zr(e,t,n){const r=ye(n);return Qr(t,e)?.emphasis?Xr(r):r}function Ht(e){if(!e)return 0;const t=e.match(/^brow_(-?\d+)\//);if(!t?.[1])return null;const n=Number.parseInt(t[1],10);return Number.isFinite(n)?ye(n):null}function Pe(e,t){return e+Math.random()*(t-e)}function Ve(){return Pe(14,32)}function es(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:i=120,doubleBlinkGapMaxMs:o=220,fullBlinkChance:a=.75,postEmotionChangeDelayMs:f=380,minDwellMs:l=550}=e;let _="neutral",p="neutral",m=0,T=0,k=0,u={kind:"idle",nextBlinkAt:performance.now()+t},y=null,d="__eyes_open__|neutral|brow:0";function R(E=_){d=`${y??"__eyes_open__"}|${E}|brow:${T}`}function S(E,A=_){y=E;const g=Ht(E);g!=null?T=g:E==null&&(T=0),R(A)}function D(E){return _n(E)}function H(E,A,g){const x=ye(A),b=ye(g);if(m=b,x===b){S(D(b));return}const C=[...Yr(x,b),D(b)];u={kind:"browBlend",paths:C,index:0,holdUntil:E+Ve(),targetBrow:b},S(C[0]??null)}function V(E,A){u={kind:"idle",nextBlinkAt:E+Pe(n,r)},p=A,S(D(m),A)}function W(E,A,g){const x=g?$r:Math.random()<a?Vr:Wr;u={kind:"playing",paths:Gr(A,x),index:0,holdUntil:E+Ve(),after:"idle",blinkEmotion:A},p=A,S(u.paths[0]??null,A)}function j(E){if(u.kind==="browBlend"){for(;E>=u.holdUntil&&u.index<u.paths.length-1;)u.index+=1,u.holdUntil=E+Ve();const A=u.paths[u.index]??null;S(A),E>=u.holdUntil&&u.index>=u.paths.length-1&&(T=u.targetBrow,m=u.targetBrow,S(D(u.targetBrow)),u={kind:"idle",nextBlinkAt:E+f});return}if(u.kind==="idle"){S(D(m),p),E>=u.nextBlinkAt&&W(E,p,!1);return}if(u.kind==="doublePause"){S(D(m),u.blinkEmotion),E>=u.resumeAt&&W(E,u.blinkEmotion,!0);return}if(u.kind==="playing"){for(;E>=u.holdUntil&&u.index<u.paths.length-1;)u.index+=1,u.holdUntil=E+Ve();if(S(u.paths[u.index]??null,u.blinkEmotion),E>=u.holdUntil&&u.index>=u.paths.length-1){if(u.after==="resumeIdle"){V(E+f,_);return}if(u.after==="doublePause"){u={kind:"doublePause",resumeAt:E+Pe(i,o),blinkEmotion:u.blinkEmotion},S(D(m),u.blinkEmotion);return}if(u.after==="idle"&&Math.random()<s){u={kind:"doublePause",resumeAt:E+Pe(i,o),blinkEmotion:u.blinkEmotion},S(D(m),u.blinkEmotion);return}V(E,u.blinkEmotion)}}}return{advance:j,setTargetEmotion(E){const A=Te(E,_);if(A===_)return;const g=performance.now();l>0&&A!==_&&!(_==="neutral"&&A!=="neutral")&&g-k<l||(_=A,p=A,k=g)},setTargetEyebrow(E){const A=ye(E);if(A===m&&u.kind!=="browBlend")return;const g=performance.now(),x=u.kind==="browBlend"?Ht(y)??T:T;m=A,!(A===x&&u.kind!=="browBlend")&&(u.kind==="playing"||u.kind==="doublePause"||H(g,x,A))},getTargetEmotion:()=>_,getTargetEyebrow:()=>m,getExpressionPath:()=>y,getDrawKey:()=>d,reset(){_="neutral",p="neutral",m=0,T=0,k=0,V(performance.now()+Pe(n,r),"neutral")}}}function ts(){const e=document.createElement("video");e.muted=!0,e.playsInline=!0,e.preload="auto",e.setAttribute("playsinline",""),e.loop=!0;let t=!1,n=null,r="";const s=()=>{n&&(URL.revokeObjectURL(n),n=null)};return{async load(i){t&&r===i||(s(),t=!1,r=i,i.startsWith("blob:")&&(n=i),await new Promise((o,a)=>{const f=()=>{const l=e.duration;if(!Number.isFinite(l)||l<=0){a(new Error(`Idle video has invalid duration: ${i}`));return}t=!0,e.currentTime=0,o()};e.addEventListener("loadeddata",f,{once:!0}),e.addEventListener("error",()=>a(new Error(`Failed to load idle video: ${i}`)),{once:!0}),e.src=i,e.load()}))},isReady(){return t},getVideo(){return e},setActive(i){t&&(i?e.paused&&e.play().catch(()=>{}):e.paused||e.pause())},restart(){t&&(e.currentTime=0)},dispose(){e.pause(),e.removeAttribute("src"),e.load(),s(),t=!1,r=""}}}const ns=50,gn=400;function rs(e,t,n){return e.filter(r=>{const s=r.queueIndex??-1;return s>=t&&s<=n})}function ss(e,t){const n=[];for(const r of e){const s=rs(t,r.start_word,r.end_word);if(s.length===0)continue;const i=s[0],o=s[s.length-1];n.push({sentence:r,startMs:Math.max(0,i.wtime-ns),endMs:o.wtime+o.wduration+gn})}return n.sort((r,s)=>r.startMs-s.startMs)}function is(e,t,n,r="neutral"){const s=Te(r);if(t.length===0)return s;if(n.length===0||e<n[0].wtime)return Te(t[0]?.emotion,s);const i=ss(t,n);let o=null;for(const l of i)e>=l.startMs&&e<=l.endMs&&(o=l);if(o)return Te(o.sentence.emotion,s);const a=n[n.length-1],f=a.wtime+a.wduration;return e>f+gn?Te(t[t.length-1]?.emotion,s):s}function pn(e,t,n,r,s=dn){const i=new OffscreenCanvas(n,r),o=i.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,n,r);const a=o.getImageData(0,0,n,r),f=o.createImageData(n,r);f.data.set(a.data);const _=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const p of t){_.clearRect(0,0,n,r),_.drawImage(p,0,0,n,r);const m=_.getImageData(0,0,n,r);for(let T=0;T<f.data.length;T+=4){const k=m.data[T],u=m.data[T+1],y=m.data[T+2];Math.max(k,u,y)>s&&(f.data[T]=k,f.data[T+1]=u,f.data[T+2]=y,f.data[T+3]=255)}}return o.putImageData(f,0,0),i}let ge=null,Le=null;const Ke=new Map;function os(){ge=null,Le=null,Ke.clear()}Jn(os);function as(e){return e??Le??void 0}function Tt(e){return new Promise((t,n)=>{const r=new Image;r.decoding="async",r.onload=()=>t(r),r.onerror=()=>n(new Error(`Failed to load image: ${e}`)),r.src=e})}async function cs(e){return Ge()?Rr(await Qe(Lt(),e??"")):Tt(Lt())}async function qt(e,t,n,r){const s=n?await Ar(e,r):await fetch(e).then(l=>{if(!l.ok)throw new Error(`Failed to load ${e}`);return l.json()}),i=s.sheets?.[0];if(!i?.path)throw new Error(`${e} has no sheets[0].path`);const o=n?await Ir(await Qe(Ur(t,i.path,n),r??"")):await Tt(`${t}/${i.path}`),a=new Map;for(const l of s.cells??[])l.path&&a.set(l.path,l);const f=new Map;for(const l of s.sheets??[])f.set(l.index,l);return{atlas:o,atlasMeta:s,cellByPath:a,sheetByIndex:f,diffBase:t}}async function ft(e){const t=as(e),n=Ge();if(n&&!t)throw new Error("Encrypted assets enabled but no key provided");if(ge&&(!n||Le===(t??null))){const i=await ge;return{...Oe(i),visemePaths:[...i.viseme.cellByPath.keys()]}}Le=t??null,Ke.clear();const r=Xn();ge=(async()=>{const i=await cs(t),o=await qt(Zn(),r,n,t);let a=null;try{a=await qt(er(),r,n,t)}catch{a=null}return{sil:i,viseme:o,expression:a}})();const s=await ge;return{...Oe(s),visemePaths:[...s.viseme.cellByPath.keys()]}}async function yn(e){if(await ft(e),!ge)throw new Error("Assets not loaded");return ge}function Oe(e){return Tr(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function us(e,t){const n=document.createElement("canvas");return n.width=t.w,n.height=t.h,n.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),n}async function Kt(e,t,n){const r=`${t.diffBase}::${n}`,s=Ke.get(r);if(s)return s;const i=(async()=>{const o=t.cellByPath.get(n);if(!o)throw new Error(`No atlas cell for path: ${n}`);const a=vr(t.atlasMeta,o),f=Oe(e);if(br(o)){const p=us(t,o),m=document.createElement("canvas");m.width=f.width,m.height=f.height;const T=m.getContext("2d");return T.fillStyle="#000",T.fillRect(0,0,f.width,f.height),T.drawImage(p,a.x,a.y),m}const l=`${t.diffBase}/${n}`,_=await Tt(l);if(_.naturalWidth===f.width&&_.naturalHeight===f.height)return _;throw new Error(`Diff PNG wrong size for ${n}`)})();return Ke.set(r,i),i}async function jt(e,t,n,r,s,i){if(s){const{width:T,height:k}=s;if(i&&!i()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const u=document.createElement("canvas");u.width=T,u.height=k;const y=u.getContext("2d");if(y.clearRect(0,0,T,k),y.drawImage(t,0,0,T,k),i&&!i())return;(e.width!==T||e.height!==k)&&(e.width=T,e.height=k);const d=e.getContext("2d");d.clearRect(0,0,T,k),d.drawImage(u,0,0);return}const o=await yn(),a=hn(o.viseme.atlasMeta),{width:f,height:l}=Oe(o);(e.width!==f||e.height!==l)&&(e.width=f,e.height=l);const _=[],p=e.getContext("2d");if(p.clearRect(0,0,f,l),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const T=o.sil;p.drawImage(T,0,0,f,l);return}if(_.length===0){p.drawImage(t,0,0,f,l);return}const m=pn(t,_,f,l,a);p.drawImage(m,0,0)}async function dt(e,t,n){const r=await yn(Le??void 0),s=hn(r.viseme.atlasMeta),{width:i,height:o}=Oe(r);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);const a=[];if(t.mouthPath){const _=new Set(r.viseme.cellByPath.keys()),p=Pr(t.mouthPath,_)??(_.has(t.mouthPath)?t.mouthPath:null);p&&a.push(await Kt(r,r.viseme,p))}t.expressionPath&&r.expression&&a.push(await Kt(r,r.expression,t.expressionPath));const f=e.getContext("2d");if(f.clearRect(0,0,i,o),a.length===0){f.drawImage(r.sil,0,0,i,o);return}const l=pn(r.sil,a,i,o,s);f.drawImage(l,0,0)}async function ls(e){await dt(e,{mouthPath:null,expressionPath:null})}function fs(e){const t=new Map;for(const n of e)t.has(n.vtime)||t.set(n.vtime,n);return[...t.values()].sort((n,r)=>n.vtime-r.vtime)}function ds(e){const t=fs(e),n=[];for(let r=0;r<t.length-1;r++){const s=t[r].viseme,i=t[r+1].viseme,o=t[r].vtime,a=t[r+1].vtime;s===i||a<=o||n.push({from:s,to:i,fromVtime:o,toVtime:a})}return n}function hs(e,t){let n=null;for(const r of e)t>=r.fromVtime&&t<r.toVtime&&(n=r);return n}function ms(e,t){return{gapMs:Math.max(0,t-e),holdEnd:e,transStart:e}}const En=400,_s=200,ws=600,gs="__sil__";function ps(){return{lastDrawnKey:""}}function he(e){e.lastDrawnKey="",e.transitionCache=void 0}function ys(e,t){if(t?.transitionCache&&t.transitionCache.queueLength===e.length)return t.transitionCache.transitions;const n=ds(e);return t&&(t.transitionCache={queueLength:e.length,transitions:n}),n}function Es(e){let t="sil";for(const n of e)n.viseme!=="sil"&&(t=n.viseme);return t}function Ss(e,t){return e==="sil"?!1:t?Lr(e,"sil",t):Et(e,"sil").length>0}function ks(){return En+ws}function Gt(e){return e??gs}function Ts(e,t,n,r){if(e.length===0)return{diffPath:null,label:"sil"};const s=ys(e,r),i=hs(s,t);if(i){const a=i.toVtime-i.fromVtime,f=ms(i.fromVtime,i.toVtime);if(t>=f.transStart&&t<i.toVtime){let l=Et(i.from,i.to);n&&(l=mn(l,n));const _=Br(l,a);if(_.length>0){const p=Fr(t,f.transStart,i.toVtime,_.length);return{diffPath:_[p]??_[_.length-1],label:`${i.from}→${i.to}`}}return{diffPath:qe(i.to),label:i.to}}}const o=Ye(e,t);return o==="sil"?{diffPath:null,label:"sil"}:{diffPath:qe(o),label:o}}function Sn(e,t){const n=t??Gt(e.expressionPath);return`${Gt(e.mouthPath)}|${n}`}function vs(e,t,n,r,s,i,o,a,f,l){const _=Ts(t,n,f,i),p={mouthPath:_.diffPath,expressionPath:r},m=Sn(p,s);m!==i.lastDrawnKey&&(a(_.label),o(e,p,m,T=>{T===m&&(i.lastDrawnKey=m)}))}function zt(e,t,n,r,s,i,o=null){const a={mouthPath:null,expressionPath:t},f=o!=null?`|idle@${Math.round(o*1e3)}`:"",l=Sn(a,n)+f;!(o!=null)&&l===r.lastDrawnKey||(i(o!=null?"idle":"sil"),s(e,a,l,p=>{p===l&&(r.lastDrawnKey=l)}))}function kn(e){let t=0,n=null,r=!1,s=!1;const i=async()=>{if(!r){r=!0;try{for(;n;){const a=n;n=null;const f=++t,l=await e(a.diffs,f,a);n||l&&f===t&&a.onDisplayed?.(a.drawKey)}}finally{r=!1,n&&i()}}},o=()=>{s||(s=!0,queueMicrotask(()=>{s=!1,i()}))};return{enqueue(a,f,l){n={diffs:a,drawKey:f,onDisplayed:l},o()},reset(){t+=1,n=null},isLatestSeq(a){return a===t},getLatestSeq(){return t}}}const Yt=-1,Jt=new Map;async function bs(e){let t=Jt.get(e);if(!t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to fetch worker script (${n.status}): ${e}`);const r=await n.blob();t=URL.createObjectURL(r),Jt.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function As(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function Is(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const n=e.getContext("2d");n&&n.drawImage(t,0,0)}function Qt(e,t){let n=0,r=null;const s=kn(async(i,o)=>(await ft(),await dt(e,i),s.isLatestSeq(o)));return{usesWorker:!1,async loadAssets(i){const o=await ft(i);return r=o.visemePaths?new Set(o.visemePaths):null,o},getVisemeAtlasPaths(){return r},async renderSilOnly(){n+=1,s.reset(),await ls(e)},drawLiveFrame(i,o,a,f){s.enqueue(o,a,f)},async renderViseme(i,o){n+=1;const a=n;t.onStatus?.(o),await dt(i,{mouthPath:qe(o),expressionPath:null})},dispose(){n+=1,s.reset()}}}function Rs(e,t,n){let r=null,s=1,i=0,o=!1,a=!1,f=null;const l=new Map,_=kn(async(y,d,R)=>(l.set(d,{drawKey:R.drawKey,onDisplayed:R.onDisplayed}),await k({type:"drawFrame",diffPath:y.mouthPath,expressionDiffPath:y.expressionPath,liveSeq:d},void 0,!0),!1)),p=y=>{if(y===Yt){let S=-1,D;for(const[H,V]of l)H>=S&&(S=H,D=V);S>=0&&l.delete(S),D?.onDisplayed?.(D.drawKey);return}const d=-y,R=l.get(d);l.delete(d),R?.onDisplayed?.(R.drawKey)},m=new Map,T=(y,d)=>{a||!r||r.postMessage(y,[])},k=(y,d,R=!1)=>{if(a)return Promise.resolve();const S=s++;return new Promise((D,H)=>{m.set(S,{resolve:V=>D(V),reject:H,generation:typeof y.generation=="number"?y.generation:void 0,expectRenderDone:R}),T({...y,requestId:S})})},u=(async()=>{r=await bs(n),r.onmessage=y=>{if(a)return;const d=y.data;if(d.type==="status"){t.onStatus?.(d.label);return}if(d.type==="frame"){const S=d.generation<0;if(!S&&d.generation!==i){d.bitmap.close();return}if(S&&!(d.generation===Yt)){const H=-d.generation;if(!_.isLatestSeq(H)){d.bitmap.close();return}}if(S&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){d.bitmap.close();return}Is(e,d.bitmap),d.bitmap.close(),S&&p(d.generation);return}const R=m.get(d.requestId);if(R)switch(d.type){case"ready":m.delete(d.requestId),R.resolve();break;case"loadAssetsDone":m.delete(d.requestId),f=d.visemePaths?.length?new Set(d.visemePaths):null,R.resolve({width:d.width,height:d.height,visemePaths:d.visemePaths});break;case"renderDone":if(R.expectRenderDone&&R.generation!=null&&d.generation!==R.generation)return;m.delete(d.requestId),R.resolve();break;case"renderAborted":m.delete(d.requestId),R.resolve();break;case"error":m.delete(d.requestId),t.onError?.(d.message),R.reject(new Error(d.message));break}},r.onerror=y=>{if(a)return;const d=y.message||"Worker error";t.onError?.(d);for(const[,R]of m)R.reject(new Error(d));m.clear()},await k({type:"init"}),o=!0})();return{usesWorker:!0,async loadAssets(y){if(a)return{width:842,height:1264};if(await u,a)return{width:842,height:1264};if(!o)throw new Error("Worker init failed");return await k({type:"loadAssets",keyHex:y,urls:Yn(Se())})??{width:842,height:1264}},getVisemeAtlasPaths(){return f},async renderSilOnly(){if(a||(await u,a))return;i+=1,_.reset(),l.clear();const y=i;await k({type:"cancelLiveDraws"}),await k({type:"renderSil",generation:y},void 0,!0)},drawLiveFrame(y,d,R,S){a||u.then(()=>{a||_.enqueue(d,R,S)})},async renderViseme(y,d,R={}){if(a||(await u,a))return;i+=1,_.reset(),l.clear();const S=i;try{await k({type:"cancelLiveDraws"}),await k({type:"renderViseme",generation:S,to:d,from:R.from,transitionDurationMs:R.transitionDurationMs,gapMs:R.gapMs,threshold:R.threshold},void 0,!0)}catch(D){if(D?.name==="AbortError")return;throw D}},dispose(){if(!a){a=!0,i+=1,_.reset(),l.clear();for(const[,y]of m)y.resolve();m.clear(),r?.terminate(),r=null}}}}function Ds(e,t={}){if(!As())return Qt(e,t);const n=Ln(t.workerScriptUrl);try{return Rs(e,t,n)}catch{return Qt(e,t)}}class Cs extends Error{constructor(t,n){super(`${t} timed out after ${Math.round(n/1e3)}s`),this.name="LoadTimeoutError"}}function st(e,t,n){return new Promise((r,s)=>{const i=globalThis.setTimeout(()=>{s(new Cs(n,t))},t);e.then(o=>{globalThis.clearTimeout(i),r(o)},o=>{globalThis.clearTimeout(i),s(o)})})}const Ms={position:"relative",display:"inline-block",lineHeight:0},xs={display:"block",maxWidth:"100%",height:"auto"},Ps={position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",padding:"1rem",background:"rgba(0,0,0,0.55)",color:"#fff",fontSize:"0.875rem",textAlign:"center"};function xe(e){return Un(e)?un:wt}function me(e,t){return t?.isSpeaking()?t:e.isSpeaking()?e:t?.isConnected()?t:e}const Ls=w.forwardRef(function({id:t,avatarId:n,faceId:r,assets:s,authToken:i,ttsEngineId:o=pe,voiceId:a,speakingRate:f=Je,className:l,style:_,canvasStyle:p,onStatusChange:m,onDisplayStatus:T,onError:k,onReady:u,showErrorOverlay:y=!0,onUserTranscript:d,onBotOutput:R,onRealtimeLipsyncDebug:S},D){const H=w.useRef(null),V=w.useRef(null),W=w.useRef(ps()),j=w.useRef(es()),E=w.useRef(0),A=w.useRef(0),g=w.useRef(0),x=w.useRef(null),b=w.useRef(null),C=w.useRef(!1),I=w.useRef(!1),L=w.useRef(!1),O=w.useRef("sil"),$=w.useRef(!1),U=w.useRef(null),X=w.useRef(!1),[se,Z]=w.useState("idle"),[ae,J]=w.useState(null),[c,v]=w.useState(!1),[B,z]=w.useState(!1),[ne,Ae]=w.useState({width:842,height:1264}),G=w.useRef(i??null),ke=w.useRef(null),Y=w.useRef(null),Ie=w.useRef(!1),Xe=w.useRef({width:842,height:1264}),Ze=w.useRef(null),vt=w.useRef(u),ee=w.useRef(T),bt=w.useRef(k),At=w.useRef(d),It=w.useRef(R),Rt=w.useRef(S);vt.current=u,ee.current=T,bt.current=k,At.current=d,It.current=R,Rt.current=S;const Dt=w.useCallback(h=>{const M=h?.ttsEngineId??o??Y.current?.ttsEngineId??pe,P=h?.voiceId??a??Y.current?.voiceId??xe(M);return{ttsEngineId:M,voiceId:P}},[o,a]),le=w.useCallback(h=>{J(h),bt.current?.(h)},[]),Re=w.useCallback(h=>{Z(h),m?.(h)},[m]),q=w.useRef(ct(Re,ve)),K=w.useRef(null);w.useEffect(()=>(q.current=ct(Re,ve),i&&q.current.setDeveloperToken(i),q.current.setTtsEngineId(o),()=>{q.current.stop();const h=K.current;h&&(h.disconnect(),K.current=null)}),[Re,i,o]);const Ct=w.useCallback(()=>(K.current||(K.current=fn(Re,{speakingRate:f,onUserTranscript:(h,M)=>At.current?.(h,M),onBotOutput:h=>It.current?.(h),onLipsyncDebug:h=>Rt.current?.(h)}),K.current.setTtsEngineId(o)),K.current),[Re,f,o]),ie=w.useCallback(()=>{x.current!=null&&(clearTimeout(x.current),x.current=null)},[]),ce=w.useCallback(()=>{b.current!=null&&(clearTimeout(b.current),b.current=null),L.current=!1},[]),Mt=w.useCallback(()=>{!X.current||!U.current||(ce(),L.current=!0,b.current=setTimeout(()=>{if(b.current=null,me(q.current,K.current).isSpeaking()){L.current=!1;return}if(C.current){L.current=!1;return}const h=U.current;if(!h){L.current=!1;return}h.restart(),h.setActive(!0);const M=h.getVideo();let P=!1;const F=()=>{P||(P=!0,M.removeEventListener("playing",N),L.current=!1)},N=()=>{F()};if(M.addEventListener("playing",N,{once:!0}),M.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!M.paused){F();return}setTimeout(F,300)},_s))},[ce]),Be=w.useCallback(()=>{if(ie(),ee.current?.("sil"),C.current=!1,!X.current||!U.current){L.current=!1;return}Mt()},[ie,Mt]),et=w.useCallback(async h=>{const M=A.current,P=V.current,F=H.current,N=()=>{h?.fallbackOnly||(C.current=!1)};if(!P||!F||!c){N();return}if(me(q.current,K.current).isSpeaking()){N();return}if(h?.fallbackOnly){if(!C.current||(he(W.current),await P.renderSilOnly(),M!==A.current)||me(q.current,K.current).isSpeaking())return;Be();return}const te=O.current!=="sil"?O.current:Es(me(q.current,K.current).getVisemeQueue()),oe=Ss(te,Ze.current);if(he(W.current),!oe){if(await P.renderSilOnly(),M!==A.current||me(q.current,K.current).isSpeaking())return;Be();return}try{await P.renderViseme(F,"sil",{from:te,transitionDurationMs:En})}catch(fe){if(fe?.name!=="AbortError")throw fe}if(M!==A.current){N();return}if(me(q.current,K.current).isSpeaking()){N();return}Be()},[c,Be]),De=w.useCallback(()=>{if(I.current||C.current)return;I.current=!0,C.current=!0,ie();const h=A.current;et().finally(()=>{I.current=!1}),x.current=setTimeout(()=>{x.current=null,h===A.current&&et({fallbackOnly:!0})},ks())},[ie,et]),xt=w.useCallback(async()=>{const h=Math.floor(Date.now()/1e3);if(ke.current&&h<ke.current.expiresAt-60)return ke.current.key;const M=G.current??await $e();G.current=M,q.current.setDeveloperToken(M);const P=await Kn(M,ve);return ke.current={key:P.value,expiresAt:P.expiresAt},P.value},[]);w.useEffect(()=>{i&&(G.current=i,q.current.setDeveloperToken(i))},[i]),w.useEffect(()=>{q.current.setTtsEngineId(o),K.current?.setTtsEngineId(o),Y.current&&(Y.current.ttsEngineId=o)},[o]),w.useEffect(()=>{const h=a??xe(o);Y.current&&(Y.current.voiceId=h)},[a,o]);const _e=w.useMemo(()=>rn(n,r),[n,r]),vn=w.useMemo(()=>s?JSON.stringify(s):_e?`face:${_e}`:`id:${t??""}`,[s,_e,t]);return w.useEffect(()=>{if(!t&&!s&&!_e){le("AiTwin requires `assets`, `avatarId`/`faceId`, or `id`");return}let h=!1;Ie.current=!1,v(!1),z(!1),J(null),Y.current=null,U.current=null,X.current=!1;const M=H.current;if(!M)return;let P=null;return(async()=>{try{let F,N,te;if(s)F=s,N=o,te=a??xe(N);else if(_e)ee.current?.("loadFace"),F=at(_e),N=o,te=a??xe(N);else{ee.current?.("getAiTwin");const Q=await st(nn(t),3e4,"getAiTwin");if(h)return;if(!Q.faceId?.trim())throw new Error("getAiTwin returned no faceId");N=Q.ttsEngineId,te=a??Q.voiceId??xe(N),F=at(Q.faceId)}Y.current={ttsEngineId:N,voiceId:te},sn(F),U.current=ts(),P=Ds(M,{onStatus:Q=>{h||ee.current?.(Q)},onError:Q=>{h||le(Q)},shouldAcceptLiveFrame:()=>me(q.current,K.current).isSpeaking()&&!C.current}),V.current=P,ee.current?.("auth");const oe=G.current??i??await st($e(),3e4,"getAuthToken");if(h)return;G.current=oe,q.current.setDeveloperToken(oe),q.current.setTtsEngineId(N);const fe=Ge()?await xt():void 0;if(h)return;ee.current?.("loadAssets");const ue=await st(P.loadAssets(fe),18e4,"load face assets");if(!h&&ue.width>0&&ue.height>0&&(Xe.current=ue,Ae(ue)),h)return;Ze.current=P.getVisemeAtlasPaths(),Ie.current=!0,v(!0),vt.current?.(),P.renderSilOnly().then(()=>{h||ee.current?.("sil")}),(async()=>{try{const Q=await Cr(fe);if(await U.current.load(Q),h)return;X.current=!0,z(!0),he(W.current),U.current.setActive(!0);const re=U.current.getVideo();await jt(M,re,null,void 0,Xe.current),h||ee.current?.("idle")}catch(Q){if(h)return;X.current=!1,z(!1),console.warn("[AiTwin] Idle video unavailable:",Q)}})()}catch(F){if(h)return;if(F instanceof We){le(F.message);return}le(F instanceof Error?F.message:"Failed to load AI twin")}})(),()=>{h=!0,U.current?.dispose(),P?.dispose(),V.current=null}},[vn,s,_e,t,o,a,i,xt,le]),w.useEffect(()=>{const h={current:!1},M=()=>{const P=H.current,F=V.current,N=me(q.current,K.current);if(!P||!F||!c){E.current=requestAnimationFrame(M);return}const te=N.isSpeaking(),oe=te&&!$.current,fe=$.current&&!te;oe&&(ie(),ce(),C.current=!1,I.current=!1,he(W.current),g.current=0,O.current="sil",U.current?.setActive(!1)),fe&&(j.current.reset(),U.current?.setActive(!1),De());const ue=N.getPlaybackElapsedMs(),Q=fe||C.current||L.current;if(te){X.current&&U.current?.setActive(!1);const re=N.getVisemeQueue(),we=re.length>0?Ye(re,ue):"sil";we!=="sil"&&(O.current=we);const de=is(ue,N.getSentenceEmotions(),N.getWordQueue(),N.getStreamMood());j.current.setTargetEmotion(de),j.current.setTargetEyebrow(Zr(ue,N.getWordQueue(),kt(de))),j.current.advance(performance.now());const Ce=j.current.getExpressionPath(),tt=j.current.getDrawKey();vs(P,re,ue,Ce,tt,W.current,(Me,bn,An,In)=>F.drawLiveFrame(Me,bn,An,In),Me=>ee.current?.(Me),Ze.current)}else if(!Q&&X.current&&U.current){const re=U.current,we=re.getVideo();we.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?(re.setActive(!0),h.current||(h.current=!0,jt(P,we,null,void 0,Xe.current,()=>!N.isSpeaking()&&!C.current&&!L.current).then(()=>ee.current?.("idle")).finally(()=>{h.current=!1}))):zt(P,null,"__sil__",W.current,(de,Ce,tt,Me)=>F.drawLiveFrame(de,Ce,tt,Me),de=>ee.current?.(de))}else Q||zt(P,null,"__sil__",W.current,(re,we,de,Ce)=>F.drawLiveFrame(re,we,de,Ce),re=>ee.current?.(re));$.current=te,E.current=requestAnimationFrame(M)};return E.current=requestAnimationFrame(M),()=>{cancelAnimationFrame(E.current),ie(),ce(),q.current.stop(),K.current?.stop()}},[c,B,De,ie,ce]),w.useImperativeHandle(D,()=>({speakText:async(h,M)=>{const P=h.trim();if(!P)return;const F=V.current;if(!F||!Ie.current&&!c)throw new Error("AI twin is not ready");const{ttsEngineId:N,voiceId:te}=Dt(M);J(null),A.current+=1,ie(),ce(),C.current=!1,I.current=!1,O.current="sil",he(W.current),j.current.reset(),$.current=!1;try{X.current?(U.current?.setActive(!1),U.current?.restart(),he(W.current)):await F.renderSilOnly(),await q.current.speak(P,{voiceId:te,speakingRate:M?.speakingRate??f,ttsEngineId:N})}catch(oe){throw le(oe instanceof Error?oe.message:"TTS failed"),oe}},stop:()=>{A.current+=1,ie(),ce(),C.current=!1,I.current=!1,q.current.stop(),K.current?.stop(),he(W.current),$.current=!1,De()},setTtsEngineId:h=>{q.current.setTtsEngineId(h),K.current?.setTtsEngineId(h),Y.current&&(Y.current.ttsEngineId=h)},renderViseme:async(h,M)=>{const P=V.current,F=H.current;if(!P||!F||!Ie.current)throw new Error("AI twin is not ready");await P.renderViseme(F,h,{from:M?.from,transitionDurationMs:M?.transitionDurationMs,gapMs:M?.gapMs,onStatus:N=>ee.current?.(N)})},isReady:()=>Ie.current,getStatus:()=>se,connect:async(h={})=>{J(null);try{let M=h.authToken?.trim();M||(M=G.current??i??await $e(),G.current=M);const P=h.voiceId?.trim()??a??Y.current?.voiceId,F=Ct();await F.unlockAudio(),await F.connect({...h,authToken:M,voiceId:P,speakingRate:h.speakingRate??f})}catch(M){throw le(M instanceof Error?M.message:"Voice connect failed"),M}},disconnect:async()=>{const h=K.current;h&&(A.current+=1,ie(),ce(),C.current=!1,I.current=!1,he(W.current),$.current=!1,await h.disconnect(),De())},isConnected:()=>K.current?.isConnected()??!1}),[c,f,i,a,ie,ce,De,le,Dt,Ct,se]),nt.jsxs("div",{className:l,style:{...Ms,..._},children:[nt.jsx("canvas",{ref:H,width:ne.width,height:ne.height,style:{...xs,...p},"aria-label":t?`AI twin ${t}`:"AI twin face"}),y&&ae?nt.jsx("div",{style:Ps,children:ae}):null]})}),Tn="thumbnail.webp";function Os(e,t=en){const n=e.trim();if(!n)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${n}/${Tn}`}exports.AI_TWIN_THUMBNAIL_FILENAME=Tn;exports.AiTwin=Ls;exports.AiTwinNotFoundError=We;exports.DEFAULT_TTS_ENGINE_ID=pe;exports.MIC_SAMPLE_RATE=gt;exports.OCULUS_VISEME_IDS=on;exports.PLAYBACK_SAMPLE_RATE=pt;exports.SPEAKING_RATE_MAX=_t;exports.SPEAKING_RATE_MIN=mt;exports.TTS_ENGINE_CARTESIA=Ee;exports.TTS_ENGINE_GOOGLE=ht;exports.TTS_ENGINE_INWORLD=je;exports.VISEME_IDS=Mr;exports.VISEME_TEST_CARTESIA_VOICE_ID=un;exports.VISEME_TEST_SPEAKING_RATE=Je;exports.VISEME_TEST_VOICE_ID=wt;exports.buildVoiceWebSocketUrl=ln;exports.createAvatarTtsLipsyncController=ct;exports.createRealtimeVoiceLipsyncController=fn;exports.faceAssetUrls=at;exports.fetchAiTwin=nn;exports.fetchDevAuthToken=$e;exports.getAiTwinThumbnailUrl=Os;exports.normalizeOculusViseme=an;exports.resolveDirectFaceId=rn;exports.resolveVisemeAtTime=Ye;exports.resolveWordAtTime=gr;exports.setActiveTwinAssets=sn;
3
+ `);A=C.pop()??"";for(const I of C)await g(I);if(x){A.trim()&&await g(A.trim()),p.markStreamComplete(r);break}}}catch(V){throw console.error("[avatarTtsLipsync]",V),m(),e("error"),V}},stop:m,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>i,getStreamMood:()=>o,getPlaybackElapsedMs:()=>p.getPlaybackElapsedMs(),isSpeaking:()=>p.isSpeaking(),setPlaybackSpeed:k=>{p.setPlaybackSpeed(k)},getPlaybackSpeed:()=>p.getPlaybackSpeed(),subscribe:k=>(l.add(k),()=>l.delete(k))}}const gt=16e3,pt=24e3;class pr extends Xt.DailyMediaManager{constructor(){super(!0,!0,void 0,void 0,512,gt,pt)}bufferBotAudio(t,n){}}const yr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Er(e){return yr.test(e.trim())}function Sr(e){const t=e.replace(/\/$/,"");return t.startsWith("https://")?`wss://${t.slice(8)}`:t.startsWith("http://")?`ws://${t.slice(7)}`:t}function ln(e){if(e.wsUrl?.trim())return e.wsUrl.trim();const t=e.authToken?.trim();if(!t)throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");const n=e.apiBase??ve,r=new URL(`${Sr(n)}/ws/voice`);r.searchParams.set("authToken",t);const s=e.voiceId?.trim();s&&Er(s)&&r.searchParams.set("voiceId",s);const i=e.speakingRate??Je;return r.searchParams.set("speaking_rate",String(Math.max(mt,Math.min(_t,i)))),e.ttsStream===!1?r.searchParams.set("tts_stream","false"):r.searchParams.set("tts_stream","true"),r.toString()}function Ft(e,t){if(!Array.isArray(e))return[];const n=[];for(const r of e)!r||typeof r!="object"||n.push({sentence_index:Number(r.sentence_index??0),text:String(r.text??""),sentiment:typeof r.sentiment=="string"?r.sentiment:void 0,emotion:String(r.emotion??t),start_word:Number(r.start_word??0),end_word:Number(r.end_word??0)});return n}function kr(e){if(!e||typeof e!="object")return null;const t=e,n=t.data;return n&&typeof n=="object"?n:t}function fn(e,t={}){const n=[],r=[],s=[],i=[];let o="",a="neutral",f=Ee,l=!1,_=null,p=null,m={...t},T=0,k=null;const u=new Set,y=[];let d=!1,R=null;const S=()=>u.forEach(c=>c()),D=cn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:S},f),H=()=>{if(R)return R;const c=new MessageChannel;return c.port1.onmessage=()=>{const v=y.shift();v!==void 0&&$(v),y.length>0?c.port2.postMessage(null):d=!1},R=c,c},V=c=>{y.push(c),!d&&(d=!0,H().port2.postMessage(null))},W=()=>D.getPlaybackElapsedMs(),j=(c,v=o)=>{if(!m.onLipsyncDebug)return;const B=W(),z=n;m.onLipsyncDebug({event:c,spokenText:v,mood:a,playbackAnchorPerf:D.getPlaybackStartPerf(),playbackElapsedMs:B,queueLength:z.length,activeViseme:Ye(z,B),segments:i.map(ne=>({...ne,visemes:[...ne.visemes]}))})},E=(c,v)=>{m.onLipsyncDebug&&(k={event:c,spokenText:v},T===0&&(T=requestAnimationFrame(()=>{T=0;const B=k;k=null,B&&j(B.event,B.spokenText)})))},A=()=>{n.length=0,r.length=0,i.length=0,o="",a="neutral",s.length=0},g=()=>{D.stop(),A(),E("reset"),S()},x=()=>{g(),e("idle"),S()},b=(c,v)=>{typeof c.mood=="string"&&c.mood.trim()&&(a=c.mood.trim()),Array.isArray(c.sentence_emotions)&&c.sentence_emotions.length>0&&(v&&(s.length=0),s.push(...Ft(c.sentence_emotions,a)))},C=c=>{const v=String(c.spoken_text??c.text??"").trim();v&&(o=v),D.resetForNewStream(f),typeof c.mood=="string"&&c.mood.trim()&&(a=c.mood.trim()),s.length=0,s.push(...Ft(c.sentence_emotions,a)),Array.isArray(c.gestures)&&c.gestures,E("metadata",v),S()},I=(c,v,B,z)=>{if(!m.onLipsyncDebug)return;const ne=n.slice(B),Ae=z.filter(G=>String(G.symbol??G.viseme??"").trim());i.push({spokenText:v,audioOffsetMs:ne[0]?.vtime??0,isNewSegment:!!c.is_new_segment,chunkDurationMs:c.chunk_duration_ms!=null?Number(c.chunk_duration_ms):null,receivedAtMs:performance.now(),visemes:ne.map((G,ke)=>{const Y=Ae[ke];return{rawSymbol:String(Y?.symbol??Y?.viseme??G.viseme).trim(),viseme:G.viseme,startSec:Number(Y?.start??0),durationSec:Number(Y?.duration??0),vtimeMs:G.vtime,vdurationMs:G.vduration}})})},L=c=>{const v=String(c.spoken_text??c.text??"").trim(),B=!!c.is_new_segment||Number(c.chunk_index??-1)===0,z=c.visemes??[],ne=c.words??[],Ae=String(c.chunk??"").trim();v&&(o=v),b(c,B),B&&D.resetForNewStream(f);const G=n.length;D.playAudioChunk(n,r,Ae,z,ne,B,()=>{i.length=0}).then(()=>{I(c,v,G,z),E("audio_chunk",v)})},O=c=>{console.warn("[realtimeVoice] avatar_lipsync is deprecated — expect avatar_audio_chunk");const v=String(c.spoken_text??c.text??"").trim();v&&(o=v),b(c,!!c.is_new_segment);const B=n.length;D.queueTimingsOnly(n,r,c.visemes??[],c.words??[],!!c.is_new_segment),I(c,v,B,c.visemes??[]),E("lipsync",v),S()},$=c=>{const v=kr(c);if(!v){console.warn("[realtimeVoice] server message with no payload",c);return}const B=String(v.type??"unknown");switch(B!=="avatar_metadata"&&B!=="avatar_audio_chunk"&&B!=="avatar_lipsync"&&B!=="error"&&console.debug("[realtimeVoice] server message",B,v),B){case"avatar_metadata":C(v);break;case"avatar_audio_chunk":L(v);break;case"avatar_lipsync":O(v);break;case"error":{const z=String(v.message??"Voice session error");console.error("[realtimeVoice] server error:",z),e("error"),S();break}case"avatar_speak":console.warn("[realtimeVoice] avatar_speak received; tts_stream=false Mode B is not wired yet:",v.text);break}},U=()=>_||(p=new pr,_=new Rn.PipecatClient({transport:new Xt.WebSocketTransport({mediaManager:p,recorderSampleRate:gt,playerSampleRate:pt}),enableCam:!1,enableMic:!0,callbacks:{onBotReady:()=>{l=!0,e("idle"),S()},onDisconnected:()=>{l=!1,x()},onError:c=>{const v=typeof c=="object"&&c!==null&&"data"in c?String(c.data??c):String(c);console.error("[realtimeVoice]",v,c),e("error"),S()},onTransportStateChanged:c=>{c==="error"&&(e("error"),S())},onServerMessage:c=>{V(c)},onUserStartedSpeaking:()=>{D.bargeIn(),A(),e("idle"),E("user_started"),S()},onBotStartedSpeaking:()=>{E("bot_started")},onBotStoppedSpeaking:()=>{D.markStreamComplete(n),E("bot_stopped")},onUserTranscript:c=>{m.onUserTranscript?.(c.text,!!c.final)},onBotOutput:c=>{m.onBotOutput?.(c.text)}}}),_);return{setDeveloperToken:c=>{},setTtsEngineId:c=>{f=c,D.setStreamTtsEngineId(c)},getTtsEngineId:()=>f,speak:async(c,v)=>{const B=c.trim();if(B){if(!_||!l)throw new Error("Realtime voice is not connected. Call connect() first.");g(),await _.sendText(B)}},stop:()=>{g(),e("idle"),S()},connect:async c=>{if(c&&(m={...m,...c}),!m.authToken?.trim())throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");e("loading");const v=U();await v.initDevices();const B=ln(m);console.info("[realtimeVoice] connecting (avatar_audio_chunk RTVI):",B);try{await v.connect({wsUrl:B})}catch(z){l=!1,e("error"),S();const ne=z instanceof Error?z.message:"Realtime voice connect failed (WebSocket 101 only means upgrade OK — check authToken/origin/RTVI handshake)";throw new Error(`${ne} — ${B}`)}l=!0,e("idle"),S()},disconnect:async()=>{if(y.length=0,d=!1,_)try{await _.disconnect()}catch(c){console.error("[realtimeVoice] disconnect failed:",c)}l=!1,x()},isConnected:()=>l,unlockAudio:async()=>{await D.primeAudioContext()},getVisemeQueue:()=>n,getWordQueue:()=>r,getSentenceEmotions:()=>s,getStreamMood:()=>a,getPlaybackElapsedMs:W,isSpeaking:()=>D.isSpeaking(),setPlaybackSpeed:c=>{D.setPlaybackSpeed(c)},getPlaybackSpeed:()=>D.getPlaybackSpeed(),subscribe:c=>(u.add(c),()=>u.delete(c))}}function Tr(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function vr(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function br(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function Qe(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return zn(r,t)}async function Ar(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await Qe(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function Ir(e){const t=new Blob([e],{type:"image/webp"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(n),r(i)},i.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted image"))},i.src=n})}async function Rr(e){const t=new Blob([e],{type:"image/png"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(n),r(i)},i.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted PNG"))},i.src=n})}function Dr(e){const t=new Blob([e],{type:"video/mp4"});return URL.createObjectURL(t)}async function Cr(e){const t=Qn();if(!Ge())return t;if(!e)throw new Error("Missing asset key for encrypted idle video");const n=await Qe(t,e);return Dr(n)}function yt(e,t){return`${e}/${t}_minus_sil.png`}const dn=5;function hn(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const n=e?.crop?.content_threshold;return typeof n=="number"&&n>0?n:dn}const Mr=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],ut=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],xr=ut.length,Ut=new Set(["aa__kk","aa__nn","aa__sil","CH__aa","CH__DD","CH__E","CH__FF","CH__I","CH__kk","CH__nn","CH__O","CH__PP","CH__RR","CH__sil","CH__SS","CH__TH","CH__U","DD__aa","DD__E","DD__FF","DD__I","DD__kk","DD__nn","DD__O","DD__PP","DD__RR","DD__sil","DD__SS","DD__TH","DD__U","E__aa","E__FF","E__I","E__kk","E__nn","E__O","E__PP","E__RR","E__sil","E__SS","E__TH","E__U","FF__aa","FF__I","FF__kk","FF__nn","FF__O","FF__PP","FF__RR","FF__sil","FF__SS","FF__TH","FF__U","I__aa","I__kk","I__nn","I__O","I__PP","I__RR","I__sil","I__SS","I__TH","I__U","kk__nn","kk__sil","nn__sil","O__aa","O__kk","O__nn","O__PP","O__RR","O__sil","O__SS","O__TH","O__U","PP__aa","PP__kk","PP__nn","PP__RR","PP__sil","PP__SS","PP__TH","PP__U","RR__aa","RR__kk","RR__nn","RR__sil","RR__SS","RR__TH","RR__U","SS__aa","SS__kk","SS__nn","SS__sil","SS__TH","SS__U","TH__aa","TH__kk","TH__nn","TH__sil","TH__U","U__aa","U__kk","U__nn","U__sil"]);function Nt(e,t){return`${e}__${t}`}function Vt(e,t){return`pairs/${e}/${t}_minus_sil.png`}function qe(e){return`${e}_minus_sil.png`}function Pr(e,t){if(t.has(e))return e;const n=/^pairs\/([^/]+)\//.exec(e);if(!n)return null;const r=n[1].split("__")[1];if(!r||r==="sil")return null;const s=qe(r);return t.has(s)?s:null}function mn(e,t){return e.filter(n=>t.has(n))}function Lr(e,t,n){const r=Et(e,t);return mn(r,n).length>0}function Et(e,t){if(e===t)return[];const n=[],r=Nt(e,t);if(Ut.has(r)){for(const i of ut)n.push(Vt(r,i));return n}const s=Nt(t,e);if(Ut.has(s)){for(let i=xr-1;i>=0;i--)n.push(Vt(s,ut[i]));return n}return[]}function Or(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function Br(e,t){return Or(t).map(r=>e[r]).filter(r=>r!=null)}function Fr(e,t,n,r){if(r<=0)return 0;const i=Math.max(1,n-t)/r,o=e-t,a=Math.floor(o/i);return Math.min(r-1,Math.max(0,a))}function Ur(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}const Nr=[.32,.24,.16,.08,0],Vr=Nr.map(e=>`eyes_${e.toFixed(2)}`),$r=["eyes_0.32","eyes_0.16","eyes_0.00"],Wr=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],$t=2,Hr=-30,lt=20,qr={fear:-30,anger:-20,sad:-10,neutral:0,love:10,happy:0,disgust:20};function St(e){return`brow_${e}`}function kt(e){return qr[e]}function ye(e){return Math.max(Hr,Math.min(lt,e))}function Kr(e,t){if(e===t)return[];const n=e<t?$t:-$t,r=[];let s=e;for(;s!==t;)s+=n,r.push(s);return r}const jr={fear:"fear",afraid:"fear",anxiety:"fear",anger:"anger",angry:"anger",mad:"anger",sad:"sad",sadness:"sad",sorrow:"sad",neutral:"neutral",calm:"neutral",love:"love",loving:"love",affection:"love",happy:"happy",happiness:"happy",joy:"happy",joyful:"happy",excited:"happy",disgust:"disgust",disgusted:"disgust"};function Te(e,t="neutral"){const n=String(e??"").trim().toLowerCase();return n?jr[n]??t:t}function _n(e){return e===0?null:yt(St(e),"eyes_standard")}function wn(e){return _n(kt(e))}function Wt(e,t){const n=St(kt(e));return!t||t==="eyes_standard"?wn(e):e==="neutral"&&t==="eyes_standard"?null:yt(n,t)}function Gr(e,t){const n=[];for(const r of t)n.push(Wt(e,r));for(let r=t.length-2;r>=0;r--)n.push(Wt(e,t[r]));return n.push(wn(e)),n}function zr(e){return e===0?null:yt(St(e),"eyes_standard")}function Yr(e,t){return Kr(e,t).map(zr).filter(n=>n!=null)}const Jr=6;function Qr(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function Xr(e){const t=ye(e);return t>=lt?lt:ye(t+Jr)}function Zr(e,t,n){const r=ye(n);return Qr(t,e)?.emphasis?Xr(r):r}function Ht(e){if(!e)return 0;const t=e.match(/^brow_(-?\d+)\//);if(!t?.[1])return null;const n=Number.parseInt(t[1],10);return Number.isFinite(n)?ye(n):null}function Pe(e,t){return e+Math.random()*(t-e)}function Ve(){return Pe(14,32)}function es(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:i=120,doubleBlinkGapMaxMs:o=220,fullBlinkChance:a=.75,postEmotionChangeDelayMs:f=380,minDwellMs:l=550}=e;let _="neutral",p="neutral",m=0,T=0,k=0,u={kind:"idle",nextBlinkAt:performance.now()+t},y=null,d="__eyes_open__|neutral|brow:0";function R(E=_){d=`${y??"__eyes_open__"}|${E}|brow:${T}`}function S(E,A=_){y=E;const g=Ht(E);g!=null?T=g:E==null&&(T=0),R(A)}function D(E){return _n(E)}function H(E,A,g){const x=ye(A),b=ye(g);if(m=b,x===b){S(D(b));return}const C=[...Yr(x,b),D(b)];u={kind:"browBlend",paths:C,index:0,holdUntil:E+Ve(),targetBrow:b},S(C[0]??null)}function V(E,A){u={kind:"idle",nextBlinkAt:E+Pe(n,r)},p=A,S(D(m),A)}function W(E,A,g){const x=g?$r:Math.random()<a?Vr:Wr;u={kind:"playing",paths:Gr(A,x),index:0,holdUntil:E+Ve(),after:"idle",blinkEmotion:A},p=A,S(u.paths[0]??null,A)}function j(E){if(u.kind==="browBlend"){for(;E>=u.holdUntil&&u.index<u.paths.length-1;)u.index+=1,u.holdUntil=E+Ve();const A=u.paths[u.index]??null;S(A),E>=u.holdUntil&&u.index>=u.paths.length-1&&(T=u.targetBrow,m=u.targetBrow,S(D(u.targetBrow)),u={kind:"idle",nextBlinkAt:E+f});return}if(u.kind==="idle"){S(D(m),p),E>=u.nextBlinkAt&&W(E,p,!1);return}if(u.kind==="doublePause"){S(D(m),u.blinkEmotion),E>=u.resumeAt&&W(E,u.blinkEmotion,!0);return}if(u.kind==="playing"){for(;E>=u.holdUntil&&u.index<u.paths.length-1;)u.index+=1,u.holdUntil=E+Ve();if(S(u.paths[u.index]??null,u.blinkEmotion),E>=u.holdUntil&&u.index>=u.paths.length-1){if(u.after==="resumeIdle"){V(E+f,_);return}if(u.after==="doublePause"){u={kind:"doublePause",resumeAt:E+Pe(i,o),blinkEmotion:u.blinkEmotion},S(D(m),u.blinkEmotion);return}if(u.after==="idle"&&Math.random()<s){u={kind:"doublePause",resumeAt:E+Pe(i,o),blinkEmotion:u.blinkEmotion},S(D(m),u.blinkEmotion);return}V(E,u.blinkEmotion)}}}return{advance:j,setTargetEmotion(E){const A=Te(E,_);if(A===_)return;const g=performance.now();l>0&&A!==_&&!(_==="neutral"&&A!=="neutral")&&g-k<l||(_=A,p=A,k=g)},setTargetEyebrow(E){const A=ye(E);if(A===m&&u.kind!=="browBlend")return;const g=performance.now(),x=u.kind==="browBlend"?Ht(y)??T:T;m=A,!(A===x&&u.kind!=="browBlend")&&(u.kind==="playing"||u.kind==="doublePause"||H(g,x,A))},getTargetEmotion:()=>_,getTargetEyebrow:()=>m,getExpressionPath:()=>y,getDrawKey:()=>d,reset(){_="neutral",p="neutral",m=0,T=0,k=0,V(performance.now()+Pe(n,r),"neutral")}}}function ts(){const e=document.createElement("video");e.muted=!0,e.playsInline=!0,e.preload="auto",e.setAttribute("playsinline",""),e.loop=!0;let t=!1,n=null,r="";const s=()=>{n&&(URL.revokeObjectURL(n),n=null)};return{async load(i){t&&r===i||(s(),t=!1,r=i,i.startsWith("blob:")&&(n=i),await new Promise((o,a)=>{const f=()=>{const l=e.duration;if(!Number.isFinite(l)||l<=0){a(new Error(`Idle video has invalid duration: ${i}`));return}t=!0,e.currentTime=0,o()};e.addEventListener("loadeddata",f,{once:!0}),e.addEventListener("error",()=>a(new Error(`Failed to load idle video: ${i}`)),{once:!0}),e.src=i,e.load()}))},isReady(){return t},getVideo(){return e},setActive(i){t&&(i?e.paused&&e.play().catch(()=>{}):e.paused||e.pause())},restart(){t&&(e.currentTime=0)},dispose(){e.pause(),e.removeAttribute("src"),e.load(),s(),t=!1,r=""}}}const ns=50,gn=400;function rs(e,t,n){return e.filter(r=>{const s=r.queueIndex??-1;return s>=t&&s<=n})}function ss(e,t){const n=[];for(const r of e){const s=rs(t,r.start_word,r.end_word);if(s.length===0)continue;const i=s[0],o=s[s.length-1];n.push({sentence:r,startMs:Math.max(0,i.wtime-ns),endMs:o.wtime+o.wduration+gn})}return n.sort((r,s)=>r.startMs-s.startMs)}function is(e,t,n,r="neutral"){const s=Te(r);if(t.length===0)return s;if(n.length===0||e<n[0].wtime)return Te(t[0]?.emotion,s);const i=ss(t,n);let o=null;for(const l of i)e>=l.startMs&&e<=l.endMs&&(o=l);if(o)return Te(o.sentence.emotion,s);const a=n[n.length-1],f=a.wtime+a.wduration;return e>f+gn?Te(t[t.length-1]?.emotion,s):s}function pn(e,t,n,r,s=dn){const i=new OffscreenCanvas(n,r),o=i.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,n,r);const a=o.getImageData(0,0,n,r),f=o.createImageData(n,r);f.data.set(a.data);const _=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const p of t){_.clearRect(0,0,n,r),_.drawImage(p,0,0,n,r);const m=_.getImageData(0,0,n,r);for(let T=0;T<f.data.length;T+=4){const k=m.data[T],u=m.data[T+1],y=m.data[T+2];Math.max(k,u,y)>s&&(f.data[T]=k,f.data[T+1]=u,f.data[T+2]=y,f.data[T+3]=255)}}return o.putImageData(f,0,0),i}let ge=null,Le=null;const Ke=new Map;function os(){ge=null,Le=null,Ke.clear()}Jn(os);function as(e){return e??Le??void 0}function Tt(e){return new Promise((t,n)=>{const r=new Image;r.decoding="async",r.onload=()=>t(r),r.onerror=()=>n(new Error(`Failed to load image: ${e}`)),r.src=e})}async function cs(e){return Ge()?Rr(await Qe(Lt(),e??"")):Tt(Lt())}async function qt(e,t,n,r){const s=n?await Ar(e,r):await fetch(e).then(l=>{if(!l.ok)throw new Error(`Failed to load ${e}`);return l.json()}),i=s.sheets?.[0];if(!i?.path)throw new Error(`${e} has no sheets[0].path`);const o=n?await Ir(await Qe(Ur(t,i.path,n),r??"")):await Tt(`${t}/${i.path}`),a=new Map;for(const l of s.cells??[])l.path&&a.set(l.path,l);const f=new Map;for(const l of s.sheets??[])f.set(l.index,l);return{atlas:o,atlasMeta:s,cellByPath:a,sheetByIndex:f,diffBase:t}}async function ft(e){const t=as(e),n=Ge();if(n&&!t)throw new Error("Encrypted assets enabled but no key provided");if(ge&&(!n||Le===(t??null))){const i=await ge;return{...Oe(i),visemePaths:[...i.viseme.cellByPath.keys()]}}Le=t??null,Ke.clear();const r=Xn();ge=(async()=>{const i=await cs(t),o=await qt(Zn(),r,n,t);let a=null;try{a=await qt(er(),r,n,t)}catch{a=null}return{sil:i,viseme:o,expression:a}})();const s=await ge;return{...Oe(s),visemePaths:[...s.viseme.cellByPath.keys()]}}async function yn(e){if(await ft(e),!ge)throw new Error("Assets not loaded");return ge}function Oe(e){return Tr(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function us(e,t){const n=document.createElement("canvas");return n.width=t.w,n.height=t.h,n.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),n}async function Kt(e,t,n){const r=`${t.diffBase}::${n}`,s=Ke.get(r);if(s)return s;const i=(async()=>{const o=t.cellByPath.get(n);if(!o)throw new Error(`No atlas cell for path: ${n}`);const a=vr(t.atlasMeta,o),f=Oe(e);if(br(o)){const p=us(t,o),m=document.createElement("canvas");m.width=f.width,m.height=f.height;const T=m.getContext("2d");return T.fillStyle="#000",T.fillRect(0,0,f.width,f.height),T.drawImage(p,a.x,a.y),m}const l=`${t.diffBase}/${n}`,_=await Tt(l);if(_.naturalWidth===f.width&&_.naturalHeight===f.height)return _;throw new Error(`Diff PNG wrong size for ${n}`)})();return Ke.set(r,i),i}async function jt(e,t,n,r,s,i){if(s){const{width:T,height:k}=s;if(i&&!i()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const u=document.createElement("canvas");u.width=T,u.height=k;const y=u.getContext("2d");if(y.clearRect(0,0,T,k),y.drawImage(t,0,0,T,k),i&&!i())return;(e.width!==T||e.height!==k)&&(e.width=T,e.height=k);const d=e.getContext("2d");d.clearRect(0,0,T,k),d.drawImage(u,0,0);return}const o=await yn(),a=hn(o.viseme.atlasMeta),{width:f,height:l}=Oe(o);(e.width!==f||e.height!==l)&&(e.width=f,e.height=l);const _=[],p=e.getContext("2d");if(p.clearRect(0,0,f,l),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const T=o.sil;p.drawImage(T,0,0,f,l);return}if(_.length===0){p.drawImage(t,0,0,f,l);return}const m=pn(t,_,f,l,a);p.drawImage(m,0,0)}async function dt(e,t,n){const r=await yn(Le??void 0),s=hn(r.viseme.atlasMeta),{width:i,height:o}=Oe(r);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);const a=[];if(t.mouthPath){const _=new Set(r.viseme.cellByPath.keys()),p=Pr(t.mouthPath,_)??(_.has(t.mouthPath)?t.mouthPath:null);p&&a.push(await Kt(r,r.viseme,p))}t.expressionPath&&r.expression&&a.push(await Kt(r,r.expression,t.expressionPath));const f=e.getContext("2d");if(f.clearRect(0,0,i,o),a.length===0){f.drawImage(r.sil,0,0,i,o);return}const l=pn(r.sil,a,i,o,s);f.drawImage(l,0,0)}async function ls(e){await dt(e,{mouthPath:null,expressionPath:null})}function fs(e){const t=new Map;for(const n of e)t.has(n.vtime)||t.set(n.vtime,n);return[...t.values()].sort((n,r)=>n.vtime-r.vtime)}function ds(e){const t=fs(e),n=[];for(let r=0;r<t.length-1;r++){const s=t[r].viseme,i=t[r+1].viseme,o=t[r].vtime,a=t[r+1].vtime;s===i||a<=o||n.push({from:s,to:i,fromVtime:o,toVtime:a})}return n}function hs(e,t){let n=null;for(const r of e)t>=r.fromVtime&&t<r.toVtime&&(n=r);return n}function ms(e,t){return{gapMs:Math.max(0,t-e),holdEnd:e,transStart:e}}const En=400,_s=200,ws=600,gs="__sil__";function ps(){return{lastDrawnKey:""}}function he(e){e.lastDrawnKey="",e.transitionCache=void 0}function ys(e,t){if(t?.transitionCache&&t.transitionCache.queueLength===e.length)return t.transitionCache.transitions;const n=ds(e);return t&&(t.transitionCache={queueLength:e.length,transitions:n}),n}function Es(e){let t="sil";for(const n of e)n.viseme!=="sil"&&(t=n.viseme);return t}function Ss(e,t){return e==="sil"?!1:t?Lr(e,"sil",t):Et(e,"sil").length>0}function ks(){return En+ws}function Gt(e){return e??gs}function Ts(e,t,n,r){if(e.length===0)return{diffPath:null,label:"sil"};const s=ys(e,r),i=hs(s,t);if(i){const a=i.toVtime-i.fromVtime,f=ms(i.fromVtime,i.toVtime);if(t>=f.transStart&&t<i.toVtime){let l=Et(i.from,i.to);n&&(l=mn(l,n));const _=Br(l,a);if(_.length>0){const p=Fr(t,f.transStart,i.toVtime,_.length);return{diffPath:_[p]??_[_.length-1],label:`${i.from}→${i.to}`}}return{diffPath:qe(i.to),label:i.to}}}const o=Ye(e,t);return o==="sil"?{diffPath:null,label:"sil"}:{diffPath:qe(o),label:o}}function Sn(e,t){const n=t??Gt(e.expressionPath);return`${Gt(e.mouthPath)}|${n}`}function vs(e,t,n,r,s,i,o,a,f,l){const _=Ts(t,n,f,i),p={mouthPath:_.diffPath,expressionPath:r},m=Sn(p,s);m!==i.lastDrawnKey&&(a(_.label),o(e,p,m,T=>{T===m&&(i.lastDrawnKey=m)}))}function zt(e,t,n,r,s,i,o=null){const a={mouthPath:null,expressionPath:t},f=o!=null?`|idle@${Math.round(o*1e3)}`:"",l=Sn(a,n)+f;!(o!=null)&&l===r.lastDrawnKey||(i(o!=null?"idle":"sil"),s(e,a,l,p=>{p===l&&(r.lastDrawnKey=l)}))}function kn(e){let t=0,n=null,r=!1,s=!1;const i=async()=>{if(!r){r=!0;try{for(;n;){const a=n;n=null;const f=++t,l=await e(a.diffs,f,a);n||l&&f===t&&a.onDisplayed?.(a.drawKey)}}finally{r=!1,n&&i()}}},o=()=>{s||(s=!0,queueMicrotask(()=>{s=!1,i()}))};return{enqueue(a,f,l){n={diffs:a,drawKey:f,onDisplayed:l},o()},reset(){t+=1,n=null},isLatestSeq(a){return a===t},getLatestSeq(){return t}}}const Yt=-1,Jt=new Map;async function bs(e){let t=Jt.get(e);if(!t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to fetch worker script (${n.status}): ${e}`);const r=await n.blob();t=URL.createObjectURL(r),Jt.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function As(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function Is(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const n=e.getContext("2d");n&&n.drawImage(t,0,0)}function Qt(e,t){let n=0,r=null;const s=kn(async(i,o)=>(await ft(),await dt(e,i),s.isLatestSeq(o)));return{usesWorker:!1,async loadAssets(i){const o=await ft(i);return r=o.visemePaths?new Set(o.visemePaths):null,o},getVisemeAtlasPaths(){return r},async renderSilOnly(){n+=1,s.reset(),await ls(e)},drawLiveFrame(i,o,a,f){s.enqueue(o,a,f)},async renderViseme(i,o){n+=1;const a=n;t.onStatus?.(o),await dt(i,{mouthPath:qe(o),expressionPath:null})},dispose(){n+=1,s.reset()}}}function Rs(e,t,n){let r=null,s=1,i=0,o=!1,a=!1,f=null;const l=new Map,_=kn(async(y,d,R)=>(l.set(d,{drawKey:R.drawKey,onDisplayed:R.onDisplayed}),await k({type:"drawFrame",diffPath:y.mouthPath,expressionDiffPath:y.expressionPath,liveSeq:d},void 0,!0),!1)),p=y=>{if(y===Yt){let S=-1,D;for(const[H,V]of l)H>=S&&(S=H,D=V);S>=0&&l.delete(S),D?.onDisplayed?.(D.drawKey);return}const d=-y,R=l.get(d);l.delete(d),R?.onDisplayed?.(R.drawKey)},m=new Map,T=(y,d)=>{a||!r||r.postMessage(y,[])},k=(y,d,R=!1)=>{if(a)return Promise.resolve();const S=s++;return new Promise((D,H)=>{m.set(S,{resolve:V=>D(V),reject:H,generation:typeof y.generation=="number"?y.generation:void 0,expectRenderDone:R}),T({...y,requestId:S})})},u=(async()=>{r=await bs(n),r.onmessage=y=>{if(a)return;const d=y.data;if(d.type==="status"){t.onStatus?.(d.label);return}if(d.type==="frame"){const S=d.generation<0;if(!S&&d.generation!==i){d.bitmap.close();return}if(S&&!(d.generation===Yt)){const H=-d.generation;if(!_.isLatestSeq(H)){d.bitmap.close();return}}if(S&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){d.bitmap.close();return}Is(e,d.bitmap),d.bitmap.close(),S&&p(d.generation);return}const R=m.get(d.requestId);if(R)switch(d.type){case"ready":m.delete(d.requestId),R.resolve();break;case"loadAssetsDone":m.delete(d.requestId),f=d.visemePaths?.length?new Set(d.visemePaths):null,R.resolve({width:d.width,height:d.height,visemePaths:d.visemePaths});break;case"renderDone":if(R.expectRenderDone&&R.generation!=null&&d.generation!==R.generation)return;m.delete(d.requestId),R.resolve();break;case"renderAborted":m.delete(d.requestId),R.resolve();break;case"error":m.delete(d.requestId),t.onError?.(d.message),R.reject(new Error(d.message));break}},r.onerror=y=>{if(a)return;const d=y.message||"Worker error";t.onError?.(d);for(const[,R]of m)R.reject(new Error(d));m.clear()},await k({type:"init"}),o=!0})();return{usesWorker:!0,async loadAssets(y){if(a)return{width:842,height:1264};if(await u,a)return{width:842,height:1264};if(!o)throw new Error("Worker init failed");return await k({type:"loadAssets",keyHex:y,urls:Yn(Se())})??{width:842,height:1264}},getVisemeAtlasPaths(){return f},async renderSilOnly(){if(a||(await u,a))return;i+=1,_.reset(),l.clear();const y=i;await k({type:"cancelLiveDraws"}),await k({type:"renderSil",generation:y},void 0,!0)},drawLiveFrame(y,d,R,S){a||u.then(()=>{a||_.enqueue(d,R,S)})},async renderViseme(y,d,R={}){if(a||(await u,a))return;i+=1,_.reset(),l.clear();const S=i;try{await k({type:"cancelLiveDraws"}),await k({type:"renderViseme",generation:S,to:d,from:R.from,transitionDurationMs:R.transitionDurationMs,gapMs:R.gapMs,threshold:R.threshold},void 0,!0)}catch(D){if(D?.name==="AbortError")return;throw D}},dispose(){if(!a){a=!0,i+=1,_.reset(),l.clear();for(const[,y]of m)y.resolve();m.clear(),r?.terminate(),r=null}}}}function Ds(e,t={}){if(!As())return Qt(e,t);const n=Ln(t.workerScriptUrl);try{return Rs(e,t,n)}catch{return Qt(e,t)}}class Cs extends Error{constructor(t,n){super(`${t} timed out after ${Math.round(n/1e3)}s`),this.name="LoadTimeoutError"}}function st(e,t,n){return new Promise((r,s)=>{const i=globalThis.setTimeout(()=>{s(new Cs(n,t))},t);e.then(o=>{globalThis.clearTimeout(i),r(o)},o=>{globalThis.clearTimeout(i),s(o)})})}const Ms={position:"relative",display:"inline-block",lineHeight:0},xs={display:"block",maxWidth:"100%",height:"auto"},Ps={position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",padding:"1rem",background:"rgba(0,0,0,0.55)",color:"#fff",fontSize:"0.875rem",textAlign:"center"};function xe(e){return Un(e)?un:wt}function me(e,t){return t?.isSpeaking()?t:e.isSpeaking()?e:t?.isConnected()?t:e}const Ls=w.forwardRef(function({id:t,avatarId:n,faceId:r,assets:s,authToken:i,ttsEngineId:o=pe,voiceId:a,speakingRate:f=Je,className:l,style:_,canvasStyle:p,onStatusChange:m,onDisplayStatus:T,onError:k,onReady:u,showErrorOverlay:y=!0,onUserTranscript:d,onBotOutput:R,onRealtimeLipsyncDebug:S},D){const H=w.useRef(null),V=w.useRef(null),W=w.useRef(ps()),j=w.useRef(es()),E=w.useRef(0),A=w.useRef(0),g=w.useRef(0),x=w.useRef(null),b=w.useRef(null),C=w.useRef(!1),I=w.useRef(!1),L=w.useRef(!1),O=w.useRef("sil"),$=w.useRef(!1),U=w.useRef(null),X=w.useRef(!1),[se,Z]=w.useState("idle"),[ae,J]=w.useState(null),[c,v]=w.useState(!1),[B,z]=w.useState(!1),[ne,Ae]=w.useState({width:842,height:1264}),G=w.useRef(i??null),ke=w.useRef(null),Y=w.useRef(null),Ie=w.useRef(!1),Xe=w.useRef({width:842,height:1264}),Ze=w.useRef(null),vt=w.useRef(u),ee=w.useRef(T),bt=w.useRef(k),At=w.useRef(d),It=w.useRef(R),Rt=w.useRef(S);vt.current=u,ee.current=T,bt.current=k,At.current=d,It.current=R,Rt.current=S;const Dt=w.useCallback(h=>{const M=h?.ttsEngineId??o??Y.current?.ttsEngineId??pe,P=h?.voiceId??a??Y.current?.voiceId??xe(M);return{ttsEngineId:M,voiceId:P}},[o,a]),le=w.useCallback(h=>{J(h),bt.current?.(h)},[]),Re=w.useCallback(h=>{Z(h),m?.(h)},[m]),q=w.useRef(ct(Re,ve)),K=w.useRef(null);w.useEffect(()=>(q.current=ct(Re,ve),i&&q.current.setDeveloperToken(i),q.current.setTtsEngineId(o),()=>{q.current.stop();const h=K.current;h&&(h.disconnect(),K.current=null)}),[Re,i,o]);const Ct=w.useCallback(()=>(K.current||(K.current=fn(Re,{speakingRate:f,onUserTranscript:(h,M)=>At.current?.(h,M),onBotOutput:h=>It.current?.(h),onLipsyncDebug:h=>Rt.current?.(h)}),K.current.setTtsEngineId(o)),K.current),[Re,f,o]),ie=w.useCallback(()=>{x.current!=null&&(clearTimeout(x.current),x.current=null)},[]),ce=w.useCallback(()=>{b.current!=null&&(clearTimeout(b.current),b.current=null),L.current=!1},[]),Mt=w.useCallback(()=>{!X.current||!U.current||(ce(),L.current=!0,b.current=setTimeout(()=>{if(b.current=null,me(q.current,K.current).isSpeaking()){L.current=!1;return}if(C.current){L.current=!1;return}const h=U.current;if(!h){L.current=!1;return}h.restart(),h.setActive(!0);const M=h.getVideo();let P=!1;const F=()=>{P||(P=!0,M.removeEventListener("playing",N),L.current=!1)},N=()=>{F()};if(M.addEventListener("playing",N,{once:!0}),M.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!M.paused){F();return}setTimeout(F,300)},_s))},[ce]),Be=w.useCallback(()=>{if(ie(),ee.current?.("sil"),C.current=!1,!X.current||!U.current){L.current=!1;return}Mt()},[ie,Mt]),et=w.useCallback(async h=>{const M=A.current,P=V.current,F=H.current,N=()=>{h?.fallbackOnly||(C.current=!1)};if(!P||!F||!c){N();return}if(me(q.current,K.current).isSpeaking()){N();return}if(h?.fallbackOnly){if(!C.current||(he(W.current),await P.renderSilOnly(),M!==A.current)||me(q.current,K.current).isSpeaking())return;Be();return}const te=O.current!=="sil"?O.current:Es(me(q.current,K.current).getVisemeQueue()),oe=Ss(te,Ze.current);if(he(W.current),!oe){if(await P.renderSilOnly(),M!==A.current||me(q.current,K.current).isSpeaking())return;Be();return}try{await P.renderViseme(F,"sil",{from:te,transitionDurationMs:En})}catch(fe){if(fe?.name!=="AbortError")throw fe}if(M!==A.current){N();return}if(me(q.current,K.current).isSpeaking()){N();return}Be()},[c,Be]),De=w.useCallback(()=>{if(I.current||C.current)return;I.current=!0,C.current=!0,ie();const h=A.current;et().finally(()=>{I.current=!1}),x.current=setTimeout(()=>{x.current=null,h===A.current&&et({fallbackOnly:!0})},ks())},[ie,et]),xt=w.useCallback(async()=>{const h=Math.floor(Date.now()/1e3);if(ke.current&&h<ke.current.expiresAt-60)return ke.current.key;const M=G.current??await $e();G.current=M,q.current.setDeveloperToken(M);const P=await Kn(M,ve);return ke.current={key:P.value,expiresAt:P.expiresAt},P.value},[]);w.useEffect(()=>{i&&(G.current=i,q.current.setDeveloperToken(i))},[i]),w.useEffect(()=>{q.current.setTtsEngineId(o),K.current?.setTtsEngineId(o),Y.current&&(Y.current.ttsEngineId=o)},[o]),w.useEffect(()=>{const h=a??xe(o);Y.current&&(Y.current.voiceId=h)},[a,o]);const _e=w.useMemo(()=>rn(n,r),[n,r]),vn=w.useMemo(()=>s?JSON.stringify(s):_e?`face:${_e}`:`id:${t??""}`,[s,_e,t]);return w.useEffect(()=>{if(!t&&!s&&!_e){le("AiTwin requires `assets`, `avatarId`/`faceId`, or `id`");return}let h=!1;Ie.current=!1,v(!1),z(!1),J(null),Y.current=null,U.current=null,X.current=!1;const M=H.current;if(!M)return;let P=null;return(async()=>{try{let F,N,te;if(s)F=s,N=o,te=a??xe(N);else if(_e)ee.current?.("loadFace"),F=at(_e),N=o,te=a??xe(N);else{ee.current?.("getAiTwin");const Q=await st(nn(t),3e4,"getAiTwin");if(h)return;if(!Q.faceId?.trim())throw new Error("getAiTwin returned no faceId");N=Q.ttsEngineId,te=a??Q.voiceId??xe(N),F=at(Q.faceId)}Y.current={ttsEngineId:N,voiceId:te},sn(F),U.current=ts(),P=Ds(M,{onStatus:Q=>{h||ee.current?.(Q)},onError:Q=>{h||le(Q)},shouldAcceptLiveFrame:()=>me(q.current,K.current).isSpeaking()&&!C.current}),V.current=P,ee.current?.("auth");const oe=G.current??i??await st($e(),3e4,"getAuthToken");if(h)return;G.current=oe,q.current.setDeveloperToken(oe),q.current.setTtsEngineId(N);const fe=Ge()?await xt():void 0;if(h)return;ee.current?.("loadAssets");const ue=await st(P.loadAssets(fe),18e4,"load face assets");if(!h&&ue.width>0&&ue.height>0&&(Xe.current=ue,Ae(ue)),h)return;Ze.current=P.getVisemeAtlasPaths(),Ie.current=!0,v(!0),vt.current?.(),P.renderSilOnly().then(()=>{h||ee.current?.("sil")}),(async()=>{try{const Q=await Cr(fe);if(await U.current.load(Q),h)return;X.current=!0,z(!0),he(W.current),U.current.setActive(!0);const re=U.current.getVideo();await jt(M,re,null,void 0,Xe.current),h||ee.current?.("idle")}catch(Q){if(h)return;X.current=!1,z(!1),console.warn("[AiTwin] Idle video unavailable:",Q)}})()}catch(F){if(h)return;if(F instanceof We){le(F.message);return}le(F instanceof Error?F.message:"Failed to load AI twin")}})(),()=>{h=!0,U.current?.dispose(),P?.dispose(),V.current=null}},[vn,s,_e,t,o,a,i,xt,le]),w.useEffect(()=>{const h={current:!1},M=()=>{const P=H.current,F=V.current,N=me(q.current,K.current);if(!P||!F||!c){E.current=requestAnimationFrame(M);return}const te=N.isSpeaking(),oe=te&&!$.current,fe=$.current&&!te;oe&&(ie(),ce(),C.current=!1,I.current=!1,he(W.current),g.current=0,O.current="sil",U.current?.setActive(!1)),fe&&(j.current.reset(),U.current?.setActive(!1),De());const ue=N.getPlaybackElapsedMs(),Q=fe||C.current||L.current;if(te){X.current&&U.current?.setActive(!1);const re=N.getVisemeQueue(),we=re.length>0?Ye(re,ue):"sil";we!=="sil"&&(O.current=we);const de=is(ue,N.getSentenceEmotions(),N.getWordQueue(),N.getStreamMood());j.current.setTargetEmotion(de),j.current.setTargetEyebrow(Zr(ue,N.getWordQueue(),kt(de))),j.current.advance(performance.now());const Ce=j.current.getExpressionPath(),tt=j.current.getDrawKey();vs(P,re,ue,Ce,tt,W.current,(Me,bn,An,In)=>F.drawLiveFrame(Me,bn,An,In),Me=>ee.current?.(Me),Ze.current)}else if(!Q&&X.current&&U.current){const re=U.current,we=re.getVideo();we.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?(re.setActive(!0),h.current||(h.current=!0,jt(P,we,null,void 0,Xe.current,()=>!N.isSpeaking()&&!C.current&&!L.current).then(()=>ee.current?.("idle")).finally(()=>{h.current=!1}))):zt(P,null,"__sil__",W.current,(de,Ce,tt,Me)=>F.drawLiveFrame(de,Ce,tt,Me),de=>ee.current?.(de))}else Q||zt(P,null,"__sil__",W.current,(re,we,de,Ce)=>F.drawLiveFrame(re,we,de,Ce),re=>ee.current?.(re));$.current=te,E.current=requestAnimationFrame(M)};return E.current=requestAnimationFrame(M),()=>{cancelAnimationFrame(E.current),ie(),ce(),q.current.stop(),K.current?.stop()}},[c,B,De,ie,ce]),w.useImperativeHandle(D,()=>({speakText:async(h,M)=>{const P=h.trim();if(!P)return;const F=V.current;if(!F||!Ie.current&&!c)throw new Error("AI twin is not ready");const{ttsEngineId:N,voiceId:te}=Dt(M);J(null),A.current+=1,ie(),ce(),C.current=!1,I.current=!1,O.current="sil",he(W.current),j.current.reset(),$.current=!1;try{X.current?(U.current?.setActive(!1),U.current?.restart(),he(W.current)):await F.renderSilOnly(),await q.current.speak(P,{voiceId:te,speakingRate:M?.speakingRate??f,ttsEngineId:N})}catch(oe){throw le(oe instanceof Error?oe.message:"TTS failed"),oe}},stop:()=>{A.current+=1,ie(),ce(),C.current=!1,I.current=!1,q.current.stop(),K.current?.stop(),he(W.current),$.current=!1,De()},setTtsEngineId:h=>{q.current.setTtsEngineId(h),K.current?.setTtsEngineId(h),Y.current&&(Y.current.ttsEngineId=h)},renderViseme:async(h,M)=>{const P=V.current,F=H.current;if(!P||!F||!Ie.current)throw new Error("AI twin is not ready");await P.renderViseme(F,h,{from:M?.from,transitionDurationMs:M?.transitionDurationMs,gapMs:M?.gapMs,onStatus:N=>ee.current?.(N)})},isReady:()=>Ie.current,getStatus:()=>se,connect:async(h={})=>{J(null);try{let M=h.authToken?.trim();M||(M=G.current??i??await $e(),G.current=M);const P=h.voiceId?.trim()??a??Y.current?.voiceId,F=Ct();await F.unlockAudio(),await F.connect({...h,authToken:M,voiceId:P,speakingRate:h.speakingRate??f})}catch(M){throw le(M instanceof Error?M.message:"Voice connect failed"),M}},disconnect:async()=>{const h=K.current;h&&(A.current+=1,ie(),ce(),C.current=!1,I.current=!1,he(W.current),$.current=!1,await h.disconnect(),De())},isConnected:()=>K.current?.isConnected()??!1}),[c,f,i,a,ie,ce,De,le,Dt,Ct,se]),nt.jsxs("div",{className:l,style:{...Ms,..._},children:[nt.jsx("canvas",{ref:H,width:ne.width,height:ne.height,style:{...xs,...p},"aria-label":t?`AI twin ${t}`:"AI twin face"}),y&&ae?nt.jsx("div",{style:Ps,children:ae}):null]})}),Tn="thumbnail.webp";function Os(e,t=en){const n=e.trim();if(!n)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${n}/${Tn}`}exports.AI_TWIN_THUMBNAIL_FILENAME=Tn;exports.AiTwin=Ls;exports.AiTwinNotFoundError=We;exports.DEFAULT_TTS_ENGINE_ID=pe;exports.MIC_SAMPLE_RATE=gt;exports.OCULUS_VISEME_IDS=on;exports.PLAYBACK_SAMPLE_RATE=pt;exports.SPEAKING_RATE_MAX=_t;exports.SPEAKING_RATE_MIN=mt;exports.TTS_ENGINE_CARTESIA=Ee;exports.TTS_ENGINE_GOOGLE=ht;exports.TTS_ENGINE_INWORLD=je;exports.VISEME_IDS=Mr;exports.VISEME_TEST_CARTESIA_VOICE_ID=un;exports.VISEME_TEST_SPEAKING_RATE=Je;exports.VISEME_TEST_VOICE_ID=wt;exports.buildVoiceWebSocketUrl=ln;exports.createAvatarTtsLipsyncController=ct;exports.createRealtimeVoiceLipsyncController=fn;exports.faceAssetUrls=at;exports.fetchAiTwin=nn;exports.fetchDevAuthToken=$e;exports.getAiTwinThumbnailUrl=Os;exports.normalizeOculusViseme=an;exports.resolveDirectFaceId=rn;exports.resolveVisemeAtTime=Ye;exports.resolveWordAtTime=gr;exports.setActiveTwinAssets=sn;
package/dist/aitwin.js CHANGED
@@ -4,7 +4,7 @@ import { PipecatClient as bn } from "@pipecat-ai/client-js";
4
4
  import { WebSocketTransport as Tn, DailyMediaManager as An } from "@pipecat-ai/websocket-transport";
5
5
  const In = "https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev", Yt = {
6
6
  publicBase: In
7
- }, Rn = "0.2.2", Dn = {
7
+ }, Rn = "0.2.3", Dn = {
8
8
  version: Rn
9
9
  }, Mn = {}, be = "https://ai.streamoji.com", Jt = `${Yt.publicBase}/custom-faces`, xn = `${Yt.publicBase}/aitwin-workers/v${Dn.version}/visemeDiffPreview.worker.js`;
10
10
  function Cn(e) {
@@ -1382,10 +1382,10 @@ function Qr(e, t) {
1382
1382
  return n.push(ln(e)), n;
1383
1383
  }
1384
1384
  function Xr(e) {
1385
- return dt(mt(e), "eyes_standard");
1385
+ return e === 0 ? null : dt(mt(e), "eyes_standard");
1386
1386
  }
1387
1387
  function Zr(e, t) {
1388
- return Yr(e, t).map(Xr);
1388
+ return Yr(e, t).map(Xr).filter((n) => n != null);
1389
1389
  }
1390
1390
  const es = 6;
1391
1391
  function ts(e, t) {
@@ -1 +1 @@
1
- (function(){"use strict";function et(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function nt(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function rt(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}function k(e){return e.includes("/custom-faces/")}function z(e){const t=e.binBase.trim().replace(/\/$/,""),n=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||k(t)||k(n)))return{twinBase:n,binBase:t,encrypted:!1};const s=k(t)?t:k(n)?n:t;return{twinBase:s,binBase:s,encrypted:!0}}function m(e){return z(e).encrypted}function st(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function P(e){return Uint8Array.from(e)}function at(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function ot(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=P(n.subarray(0,12)),s=P(n.subarray(12,28)),a=P(n.subarray(28)),o=P(at(a,s)),l=await crypto.subtle.importKey("raw",st(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},l,o)}async function T(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return ot(r,t)}async function it(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await T(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function ct(e){const t=new Blob([e],{type:"image/webp"});return createImageBitmap(t)}async function lt(e){const t=new Blob([e],{type:"image/png"});return createImageBitmap(t)}const W=5;function K(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const n=e?.crop?.content_threshold;return typeof n=="number"&&n>0?n:W}const x=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],_t=x.length,J=new Set(["aa__kk","aa__nn","aa__sil","CH__aa","CH__DD","CH__E","CH__FF","CH__I","CH__kk","CH__nn","CH__O","CH__PP","CH__RR","CH__sil","CH__SS","CH__TH","CH__U","DD__aa","DD__E","DD__FF","DD__I","DD__kk","DD__nn","DD__O","DD__PP","DD__RR","DD__sil","DD__SS","DD__TH","DD__U","E__aa","E__FF","E__I","E__kk","E__nn","E__O","E__PP","E__RR","E__sil","E__SS","E__TH","E__U","FF__aa","FF__I","FF__kk","FF__nn","FF__O","FF__PP","FF__RR","FF__sil","FF__SS","FF__TH","FF__U","I__aa","I__kk","I__nn","I__O","I__PP","I__RR","I__sil","I__SS","I__TH","I__U","kk__nn","kk__sil","nn__sil","O__aa","O__kk","O__nn","O__PP","O__RR","O__sil","O__SS","O__TH","O__U","PP__aa","PP__kk","PP__nn","PP__RR","PP__sil","PP__SS","PP__TH","PP__U","RR__aa","RR__kk","RR__nn","RR__sil","RR__SS","RR__TH","RR__U","SS__aa","SS__kk","SS__nn","SS__sil","SS__TH","SS__U","TH__aa","TH__kk","TH__nn","TH__sil","TH__U","U__aa","U__kk","U__nn","U__sil"]);function G(e,t){return`${e}__${t}`}function j(e,t){return`pairs/${e}/${t}_minus_sil.png`}function $(e){return`${e}_minus_sil.png`}function ft(e,t){if(t.has(e))return e;const n=/^pairs\/([^/]+)\//.exec(e);if(!n)return null;const r=n[1].split("__")[1];if(!r||r==="sil")return null;const s=$(r);return t.has(s)?s:null}function ut(e,t){return e.filter(n=>t.has(n))}function dt(e,t){if(e===t)return[];const n=[],r=G(e,t);if(J.has(r)){for(const a of x)n.push(j(r,a));return n}const s=G(t,e);if(J.has(s)){for(let a=_t-1;a>=0;a--)n.push(j(s,x[a]));return n}return[]}function wt(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function ht(e,t){return wt(t).map(r=>e[r]).filter(r=>r!=null)}function pt(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}function yt(e,t,n,r,s=W){const a=new OffscreenCanvas(n,r),o=a.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,n,r);const l=o.getImageData(0,0,n,r),i=o.createImageData(n,r);i.data.set(l.data);const u=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const h of t){u.clearRect(0,0,n,r),u.drawImage(h,0,0,n,r);const _=u.getImageData(0,0,n,r);for(let f=0;f<i.data.length;f+=4){const y=_.data[f],F=_.data[f+1],O=_.data[f+2];Math.max(y,F,O)>s&&(i.data[f]=y,i.data[f+1]=F,i.data[f+2]=O,i.data[f+3]=255)}}return o.putImageData(i,0,0),a}const Q=-1;function mt(e){return e<0}let g=0,X=Promise.resolve(),D=!1;function v(e){const t=X.then(e);return X=t.then(()=>{},()=>{}),t}function I(e,t){const n=t>0?-t:Q,r={type:"renderDone",requestId:e,generation:n};self.postMessage(r)}async function gt(e){const t=typeof e.liveSeq=="number"?e.liveSeq:0;if(D){I(e.requestId,t);return}if(t>0&&(g=t),!d)throw new Error("Worker not initialized");if(t>0&&t!==g){I(e.requestId,t);return}if(await S(p??void 0),t>0&&t!==g){I(e.requestId,t);return}if(await tt(d,e.diffPath,e.expressionDiffPath??null,e.threshold),t>0&&t!==g){I(e.requestId,t);return}const n=t>0?-t:Q;await U(n),I(e.requestId,t)}let d=null,E=null,p=null,q=null,A="sil";const B=new Map;function b(){if(!q)throw new Error("Worker assets not configured");return q}function Y(){const e=b();return m(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Dt(){const e=b();return m(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function It(){const e=b();return m(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function Et(){const e=b();return m(e)?e.binBase:e.twinBase}function R(){return m(b())}async function H(e){const t=await fetch(e);if(!t.ok)throw new Error(`Failed to load image: ${e}`);const n=await t.blob();return createImageBitmap(n)}async function bt(e){return R()?lt(await T(Y(),e??"")):H(Y())}async function Z(e,t,n){const r=R(),s=r?await it(e,n):await fetch(e).then(c=>{if(!c.ok)throw new Error(`Failed to load ${e}`);return c.json()}),a=s.sheets?.[0];if(!a?.path)throw new Error(`${e} has no sheets[0].path`);const o=r?await ct(await T(pt(t,a.path,r),n??"")):await H(`${t}/${a.path}`),l=new Map;for(const c of s.cells??[])c.path&&l.set(c.path,c);const i=new Map;for(const c of s.sheets??[])i.set(c.index,c);return{atlas:o,atlasMeta:s,cellByPath:l,sheetByIndex:i,diffBase:t}}async function S(e){if(R()&&!e)throw new Error("Encrypted assets enabled but no key provided");if(E&&(!R()||p===(e??null)))return E;p=e??null,B.clear();const t=Et();return E=(async()=>{const n=await bt(e),r=await Z(Dt(),t,e);let s=null;try{s=await Z(It(),t,e)}catch{s=null}return{sil:n,viseme:r,expression:s}})(),E}function M(e){return et(e.viseme.atlasMeta,e.sil.width,e.sil.height)}function St(e,t){if(!e.sheetByIndex.get(t.sheet??0))throw new Error(`Unknown sheet index: ${t.sheet}`);const r=new OffscreenCanvas(t.w,t.h);return r.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),r}async function Ft(e){return createImageBitmap(e)}async function V(e,t,n){const r=`${t.diffBase}::${n}`,s=B.get(r);if(s)return s;const a=(async()=>{const o=t.cellByPath.get(n);if(!o)throw new Error(`No atlas cell for path: ${n}`);const l=nt(t.atlasMeta,o),i=M(e);if(rt(o)){const h=St(t,o),_=new OffscreenCanvas(i.width,i.height),f=_.getContext("2d");return f.fillStyle="#000",f.fillRect(0,0,i.width,i.height),f.drawImage(h,l.x,l.y),Ft(_)}const c=`${t.diffBase}/${n}`,u=await H(c);if(u.width===i.width&&u.height===i.height)return u;throw u.close(),new Error(`Diff PNG wrong size for ${n}`)})();return B.set(r,a),a}function C(e,t){const n={type:"status",label:e,generation:t};self.postMessage(n)}function kt(e,t){const n={type:"error",requestId:e,message:t};self.postMessage(n)}async function U(e){if(!d)return;const t=mt(e);if(!t&&e!==w)return;const n=await createImageBitmap(d);if(!t&&e!==w){n.close();return}const r={type:"frame",bitmap:n,generation:e};self.postMessage(r,{transfer:[n]})}function Pt(e,t,n){return new Promise((r,s)=>{setTimeout(()=>{t!==n?s(new DOMException("Aborted","AbortError")):r()},e)})}async function tt(e,t,n,r){const s=await S(p??void 0),a=r??K(s.viseme.atlasMeta),{width:o,height:l}=M(s);(e.width!==o||e.height!==l)&&(e.width=o,e.height=l);const i=[];if(t){const h=new Set(s.viseme.cellByPath.keys()),_=ft(t,h)??(h.has(t)?t:null);_&&i.push(await V(s,s.viseme,_))}if(n&&s.expression){if(!s.expression.cellByPath.get(n))throw new Error(`No expression atlas cell for path: ${n}`);i.push(await V(s,s.expression,n))}const c=e.getContext("2d");if(c.clearRect(0,0,o,l),i.length===0){c.drawImage(s.sil,0,0,o,l);return}const u=yt(s.sil,i,o,l,a);c.drawImage(u,0,0)}async function N(e,t,n,r,s){await tt(e,t,n,s),await U(r)}async function L(e,t,n){A="sil";const r=await S(p??void 0);if(t!==n)throw new DOMException("Aborted","AbortError");const{width:s,height:a}=M(r);e.width=s,e.height=a;const o=e.getContext("2d");o.clearRect(0,0,s,a),o.drawImage(r.sil,0,0,s,a),await U(t)}async function At(e,t,n,r,s={}){const a=s.from??A,o=s.transitionDurationMs??400,l=s.gapMs??o,i=await S(p??void 0),c=s.threshold??K(i.viseme.atlasMeta);if(a===t){if(C(t,n),t==="sil"?await L(e,n,r):await N(e,$(t),null,n,c),n!==r)throw new DOMException("Aborted","AbortError");A=t;return}const u=dt(a,t),h=new Set(i.viseme.cellByPath.keys());let _=ht(u,l);_=ut(_,h);const f=_.length>0?o/_.length:0;_.length===0&&C(`${a} → ${t} (no pair; final only)`,n);for(let y=0;y<_.length;y++){if(n!==r)throw new DOMException("Aborted","AbortError");const F=_[y],O=F.split("/").pop()?.replace("_minus_sil.png","")??`frame ${y}`;if(C(`${a} → ${t} (${O})`,n),await N(e,F,null,n,c),n!==r)throw new DOMException("Aborted","AbortError");f>0&&await Pt(f,n,r)}if(n!==r)throw new DOMException("Aborted","AbortError");if(C(t,n),t==="sil"?await L(e,n,r):await N(e,$(t),null,n,c),n!==r)throw new DOMException("Aborted","AbortError");A=t}let w=0;self.onmessage=e=>{const t=e.data;(async()=>{try{switch(t.type){case"init":{d=new OffscreenCanvas(1,1);const n={type:"ready",requestId:t.requestId};self.postMessage(n);break}case"loadAssets":{q=z(t.urls),E=null,p=null,B.clear();const n=await S(t.keyHex),{width:r,height:s}=M(n),a={type:"loadAssetsDone",requestId:t.requestId,width:r,height:s,visemePaths:[...n.viseme.cellByPath.keys()]};self.postMessage(a);break}case"cancelLiveDraws":{g+=1;const n={type:"renderDone",requestId:t.requestId,generation:w};self.postMessage(n);break}case"drawFrame":{await v(()=>gt(t));break}case"renderSil":{await v(async()=>{D=!0;try{if(w=t.generation,!d)throw new Error("Worker not initialized");await L(d,t.generation,w)}finally{D=!1}});const n={type:"renderDone",requestId:t.requestId,generation:t.generation};self.postMessage(n);break}case"renderViseme":{await v(async()=>{D=!0;try{if(w=t.generation,!d)throw new Error("Worker not initialized");await At(d,t.to,t.generation,w,{from:t.from,transitionDurationMs:t.transitionDurationMs,gapMs:t.gapMs,threshold:t.threshold})}finally{D=!1}});const n={type:"renderDone",requestId:t.requestId,generation:t.generation};self.postMessage(n);break}default:break}}catch(n){if(n instanceof DOMException&&n.name==="AbortError"){const r={type:"renderAborted",requestId:t.requestId,generation:"generation"in t?t.generation:w};self.postMessage(r);return}kt(t.requestId,n instanceof Error?n.message:String(n))}})()}})();
1
+ (function(){"use strict";function et(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function nt(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function rt(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}function k(e){return e.includes("/custom-faces/")}function z(e){const t=e.binBase.trim().replace(/\/$/,""),n=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||k(t)||k(n)))return{twinBase:n,binBase:t,encrypted:!1};const s=k(t)?t:k(n)?n:t;return{twinBase:s,binBase:s,encrypted:!0}}function m(e){return z(e).encrypted}function st(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function P(e){return Uint8Array.from(e)}function at(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function ot(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=P(n.subarray(0,12)),s=P(n.subarray(12,28)),a=P(n.subarray(28)),o=P(at(a,s)),l=await crypto.subtle.importKey("raw",st(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},l,o)}async function O(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return ot(r,t)}async function it(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await O(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function ct(e){const t=new Blob([e],{type:"image/webp"});return createImageBitmap(t)}async function lt(e){const t=new Blob([e],{type:"image/png"});return createImageBitmap(t)}const W=5;function K(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const n=e?.crop?.content_threshold;return typeof n=="number"&&n>0?n:W}const T=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],_t=T.length,J=new Set(["aa__kk","aa__nn","aa__sil","CH__aa","CH__DD","CH__E","CH__FF","CH__I","CH__kk","CH__nn","CH__O","CH__PP","CH__RR","CH__sil","CH__SS","CH__TH","CH__U","DD__aa","DD__E","DD__FF","DD__I","DD__kk","DD__nn","DD__O","DD__PP","DD__RR","DD__sil","DD__SS","DD__TH","DD__U","E__aa","E__FF","E__I","E__kk","E__nn","E__O","E__PP","E__RR","E__sil","E__SS","E__TH","E__U","FF__aa","FF__I","FF__kk","FF__nn","FF__O","FF__PP","FF__RR","FF__sil","FF__SS","FF__TH","FF__U","I__aa","I__kk","I__nn","I__O","I__PP","I__RR","I__sil","I__SS","I__TH","I__U","kk__nn","kk__sil","nn__sil","O__aa","O__kk","O__nn","O__PP","O__RR","O__sil","O__SS","O__TH","O__U","PP__aa","PP__kk","PP__nn","PP__RR","PP__sil","PP__SS","PP__TH","PP__U","RR__aa","RR__kk","RR__nn","RR__sil","RR__SS","RR__TH","RR__U","SS__aa","SS__kk","SS__nn","SS__sil","SS__TH","SS__U","TH__aa","TH__kk","TH__nn","TH__sil","TH__U","U__aa","U__kk","U__nn","U__sil"]);function G(e,t){return`${e}__${t}`}function j(e,t){return`pairs/${e}/${t}_minus_sil.png`}function $(e){return`${e}_minus_sil.png`}function ft(e,t){if(t.has(e))return e;const n=/^pairs\/([^/]+)\//.exec(e);if(!n)return null;const r=n[1].split("__")[1];if(!r||r==="sil")return null;const s=$(r);return t.has(s)?s:null}function ut(e,t){return e.filter(n=>t.has(n))}function dt(e,t){if(e===t)return[];const n=[],r=G(e,t);if(J.has(r)){for(const a of T)n.push(j(r,a));return n}const s=G(t,e);if(J.has(s)){for(let a=_t-1;a>=0;a--)n.push(j(s,T[a]));return n}return[]}function wt(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function ht(e,t){return wt(t).map(r=>e[r]).filter(r=>r!=null)}function pt(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}function yt(e,t,n,r,s=W){const a=new OffscreenCanvas(n,r),o=a.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,n,r);const l=o.getImageData(0,0,n,r),i=o.createImageData(n,r);i.data.set(l.data);const u=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const w of t){u.clearRect(0,0,n,r),u.drawImage(w,0,0,n,r);const _=u.getImageData(0,0,n,r);for(let f=0;f<i.data.length;f+=4){const y=_.data[f],F=_.data[f+1],x=_.data[f+2];Math.max(y,F,x)>s&&(i.data[f]=y,i.data[f+1]=F,i.data[f+2]=x,i.data[f+3]=255)}}return o.putImageData(i,0,0),a}const Q=-1;function mt(e){return e<0}let g=0,X=Promise.resolve(),D=!1;function v(e){const t=X.then(e);return X=t.then(()=>{},()=>{}),t}function I(e,t){const n=t>0?-t:Q,r={type:"renderDone",requestId:e,generation:n};self.postMessage(r)}async function gt(e){const t=typeof e.liveSeq=="number"?e.liveSeq:0;if(D){I(e.requestId,t);return}if(t>0&&(g=t),!d)throw new Error("Worker not initialized");if(t>0&&t!==g){I(e.requestId,t);return}if(await S(p??void 0),t>0&&t!==g){I(e.requestId,t);return}if(await tt(d,e.diffPath,e.expressionDiffPath??null,e.threshold),t>0&&t!==g){I(e.requestId,t);return}const n=t>0?-t:Q;await U(n),I(e.requestId,t)}let d=null,E=null,p=null,q=null,A="sil";const B=new Map;function b(){if(!q)throw new Error("Worker assets not configured");return q}function Y(){const e=b();return m(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Dt(){const e=b();return m(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function It(){const e=b();return m(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function Et(){const e=b();return m(e)?e.binBase:e.twinBase}function R(){return m(b())}async function H(e){const t=await fetch(e);if(!t.ok)throw new Error(`Failed to load image: ${e}`);const n=await t.blob();return createImageBitmap(n)}async function bt(e){return R()?lt(await O(Y(),e??"")):H(Y())}async function Z(e,t,n){const r=R(),s=r?await it(e,n):await fetch(e).then(c=>{if(!c.ok)throw new Error(`Failed to load ${e}`);return c.json()}),a=s.sheets?.[0];if(!a?.path)throw new Error(`${e} has no sheets[0].path`);const o=r?await ct(await O(pt(t,a.path,r),n??"")):await H(`${t}/${a.path}`),l=new Map;for(const c of s.cells??[])c.path&&l.set(c.path,c);const i=new Map;for(const c of s.sheets??[])i.set(c.index,c);return{atlas:o,atlasMeta:s,cellByPath:l,sheetByIndex:i,diffBase:t}}async function S(e){if(R()&&!e)throw new Error("Encrypted assets enabled but no key provided");if(E&&(!R()||p===(e??null)))return E;p=e??null,B.clear();const t=Et();return E=(async()=>{const n=await bt(e),r=await Z(Dt(),t,e);let s=null;try{s=await Z(It(),t,e)}catch{s=null}return{sil:n,viseme:r,expression:s}})(),E}function M(e){return et(e.viseme.atlasMeta,e.sil.width,e.sil.height)}function St(e,t){if(!e.sheetByIndex.get(t.sheet??0))throw new Error(`Unknown sheet index: ${t.sheet}`);const r=new OffscreenCanvas(t.w,t.h);return r.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),r}async function Ft(e){return createImageBitmap(e)}async function V(e,t,n){const r=`${t.diffBase}::${n}`,s=B.get(r);if(s)return s;const a=(async()=>{const o=t.cellByPath.get(n);if(!o)throw new Error(`No atlas cell for path: ${n}`);const l=nt(t.atlasMeta,o),i=M(e);if(rt(o)){const w=St(t,o),_=new OffscreenCanvas(i.width,i.height),f=_.getContext("2d");return f.fillStyle="#000",f.fillRect(0,0,i.width,i.height),f.drawImage(w,l.x,l.y),Ft(_)}const c=`${t.diffBase}/${n}`,u=await H(c);if(u.width===i.width&&u.height===i.height)return u;throw u.close(),new Error(`Diff PNG wrong size for ${n}`)})();return B.set(r,a),a}function C(e,t){const n={type:"status",label:e,generation:t};self.postMessage(n)}function kt(e,t){const n={type:"error",requestId:e,message:t};self.postMessage(n)}async function U(e){if(!d)return;const t=mt(e);if(!t&&e!==h)return;const n=await createImageBitmap(d);if(!t&&e!==h){n.close();return}const r={type:"frame",bitmap:n,generation:e};self.postMessage(r,{transfer:[n]})}function Pt(e,t,n){return new Promise((r,s)=>{setTimeout(()=>{t!==n?s(new DOMException("Aborted","AbortError")):r()},e)})}async function tt(e,t,n,r){const s=await S(p??void 0),a=r??K(s.viseme.atlasMeta),{width:o,height:l}=M(s);(e.width!==o||e.height!==l)&&(e.width=o,e.height=l);const i=[];if(t){const w=new Set(s.viseme.cellByPath.keys()),_=ft(t,w)??(w.has(t)?t:null);_&&i.push(await V(s,s.viseme,_))}n&&s.expression&&s.expression.cellByPath.get(n)&&i.push(await V(s,s.expression,n));const c=e.getContext("2d");if(c.clearRect(0,0,o,l),i.length===0){c.drawImage(s.sil,0,0,o,l);return}const u=yt(s.sil,i,o,l,a);c.drawImage(u,0,0)}async function N(e,t,n,r,s){await tt(e,t,n,s),await U(r)}async function L(e,t,n){A="sil";const r=await S(p??void 0);if(t!==n)throw new DOMException("Aborted","AbortError");const{width:s,height:a}=M(r);e.width=s,e.height=a;const o=e.getContext("2d");o.clearRect(0,0,s,a),o.drawImage(r.sil,0,0,s,a),await U(t)}async function At(e,t,n,r,s={}){const a=s.from??A,o=s.transitionDurationMs??400,l=s.gapMs??o,i=await S(p??void 0),c=s.threshold??K(i.viseme.atlasMeta);if(a===t){if(C(t,n),t==="sil"?await L(e,n,r):await N(e,$(t),null,n,c),n!==r)throw new DOMException("Aborted","AbortError");A=t;return}const u=dt(a,t),w=new Set(i.viseme.cellByPath.keys());let _=ht(u,l);_=ut(_,w);const f=_.length>0?o/_.length:0;_.length===0&&C(`${a} → ${t} (no pair; final only)`,n);for(let y=0;y<_.length;y++){if(n!==r)throw new DOMException("Aborted","AbortError");const F=_[y],x=F.split("/").pop()?.replace("_minus_sil.png","")??`frame ${y}`;if(C(`${a} → ${t} (${x})`,n),await N(e,F,null,n,c),n!==r)throw new DOMException("Aborted","AbortError");f>0&&await Pt(f,n,r)}if(n!==r)throw new DOMException("Aborted","AbortError");if(C(t,n),t==="sil"?await L(e,n,r):await N(e,$(t),null,n,c),n!==r)throw new DOMException("Aborted","AbortError");A=t}let h=0;self.onmessage=e=>{const t=e.data;(async()=>{try{switch(t.type){case"init":{d=new OffscreenCanvas(1,1);const n={type:"ready",requestId:t.requestId};self.postMessage(n);break}case"loadAssets":{q=z(t.urls),E=null,p=null,B.clear();const n=await S(t.keyHex),{width:r,height:s}=M(n),a={type:"loadAssetsDone",requestId:t.requestId,width:r,height:s,visemePaths:[...n.viseme.cellByPath.keys()]};self.postMessage(a);break}case"cancelLiveDraws":{g+=1;const n={type:"renderDone",requestId:t.requestId,generation:h};self.postMessage(n);break}case"drawFrame":{await v(()=>gt(t));break}case"renderSil":{await v(async()=>{D=!0;try{if(h=t.generation,!d)throw new Error("Worker not initialized");await L(d,t.generation,h)}finally{D=!1}});const n={type:"renderDone",requestId:t.requestId,generation:t.generation};self.postMessage(n);break}case"renderViseme":{await v(async()=>{D=!0;try{if(h=t.generation,!d)throw new Error("Worker not initialized");await At(d,t.to,t.generation,h,{from:t.from,transitionDurationMs:t.transitionDurationMs,gapMs:t.gapMs,threshold:t.threshold})}finally{D=!1}});const n={type:"renderDone",requestId:t.requestId,generation:t.generation};self.postMessage(n);break}default:break}}catch(n){if(n instanceof DOMException&&n.name==="AbortError"){const r={type:"renderAborted",requestId:t.requestId,generation:"generation"in t?t.generation:h};self.postMessage(r);return}kt(t.requestId,n instanceof Error?n.message:String(n))}})()}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamoji/aitwin",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Embeddable React AI twin face with TTS lipsync",
5
5
  "type": "module",
6
6
  "main": "./dist/aitwin.cjs",