@streamoji/aitwin 0.5.1 → 0.5.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/README.md CHANGED
@@ -59,6 +59,7 @@ Provide **`id`** (cloud twin), **`avatarId`/`faceId`** (R2 custom face), or **`a
59
59
  | `voiceId` | Override TTS voice |
60
60
  | `speakingRate` | Default `0.85` |
61
61
  | `showErrorOverlay` | Canvas error overlay (default `true`) |
62
+ | `captions` | Plain subtitles during TTS/voice playback (`true` or style options) |
62
63
  | `onReady` | Assets loaded and canvas ready |
63
64
  | `onStatusChange` | TTS status: idle, loading, speaking, done, error |
64
65
  | `onDisplayStatus` | Compositor label (viseme / idle / transition) |
@@ -69,6 +70,28 @@ Provide **`id`** (cloud twin), **`avatarId`/`faceId`** (R2 custom face), or **`a
69
70
 
70
71
  Use **stable** `useCallback` handlers for `onReady` / `onError` / `onDisplayStatus` in parent components.
71
72
 
73
+ #### Captions
74
+
75
+ Enable plain subtitles for the current speech segment:
76
+
77
+ ```tsx
78
+ <AiTwin
79
+ id="olivia"
80
+ captions={true}
81
+ />
82
+
83
+ <AiTwin
84
+ id="olivia"
85
+ captions={{
86
+ fontFamily: "Georgia, serif",
87
+ fontSize: "1.1rem",
88
+ color: "rgba(255,255,255,0.85)",
89
+ }}
90
+ />
91
+ ```
92
+
93
+ Works for both `speakText()` and voice `connect()` bot responses.
94
+
72
95
  ### Ref handle
73
96
 
74
97
  | Method | Description |
package/dist/aitwin.cjs CHANGED
@@ -1,8 +1,8 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ir=require("react/jsx-runtime"),b=require("react"),ws=require("@pipecat-ai/client-js"),br=require("@pipecat-ai/websocket-transport"),ce=require("pixi.js");var or=typeof document<"u"?document.currentScript:null;const gs="https://aitwin.bubu.social",yn={publicBase:gs},ys="0.5.1",_s={version:ys},Ss={},vt="https://ai.aitwin.me",Tr=`${yn.publicBase}/custom-faces`,vs=`${yn.publicBase}/aitwin-workers/v${_s.version}/visemeDiffPreview.worker.js`;function bs(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:or&&or.tagName.toUpperCase()==="SCRIPT"&&or.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?Ss?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():vs}const Ts="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",ar=3e3,yt=16,zr=3e3,ks=4e3,Es=.85,Is=8,kr=400,Er="eng_a7f2b9c1",Gt="eng_d4e8f3a2",Ze="eng_c9b1e6d4",Xe=Ze,_n=new Set([Gt,Ze]),Sn={[Er]:"oculus_direct",[Ze]:"oculus_direct",[Gt]:"inworld_word"},jr={google:Er,inworld:Gt,cartesia:Ze};function vn(e){return Sn[e]}function As(e){return e===Ze}function Ps(e){if(e.ttsEngineId&&e.ttsEngineId in Sn)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in jr?jr[t]:Xe}class Vt extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function bn(e,t=Ts){const r=new URL(t);r.searchParams.set("id",e);const n=await fetch(r.toString());if(n.status===404)throw new Vt;if(!n.ok)throw new Error(`getAiTwin failed (${n.status})`);const s=await n.json();if(!s.success)throw new Vt(s.error??"AI twin not found");const{tts:i,token:o,...a}=s.data,l=String(a.authToken??o??"").trim()||void 0;return{...a,ttsEngineId:Ps(s.data),authToken:l}}const xs="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",Ms="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Rs="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Cs="6TNvp3SCs4Og0a1ijm9TommXLql1",Ds="Swaraj Mali";async function Nt(){const e=await fetch(xs,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":Ms,"Client-Secret":Rs},body:JSON.stringify({userId:Cs,userName:Ds})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),r=t.authToken??t.token??"";if(!r.trim())throw new Error("getAuthToken returned no token");return r}async function Fs(e,t=vt){const r=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!r.ok)throw new Error(`/api/session-value failed (${r.status})`);const n=await r.json();if(!n.value)throw new Error("Session value response missing value");return n}function Bs(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const r=new Uint8Array(t.length/2);for(let n=0;n<t.length;n+=2)r[n/2]=Number.parseInt(t.slice(n,n+2),16);return r}function Dt(e){return Uint8Array.from(e)}function Ls(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}async function Os(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 r=new Uint8Array(e),n=Dt(r.subarray(0,12)),s=Dt(r.subarray(12,28)),i=Dt(r.subarray(28)),o=Dt(Ls(i,s)),a=await crypto.subtle.importKey("raw",Bs(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:n,tagLength:128},a,o)}let mr=null;const hr=new Set;function Ft(e){return e.includes("/custom-faces/")}function Ut(e){const t=e.binBase.trim().replace(/\/$/,""),r=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||Ft(t)||Ft(r)))return{twinBase:r,binBase:t,encrypted:!1};const s=Ft(t)?t:Ft(r)?r:t;return{twinBase:s,binBase:s,encrypted:!0}}function et(e){return Ut(e).encrypted}function pr(e,t=Tr){const r=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:r,binBase:r,encrypted:!0}}function Tn(e,t){return(e??t)?.trim()||void 0}function kn(e){mr=Ut(e);for(const t of hr)t()}function Ns(e,t){const r=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!r)return Ut(e);const n=s=>{try{return new URL(s,r).href.replace(/\/$/,"")}catch{return s}};return Ut({twinBase:n(e.twinBase),binBase:n(e.binBase),encrypted:e.encrypted})}function tt(){if(!mr)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return mr}function En(e){return hr.add(e),()=>hr.delete(e)}function Wt(){const e=tt();return et(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Vs(){const e=tt();return et(e)?`${e.binBase}/idle.bin`:`${e.twinBase}/idle.mp4`}function In(){const e=tt();return et(e)?e.binBase:e.twinBase}function bt(){return et(tt())}function An(){const e=tt();return et(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function Pn(){const e=tt();return et(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function xn(e){const t=e.split(/\r?\n/);let r="",n="";for(const i of t)i.startsWith("event:")?r=i.slice(6).trim():i.startsWith("data:")&&(n=i.slice(5).trim());if(!r)return null;let s={};if(n)try{s=JSON.parse(n)}catch{s={raw:n}}return{event:r,data:s}}const Mn=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],Us=new Map(Mn.map(e=>[e.toLowerCase(),e]));function Ir(e){const t=(e??"").trim();return t?Us.get(t.toLowerCase())??"sil":"sil"}const Ws={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 $s(e){if(!e)return["sil"];const t=e.toLowerCase();return Ws[t]??["sil"]}function Rn(e,t){const r=e.word.length;if(r<=0)return 0;const n=e.wduration;if(n<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/n));return Math.min(r-1,Math.floor(s*r))}function ct(e,t){const r=Math.max(0,Math.min(e.length-1,t));return e[r]??""}function zt(e,t){const r=e.toLowerCase().replace(/[^a-z']/g,"");return!r||ct(r,t)!=="w"||t>0&&ct(r,t-1)==="q"?!1:t===0&&/^w[aeiouy]/.test(r)}function Hs(e,t){const r=e.toLowerCase(),n=ct(r,t),s=r.slice(Math.max(0,t-1),t+2);return n==="i"||n==="y"?"I":s.includes("ee")||n==="e"||r.includes("ea")?"E":"aa"}function Ks(e,t){const r=e.toLowerCase(),n=r.slice(Math.max(0,t-1),Math.min(r.length,t+3));if(/oo|ou|uw/.test(n))return"U";if(zt(r,t))return"FF";const s=ct(r,t);return s==="u"||s==="w"?"U":"O"}function qs(e,t){const r=e.toLowerCase();if(zt(r,t))return"FF";const n=r.slice(Math.max(0,t-2),t+1);if(/qu/.test(n))return"U";const s=r.slice(Math.max(0,t-1),Math.min(r.length,t+3));return/wo|wh/.test(s)||ct(r,t)==="o"?"O":"U"}const Bt={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function Gs(e,t){const r=e.toLowerCase(),n=ct(r,t);if(Bt[n])return Bt[n];for(let s=0;s<r.length;s++){const i=r[s];if(Bt[i])return Bt[i]}return"DD"}function Cn(e,t,r){const n=$s(e);if(n.length===1)return n[0];const s=t.trim();if(!s)return n[0];const i=(e??"").toLowerCase(),o=Math.max(0,r);switch(i){case"aei":return Hs(s,o);case"o":return Ks(s,o);case"qw":return qs(s,o);case"cdgknstxyz":return Gs(s,o);default:return n[0]}}const wr=47,Dn=new Set(["aa","E","I"]),Fn=new Set(["DD","SS","kk","CH"]),zs=[Dn,Fn];function js(e){for(const t of zs)if(t.has(e))return t;return null}function Ys(e){return js(e)!=null}function Bn(e){const t=new Map;for(const r of e)t.has(r.vtime)||t.set(r.vtime,r);return[...t.values()].sort((r,n)=>r.vtime-n.vtime)}function Ar(e){if(e.length<=2)return[...e];const t=[e[0]];for(let r=1;r<e.length-1;r++){const n=e[r],s=e[r+1],i=t[t.length-1];if(!Ys(n.viseme)){t.push(n);continue}const o=n.vtime-i.vtime,a=s.vtime-n.vtime;o<wr||a<wr||t.push(n)}return t.push(e[e.length-1]),t}function jt(e){return Ar(Bn(e))}function Ln(e,t){let r="sil";for(const n of e){if(n.vtime>t)break;n.viseme!=="sil"&&(r=n.viseme)}return r}function Yt(e){const t=Bn(e),r=Ar(t),n=new Set(r.map(i=>i.vtime)),s=[];for(const i of t)n.has(i.vtime)||s.push({viseme:i.viseme,vtime:i.vtime});return s}function Qs(e){return Yt(e).map(t=>t.viseme)}const Xs=32,Yr=48;function On(e){let t=0;for(const r of e)t=Math.max(t,r.vtime+r.vduration);return t}function Nn(e){let t=null,r=-1;for(const n of e){if(n.viseme==="sil")continue;const s=n.vtime+n.vduration;s>r&&(t=n,r=s)}return t}function Js(e){return jt(e).find(r=>r.viseme!=="sil")??null}function Ye(e){const t=On(e);return Nn(e)?t+kr:t}function Zs(e){const t=[],r=Js(e);r&&r.vtime>0&&t.push({from:"sil",to:r.viseme,fromVtime:0,toVtime:r.vtime});const n=Nn(e),s=On(e);return n&&t.push({from:n.viseme,to:"sil",fromVtime:s,toVtime:s+kr}),t}function Vn(e){const t=jt(e),r=[];for(let n=0;n<t.length-1;n++){const s=t[n].viseme,i=t[n+1].viseme,o=t[n].vtime,a=t[n+1].vtime;s===i||a<=o||r.push({from:s,to:i,fromVtime:o,toVtime:a})}return r}function Un(e){return e.length===0?[]:[...Zs(e),...Vn(e)]}function ei(e,t){let r=null;for(const n of e){const{transStart:s,transEnd:i}=Wn(n.fromVtime,n.toVtime,n.from,n.to);t>=s&&t<i&&(r=n)}return r}function Wn(e,t,r,n){const s=Math.max(0,t-e);if((r==="PP"||n==="PP")&&s>Yr){const f=Yr,c=t,u=c-f;return{gapMs:s,effectiveGapMs:f,holdEnd:u,transStart:u,transEnd:c}}const o=s>0?Math.max(s,Xs):0,a=e,l=e+o;return{gapMs:s,effectiveGapMs:o,holdEnd:e,transStart:a,transEnd:l}}function $n(e,t,r,n,s,i){n.forEach(o=>{const l=String(o.word??"").trim();if(!l)return;const f=Math.round((o.start??0)*1e3),c=o.duration!=null?Math.round(o.duration*1e3):o.end!=null?Math.max(1,Math.round((o.end-(o.start??0))*1e3)):0;t.push({word:l,wtime:s+f,wduration:c,queueIndex:t.length,gesture:o.gesture??null,emphasis:o.emphasis===!0})}),r.forEach(o=>{const a=String(o.symbol??o.viseme??"").trim();if(!a)return;const l=Math.round((o.start??0)*1e3),f=Math.max(1,Math.round((o.duration??0)*1e3)),c=s+l,u=qn(t,c),d=u?Rn(u,c):0;let p;vn(i)==="oculus_direct"?(p=Ir(a),p==="U"&&u&&zt(u.word,d)&&(p="FF")):p=Cn(a,u?.word??"",d),e.push({viseme:p,weight:1,vtime:c,vduration:f})})}function Hn(e,t){let r=t;for(const n of e){const s=Math.round((n.start??0)*1e3),i=Math.max(1,Math.round((n.duration??0)*1e3));r=Math.max(r,t+s+i)}return r}const Pr=16e3;function Kn(e){const t=Number(e);return Number.isFinite(t)&&t>0?t:Pr}function ti(e){let t=e.trim();const r=t.indexOf(",");t.startsWith("data:")&&r>=0&&(t=t.slice(r+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const n=t.length%4;return n!==0&&(t+="=".repeat(4-n)),t}function ri(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function Qr(e,t,r=Pr){const n=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(n.buffer,n.byteOffset,n.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,r);return o.copyToChannel(i,0),o}function ni(e){const t=ti(e),r=window.atob(t),n=new Uint8Array(r.length);for(let s=0;s<r.length;s++)n[s]=r.charCodeAt(s);return n}async function Xr(e,t,r=Pr){const n=ni(t),s=Kn(r);if(ri(n)){const i=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(i)}if(n.length>=2&&n.length%2===0)return Qr(e,n,s);try{const i=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(i)}catch{return Qr(e,n,s)}}function qn(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function Gn(e,t){const r=[],n=[];let s=null,i=!1,o=!1,a=!1,l=!1,f=0,c=0,u=0,d=null,p=1,T=t,m=!1,A=null,x=0,M=!1,g=!1,v=0,L=null,N=null,Q=null;const fe=()=>({visemes:[],words:[],segmentOffsetMs:0}),P=()=>{g=!1,v=0},C=()=>{d!=null&&(clearTimeout(d),d=null)},V=()=>{for(const y of n){try{y.onended=null,y.stop(0)}catch{}try{y.disconnect()}catch{}}n.length=0},Y=()=>{for(const y of n)y.playbackRate.value=p},D=()=>{if(!(m||!l||i||g||n.length>0||r.length>0)){if(a&&s&&c>0){const y=(s.currentTime-c)*1e3;if(y<x){C();const B=p>0?(x-y)/p:x-y;d=setTimeout(()=>{m||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},Math.max(0,B));return}}o=!1,C(),e.onSpeakingDone(),e.onNotify()}},j=y=>{const B=Ye(y);if(B<=0){e.onSpeakingDone(),e.onNotify();return}o=!0,f=performance.now(),e.onSpeakingStart(),e.onNotify(),C();const H=p>0?B/p:B;d=setTimeout(()=>{m||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},H)},$=(y,B,H,X)=>{B.length!==0&&(u=H,B.forEach(R=>{const w=String(R.word??"").trim();if(!w)return;const _=Math.round((R.start??0)*1e3),k=R.duration!=null?Math.round(R.duration*1e3):R.end!=null?Math.max(1,Math.round((R.end-(R.start??0))*1e3)):0;y.push({word:w,wtime:u+_,wduration:k,queueIndex:y.length,gesture:R.gesture??null,emphasis:R.emphasis===!0})}))},z=(y,B,H,X,R,F)=>{H.length!==0&&(u=X,H.forEach(w=>{const _=String(w.symbol??w.viseme??"").trim();if(!_)return;const k=Math.round((w.start??0)*1e3),le=Math.max(1,Math.round((w.duration??0)*1e3)),ie=u+k,ge=qn(B,ie),be=ge?Rn(ge,ie):0;let Te;vn(F)==="oculus_direct"?(Te=Ir(_),Te==="U"&&ge&&zt(ge.word,be)&&(Te="FF")):Te=Cn(_,ge?.word??"",be),y.push({viseme:Te,weight:1,vtime:ie,vduration:le})}),e.onNotify())},E=(y,B,H,X,R,F,w)=>{$(B,X,R),z(y,B,H,R,F,w)},Z=(y,B,H)=>{Q===y&&y.visemes===B||(B.length=0,H.length=0,y.visemes=B,y.words=H,y.segmentOffsetMs=0,Q=y,u=0,x=0)},me=async()=>{const y=window.AudioContext||window.webkitAudioContext;return s||(s=new y),s.state==="suspended"&&await s.resume(),A||(A=s.createMediaStreamDestination()),s},ee=(y,B)=>{if(!s)return 0;const H=s,X=H.currentTime,R=X+.02;if(B.isFirstAudio){c<=0&&(c=R);const w=(R-X)*1e3;f=performance.now()+w}B.onBeforeStart?.(R);const F=H.createBufferSource();return F.buffer=y,F.playbackRate.value=p,F.connect(H.destination),A&&F.connect(A),n.push(F),m?(n.pop(),R):(B.isFirstAudio&&(o=!0,e.onSpeakingStart()),F.onended=()=>{const w=n.indexOf(F);w>=0&&n.splice(w,1),e.onNotify(),!m&&(L&&N?oe(L,N):D())},F.start(R),R)},oe=async(y,B)=>{if(m||i||n.length>0)return;if(r.length===0){D();return}i=!0;let H=!1;try{const X=await me();if(m)return;const R=r[0];if(!R)return;const F=!o;if(M&&(e.shouldHoldForWarmup?.()??!1)&&(g||F&&R.newStream)||g){Z(R.utterance,y,B);for(const k of r){if(m)return;k.audioBuffer||(k.audioBuffer=await Xr(X,k.audio,k.sampleRate)),a=!0,g||(g=!0,v=0,k.newStream&&k.onNewStream?.()),k.visemesQueued||(E(y,B,k.visemes,k.words,v,!0,T),k.visemesQueued=!0,v+=Math.round(k.audioBuffer.duration*1e3),x=Math.max(x,Ye(y)))}e.onNotify();return}const _=r.shift();if(!_||(_.audioBuffer||(_.audioBuffer=await Xr(X,_.audio,_.sampleRate)),m))return;a=!0,C(),Z(_.utterance,y,B),_.visemesQueued=!1,_.newStream&&_.onNewStream?.(),ee(_.audioBuffer,{isFirstAudio:!o,isFirstOfStream:!1,onBeforeStart:k=>{c=k,u=0,f=performance.now()+(k-X.currentTime)*1e3,E(y,B,_.visemes,_.words,0,!0,T),_.visemesQueued=!0,_.utterance.segmentOffsetMs=0}}),H=n.length>0,x=Ye(y),e.onNotify()}catch(X){console.error("[avatarChunkPlayback] audio chunk failed:",X)}finally{if(i=!1,m)return;if(g){r.some(R=>!R.audioBuffer||!R.visemesQueued)&&oe(y,B);return}!H&&n.length===0&&r.length>0?oe(y,B):n.length===0&&r.length===0&&D()}};return{setStreamTtsEngineId:y=>{T=y},setPlaybackSpeed:y=>{p=Math.max(.1,Math.min(1,y)),Y()},getPlaybackSpeed:()=>p,setLipsyncWarmupHoldEnabled:y=>{M=y,y||P()},isWarmingUp:()=>g,releaseWarmupHold:()=>{if(g){g=!1,v=0;for(const y of r)y.newStream=!1;L&&N&&oe(L,N),e.onNotify()}},resetForNewStream:y=>{m=!1,l=!1,T=y,x=0},resumeBotStream:()=>{m=!1,l=!1},clearAborted:()=>{m=!1},primeAudioContext:async()=>{const y=window.AudioContext||window.webkitAudioContext;s||(s=new y),s.state==="suspended"&&await s.resume(),s&&!A&&(A=s.createMediaStreamDestination())},stop:()=>{m=!0,C(),V(),r.length=0,i=!1,o=!1,a=!1,l=!1,u=0,c=0,f=0,x=0,L=null,N=null,Q=null,P()},markStreamComplete:y=>{if(x=Math.max(x,Ye(y)),l=!0,!a&&y.length>0){j(y);return}D()},playAudioChunk:async(y,B,H,X,R,F,w)=>{if(m)return;L=y,N=B;const _=Kn(w?.sampleRate),k=w?.newStream===!0,le=fe();if(!H?.trim()){if(X.length===0&&R.length===0)return;Z(le,y,B),k&&w?.onNewStream?.(),E(y,B,X,R,0,!0,T),x=Ye(y),!o&&x>0&&j(y),e.onNotify();return}r.push({audio:H,audioBuffer:null,utterance:le,visemes:X,words:R,isNewSegment:F,sampleRate:_,newStream:k,onNewStream:w?.onNewStream,visemesQueued:!1}),await oe(y,B)},queueTimingsOnly:(y,B,H,X,R)=>{if(m)return;const F=fe();Z(F,y,B),E(y,B,H,X,0,!0,T),F.segmentOffsetMs=0,x=Ye(y)},getPlaybackElapsedMs:()=>a&&s&&c>0?Math.max(0,(s.currentTime-c)*1e3):o?Math.max(0,(performance.now()-f)*p):0,isSpeaking:()=>o?!0:!a||!s||c<=0?!1:(s.currentTime-c)*1e3<x,getVoiceStream:()=>A?A.stream:null,getPlaybackStartPerf:()=>f,bargeIn:()=>{m=!0,l=!1,C(),V(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,x=0,L=null,N=null,Q=null,P()},interrupt:()=>{m=!0,l=!1,C(),V(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,x=0,L=null,N=null,Q=null,P()}}}function Tt(e,t){let r=null;for(const n of e)t<n.vtime||t>=n.vtime+n.vduration||(!r||n.vtime>=r.vtime)&&(r=n);return r?r.viseme:"sil"}function zn(e,t,r=120){const n=Tt(e,t);if(n!=="sil")return n;let s=null;for(const i of e){if(i.vtime>t)break;const o=i.vtime+i.vduration;o<=t&&t-o<=r&&(s=i)}return s?.viseme??"sil"}function si(e,t,r=400){const n=t-r;let s=0;for(const i of e)if(i.vtime+i.vduration<n)s+=1;else break;s>0&&e.splice(0,s)}function ii(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function Jr(e,t){const r=e.trim();if(!r)return null;try{const n=r.replace(/\s+/g,""),s=atob(n).length,i=Number.isFinite(t)&&(t??0)>0?Number(t):16e3;return s/2/i*1e3}catch{return null}}const Qt=.5,Xt=1.5,xr="Olivia",Mr="f786b574-daa5-4673-aa0c-cbe3e8534c02",kt=.85;function gr(e,t=vt,r={}){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,s=[],i=[],o=[],a=[];let l=0;const f=[],c=[];let u="",d="neutral",p="",T=Xe;const m=new Set;let A=0,x=null;const M=()=>m.forEach(D=>D());let g=()=>!1;const v=Gn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:M,shouldHoldForWarmup:()=>g()},Xe),L=()=>v.getPlaybackElapsedMs(),N=(D,j=u)=>{if(!r.onLipsyncDebug)return;const $=L(),z=s,E=Yt(o).map(Z=>({viseme:Z.viseme,vtimeMs:Z.vtime}));r.onLipsyncDebug({event:D,spokenText:j,mood:d,playbackAnchorPerf:v.getPlaybackStartPerf(),playbackElapsedMs:$,queueLength:z.length,activeViseme:Tt(z,$),segments:c.map(Z=>({...Z,visemes:[...Z.visemes]})),fullQueueLength:o.length,compactionSkipped:E})},Q=(D,j)=>{r.onLipsyncDebug&&(x={event:D,spokenText:j},A===0&&(A=requestAnimationFrame(()=>{A=0;const $=x;x=null,$&&N($.event,$.spokenText)})))},fe=(D,j,$,z,E)=>{if(!r.onLipsyncDebug)return;const Z=s.slice(j),me=$.filter(ee=>String(ee.symbol??ee.viseme??"").trim());c.push({spokenText:D,audioOffsetMs:Z[0]?.vtime??0,isNewSegment:z,chunkDurationMs:E,receivedAtMs:performance.now(),visemes:Z.map((ee,oe)=>{const se=me[oe];return{rawSymbol:String(se?.symbol??se?.viseme??ee.viseme).trim(),viseme:ee.viseme,startSec:Number(se?.start??0),durationSec:Number(se?.duration??0),vtimeMs:ee.vtime,vdurationMs:ee.vduration}})})},P=()=>{o.length=0,a.length=0,l=0},C=(D,j,$,z,E)=>{if(D.length===0&&j.length===0)return;$n(o,a,D,j,l,E);const Z=Hn(D,l),me=$?Jr($,z):null;l=Math.max(Z,me!=null?l+me:l)},V=()=>{v.stop(),s.length=0,i.length=0,P(),f.length=0,d="neutral",c.length=0,u="",e("idle"),Q("reset"),M()};return{setDeveloperToken:D=>{p=D},setTtsEngineId:D=>{T=D},getTtsEngineId:()=>T,speak:async(D,j)=>{V(),e("loading"),u=D,c.length=0,P();const $=p.trim();if(!$)throw new Error("Developer token required for avatar_ttsWithPoses");const z=j?.ttsEngineId??T??Xe;v.resetForNewStream(z),v.setStreamTtsEngineId(z);const E=j?.voiceId??xr,Z=j?.speakingRate??kt,me=Math.max(Qt,Math.min(Xt,Z)),ee={user_query:D,ttsEngineId:z,speakingRate:me,tenant:"aiTwin"};_n.has(z)&&(ee.voice_id=E);try{const oe=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${$}`},body:JSON.stringify(ee)});if(!oe.ok)throw new Error(`avatar_ttsWithPoses failed (${oe.status})`);const se=oe.body;if(!se)throw new Error("No response body");const _e=se.getReader(),he=new TextDecoder;let y="",B=!0;const H=async X=>{const R=xn(X);if(R){if(R.event==="audio"){const F=R.data.chunk,w=R.data.visemes??[],_=R.data.words??[],k=R.data.is_new_segment??!1,le=R.data.sample_rate,ie=s.length;if(C(w,_,F,le,z),F){const ge=B;B=!1,await v.playAudioChunk(s,i,F,w,_,k,{sampleRate:le,newStream:ge}),fe(D,ie,w,k,Jr(F,le)),Q("audio_chunk",D)}else(w.length>0||_.length>0)&&(v.queueTimingsOnly(s,i,w,_,k),fe(D,ie,w,k,null),Q("audio_chunk",D))}else if(R.event==="metadata"){const F=R.data.mood;typeof F=="string"&&F.trim()&&(d=F.trim());const w=R.data.sentence_emotions;if(Array.isArray(w)){f.length=0;for(const _ of w){if(!_||typeof _!="object")continue;const k=_;f.push({sentence_index:Number(k.sentence_index??0),text:String(k.text??""),sentiment:typeof k.sentiment=="string"?k.sentiment:void 0,emotion:String(k.emotion??d),start_word:Number(k.start_word??0),end_word:Number(k.end_word??0)})}}M(),Q("metadata",D)}else if(R.event==="error")throw new Error(String(R.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:X,value:R}=await _e.read();R&&(y+=he.decode(R,{stream:!0}));const F=y.split(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const rt=require("react/jsx-runtime"),v=require("react"),Ps=require("@pipecat-ai/client-js"),Rr=require("@pipecat-ai/websocket-transport"),fe=require("pixi.js");var pr=typeof document<"u"?document.currentScript:null;const Ms="https://aitwin.bubu.social",Mn={publicBase:Ms},Rs="0.5.3",Cs={version:Rs},Ds={},Mt="https://ai.aitwin.me",Cr=`${Mn.publicBase}/custom-faces`,Fs=`${Mn.publicBase}/aitwin-workers/v${Cs.version}/visemeDiffPreview.worker.js`;function Bs(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:pr&&pr.tagName.toUpperCase()==="SCRIPT"&&pr.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?Ds?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():Fs}const Ls="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",wr=3e3,At=16,on=3e3,Os=4e3,Ns=.85,Vs=8,Dr=400,Fr="eng_a7f2b9c1",rr="eng_d4e8f3a2",ot="eng_c9b1e6d4",st=ot,Rn=new Set([rr,ot]),Cn={[Fr]:"oculus_direct",[ot]:"oculus_direct",[rr]:"inworld_word"},an={google:Fr,inworld:rr,cartesia:ot};function Dn(e){return Cn[e]}function Us(e){return e===ot}function Ws(e){if(e.ttsEngineId&&e.ttsEngineId in Cn)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in an?an[t]:st}class Yt extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function Fn(e,t=Ls){const r=new URL(t);r.searchParams.set("id",e);const n=await fetch(r.toString());if(n.status===404)throw new Yt;if(!n.ok)throw new Error(`getAiTwin failed (${n.status})`);const s=await n.json();if(!s.success)throw new Yt(s.error??"AI twin not found");const{tts:i,token:o,...a}=s.data,l=String(a.authToken??o??"").trim()||void 0;return{...a,ttsEngineId:Ws(s.data),authToken:l}}const $s="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",Hs="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Ks="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",qs="6TNvp3SCs4Og0a1ijm9TommXLql1",Gs="Swaraj Mali";async function jt(){const e=await fetch($s,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":Hs,"Client-Secret":Ks},body:JSON.stringify({userId:qs,userName:Gs})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),r=t.authToken??t.token??"";if(!r.trim())throw new Error("getAuthToken returned no token");return r}async function zs(e,t=Mt){const r=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!r.ok)throw new Error(`/api/session-value failed (${r.status})`);const n=await r.json();if(!n.value)throw new Error("Session value response missing value");return n}function js(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const r=new Uint8Array(t.length/2);for(let n=0;n<t.length;n+=2)r[n/2]=Number.parseInt(t.slice(n,n+2),16);return r}function $t(e){return Uint8Array.from(e)}function Ys(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}async function Qs(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 r=new Uint8Array(e),n=$t(r.subarray(0,12)),s=$t(r.subarray(12,28)),i=$t(r.subarray(28)),o=$t(Ys(i,s)),a=await crypto.subtle.importKey("raw",js(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:n,tagLength:128},a,o)}let br=null;const Tr=new Set;function Ht(e){return e.includes("/custom-faces/")}function Qt(e){const t=e.binBase.trim().replace(/\/$/,""),r=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||Ht(t)||Ht(r)))return{twinBase:r,binBase:t,encrypted:!1};const s=Ht(t)?t:Ht(r)?r:t;return{twinBase:s,binBase:s,encrypted:!0}}function at(e){return Qt(e).encrypted}function Er(e,t=Cr){const r=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:r,binBase:r,encrypted:!0}}function Bn(e,t){return(e??t)?.trim()||void 0}function Ln(e){br=Qt(e);for(const t of Tr)t()}function Xs(e,t){const r=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!r)return Qt(e);const n=s=>{try{return new URL(s,r).href.replace(/\/$/,"")}catch{return s}};return Qt({twinBase:n(e.twinBase),binBase:n(e.binBase),encrypted:e.encrypted})}function ut(){if(!br)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return br}function On(e){return Tr.add(e),()=>Tr.delete(e)}function Xt(){const e=ut();return at(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Js(){const e=ut();return at(e)?`${e.binBase}/idle.bin`:`${e.twinBase}/idle.mp4`}function Nn(){const e=ut();return at(e)?e.binBase:e.twinBase}function Rt(){return at(ut())}function Vn(){const e=ut();return at(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function Un(){const e=ut();return at(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function Wn(e){const t=e.split(/\r?\n/);let r="",n="";for(const i of t)i.startsWith("event:")?r=i.slice(6).trim():i.startsWith("data:")&&(n=i.slice(5).trim());if(!r)return null;let s={};if(n)try{s=JSON.parse(n)}catch{s={raw:n}}return{event:r,data:s}}const $n=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],Zs=new Map($n.map(e=>[e.toLowerCase(),e]));function nr(e){const t=(e??"").trim();return t?Zs.get(t.toLowerCase())??"sil":"sil"}const ei={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 ti(e){if(!e)return["sil"];const t=e.toLowerCase();return ei[t]??["sil"]}function Hn(e,t){const r=e.word.length;if(r<=0)return 0;const n=e.wduration;if(n<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/n));return Math.min(r-1,Math.floor(s*r))}function gt(e,t){const r=Math.max(0,Math.min(e.length-1,t));return e[r]??""}function sr(e,t){const r=e.toLowerCase().replace(/[^a-z']/g,"");return!r||gt(r,t)!=="w"||t>0&&gt(r,t-1)==="q"?!1:t===0&&/^w[aeiouy]/.test(r)}function ri(e,t){const r=e.toLowerCase(),n=gt(r,t),s=r.slice(Math.max(0,t-1),t+2);return n==="i"||n==="y"?"I":s.includes("ee")||n==="e"||r.includes("ea")?"E":"aa"}function ni(e,t){const r=e.toLowerCase(),n=r.slice(Math.max(0,t-1),Math.min(r.length,t+3));if(/oo|ou|uw/.test(n))return"U";if(sr(r,t))return"FF";const s=gt(r,t);return s==="u"||s==="w"?"U":"O"}function si(e,t){const r=e.toLowerCase();if(sr(r,t))return"FF";const n=r.slice(Math.max(0,t-2),t+1);if(/qu/.test(n))return"U";const s=r.slice(Math.max(0,t-1),Math.min(r.length,t+3));return/wo|wh/.test(s)||gt(r,t)==="o"?"O":"U"}const Kt={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function ii(e,t){const r=e.toLowerCase(),n=gt(r,t);if(Kt[n])return Kt[n];for(let s=0;s<r.length;s++){const i=r[s];if(Kt[i])return Kt[i]}return"DD"}function Kn(e,t,r){const n=ti(e);if(n.length===1)return n[0];const s=t.trim();if(!s)return n[0];const i=(e??"").toLowerCase(),o=Math.max(0,r);switch(i){case"aei":return ri(s,o);case"o":return ni(s,o);case"qw":return si(s,o);case"cdgknstxyz":return ii(s,o);default:return n[0]}}const Br=16,kr=47,qn=new Set(["aa","E","I"]),Gn=new Set(["DD","SS","kk","CH"]),oi=[qn,Gn];function ai(e){for(const t of oi)if(t.has(e))return t;return null}function ui(e){return ai(e)!=null}function zn(e,t=Br){return t<=0?e:Math.round(e/t)*t}function Lr(e,t=Br){return e.map(r=>({...r,vtime:zn(r.vtime,t)}))}function jn(e){const t=new Map;for(const r of e)t.has(r.vtime)||t.set(r.vtime,r);return[...t.values()].sort((r,n)=>r.vtime-n.vtime)}function Or(e){if(e.length<=2)return[...e];const t=[e[0]];for(let r=1;r<e.length-1;r++){const n=e[r],s=e[r+1],i=t[t.length-1];if(!ui(n.viseme)){t.push(n);continue}const o=n.vtime-i.vtime,a=s.vtime-n.vtime;o<kr||a<kr||t.push(n)}return t.push(e[e.length-1]),t}function ir(e){return Or(jn(Lr(e)))}function Yn(e,t){let r="sil";for(const n of e){if(n.vtime>t)break;n.viseme!=="sil"&&(r=n.viseme)}return r}function or(e){const t=jn(Lr(e)),r=Or(t),n=new Set(r.map(i=>i.vtime)),s=[];for(const i of t)n.has(i.vtime)||s.push({viseme:i.viseme,vtime:i.vtime});return s}function ci(e){return or(e).map(t=>t.viseme)}const li=32,un=48;function Qn(e){let t=0;for(const r of e)t=Math.max(t,r.vtime+r.vduration);return t}function Xn(e){let t=null,r=-1;for(const n of e){if(n.viseme==="sil")continue;const s=n.vtime+n.vduration;s>r&&(t=n,r=s)}return t}function fi(e){return ir(e).find(r=>r.viseme!=="sil")??null}function tt(e){const t=Qn(e);return Xn(e)?t+Dr:t}function di(e){const t=[],r=fi(e);r&&r.vtime>0&&t.push({from:"sil",to:r.viseme,fromVtime:0,toVtime:r.vtime});const n=Xn(e),s=Qn(e);return n&&t.push({from:n.viseme,to:"sil",fromVtime:s,toVtime:s+Dr}),t}function Jn(e){const t=ir(e),r=[];for(let n=0;n<t.length-1;n++){const s=t[n].viseme,i=t[n+1].viseme,o=t[n].vtime,a=t[n+1].vtime;s===i||a<=o||r.push({from:s,to:i,fromVtime:o,toVtime:a})}return r}function Zn(e){return e.length===0?[]:[...di(e),...Jn(e)]}function mi(e,t){let r=null;for(const n of e){const{transStart:s,transEnd:i}=es(n.fromVtime,n.toVtime,n.from,n.to);t>=s&&t<i&&(r=n)}return r}function es(e,t,r,n){const s=Math.max(0,t-e);if((r==="PP"||n==="PP")&&s>un){const f=un,c=t,u=c-f;return{gapMs:s,effectiveGapMs:f,holdEnd:u,transStart:u,transEnd:c}}const o=s>0?Math.max(s,li):0,a=e,l=e+o;return{gapMs:s,effectiveGapMs:o,holdEnd:e,transStart:a,transEnd:l}}function ts(e,t,r,n,s,i){n.forEach(o=>{const l=String(o.word??"").trim();if(!l)return;const f=Math.round((o.start??0)*1e3),c=o.duration!=null?Math.round(o.duration*1e3):o.end!=null?Math.max(1,Math.round((o.end-(o.start??0))*1e3)):0;t.push({word:l,wtime:s+f,wduration:c,queueIndex:t.length,gesture:o.gesture??null,emphasis:o.emphasis===!0})}),r.forEach(o=>{const a=String(o.symbol??o.viseme??"").trim();if(!a)return;const l=Math.round((o.start??0)*1e3),f=Math.max(1,Math.round((o.duration??0)*1e3)),c=s+l,u=ss(t,c),d=u?Hn(u,c):0;let p;Dn(i)==="oculus_direct"?(p=nr(a),p==="U"&&u&&sr(u.word,d)&&(p="FF")):p=Kn(a,u?.word??"",d),e.push({viseme:p,weight:1,vtime:c,vduration:f})})}function rs(e,t){let r=t;for(const n of e){const s=Math.round((n.start??0)*1e3),i=Math.max(1,Math.round((n.duration??0)*1e3));r=Math.max(r,t+s+i)}return r}const Nr=16e3;function ns(e){const t=Number(e);return Number.isFinite(t)&&t>0?t:Nr}function hi(e){let t=e.trim();const r=t.indexOf(",");t.startsWith("data:")&&r>=0&&(t=t.slice(r+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const n=t.length%4;return n!==0&&(t+="=".repeat(4-n)),t}function pi(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function cn(e,t,r=Nr){const n=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(n.buffer,n.byteOffset,n.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,r);return o.copyToChannel(i,0),o}function wi(e){const t=hi(e),r=window.atob(t),n=new Uint8Array(r.length);for(let s=0;s<r.length;s++)n[s]=r.charCodeAt(s);return n}async function ln(e,t,r=Nr){const n=wi(t),s=ns(r);if(pi(n)){const i=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(i)}if(n.length>=2&&n.length%2===0)return cn(e,n,s);try{const i=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(i)}catch{return cn(e,n,s)}}function ss(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function is(e,t){const r=[],n=[];let s=null,i=!1,o=!1,a=!1,l=!1,f=0,c=0,u=0,d=null,p=1,E=t,h=!1,x=null,R=0,P=!1,w=!1,k=0,B=null,I=null,Z=null;const he=()=>({visemes:[],words:[],segmentOffsetMs:0}),A=()=>{w=!1,k=0},V=()=>{d!=null&&(clearTimeout(d),d=null)},K=()=>{for(const S of n){try{S.onended=null,S.stop(0)}catch{}try{S.disconnect()}catch{}}n.length=0},Q=()=>{for(const S of n)S.playbackRate.value=p},j=()=>{if(!(h||!l||i||w||n.length>0||r.length>0)){if(a&&s&&c>0){const S=(s.currentTime-c)*1e3;if(S<R){V();const U=p>0?(R-S)/p:R-S;d=setTimeout(()=>{h||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},Math.max(0,U));return}}o=!1,V(),e.onSpeakingDone(),e.onNotify()}},me=S=>{const U=tt(S);if(U<=0){e.onSpeakingDone(),e.onNotify();return}o=!0,f=performance.now(),e.onSpeakingStart(),e.onNotify(),V();const W=p>0?U/p:U;d=setTimeout(()=>{h||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},W)},F=(S,U,W,G)=>{U.length!==0&&(u=W,U.forEach(L=>{const _=String(L.word??"").trim();if(!_)return;const y=Math.round((L.start??0)*1e3),T=L.duration!=null?Math.round(L.duration*1e3):L.end!=null?Math.max(1,Math.round((L.end-(L.start??0))*1e3)):0;S.push({word:_,wtime:u+y,wduration:T,queueIndex:S.length,gesture:L.gesture??null,emphasis:L.emphasis===!0})}))},J=(S,U,W,G,L,$)=>{W.length!==0&&(u=G,W.forEach(_=>{const y=String(_.symbol??_.viseme??"").trim();if(!y)return;const T=Math.round((_.start??0)*1e3),O=Math.max(1,Math.round((_.duration??0)*1e3)),Y=u+T,re=ss(U,Y),Ee=re?Hn(re,Y):0;let ye;Dn($)==="oculus_direct"?(ye=nr(y),ye==="U"&&re&&sr(re.word,Ee)&&(ye="FF")):ye=Kn(y,re?.word??"",Ee),S.push({viseme:ye,weight:1,vtime:Y,vduration:O})}),e.onNotify())},b=(S,U,W,G,L,$,_)=>{F(U,G,L),J(S,U,W,L,$,_)},z=(S,U,W)=>{Z===S&&S.visemes===U||(U.length=0,W.length=0,S.visemes=U,S.words=W,S.segmentOffsetMs=0,Z=S,u=0,R=0)},ne=async()=>{const S=window.AudioContext||window.webkitAudioContext;return s||(s=new S),s.state==="suspended"&&await s.resume(),x||(x=s.createMediaStreamDestination()),s},ae=(S,U)=>{if(!s)return 0;const W=s,G=W.currentTime,L=G+.02;if(U.isFirstAudio){c<=0&&(c=L);const _=(L-G)*1e3;f=performance.now()+_}U.onBeforeStart?.(L);const $=W.createBufferSource();return $.buffer=S,$.playbackRate.value=p,$.connect(W.destination),x&&$.connect(x),n.push($),h?(n.pop(),L):(U.isFirstAudio&&(o=!0,e.onSpeakingStart()),$.onended=()=>{const _=n.indexOf($);_>=0&&n.splice(_,1),e.onNotify(),!h&&(B&&I?te(B,I):j())},$.start(L),L)},te=async(S,U)=>{if(h||i||n.length>0)return;if(r.length===0){j();return}i=!0;let W=!1;try{const G=await ne();if(h)return;const L=r[0];if(!L)return;const $=!o;if(P&&(e.shouldHoldForWarmup?.()??!1)&&(w||$&&L.newStream)||w){z(L.utterance,S,U);for(const T of r){if(h)return;T.audioBuffer||(T.audioBuffer=await ln(G,T.audio,T.sampleRate)),a=!0,w||(w=!0,k=0,T.newStream&&T.onNewStream?.()),T.visemesQueued||(b(S,U,T.visemes,T.words,k,!0,E),T.visemesQueued=!0,k+=Math.round(T.audioBuffer.duration*1e3),R=Math.max(R,tt(S)))}e.onNotify();return}const y=r.shift();if(!y||(y.audioBuffer||(y.audioBuffer=await ln(G,y.audio,y.sampleRate)),h))return;a=!0,V(),z(y.utterance,S,U),y.visemesQueued=!1,y.newStream&&y.onNewStream?.(),ae(y.audioBuffer,{isFirstAudio:!o,isFirstOfStream:!1,onBeforeStart:T=>{c=T,u=0,f=performance.now()+(T-G.currentTime)*1e3,S.length=0,U.length=0,b(S,U,y.visemes,y.words,0,!0,E),y.visemesQueued=!0,y.utterance.segmentOffsetMs=0}}),W=n.length>0,R=tt(S),e.onNotify()}catch(G){console.error("[avatarChunkPlayback] audio chunk failed:",G)}finally{if(i=!1,h)return;if(w){r.some(L=>!L.audioBuffer||!L.visemesQueued)&&te(S,U);return}!W&&n.length===0&&r.length>0?te(S,U):n.length===0&&r.length===0&&j()}};return{setStreamTtsEngineId:S=>{E=S},setPlaybackSpeed:S=>{p=Math.max(.1,Math.min(1,S)),Q()},getPlaybackSpeed:()=>p,setLipsyncWarmupHoldEnabled:S=>{P=S,S||A()},isWarmingUp:()=>w,releaseWarmupHold:()=>{if(w){w=!1,k=0;for(const S of r)S.newStream=!1;B&&I&&te(B,I),e.onNotify()}},resetForNewStream:S=>{h=!1,l=!1,E=S,R=0},resumeBotStream:()=>{h=!1,l=!1},clearAborted:()=>{h=!1},primeAudioContext:async()=>{const S=window.AudioContext||window.webkitAudioContext;s||(s=new S),s.state==="suspended"&&await s.resume(),s&&!x&&(x=s.createMediaStreamDestination())},stop:()=>{h=!0,V(),K(),r.length=0,i=!1,o=!1,a=!1,l=!1,u=0,c=0,f=0,R=0,B=null,I=null,Z=null,A()},markStreamComplete:S=>{if(R=Math.max(R,tt(S)),l=!0,!a&&S.length>0){me(S);return}j()},playAudioChunk:async(S,U,W,G,L,$,_)=>{if(h)return;B=S,I=U;const y=ns(_?.sampleRate),T=_?.newStream===!0,O=he();if(!W?.trim()){if(G.length===0&&L.length===0)return;z(O,S,U),T&&_?.onNewStream?.(),b(S,U,G,L,0,!0,E),R=tt(S),!o&&R>0&&me(S),e.onNotify();return}r.push({audio:W,audioBuffer:null,utterance:O,visemes:G,words:L,isNewSegment:$,sampleRate:y,newStream:T,onNewStream:_?.onNewStream,visemesQueued:!1}),await te(S,U)},queueTimingsOnly:(S,U,W,G,L,$)=>{if(h)return;const _=he();z(_,S,U),b(S,U,W,G,0,!0,E),_.segmentOffsetMs=0,R=tt(S)},getPlaybackElapsedMs:()=>a&&s&&c>0?Math.max(0,(s.currentTime-c)*1e3*p):o?Math.max(0,(performance.now()-f)*p):0,isSpeaking:()=>o?!0:!a||!s||c<=0?!1:(s.currentTime-c)*1e3<R,getVoiceStream:()=>x?x.stream:null,getPlaybackStartPerf:()=>f,bargeIn:()=>{h=!0,l=!1,V(),K(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,R=0,B=null,I=null,Z=null,A()},interrupt:()=>{h=!0,l=!1,V(),K(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,R=0,B=null,I=null,Z=null,A()}}}function Ct(e,t){let r=null;for(const n of e)t<n.vtime||t>=n.vtime+n.vduration||(!r||n.vtime>=r.vtime)&&(r=n);return r?r.viseme:"sil"}function os(e,t,r=120){const n=Ct(e,t);if(n!=="sil")return n;let s=null;for(const i of e){if(i.vtime>t)break;const o=i.vtime+i.vduration;o<=t&&t-o<=r&&(s=i)}return s?.viseme??"sil"}function gi(e,t,r=400){const n=t-r;let s=0;for(const i of e)if(i.vtime+i.vduration<n)s+=1;else break;s>0&&e.splice(0,s)}function yi(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function fn(e,t){const r=e.trim();if(!r)return null;try{const n=r.replace(/\s+/g,""),s=atob(n).length,i=Number.isFinite(t)&&(t??0)>0?Number(t):16e3;return s/2/i*1e3}catch{return null}}const ar=.5,ur=1.5,Vr="Olivia",Ur="f786b574-daa5-4673-aa0c-cbe3e8534c02",Dt=.85;function Ir(e,t=Mt,r={}){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,s=[],i=[],o=[],a=[];let l=0;const f=[],c=[];let u="",d="neutral",p="",E=st;const h=new Set;let x=0,R=null,P=0,w=null;const k=()=>h.forEach(F=>F());let B=()=>!1;const I=is({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:k,shouldHoldForWarmup:()=>B()},st),Z=()=>I.getPlaybackElapsedMs(),he=(F,J=u)=>{if(!r.onLipsyncDebug)return;const b=Z(),z=s,ne=or(o).map(ae=>({viseme:ae.viseme,vtimeMs:ae.vtime}));r.onLipsyncDebug({event:F,spokenText:J,mood:d,playbackAnchorPerf:I.getPlaybackStartPerf(),playbackElapsedMs:b,queueLength:z.length,activeViseme:Ct(z,b),segments:c.map(ae=>({...ae,visemes:[...ae.visemes]})),fullQueueLength:o.length,compactionSkipped:ne})},A=(F,J)=>{r.onLipsyncDebug&&(R={event:F,spokenText:J},x===0&&(x=requestAnimationFrame(()=>{x=0;const b=R;R=null,b&&he(b.event,b.spokenText)})))},V=(F,J,b,z,ne)=>{if(!r.onLipsyncDebug)return;const ae=s.slice(J),te=b.filter(ee=>String(ee.symbol??ee.viseme??"").trim());c.push({spokenText:F,audioOffsetMs:ae[0]?.vtime??0,isNewSegment:z,chunkDurationMs:ne,receivedAtMs:performance.now(),visemes:ae.map((ee,ie)=>{const we=te[ie];return{rawSymbol:String(we?.symbol??we?.viseme??ee.viseme).trim(),viseme:ee.viseme,startSec:Number(we?.start??0),durationSec:Number(we?.duration??0),vtimeMs:ee.vtime,vdurationMs:ee.vduration}})})},K=()=>{o.length=0,a.length=0,l=0},Q=(F,J,b,z,ne)=>{if(F.length===0&&J.length===0)return;ts(o,a,F,J,l,ne);const ae=rs(F,l),te=b?fn(b,z):null;l=Math.max(ae,te!=null?l+te:l)},j=()=>{P+=1,w?.abort(),w=null,I.stop(),s.length=0,i.length=0,K(),f.length=0,d="neutral",c.length=0,u="",e("idle"),A("reset"),k()};return{setDeveloperToken:F=>{p=F},setTtsEngineId:F=>{E=F},getTtsEngineId:()=>E,speak:async(F,J)=>{j();const b=P;w=new AbortController,e("loading"),u=F,c.length=0,K();const z=p.trim();if(!z)throw new Error("Developer token required for avatar_ttsWithPoses");const ne=J?.ttsEngineId??E??st;I.resetForNewStream(ne),I.setStreamTtsEngineId(ne);const ae=J?.voiceId??Vr,te=J?.speakingRate??Dt,ee=Math.max(ar,Math.min(ur,te)),ie={user_query:F,ttsEngineId:ne,speakingRate:ee,tenant:"aiTwin"};Rn.has(ne)&&(ie.voice_id=ae);try{const we=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${z}`},body:JSON.stringify(ie),signal:w.signal});if(!we.ok)throw new Error(`avatar_ttsWithPoses failed (${we.status})`);const S=we.body;if(!S)throw new Error("No response body");const U=S.getReader(),W=new TextDecoder;let G="",L=!0;const $=async _=>{if(b!==P)return;const y=Wn(_);if(y){if(y.event==="audio"){const T=y.data.chunk,O=y.data.visemes??[],Y=y.data.words??[],re=y.data.is_new_segment??!1,Ee=y.data.sample_rate,ye=Number(y.data.audio_offset_ms??0),Ne=s.length;if(Q(O,Y,T,Ee,ne),T){const Ve=L;L=!1,await I.playAudioChunk(s,i,T,O,Y,re,{sampleRate:Ee,newStream:Ve,audioOffsetMs:ye}),V(F,Ne,O,re,fn(T,Ee)),A("audio_chunk",F)}else(O.length>0||Y.length>0)&&(I.queueTimingsOnly(s,i,O,Y,re,{audioOffsetMs:ye,newStream:L}),V(F,Ne,O,re,null),A("audio_chunk",F))}else if(y.event==="metadata"){const T=y.data.mood;typeof T=="string"&&T.trim()&&(d=T.trim());const O=y.data.sentence_emotions;if(Array.isArray(O)){f.length=0;for(const Y of O){if(!Y||typeof Y!="object")continue;const re=Y;f.push({sentence_index:Number(re.sentence_index??0),text:String(re.text??""),sentiment:typeof re.sentiment=="string"?re.sentiment:void 0,emotion:String(re.emotion??d),start_word:Number(re.start_word??0),end_word:Number(re.end_word??0)})}}k(),A("metadata",F)}else if(y.event==="error")throw new Error(String(y.data.message??"avatar_ttsWithPoses stream error"))}};for(;b===P;){const{done:_,value:y}=await U.read();y&&(G+=W.decode(y,{stream:!0}));const T=G.split(`
2
2
 
3
- `);y=F.pop()??"";for(const w of F)await H(w);if(X){y.trim()&&await H(y.trim()),v.markStreamComplete(s),Q("bot_stopped",D);break}}}catch(oe){throw console.error("[avatarTtsLipsync]",oe),V(),e("error"),oe}},stop:V,getVisemeQueue:()=>s,getWordQueue:()=>i,getFullVisemeQueue:()=>o,getFullWordQueue:()=>a,getSentenceEmotions:()=>f,getStreamMood:()=>d,getPlaybackElapsedMs:()=>v.getPlaybackElapsedMs(),isSpeaking:()=>v.isSpeaking(),isWarmingUp:()=>v.isWarmingUp(),releaseWarmupHold:()=>v.releaseWarmupHold(),setLipsyncWarmupHold:(D,j)=>{j&&(g=j),v.setLipsyncWarmupHoldEnabled(D)},getVoiceStream:()=>v.getVoiceStream(),setPlaybackSpeed:D=>{v.setPlaybackSpeed(D)},getPlaybackSpeed:()=>v.getPlaybackSpeed(),subscribe:D=>(m.add(D),()=>m.delete(D))}}const Rr=16e3,Cr=16e3;class oi extends br.ProtobufFrameSerializer{async deserialize(t){try{return await super.deserialize(t)}catch(r){if(r instanceof Error&&r.message==="Unknown frame kind")return{type:"raw",message:null};throw r}}}class ai extends br.DailyMediaManager{constructor(){super(!0,!0,void 0,void 0,512,Rr,Cr)}bufferBotAudio(t,r){}}const ui=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ci(e){return ui.test(e.trim())}function li(e){const t=e.replace(/\/$/,"");return t.startsWith("https://")?`wss://${t.slice(8)}`:t.startsWith("http://")?`ws://${t.slice(7)}`:t}function jn(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 r=e.apiBase??vt,n=new URL(`${li(r)}/ws/voice`);n.searchParams.set("authToken",t),n.searchParams.set("tenant","aiTwin");const s=e.voiceId?.trim();s&&ci(s)&&n.searchParams.set("voiceId",s);const i=e.personaId?.trim();i&&n.searchParams.set("personaId",i);const o=e.speakingRate??kt;return n.searchParams.set("speaking_rate",String(Math.max(Qt,Math.min(Xt,o)))),e.ttsStream===!1?n.searchParams.set("tts_stream","false"):n.searchParams.set("tts_stream","true"),n.toString()}function ur(e,t){if(!Array.isArray(e))return[];const r=[];for(const n of e)!n||typeof n!="object"||r.push({sentence_index:Number(n.sentence_index??0),text:String(n.text??""),sentiment:typeof n.sentiment=="string"?n.sentiment:void 0,emotion:String(n.emotion??t),start_word:Number(n.start_word??0),end_word:Number(n.end_word??0)});return r}function fi(e){if(!e||typeof e!="object")return null;const t=e,n=(t.data&&typeof t.data=="object"&&!Array.isArray(t.data)?t.data:null)||t;return{...n,type:String(n.type??t.type??"server-message")}}function cr(e){return e.is_new_segment===!0||Number(e.audio_offset_ms??-1)===0?!0:Number(e.chunk_index??-1)===0}function di(e){return e?.transport?._mediaManager?._wavStreamPlayer??null}function Yn(e,t={}){const r=[],n=[],s=[],i=[];let o=0;const a=[],l=[];let f="",c="neutral",u=Ze,d=!1,p=null,T=null,m={...t},A=0,x=null;const M=new Set,g=[];let v=!1,L=null,N=!0,Q="default";const fe=new Set;let P=null,C=0,V=!1,Y=null,D=!1;const j=()=>{Y!=null&&(clearTimeout(Y),Y=null)},$=()=>M.forEach(h=>h());let z=()=>!1;const E=Gn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>{D=!1,e("done")},onNotify:$,shouldHoldForWarmup:()=>z()},u),Z=()=>{if(L)return L;const h=new MessageChannel;return h.port1.onmessage=()=>{const I=g.shift();I!==void 0&&be(I),g.length>0?h.port2.postMessage(null):v=!1},L=h,h},me=h=>{g.push(h),!v&&(v=!0,Z().port2.postMessage(null))},ee=()=>E.getPlaybackElapsedMs(),oe=(h,I=f)=>{if(!m.onLipsyncDebug)return;const G=ee(),J=r,ye=Yt(s).map(te=>({viseme:te.viseme,vtimeMs:te.vtime}));m.onLipsyncDebug({event:h,spokenText:I,mood:c,playbackAnchorPerf:E.getPlaybackStartPerf(),playbackElapsedMs:G,queueLength:J.length,activeViseme:Tt(J,G),segments:l.map(te=>({...te,visemes:[...te.visemes]})),fullQueueLength:s.length,compactionSkipped:ye})},se=(h,I)=>{m.onLipsyncDebug&&(x={event:h,spokenText:I},A===0&&(A=requestAnimationFrame(()=>{A=0;const G=x;x=null,G&&oe(G.event,G.spokenText)})))},_e=()=>{N=!0,fe.clear(),E.clearAborted()},he=()=>{const h=di(p);if(h){Q=`bot-${Date.now()}`,h.interruptedTrackIds={[Q]:!0};try{h.interrupt?.()}catch{}}const I=T;try{I?.interrupt?.()}catch{}},y=h=>{if(D&&E.isSpeaking())return!0;const I=h.context_id?.trim();return I&&fe.has(I)?!0:N?!1:!(h.type==="avatar_metadata"||cr(h))},B=h=>{N=!1,D=!1;const I=h.context_id?.trim();I&&fe.add(I),j(),V=!1,g.length=0,v=!1,P?.abort(),P=null,C+=1,he(),E.bargeIn(),H(),e("idle"),se("interrupt"),$()},H=()=>{r.length=0,n.length=0,s.length=0,i.length=0,o=0,l.length=0,f="",c="neutral",a.length=0},X=()=>{E.stop(),H(),se("reset"),$()},R=()=>{X(),e("idle"),$()},F=(h,I)=>{typeof h.mood=="string"&&h.mood.trim()&&(c=h.mood.trim()),Array.isArray(h.sentence_emotions)&&h.sentence_emotions.length>0&&(I&&(a.length=0),a.push(...ur(h.sentence_emotions,c)))},w=h=>{if(y({...h,type:"avatar_metadata"}))return;_e(),V=!0;const I=String(h.spoken_text??h.text??"").trim();I&&(f=I),E.resetForNewStream(u),typeof h.mood=="string"&&h.mood.trim()&&(c=h.mood.trim()),a.length=0,a.push(...ur(h.sentence_emotions,c)),Array.isArray(h.gestures)&&h.gestures,se("metadata",I),$()},_=(h,I,G,J)=>{if(!m.onLipsyncDebug)return;const ye=r.slice(G),te=J.filter(de=>String(de.symbol??de.viseme??"").trim());l.push({spokenText:I,audioOffsetMs:ye[0]?.vtime??0,isNewSegment:!!h.is_new_segment,chunkDurationMs:h.chunk_duration_ms!=null?Number(h.chunk_duration_ms):null,receivedAtMs:performance.now(),visemes:ye.map((de,Ae)=>{const ke=te[Ae];return{rawSymbol:String(ke?.symbol??ke?.viseme??de.viseme).trim(),viseme:de.viseme,startSec:Number(ke?.start??0),durationSec:Number(ke?.duration??0),vtimeMs:de.vtime,vdurationMs:de.vduration}})})},k=(h,I,G,J)=>{if(h.length===0&&I.length===0)return;$n(s,i,h,I,o,u);const ye=Hn(h,o);let te=null;const de=G.trim();if(de)try{const Ae=atob(de.replace(/\s+/g,"")).length,ke=Number.isFinite(J)&&(J??0)>0?Number(J):16e3;te=Ae/2/ke*1e3}catch{te=null}o=Math.max(ye,te!=null?o+te:o)},le=h=>{if(y(h))return;j(),cr(h)&&_e();const I=String(h.spoken_text??h.text??"").trim(),G=cr(h),J=h.visemes??[],ye=h.words??[],te=String(h.chunk??"").trim(),de=V;de&&(V=!1),I&&(f=I),F(h,G),de?(E.resetForNewStream(u),s.length=0,i.length=0,o=0):G&&E.resumeBotStream(),k(J,ye,te,h.sample_rate);const Ae=r.length;E.playAudioChunk(r,n,te,J,ye,G,{sampleRate:h.sample_rate,newStream:de,onNewStream:()=>{l.length=0}}).then(()=>{_(h,I,Ae,J),se("audio_chunk",I)})},ie=h=>{if(y(h))return;(h.is_new_segment||Number(h.audio_offset_ms??-1)===0)&&_e(),console.warn("[realtimeVoice] avatar_lipsync is deprecated — expect avatar_audio_chunk");const I=String(h.spoken_text??h.text??"").trim();I&&(f=I),F(h,!!h.is_new_segment),k(h.visemes??[],h.words??[],"",void 0);const G=r.length;E.queueTimingsOnly(r,n,h.visemes??[],h.words??[],!!h.is_new_segment),_(h,I,G,h.visemes??[]),se("lipsync",I),$()},ge=async h=>{const I=String(h.text??"").trim();if(!I)return;const G=m.authToken?.trim();if(!G){console.error("[realtimeVoice] avatar_speak requires authToken");return}P?.abort();const J=new AbortController;P=J;const ye=++C;_e(),E.resetForNewStream(u),f=I;const de=`${(m.apiBase??vt).replace(/\/$/,"")}/avatar_ttsWithPoses`,Ae=m.voiceId?.trim()||Mr,ke=m.speakingRate??kt,Et=Math.max(Qt,Math.min(Xt,ke)),Ge={user_query:I,ttsEngineId:u,speakingRate:Et,tenant:"aiTwin"};_n.has(u)&&(Ge.voice_id=Ae);try{const Le=await fetch(de,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${G}`},body:JSON.stringify(Ge),signal:J.signal});if(!Le.ok)throw new Error(`avatar_ttsWithPoses failed (${Le.status})`);const ht=Le.body;if(!ht)throw new Error("No response body");const pe=ht.getReader(),It=new TextDecoder;let $e="",pt=!0;const ze=async nt=>{if(ye!==C||J.signal.aborted||!N)return;const we=xn(nt);if(we){if(we.event==="audio"){const Pe=we.data.chunk,Se=we.data.visemes??[],Oe=we.data.words??[],ne=we.data.is_new_segment??!1,ae=we.data.sample_rate;if(Pe){const At=pt;pt=!1,await E.playAudioChunk(r,n,Pe,Se,Oe,ne,{sampleRate:ae,newStream:At})}else(Se.length>0||Oe.length>0)&&E.queueTimingsOnly(r,n,Se,Oe,ne)}else if(we.event==="metadata"){const Pe=we.data.mood;typeof Pe=="string"&&Pe.trim()&&(c=Pe.trim());const Se=we.data.sentence_emotions;Array.isArray(Se)&&(a.length=0,a.push(...ur(Se,c))),$()}else if(we.event==="error")throw new Error(String(we.data.message??"avatar_ttsWithPoses stream error"))}};for(;!(ye!==C||J.signal.aborted||!N);){const{done:nt,value:we}=await pe.read();we&&($e+=It.decode(we,{stream:!0}));const Pe=$e.split(`
3
+ `);G=T.pop()??"";for(const O of T)await $(O);if(_){G.trim()&&await $(G.trim()),I.markStreamComplete(s),A("bot_stopped",F);break}}}catch(we){if(b!==P||we instanceof DOMException&&we.name==="AbortError")return;throw console.error("[avatarTtsLipsync]",we),j(),e("error"),we}},stop:j,getVisemeQueue:()=>s,getWordQueue:()=>i,getFullVisemeQueue:()=>o,getFullWordQueue:()=>a,getSentenceEmotions:()=>f,getStreamMood:()=>d,getPlaybackElapsedMs:()=>I.getPlaybackElapsedMs(),isSpeaking:()=>I.isSpeaking(),isWarmingUp:()=>I.isWarmingUp(),releaseWarmupHold:()=>I.releaseWarmupHold(),setLipsyncWarmupHold:(F,J)=>{J&&(B=J),I.setLipsyncWarmupHoldEnabled(F)},getVoiceStream:()=>I.getVoiceStream(),setPlaybackSpeed:F=>{I.setPlaybackSpeed(F)},getPlaybackSpeed:()=>I.getPlaybackSpeed(),subscribe:F=>(h.add(F),()=>h.delete(F))}}const Wr=16e3,$r=16e3;class _i extends Rr.ProtobufFrameSerializer{async deserialize(t){try{return await super.deserialize(t)}catch(r){if(r instanceof Error&&r.message==="Unknown frame kind")return{type:"raw",message:null};throw r}}}class Si extends Rr.DailyMediaManager{constructor(){super(!0,!0,void 0,void 0,512,Wr,$r)}bufferBotAudio(t,r){}}const vi=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function bi(e){return vi.test(e.trim())}function Ti(e){const t=e.replace(/\/$/,"");return t.startsWith("https://")?`wss://${t.slice(8)}`:t.startsWith("http://")?`ws://${t.slice(7)}`:t}function as(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 r=e.apiBase??Mt,n=new URL(`${Ti(r)}/ws/voice`);n.searchParams.set("authToken",t),n.searchParams.set("tenant","aiTwin");const s=e.voiceId?.trim();s&&bi(s)&&n.searchParams.set("voiceId",s);const i=e.personaId?.trim();i&&n.searchParams.set("personaId",i);const o=e.speakingRate??Dt;return n.searchParams.set("speaking_rate",String(Math.max(ar,Math.min(ur,o)))),e.ttsStream===!1?n.searchParams.set("tts_stream","false"):n.searchParams.set("tts_stream","true"),n.toString()}function gr(e,t){if(!Array.isArray(e))return[];const r=[];for(const n of e)!n||typeof n!="object"||r.push({sentence_index:Number(n.sentence_index??0),text:String(n.text??""),sentiment:typeof n.sentiment=="string"?n.sentiment:void 0,emotion:String(n.emotion??t),start_word:Number(n.start_word??0),end_word:Number(n.end_word??0)});return r}function Ei(e){if(!e||typeof e!="object")return null;const t=e,n=(t.data&&typeof t.data=="object"&&!Array.isArray(t.data)?t.data:null)||t;return{...n,type:String(n.type??t.type??"server-message")}}function yr(e){return e.is_new_segment===!0||Number(e.audio_offset_ms??-1)===0?!0:Number(e.chunk_index??-1)===0}function ki(e){return e?.transport?._mediaManager?._wavStreamPlayer??null}function us(e,t={}){const r=[],n=[],s=[],i=[];let o=0;const a=[],l=[];let f="",c="neutral",u=ot,d=!1,p=null,E=null,h={...t},x=0,R=null;const P=new Set,w=[];let k=!1,B=null,I=!0,Z="default";const he=new Set;let A=null,V=0,K=!1,Q=null,j=!1;const me=()=>{Q!=null&&(clearTimeout(Q),Q=null)},F=()=>P.forEach(m=>m());let J=()=>!1;const b=is({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>{j=!1,e("done")},onNotify:F,shouldHoldForWarmup:()=>J()},u),z=()=>{if(B)return B;const m=new MessageChannel;return m.port1.onmessage=()=>{const C=w.shift();C!==void 0&&Ee(C),w.length>0?m.port2.postMessage(null):k=!1},B=m,m},ne=m=>{w.push(m),!k&&(k=!0,z().port2.postMessage(null))},ae=()=>b.getPlaybackElapsedMs(),te=(m,C=f)=>{if(!h.onLipsyncDebug)return;const X=ae(),oe=r,Se=or(s).map(_e=>({viseme:_e.viseme,vtimeMs:_e.vtime}));h.onLipsyncDebug({event:m,spokenText:C,mood:c,playbackAnchorPerf:b.getPlaybackStartPerf(),playbackElapsedMs:X,queueLength:oe.length,activeViseme:Ct(oe,X),segments:l.map(_e=>({..._e,visemes:[..._e.visemes]})),fullQueueLength:s.length,compactionSkipped:Se})},ee=(m,C)=>{h.onLipsyncDebug&&(R={event:m,spokenText:C},x===0&&(x=requestAnimationFrame(()=>{x=0;const X=R;R=null,X&&te(X.event,X.spokenText)})))},ie=()=>{I=!0,he.clear(),b.clearAborted()},we=()=>{const m=ki(p);if(m){Z=`bot-${Date.now()}`,m.interruptedTrackIds={[Z]:!0};try{m.interrupt?.()}catch{}}const C=E;try{C?.interrupt?.()}catch{}},S=m=>{if(j&&b.isSpeaking())return!0;const C=m.context_id?.trim();return C&&he.has(C)?!0:I?!1:!(m.type==="avatar_metadata"||yr(m))},U=m=>{I=!1,j=!1;const C=m.context_id?.trim();C&&he.add(C),me(),K=!1,w.length=0,k=!1,A?.abort(),A=null,V+=1,we(),b.bargeIn(),W(),e("idle"),ee("interrupt"),F()},W=()=>{r.length=0,n.length=0,s.length=0,i.length=0,o=0,l.length=0,f="",c="neutral",a.length=0},G=()=>{b.stop(),W(),ee("reset"),F()},L=()=>{G(),e("idle"),F()},$=(m,C)=>{typeof m.mood=="string"&&m.mood.trim()&&(c=m.mood.trim()),Array.isArray(m.sentence_emotions)&&m.sentence_emotions.length>0&&(C&&(a.length=0),a.push(...gr(m.sentence_emotions,c)))},_=m=>{if(S({...m,type:"avatar_metadata"}))return;ie(),K=!0;const C=String(m.spoken_text??m.text??"").trim();C&&(f=C),b.resetForNewStream(u),typeof m.mood=="string"&&m.mood.trim()&&(c=m.mood.trim()),a.length=0,a.push(...gr(m.sentence_emotions,c)),Array.isArray(m.gestures)&&m.gestures,ee("metadata",C),F()},y=(m,C,X,oe)=>{if(!h.onLipsyncDebug)return;const Se=r.slice(X),_e=oe.filter(ge=>String(ge.symbol??ge.viseme??"").trim());l.push({spokenText:C,audioOffsetMs:Se[0]?.vtime??0,isNewSegment:!!m.is_new_segment,chunkDurationMs:m.chunk_duration_ms!=null?Number(m.chunk_duration_ms):null,receivedAtMs:performance.now(),visemes:Se.map((ge,ke)=>{const be=_e[ke];return{rawSymbol:String(be?.symbol??be?.viseme??ge.viseme).trim(),viseme:ge.viseme,startSec:Number(be?.start??0),durationSec:Number(be?.duration??0),vtimeMs:ge.vtime,vdurationMs:ge.vduration}})})},T=(m,C,X,oe)=>{if(m.length===0&&C.length===0)return;ts(s,i,m,C,o,u);const Se=rs(m,o);let _e=null;const ge=X.trim();if(ge)try{const ke=atob(ge.replace(/\s+/g,"")).length,be=Number.isFinite(oe)&&(oe??0)>0?Number(oe):16e3;_e=ke/2/be*1e3}catch{_e=null}o=Math.max(Se,_e!=null?o+_e:o)},O=m=>{if(S(m))return;me(),yr(m)&&ie();const C=String(m.spoken_text??m.text??"").trim(),X=yr(m),oe=m.visemes??[],Se=m.words??[],_e=String(m.chunk??"").trim(),ge=K;ge&&(K=!1),C&&(f=C),$(m,X),ge?(b.resetForNewStream(u),s.length=0,i.length=0,o=0):X&&b.resumeBotStream(),T(oe,Se,_e,m.sample_rate);const ke=r.length;b.playAudioChunk(r,n,_e,oe,Se,X,{sampleRate:m.sample_rate,newStream:ge,audioOffsetMs:Number(m.audio_offset_ms??0),onNewStream:()=>{l.length=0}}).then(()=>{y(m,C,ke,oe),ee("audio_chunk",C)})},Y=m=>{if(S(m))return;(m.is_new_segment||Number(m.audio_offset_ms??-1)===0)&&ie(),console.warn("[realtimeVoice] avatar_lipsync is deprecated — expect avatar_audio_chunk");const C=String(m.spoken_text??m.text??"").trim();C&&(f=C),$(m,!!m.is_new_segment),T(m.visemes??[],m.words??[],"",void 0);const X=r.length;b.queueTimingsOnly(r,n,m.visemes??[],m.words??[],!!m.is_new_segment,{audioOffsetMs:Number(m.audio_offset_ms??0)}),y(m,C,X,m.visemes??[]),ee("lipsync",C),F()},re=async m=>{const C=String(m.text??"").trim();if(!C)return;const X=h.authToken?.trim();if(!X){console.error("[realtimeVoice] avatar_speak requires authToken");return}A?.abort();const oe=new AbortController;A=oe;const Se=++V;ie(),b.resetForNewStream(u),f=C;const ge=`${(h.apiBase??Mt).replace(/\/$/,"")}/avatar_ttsWithPoses`,ke=h.voiceId?.trim()||Ur,be=h.speakingRate??Dt,ct=Math.max(ar,Math.min(ur,be)),Ie={user_query:C,ttsEngineId:u,speakingRate:ct,tenant:"aiTwin"};Rn.has(u)&&(Ie.voice_id=ke);try{const xe=await fetch(ge,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${X}`},body:JSON.stringify(Ie),signal:oe.signal});if(!xe.ok)throw new Error(`avatar_ttsWithPoses failed (${xe.status})`);const Je=xe.body;if(!Je)throw new Error("No response body");const je=Je.getReader(),Bt=new TextDecoder;let Ue="",Fe=!0;const Be=async Ze=>{if(Se!==V||oe.signal.aborted||!I)return;const pe=Wn(Ze);if(pe){if(pe.event==="audio"){const Pe=pe.data.chunk,We=pe.data.visemes??[],ue=pe.data.words??[],bt=pe.data.is_new_segment??!1,Lt=pe.data.sample_rate,Tt=Number(pe.data.audio_offset_ms??0);if(Pe){const lt=Fe;Fe=!1,await b.playAudioChunk(r,n,Pe,We,ue,bt,{sampleRate:Lt,newStream:lt,audioOffsetMs:Tt})}else(We.length>0||ue.length>0)&&b.queueTimingsOnly(r,n,We,ue,bt,{audioOffsetMs:Tt,newStream:Fe})}else if(pe.event==="metadata"){const Pe=pe.data.mood;typeof Pe=="string"&&Pe.trim()&&(c=Pe.trim());const We=pe.data.sentence_emotions;Array.isArray(We)&&(a.length=0,a.push(...gr(We,c))),F()}else if(pe.event==="error")throw new Error(String(pe.data.message??"avatar_ttsWithPoses stream error"))}};for(;!(Se!==V||oe.signal.aborted||!I);){const{done:Ze,value:pe}=await je.read();pe&&(Ue+=Bt.decode(pe,{stream:!0}));const Pe=Ue.split(`
4
4
 
5
- `);$e=Pe.pop()??"";for(const Se of Pe)await ze(Se);if(nt){$e.trim()&&await ze($e.trim()),ye===C&&!J.signal.aborted&&N&&E.markStreamComplete(r);break}}}catch(Le){if(J.signal.aborted||!N)return;console.error("[realtimeVoice] avatar_speak failed:",Le),e("error"),$()}finally{P===J&&(P=null)}},be=h=>{const I=fi(h);if(!I){console.warn("[realtimeVoice] server message with no payload",h);return}const G=String(I.type??"unknown");switch(G!=="avatar_metadata"&&G!=="avatar_audio_chunk"&&G!=="avatar_lipsync"&&G!=="avatar_interrupt"&&G!=="error"&&console.debug("[realtimeVoice] server message",G,I),G){case"avatar_metadata":w(I);break;case"avatar_audio_chunk":le(I);break;case"avatar_lipsync":ie(I);break;case"avatar_interrupt":B(I);break;case"error":{const J=String(I.message??"Voice session error");console.error("[realtimeVoice] server error:",J),e("error"),$();break}case"avatar_speak":ge(I);break}},Te=()=>p||(T=new ai,p=new ws.PipecatClient({transport:new br.WebSocketTransport({mediaManager:T,serializer:new oi,recorderSampleRate:Rr,playerSampleRate:Cr}),enableCam:!1,enableMic:!0,callbacks:{onBotReady:()=>{d=!0,e("idle"),$()},onDisconnected:()=>{d=!1,R()},onError:h=>{const I=typeof h=="object"&&h!==null&&"data"in h?String(h.data??h):String(h);console.error("[realtimeVoice]",I,h),e("error"),$()},onTransportStateChanged:h=>{h==="error"&&(e("error"),$())},onServerMessage:h=>{me(h)},onUserStartedSpeaking:()=>{},onBotStartedSpeaking:()=>{_e(),se("bot_started")},onBotStoppedSpeaking:()=>{N&&(j(),Y=setTimeout(()=>{Y=null,N&&(E.markStreamComplete(r),se("bot_stopped"))},450))},onUserTranscript:h=>{String(h.text??""),m.onUserTranscript?.(h.text,!!h.final)},onBotOutput:h=>{m.onBotOutput?.(h.text)}}}),p);return{setDeveloperToken:h=>{},setTtsEngineId:h=>{u=h,E.setStreamTtsEngineId(h)},getTtsEngineId:()=>u,speak:async(h,I)=>{const G=h.trim();if(G){if(!p||!d)throw new Error("Realtime voice is not connected. Call connect() first.");X(),await p.sendText(G)}},stop:()=>{P?.abort(),P=null,C+=1,_e(),X(),e("idle"),$()},connect:async h=>{if(h&&(m={...m,...h}),!m.authToken?.trim())throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");e("loading");const I=Te();await I.initDevices();const G=jn(m);console.info("[realtimeVoice] connecting (avatar_audio_chunk RTVI):",G);try{await I.connect({wsUrl:G})}catch(J){d=!1,e("error"),$();const ye=J instanceof Error?J.message:"Realtime voice connect failed (WebSocket 101 only means upgrade OK — check authToken/origin/RTVI handshake)";throw new Error(`${ye} — ${G}`)}d=!0,e("idle"),$()},disconnect:async()=>{if(j(),V=!1,g.length=0,v=!1,P?.abort(),P=null,C+=1,_e(),p)try{await p.disconnect()}catch(h){console.error("[realtimeVoice] disconnect failed:",h)}d=!1,R()},isConnected:()=>d,unlockAudio:async()=>{await E.primeAudioContext()},getVisemeQueue:()=>r,getWordQueue:()=>n,getFullVisemeQueue:()=>s,getFullWordQueue:()=>i,getSentenceEmotions:()=>a,getStreamMood:()=>c,getPlaybackElapsedMs:ee,isSpeaking:()=>E.isSpeaking(),isWarmingUp:()=>E.isWarmingUp(),releaseWarmupHold:()=>E.releaseWarmupHold(),setLipsyncWarmupHold:(h,I)=>{I&&(z=I),E.setLipsyncWarmupHoldEnabled(h)},getVoiceStream:()=>E.getVoiceStream(),setPlaybackSpeed:h=>{E.setPlaybackSpeed(h)},getPlaybackSpeed:()=>E.getPlaybackSpeed(),subscribe:h=>(M.add(h),()=>M.delete(h))}}function Qn(e,t,r){return{width:e.crop?.source_width??t,height:e.crop?.source_height??r}}function Xn(e,t){const r=e.crop;return{x:t?.source_x??r?.x??0,y:t?.source_y??r?.y??0,width:t?.source_w??r?.width??e.frame_width??0,height:t?.source_h??r?.height??e.frame_height??0}}function Jn(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function dt(e,t){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const n=await r.arrayBuffer();return Os(n,t)}async function Zn(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const r=await dt(e,t),n=new TextDecoder().decode(r);return JSON.parse(n)}async function mi(e){const t=new Blob([e],{type:"image/webp"});return createImageBitmap(t)}async function hi(e){const t=new Blob([e],{type:"image/webp"}),r=URL.createObjectURL(t);return new Promise((n,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(r),n(i)},i.onerror=()=>{URL.revokeObjectURL(r),s(new Error("Failed to decode decrypted image"))},i.src=r})}async function pi(e){const t=new Blob([e],{type:"image/png"});return createImageBitmap(t)}async function wi(e){const t=new Blob([e],{type:"image/png"}),r=URL.createObjectURL(t);return new Promise((n,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(r),n(i)},i.onerror=()=>{URL.revokeObjectURL(r),s(new Error("Failed to decode decrypted PNG"))},i.src=r})}function gi(e){const t=new Blob([e],{type:"video/mp4"});return URL.createObjectURL(t)}async function yi(e){const t=Vs();if(!bt())return t;if(!e)throw new Error("Missing asset key for encrypted idle video");const r=await dt(t,e);return gi(r)}function ut(e,t){return`${e}/${t}_minus_sil.png`}const es=5;function Jt(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const r=e?.crop?.content_threshold;return typeof r=="number"&&r>0?r:es}const _i=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],yr=["t01_0.25","t02_0.50","t03_0.75"],Si=yr.length,Zr=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 en(e,t){return`${e}__${t}`}function tn(e,t){return`pairs/${e}/${t}_minus_sil.png`}function _t(e){return`${e}_minus_sil.png`}function Dr(e,t){if(t.has(e))return e;const r=/^pairs\/([^/]+)\//.exec(e);if(!r)return null;const n=r[1].split("__")[1];if(!n||n==="sil")return null;const s=_t(n);return t.has(s)?s:null}function ts(e,t){return e.filter(r=>t.has(r))}function vi(e,t,r){const n=Fr(e,t);return ts(n,r).length>0}function Fr(e,t){if(e===t)return[];const r=[],n=en(e,t);if(Zr.has(n)){for(const i of yr)r.push(tn(n,i));return r}const s=en(t,e);if(Zr.has(s)){for(let i=Si-1;i>=0;i--)r.push(tn(s,yr[i]));return r}return[]}function bi(e){return e<16?[1]:e<24?[1]:e<35?[0,1,2]:[0,1,2]}function Ti(e,t){return bi(t).map(n=>e[n]).filter(n=>n!=null)}function ki(e,t,r,n){if(n<=0)return 0;const i=Math.max(1,r-t)/n,o=e-t,a=Math.floor(o/i);return Math.min(n-1,Math.max(0,a))}function rs(e,t,r){if(!r)return`${e}/${t}`;const n=t.split("/").pop()??t,s=n.includes(".")?n.slice(0,n.lastIndexOf(".")):n;return`${e}/${s}.bin`}const Ei=[.32,.24,.16,.08,0],Ii=Ei.map(e=>`eyes_${e.toFixed(2)}`),Ai=["eyes_0.32","eyes_0.16","eyes_0.00"],Pi=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],De=2,xi=-20,$t=16,ns={fear:-20,anger:-20,sad:-10,neutral:0,love:0,happy:0,disgust:10};function St(e){return`brow_${e}`}function Je(e){return ns[e]}function Be(e){return Math.max(xi,Math.min($t,e))}function Mi(e,t){if(e===t)return[];const r=e<t,n=r?De:-De,s=[];let i=e;if(i%De!==0){const o=r?Math.ceil(i/De)*De:Math.floor(i/De)*De,a=Be(o);if(r?a>=t:a<=t)return[t];i=a,s.push(i)}for(;;){const o=i+n;if(r?o>=t:o<=t){t!==i&&s.push(t);break}i=o,s.push(i)}return s}function rn(e){const t=Be(e);if(t%De===0)return[t];const r=Math.floor(t/De)*De,n=Math.min($t,r+De),s=Be(r);return t-s<=n-t?s===n?[s]:[s,n]:s===n?[n]:[n,s]}const Ri=["fear","anger","sad","neutral","love","happy","disgust"],Ci={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 We(e,t="neutral"){const r=String(e??"").trim().toLowerCase();return r?Ci[r]??t:t}function ss(e){return e===0?null:ut(St(e),"eyes_standard")}function is(e){return ss(Je(e))}const Di={[-20]:"anger",[-10]:"sad",0:"neutral",10:"disgust"};function Br(e,t){if(!e)return null;if(t(e))return e;const r=e.match(/^brow_(-?\d+)\/(eyes_[^/]+)_minus_sil\.png$/);if(r){const s=Number(r[1]),i=r[2];for(const a of rn(s)){if(a===s)continue;const l=ut(St(a),i);if(t(l))return l}const o=Di[s];if(o){const a=ut(o,i);if(t(a))return a}return null}const n=e.match(/^(fear|anger|sad|neutral|love|happy|disgust)\/(eyes_[^/]+)_minus_sil\.png$/);if(n){const s=n[1],i=n[2],o=Je(s);for(const a of[o,...rn(o)]){const l=ut(St(a),i);if(t(l))return l}}return null}function nn(e,t){const r=St(Je(e));return!t||t==="eyes_standard"?is(e):e==="neutral"&&t==="eyes_standard"?null:ut(r,t)}function Fi(e,t){const r=[];for(const n of t)r.push(nn(e,n));for(let n=t.length-2;n>=0;n--)r.push(nn(e,t[n]));return r.push(is(e)),r}function Bi(e){return ut(St(e),"eyes_standard")}function Li(e,t){return Mi(e,t).map(Bi)}const Oi=6;function Ni(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function Vi(e){const t=Be(e);return t>=$t?$t:Be(t+Oi)}function Ui(e,t,r){const n=Be(r);return Ni(t,e)?.emphasis?Vi(n):n}function sn(e){if(!e)return 0;const t=e.match(/^brow_(-?\d+)\//);if(!t?.[1])return null;const r=Number.parseInt(t[1],10);return Number.isFinite(r)?Be(r):null}function at(e,t){return e+Math.random()*(t-e)}function Lt(){return at(14,32)}function Wi(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:r=2200,intervalMaxMs:n=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:i=120,doubleBlinkGapMaxMs:o=220,fullBlinkChance:a=.75,postEmotionChangeDelayMs:l=380,minDwellMs:f=550}=e;let c="neutral",u="neutral",d=0,p=0,T=0,m={kind:"idle",nextBlinkAt:performance.now()+t},A=null,x="__eyes_open__|neutral|brow:0";function M(P=c){x=`${A??"__eyes_open__"}|${P}|brow:${p}`}function g(P,C=c){A=P;const V=sn(P);V!=null?p=V:P==null&&(p=0),M(C)}function v(P){return ss(P)}function L(P,C,V){const Y=Be(C),D=Be(V);if(d=D,Y===D){g(v(D));return}const j=[...Li(Y,D),v(D)];m={kind:"browBlend",paths:j,index:0,holdUntil:P+Lt(),targetBrow:D},g(j[0]??null)}function N(P,C){m={kind:"idle",nextBlinkAt:P+at(r,n)},u=C,g(v(d),C)}function Q(P,C,V){const Y=V?Ai:Math.random()<a?Ii:Pi;m={kind:"playing",paths:Fi(C,Y),index:0,holdUntil:P+Lt(),after:"idle",blinkEmotion:C},u=C,g(m.paths[0]??null,C)}function fe(P){if(m.kind==="browBlend"){for(;P>=m.holdUntil&&m.index<m.paths.length-1;)m.index+=1,m.holdUntil=P+Lt();const C=m.paths[m.index]??null;g(C),P>=m.holdUntil&&m.index>=m.paths.length-1&&(p=m.targetBrow,d=m.targetBrow,g(v(m.targetBrow)),m={kind:"idle",nextBlinkAt:P+l});return}if(m.kind==="idle"){g(v(d),u),P>=m.nextBlinkAt&&Q(P,u,!1);return}if(m.kind==="doublePause"){g(v(d),m.blinkEmotion),P>=m.resumeAt&&Q(P,m.blinkEmotion,!0);return}if(m.kind==="playing"){for(;P>=m.holdUntil&&m.index<m.paths.length-1;)m.index+=1,m.holdUntil=P+Lt();if(g(m.paths[m.index]??null,m.blinkEmotion),P>=m.holdUntil&&m.index>=m.paths.length-1){if(m.after==="resumeIdle"){N(P+l,c);return}if(m.after==="doublePause"){m={kind:"doublePause",resumeAt:P+at(i,o),blinkEmotion:m.blinkEmotion},g(v(d),m.blinkEmotion);return}if(m.after==="idle"&&Math.random()<s){m={kind:"doublePause",resumeAt:P+at(i,o),blinkEmotion:m.blinkEmotion},g(v(d),m.blinkEmotion);return}N(P,m.blinkEmotion)}}}return{advance:fe,setTargetEmotion(P,C){const V=We(P,c);if(V===c)return;const Y=performance.now();!C?.force&&f>0&&!(c==="neutral"&&V!=="neutral")&&Y-T<f||(c=V,u=V,T=Y)},setTargetEyebrow(P){const C=Be(P);if(C===d)return;const V=performance.now(),Y=m.kind==="browBlend"?sn(A)??p:p;d=C,!(C===Y&&m.kind!=="browBlend")&&(m.kind==="playing"||m.kind==="doublePause"||L(V,Y,C))},snapToEmotion(P){const C=We(P),V=Je(C);c=C,u=C,d=V,p=V,T=performance.now(),m={kind:"idle",nextBlinkAt:performance.now()+at(4e3,7e3)},g(v(V),C)},getTargetEmotion:()=>c,getTargetEyebrow:()=>d,getExpressionPath:()=>A,getDrawKey:()=>x,reset(){c="neutral",u="neutral",d=0,p=0,T=0,N(performance.now()+at(r,n),"neutral")}}}function $i(){const e=document.createElement("video");e.muted=!0,e.playsInline=!0,e.preload="auto",e.setAttribute("playsinline",""),e.loop=!0;let t=!1,r=null,n="";const s=()=>{r&&(URL.revokeObjectURL(r),r=null)};return{async load(i){t&&n===i||(s(),t=!1,n=i,i.startsWith("blob:")&&(r=i),await new Promise((o,a)=>{const l=()=>{const f=e.duration;if(!Number.isFinite(f)||f<=0){a(new Error(`Idle video has invalid duration: ${i}`));return}t=!0,e.currentTime=0,o()};e.addEventListener("loadeddata",l,{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,n=""}}}const Hi=50,os=400;function Ki(e,t,r){return e.filter(n=>{const s=n.queueIndex??-1;return s>=t&&s<=r})}function qi(e,t){const r=[];for(const n of e){const s=Ki(t,n.start_word,n.end_word);if(s.length===0)continue;const i=s[0],o=s[s.length-1];r.push({sentence:n,startMs:Math.max(0,i.wtime-Hi),endMs:o.wtime+o.wduration+os})}return r.sort((n,s)=>n.startMs-s.startMs)}function Gi(e,t,r,n="neutral"){const s=We(n);if(t.length===0)return s;if(r.length===0||e<r[0].wtime)return We(t[0]?.emotion,s);const i=qi(t,r);let o=null;for(const f of i)e>=f.startMs&&e<=f.endMs&&(o=f);if(o)return We(o.sentence.emotion,s);const a=r[r.length-1],l=a.wtime+a.wduration;return e>l+os?We(t[t.length-1]?.emotion,s):s}function Lr(e,t,r,n,s=es){const i=new OffscreenCanvas(r,n),o=i.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,r,n);const a=o.getImageData(0,0,r,n),l=o.createImageData(r,n);l.data.set(a.data);const c=new OffscreenCanvas(r,n).getContext("2d",{willReadFrequently:!0});for(const u of t){c.clearRect(0,0,r,n),c.drawImage(u,0,0,r,n);const d=c.getImageData(0,0,r,n);for(let p=0;p<l.data.length;p+=4){const T=d.data[p],m=d.data[p+1],A=d.data[p+2];Math.max(T,m,A)>s&&(l.data[p]=T,l.data[p+1]=m,l.data[p+2]=A,l.data[p+3]=255)}}return o.putImageData(l,0,0),i}let Qe=null,lt=null;const Ht=new Map;function zi(){Qe=null,lt=null,Ht.clear()}En(zi);function ji(e){return e??lt??void 0}function Or(e){return new Promise((t,r)=>{const n=new Image;n.decoding="async",n.onload=()=>t(n),n.onerror=()=>r(new Error(`Failed to load image: ${e}`)),n.src=e})}async function Yi(e){return bt()?wi(await dt(Wt(),e??"")):Or(Wt())}async function on(e,t,r,n){const s=r?await Zn(e,n):await fetch(e).then(f=>{if(!f.ok)throw new Error(`Failed to load ${e}`);return f.json()}),i=s.sheets?.[0];if(!i?.path)throw new Error(`${e} has no sheets[0].path`);const o=r?await hi(await dt(rs(t,i.path,r),n??"")):await Or(`${t}/${i.path}`),a=new Map;for(const f of s.cells??[])f.path&&a.set(f.path,f);const l=new Map;for(const f of s.sheets??[])l.set(f.index,f);return{atlas:o,atlasMeta:s,cellByPath:a,sheetByIndex:l,diffBase:t}}async function _r(e){const t=ji(e),r=bt();if(r&&!t)throw new Error("Encrypted assets enabled but no key provided");if(Qe&&(!r||lt===(t??null))){const i=await Qe;return{...ft(i),visemePaths:[...i.viseme.cellByPath.keys()]}}lt=t??null,Ht.clear();const n=In();Qe=(async()=>{const i=await Yi(t),o=await on(An(),n,r,t);let a=null;try{a=await on(Pn(),n,r,t)}catch{a=null}return{sil:i,viseme:o,expression:a}})();const s=await Qe;return{...ft(s),visemePaths:[...s.viseme.cellByPath.keys()]}}async function Nr(e){if(await _r(e),!Qe)throw new Error("Assets not loaded");return Qe}function ft(e){return Qn(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function Qi(e,t){const r=document.createElement("canvas");return r.width=t.w,r.height=t.h,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 Kt(e,t,r){const n=`${t.diffBase}::${r}`,s=Ht.get(n);if(s)return s;const i=(async()=>{const o=t.cellByPath.get(r);if(!o)throw new Error(`No atlas cell for path: ${r}`);const a=Xn(t.atlasMeta,o),l=ft(e);if(Jn(o)){const u=Qi(t,o),d=document.createElement("canvas");d.width=l.width,d.height=l.height;const p=d.getContext("2d");return p.fillStyle="#000",p.fillRect(0,0,l.width,l.height),p.drawImage(u,a.x,a.y),d}const f=`${t.diffBase}/${r}`,c=await Or(f);if(c.naturalWidth===l.width&&c.naturalHeight===l.height)return c;throw new Error(`Diff PNG wrong size for ${r}`)})();return Ht.set(n,i),i}async function lr(e,t,r,n,s,i){if(s){const{width:p,height:T}=s;if(i&&!i()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const m=document.createElement("canvas");m.width=p,m.height=T;const A=m.getContext("2d");if(A.clearRect(0,0,p,T),A.drawImage(t,0,0,p,T),i&&!i())return;(e.width!==p||e.height!==T)&&(e.width=p,e.height=T);const x=e.getContext("2d");x.clearRect(0,0,p,T),x.drawImage(m,0,0);return}const o=await Nr(),a=Jt(o.viseme.atlasMeta),{width:l,height:f}=ft(o);(e.width!==l||e.height!==f)&&(e.width=l,e.height=f);const c=[],u=e.getContext("2d");if(u.clearRect(0,0,l,f),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const p=o.sil;u.drawImage(p,0,0,l,f);return}if(c.length===0){u.drawImage(t,0,0,l,f);return}const d=Lr(t,c,l,f,a);u.drawImage(d,0,0)}async function Sr(e,t,r){const n=await Nr(lt??void 0),s=Jt(n.viseme.atlasMeta),{width:i,height:o}=ft(n);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);const a=[];if(t.mouthPath){const c=new Set(n.viseme.cellByPath.keys()),u=Dr(t.mouthPath,c)??(c.has(t.mouthPath)?t.mouthPath:null);u&&a.push(await Kt(n,n.viseme,u))}if(t.expressionPath&&n.expression){const c=Br(t.expressionPath,u=>n.expression.cellByPath.has(u));c&&a.push(await Kt(n,n.expression,c))}const l=e.getContext("2d");if(l.clearRect(0,0,i,o),a.length===0){l.drawImage(n.sil,0,0,i,o);return}const f=Lr(n.sil,a,i,o,s);l.drawImage(f,0,0)}async function Xi(e,t){const r=await Nr(lt??void 0),n=Jt(r.viseme.atlasMeta),{width:s,height:i}=ft(r),o=[];if(e.mouthPath){const c=new Set(r.viseme.cellByPath.keys()),u=Dr(e.mouthPath,c)??(c.has(e.mouthPath)?e.mouthPath:null);u&&o.push(await Kt(r,r.viseme,u))}if(e.expressionPath&&r.expression){const c=Br(e.expressionPath,u=>r.expression.cellByPath.has(u));c&&o.push(await Kt(r,r.expression,c))}const a=new OffscreenCanvas(s,i),l=a.getContext("2d");if(o.length===0)return l.drawImage(r.sil,0,0,s,i),a.transferToImageBitmap();const f=Lr(r.sil,o,s,i,n);return l.drawImage(f,0,0),a.transferToImageBitmap()}async function Ji(e){await Sr(e,{mouthPath:null,expressionPath:null})}let as=null;function an(e){as=e}function qe(){return as}function Zi(e){return`${e.from}→${e.to}@${e.fromVtime}`}function un(){return{rafTicksSpeaking:0,rafTicksDeduped:0,drawsRequested:0,drawsDisplayed:0,queueSuperseded:0,workerStale:0,rifeFramesOmittedByGap:0,transitionFramesSampled:0,transitionFramesDisplayed:0,transitionFramesMissed:0,bufferTicks:0,bufferHits:0,bufferUnderruns:0,bufferPrefetched:0}}function eo(){let e=un();const t=new Set,r=new Set,n=new Set;let s="",i=-1,o="";const a=()=>{e=un(),t.clear(),r.clear(),n.clear(),s="",i=-1,o=""},l=c=>{t.add(c)},f=c=>{c&&n.add(c)};return{reset:a,onRafTickSpeaking(){e.rafTicksSpeaking+=1},onDrawDeduped(){e.rafTicksDeduped+=1},onDrawRequested(){e.drawsRequested+=1},onQueueSuperseded(){e.queueSuperseded+=1},onWorkerStale(){e.workerStale+=1},onTransitionSample(c,u){const d=Zi(c);e.transitionFramesSampled+=1,d!==s&&(s=d,i=-1,o!==d&&(o=d,e.rifeFramesOmittedByGap+=c.omittedByGap)),u&&r.add(u),i>=0&&c.frameIdx>i+1&&(e.transitionFramesMissed+=c.frameIdx-i-1),i=c.frameIdx},onBufferTick(){e.bufferTicks+=1},onBufferHit(){e.bufferHits+=1},onBufferUnderrun(){e.bufferUnderruns+=1},onBufferPrefetched(){e.bufferPrefetched+=1},onBufferReset(){},onDrawDisplayed(c,u){if(e.drawsDisplayed+=1,c.transitionMeta){e.transitionFramesDisplayed+=1;const p=c.transitionMeta;l(p.fromVtime),l(p.toVtime),f(c.mouthPath);return}if(c.label.includes("→")){e.transitionFramesDisplayed+=1;return}const d=c.label.trim();if(!(!d||d==="sil")){for(const p of u)if(p.viseme===d&&c.elapsedMs>=p.vtime&&c.elapsedMs<p.vtime+p.vduration){l(p.vtime);break}}},snapshot(c,u){let d=0,p=0;for(const M of c)if(M.viseme!=="sil"){if(M.vtime>=u)break;d+=1,t.has(M.vtime)&&(p+=1)}const T=d>0?Math.round(p/d*100):100,m=e.queueSuperseded+e.workerStale+Math.max(0,e.drawsRequested-e.drawsDisplayed),A=d-p,x=e.bufferTicks>0?Math.round(e.bufferHits/e.bufferTicks*100):100;return{...e,bufferHitRate:x,renderLoss:m,visemeSlotsTotal:d,visemeSlotsCovered:p,visemeCoveragePct:T,transitionFramesUniqueSampled:r.size,transitionFramesUniquePainted:n.size,drawsDeduped:e.rafTicksDeduped,renderSkips:e.rafTicksDeduped+m,visemesShown:p,visemesSkipped:A,rifeTransitionFramesSkipped:e.transitionFramesMissed}}}}const us=kr,to=200,cn=300,ro=100,no=600,cs="__sil__";function ln(e){return e.lastDrawnKey?e.lastDrawnKey.split("|")[0]===cs:!1}function Vr(){return{lastDrawnKey:""}}function Ce(e){e.lastDrawnKey="",e.transitionCache=void 0}function so(e,t){if(t?.transitionCache&&t.transitionCache.queueLength===e.length)return t.transitionCache.transitions;const r=Un(e);return t&&(t.transitionCache={queueLength:e.length,transitions:r}),r}function io(e,t){let r="sil";for(const n of e)n.viseme!=="sil"&&(t!=null&&n.vtime>t||(r=n.viseme));return r}function oo(e,t,r,n=120){if(r!=="sil")return r;const s=zn(e,t,n);return s!=="sil"?s:io(e,t)}function ao(e,t){return e==="sil"?!1:t?vi(e,"sil",t):Fr(e,"sil").length>0}function uo(){return us+no}function qt(e){return e??cs}function Ur(e,t,r,n,s=ro){if(e.length===0)return{diffPath:null,label:"sil"};if(t>=Ye(e))return{diffPath:null,label:"sil"};const i=so(e,n),o=ei(i,t);if(o){const f=Wn(o.fromVtime,o.toVtime,o.from,o.to);if(t>=f.transStart&&t<f.transEnd){let c=Fr(o.from,o.to);r&&(c=ts(c,r));const u=Ti(c,f.effectiveGapMs);if(u.length>0){const d=ki(t,f.transStart,f.transEnd,u.length);return{diffPath:u[d]??u[u.length-1],label:`${o.from}→${o.to}`,transitionMeta:{from:o.from,to:o.to,fromVtime:o.fromVtime,toVtime:o.toVtime,frameIdx:d,frameCount:u.length,fullFrameCount:c.length,omittedByGap:Math.max(0,c.length-u.length)}}}return{diffPath:_t(o.to),label:o.to}}}const a=jt(e);let l=Ln(a,t);return l==="sil"&&(l=zn(e,t,s)),l==="sil"?{diffPath:null,label:"sil"}:{diffPath:_t(l),label:l}}function Zt(e,t){const r=t??qt(e.expressionPath);return`${qt(e.mouthPath)}|${r}`}function fn(e,t,r,n,s,i,o,a,l,f){const c=Ur(t,r,l,i),u={mouthPath:c.diffPath,expressionPath:n},d=Zt(u,s),p=qe();if(c.transitionMeta&&p?.onTransitionSample(c.transitionMeta,c.diffPath),d===i.lastDrawnKey){p?.onDrawDeduped();return}p?.onDrawRequested(),a(c),i.lastDrawnKey=d,o(e,u,d,T=>{T===d&&(i.lastDrawnKey=d)})}function fr(e,t,r,n,s,i,o=null){const a={mouthPath:null,expressionPath:t},l=o!=null?`|idle@${Math.round(o*1e3)}`:"",f=Zt(a,r)+l;!(o!=null)&&f===n.lastDrawnKey||(i(o!=null?t?"idle · blink":"idle":t?"blink":"sil"),s(e,a,f,u=>{u===f&&(n.lastDrawnKey=f)}))}function ls(e,t,r,n,s=yt,i){if(r<=t||e.length===0)return[];const o=i??Vr(),a=[];let l="";for(let f=t;f<r;f+=s){const c=Ur(e,f,n,o),u=qt(c.diffPath);u!==l&&(l=u,a.push({displayAtMs:f,mouthPath:c.diffPath,mouthKey:u,label:c.label}))}return a}const co=3;function Wr(e){let t=0;const r=[];let n=!1,s=!1;const i=async()=>{if(!n){n=!0;try{for(;r.length>0;){const a=r.shift(),l=++t;await e(a.diffs,l,a)&&l===t&&a.onDisplayed?.(a.drawKey)}}finally{n=!1,r.length>0&&i()}}},o=()=>{s||(s=!0,queueMicrotask(()=>{s=!1,i()}))};return{enqueue(a,l,f){const c=r.findIndex(u=>u.drawKey===l);c>=0?r[c]={diffs:a,drawKey:l,onDisplayed:f}:(r.length>=co&&(r.shift(),qe()?.onQueueSuperseded()),r.push({diffs:a,drawKey:l,onDisplayed:f})),o()},reset(){t+=1,r.length=0},isLatestSeq(a){return a===t},getLatestSeq(){return t}}}function lo(e,t=2){const r=[],n=new Set;let s=0,i=!1,o=0;const a=(f,c)=>`${f}|${c}`,l=async()=>{if(!i){i=!0;try{for(;s<t&&r.length>0;){const f=r.shift(),c=a(f.displayAtMs,f.drawKey);if(n.has(c))continue;n.add(c),s+=1;const u=o;try{await e(f)}finally{if(n.delete(c),s-=1,u!==o)return}}}finally{i=!1,r.length>0&&s<t&&l()}}};return{enqueue(f){const c=a(f.displayAtMs,f.drawKey);n.has(c)||r.some(u=>a(u.displayAtMs,u.drawKey)===c)||(r.push(f),l())},isQueuedOrInflight(f,c){const u=a(f,c);return n.has(u)?!0:r.some(d=>a(d.displayAtMs,d.drawKey)===u)},reset(){o+=1,r.length=0,n.clear(),s=0},pendingCount(){return r.length+s}}}function fo(){const e=new Map,t=r=>{r.bitmap.close()};return{has(r,n){return e.has(n)},markRendered(r,n,s,i,o){const a=e.get(n);a&&t(a),e.set(n,{displayAtMs:r,mouthKey:n,label:s,mouthPath:i,bitmap:o})},findForMouthKey(r){return e.get(r)??null},removeThrough(r){},pruneBefore(r){},reset(){for(const r of e.values())t(r);e.clear()},size(){return e.size}}}function mo(e,t,r,n,s,i){const o=ls(t,r,n,s,yt,i);if(o.length===0)return{ready:0,total:0};let a=0;for(const l of o)e.has(l.displayAtMs,l.mouthKey)&&(a+=1);return{ready:a,total:o.length}}function ho(e){const t=fo(),r=Vr(),n=new Set;let s="",i=null;const o=lo(async u=>{try{if(!e.supportsBufferedPlayback)return;const d=await e.compositePrefetchFrame(u.diffs,u.mouthKey);if(!d)return;t.markRendered(u.displayAtMs,u.mouthKey,u.label,u.mouthPath,d),qe()?.onBufferPrefetched()}finally{n.delete(u.mouthKey)}},Is),a=u=>{for(const d of u)t.has(d.displayAtMs,d.mouthKey)||n.has(d.mouthKey)||o.isQueuedOrInflight(0,d.mouthKey)||(n.add(d.mouthKey),o.enqueue({...d,displayAtMs:0,drawKey:d.mouthKey,diffs:{mouthPath:d.mouthPath,expressionPath:null}}))},l=(u,d,p,T=ar)=>{if(!e.supportsBufferedPlayback)return;const m=Math.max(0,d-yt*4),A=ls(u,m,d+T,p,yt,r);a(A)},f=(u,d)=>{s=u,i=d},c=(u,d)=>!(s!==u||i!==d);return{reset(){t.reset(),o.reset(),n.clear(),Ce(r),s="",i=null,qe()?.onBufferReset()},markExternallyDisplayed(u,d){f(u.split("|")[0]??u,d)},warmupTick(u){e.supportsBufferedPlayback&&l(u.queue,0,u.availablePaths,Math.max(zr,ar))},isWarmupReady(u,d=zr){if(!e.supportsBufferedPlayback)return!0;const{ready:p,total:T}=mo(t,u.queue,0,d,u.availablePaths,r);return T===0?!1:p/T>=Es},getBufferedFrameCount:()=>t.size(),tick(u){const d=qe();d?.onBufferTick(),l(u.queue,u.elapsedMs,u.availablePaths),t.pruneBefore(u.elapsedMs-ar);const p=Ur(u.queue,u.elapsedMs,u.availablePaths,r),T=qt(p.diffPath),m=Zt({mouthPath:p.diffPath,expressionPath:u.expressionPath},u.expressionDrawKey),A=()=>(f(T,u.expressionPath),d?.onBufferHit(),{usedBuffer:!0,underrun:!1,held:!0});if(c(T,u.expressionPath)||u.playbackState.lastDrawnKey===m)return A();const x=t.findForMouthKey(T,u.elapsedMs,yt);return x&&u.expressionPath!=null?(d?.onBufferHit(),{usedBuffer:!1,underrun:!1,held:!1}):x?(e.blitBitmap(u.canvas,x.bitmap),f(x.mouthKey,u.expressionPath),u.playbackState.lastDrawnKey=m,u.onStatus({diffPath:x.mouthPath,label:x.label}),u.onDisplayed(m,x.mouthPath,x.label),d?.onBufferHit(),{usedBuffer:!0,underrun:!1,held:!1}):(d?.onBufferUnderrun(),{usedBuffer:!1,underrun:!0,held:!1})}}}const po=`
5
+ `);Ue=Pe.pop()??"";for(const We of Pe)await Be(We);if(Ze){Ue.trim()&&await Be(Ue.trim()),Se===V&&!oe.signal.aborted&&I&&b.markStreamComplete(r);break}}}catch(xe){if(oe.signal.aborted||!I)return;console.error("[realtimeVoice] avatar_speak failed:",xe),e("error"),F()}finally{A===oe&&(A=null)}},Ee=m=>{const C=Ei(m);if(!C){console.warn("[realtimeVoice] server message with no payload",m);return}const X=String(C.type??"unknown");switch(X!=="avatar_metadata"&&X!=="avatar_audio_chunk"&&X!=="avatar_lipsync"&&X!=="avatar_interrupt"&&X!=="error"&&console.debug("[realtimeVoice] server message",X,C),X){case"avatar_metadata":_(C);break;case"avatar_audio_chunk":O(C);break;case"avatar_lipsync":Y(C);break;case"avatar_interrupt":U(C);break;case"error":{const oe=String(C.message??"Voice session error");console.error("[realtimeVoice] server error:",oe),e("error"),F();break}case"avatar_speak":re(C);break}},ye=()=>p||(E=new Si,p=new Ps.PipecatClient({transport:new Rr.WebSocketTransport({mediaManager:E,serializer:new _i,recorderSampleRate:Wr,playerSampleRate:$r}),enableCam:!1,enableMic:!0,callbacks:{onBotReady:()=>{d=!0,e("idle"),F()},onDisconnected:()=>{d=!1,L()},onError:m=>{const C=typeof m=="object"&&m!==null&&"data"in m?String(m.data??m):String(m);console.error("[realtimeVoice]",C,m),e("error"),F()},onTransportStateChanged:m=>{m==="error"&&(e("error"),F())},onServerMessage:m=>{ne(m)},onUserStartedSpeaking:()=>{},onBotStartedSpeaking:()=>{ie(),ee("bot_started")},onBotStoppedSpeaking:()=>{I&&(me(),Q=setTimeout(()=>{Q=null,I&&(b.markStreamComplete(r),ee("bot_stopped"))},450))},onUserTranscript:m=>{String(m.text??""),h.onUserTranscript?.(m.text,!!m.final)},onBotOutput:m=>{h.onBotOutput?.(m.text)}}}),p);return{setDeveloperToken:m=>{},setTtsEngineId:m=>{u=m,b.setStreamTtsEngineId(m)},getTtsEngineId:()=>u,speak:async(m,C)=>{const X=m.trim();if(X){if(!p||!d)throw new Error("Realtime voice is not connected. Call connect() first.");G(),await p.sendText(X)}},stop:()=>{A?.abort(),A=null,V+=1,ie(),G(),e("idle"),F()},connect:async m=>{if(m&&(h={...h,...m}),!h.authToken?.trim())throw new Error("Voice connect requires authToken (same JWT as SSE Authorization Bearer).");e("loading");const C=ye();await C.initDevices();const X=as(h);console.info("[realtimeVoice] connecting (avatar_audio_chunk RTVI):",X);try{await C.connect({wsUrl:X})}catch(oe){d=!1,e("error"),F();const Se=oe instanceof Error?oe.message:"Realtime voice connect failed (WebSocket 101 only means upgrade OK — check authToken/origin/RTVI handshake)";throw new Error(`${Se} — ${X}`)}d=!0,e("idle"),F()},disconnect:async()=>{if(me(),K=!1,w.length=0,k=!1,A?.abort(),A=null,V+=1,ie(),p)try{await p.disconnect()}catch(m){console.error("[realtimeVoice] disconnect failed:",m)}d=!1,L()},isConnected:()=>d,unlockAudio:async()=>{await b.primeAudioContext()},getVisemeQueue:()=>r,getWordQueue:()=>n,getFullVisemeQueue:()=>s,getFullWordQueue:()=>i,getSentenceEmotions:()=>a,getStreamMood:()=>c,getPlaybackElapsedMs:ae,isSpeaking:()=>b.isSpeaking(),isWarmingUp:()=>b.isWarmingUp(),releaseWarmupHold:()=>b.releaseWarmupHold(),setLipsyncWarmupHold:(m,C)=>{C&&(J=C),b.setLipsyncWarmupHoldEnabled(m)},getVoiceStream:()=>b.getVoiceStream(),setPlaybackSpeed:m=>{b.setPlaybackSpeed(m)},getPlaybackSpeed:()=>b.getPlaybackSpeed(),subscribe:m=>(P.add(m),()=>P.delete(m))}}function cs(e,t,r){return{width:e.crop?.source_width??t,height:e.crop?.source_height??r}}function ls(e,t){const r=e.crop;return{x:t?.source_x??r?.x??0,y:t?.source_y??r?.y??0,width:t?.source_w??r?.width??e.frame_width??0,height:t?.source_h??r?.height??e.frame_height??0}}function fs(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function vt(e,t){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const n=await r.arrayBuffer();return Qs(n,t)}async function ds(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const r=await vt(e,t),n=new TextDecoder().decode(r);return JSON.parse(n)}async function Ii(e){const t=new Blob([e],{type:"image/webp"});return createImageBitmap(t)}async function Ai(e){const t=new Blob([e],{type:"image/webp"}),r=URL.createObjectURL(t);return new Promise((n,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(r),n(i)},i.onerror=()=>{URL.revokeObjectURL(r),s(new Error("Failed to decode decrypted image"))},i.src=r})}async function xi(e){const t=new Blob([e],{type:"image/png"});return createImageBitmap(t)}async function Pi(e){const t=new Blob([e],{type:"image/png"}),r=URL.createObjectURL(t);return new Promise((n,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(r),n(i)},i.onerror=()=>{URL.revokeObjectURL(r),s(new Error("Failed to decode decrypted PNG"))},i.src=r})}function Mi(e){const t=new Blob([e],{type:"video/mp4"});return URL.createObjectURL(t)}async function Ri(e){const t=Js();if(!Rt())return t;if(!e)throw new Error("Missing asset key for encrypted idle video");const r=await vt(t,e);return Mi(r)}function wt(e,t){return`${e}/${t}_minus_sil.png`}const ms=5;function cr(e){const t=e?.composite_threshold;if(typeof t=="number"&&t>0)return t;const r=e?.crop?.content_threshold;return typeof r=="number"&&r>0?r:ms}const Ci=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],Ar=["t01_0.25","t02_0.50","t03_0.75"],Di=Ar.length,dn=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 mn(e,t){return`${e}__${t}`}function hn(e,t){return`pairs/${e}/${t}_minus_sil.png`}function yt(e){return`${e}_minus_sil.png`}function Hr(e,t){if(t.has(e))return e;const r=/^pairs\/([^/]+)\//.exec(e);if(!r)return null;const n=r[1].split("__")[1];if(!n||n==="sil")return null;const s=yt(n);return t.has(s)?s:null}function hs(e,t){return e.filter(r=>t.has(r))}function Fi(e,t,r){const n=Kr(e,t);return hs(n,r).length>0}function Kr(e,t){if(e===t)return[];const r=[],n=mn(e,t);if(dn.has(n)){for(const i of Ar)r.push(hn(n,i));return r}const s=mn(t,e);if(dn.has(s)){for(let i=Di-1;i>=0;i--)r.push(hn(s,Ar[i]));return r}return[]}function Bi(e){return e<16?[1]:e<24?[1]:e<35?[0,1,2]:[0,1,2]}function Li(e,t){return Bi(t).map(n=>e[n]).filter(n=>n!=null)}function Oi(e,t,r,n){if(n<=0)return 0;const i=Math.max(1,r-t)/n,o=e-t,a=Math.floor(o/i);return Math.min(n-1,Math.max(0,a))}function ps(e,t,r){if(!r)return`${e}/${t}`;const n=t.split("/").pop()??t,s=n.includes(".")?n.slice(0,n.lastIndexOf(".")):n;return`${e}/${s}.bin`}const Ni=[.32,.24,.16,.08,0],Vi=Ni.map(e=>`eyes_${e.toFixed(2)}`),Ui=["eyes_0.32","eyes_0.16","eyes_0.00"],Wi=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],$e=2,$i=-20,Jt=16,ws={fear:-20,anger:-20,sad:-10,neutral:0,love:0,happy:0,disgust:10};function xt(e){return`brow_${e}`}function it(e){return ws[e]}function qe(e){return Math.max($i,Math.min(Jt,e))}function Hi(e,t){if(e===t)return[];const r=e<t,n=r?$e:-$e,s=[];let i=e;if(i%$e!==0){const o=r?Math.ceil(i/$e)*$e:Math.floor(i/$e)*$e,a=qe(o);if(r?a>=t:a<=t)return[t];i=a,s.push(i)}for(;;){const o=i+n;if(r?o>=t:o<=t){t!==i&&s.push(t);break}i=o,s.push(i)}return s}function pn(e){const t=qe(e);if(t%$e===0)return[t];const r=Math.floor(t/$e)*$e,n=Math.min(Jt,r+$e),s=qe(r);return t-s<=n-t?s===n?[s]:[s,n]:s===n?[n]:[n,s]}const Ki=["fear","anger","sad","neutral","love","happy","disgust"],qi={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 ze(e,t="neutral"){const r=String(e??"").trim().toLowerCase();return r?qi[r]??t:t}function gs(e){return e===0?null:wt(xt(e),"eyes_standard")}function ys(e){return gs(it(e))}const Gi={[-20]:"anger",[-10]:"sad",0:"neutral",10:"disgust"};function qr(e,t){if(!e)return null;if(t(e))return e;const r=e.match(/^brow_(-?\d+)\/(eyes_[^/]+)_minus_sil\.png$/);if(r){const s=Number(r[1]),i=r[2];for(const a of pn(s)){if(a===s)continue;const l=wt(xt(a),i);if(t(l))return l}const o=Gi[s];if(o){const a=wt(o,i);if(t(a))return a}return null}const n=e.match(/^(fear|anger|sad|neutral|love|happy|disgust)\/(eyes_[^/]+)_minus_sil\.png$/);if(n){const s=n[1],i=n[2],o=it(s);for(const a of[o,...pn(o)]){const l=wt(xt(a),i);if(t(l))return l}}return null}function wn(e,t){const r=xt(it(e));return!t||t==="eyes_standard"?ys(e):e==="neutral"&&t==="eyes_standard"?null:wt(r,t)}function zi(e,t){const r=[];for(const n of t)r.push(wn(e,n));for(let n=t.length-2;n>=0;n--)r.push(wn(e,t[n]));return r.push(ys(e)),r}function ji(e){return wt(xt(e),"eyes_standard")}function Yi(e,t){return Hi(e,t).map(ji)}const Qi=6;function Xi(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function Ji(e){const t=qe(e);return t>=Jt?Jt:qe(t+Qi)}function Zi(e,t,r){const n=qe(r);return Xi(t,e)?.emphasis?Ji(n):n}function gn(e){if(!e)return 0;const t=e.match(/^brow_(-?\d+)\//);if(!t?.[1])return null;const r=Number.parseInt(t[1],10);return Number.isFinite(r)?qe(r):null}function pt(e,t){return e+Math.random()*(t-e)}function qt(){return pt(14,32)}function eo(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:r=2200,intervalMaxMs:n=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:i=120,doubleBlinkGapMaxMs:o=220,fullBlinkChance:a=.75,postEmotionChangeDelayMs:l=380,minDwellMs:f=550}=e;let c="neutral",u="neutral",d=0,p=0,E=0,h={kind:"idle",nextBlinkAt:performance.now()+t},x=null,R="__eyes_open__|neutral|brow:0";function P(A=c){R=`${x??"__eyes_open__"}|${A}|brow:${p}`}function w(A,V=c){x=A;const K=gn(A);K!=null?p=K:A==null&&(p=0),P(V)}function k(A){return gs(A)}function B(A,V,K){const Q=qe(V),j=qe(K);if(d=j,Q===j){w(k(j));return}const me=[...Yi(Q,j),k(j)];h={kind:"browBlend",paths:me,index:0,holdUntil:A+qt(),targetBrow:j},w(me[0]??null)}function I(A,V){h={kind:"idle",nextBlinkAt:A+pt(r,n)},u=V,w(k(d),V)}function Z(A,V,K){const Q=K?Ui:Math.random()<a?Vi:Wi;h={kind:"playing",paths:zi(V,Q),index:0,holdUntil:A+qt(),after:"idle",blinkEmotion:V},u=V,w(h.paths[0]??null,V)}function he(A){if(h.kind==="browBlend"){for(;A>=h.holdUntil&&h.index<h.paths.length-1;)h.index+=1,h.holdUntil=A+qt();const V=h.paths[h.index]??null;w(V),A>=h.holdUntil&&h.index>=h.paths.length-1&&(p=h.targetBrow,d=h.targetBrow,w(k(h.targetBrow)),h={kind:"idle",nextBlinkAt:A+l});return}if(h.kind==="idle"){w(k(d),u),A>=h.nextBlinkAt&&Z(A,u,!1);return}if(h.kind==="doublePause"){w(k(d),h.blinkEmotion),A>=h.resumeAt&&Z(A,h.blinkEmotion,!0);return}if(h.kind==="playing"){for(;A>=h.holdUntil&&h.index<h.paths.length-1;)h.index+=1,h.holdUntil=A+qt();if(w(h.paths[h.index]??null,h.blinkEmotion),A>=h.holdUntil&&h.index>=h.paths.length-1){if(h.after==="resumeIdle"){I(A+l,c);return}if(h.after==="doublePause"){h={kind:"doublePause",resumeAt:A+pt(i,o),blinkEmotion:h.blinkEmotion},w(k(d),h.blinkEmotion);return}if(h.after==="idle"&&Math.random()<s){h={kind:"doublePause",resumeAt:A+pt(i,o),blinkEmotion:h.blinkEmotion},w(k(d),h.blinkEmotion);return}I(A,h.blinkEmotion)}}}return{advance:he,setTargetEmotion(A,V){const K=ze(A,c);if(K===c)return;const Q=performance.now();!V?.force&&f>0&&!(c==="neutral"&&K!=="neutral")&&Q-E<f||(c=K,u=K,E=Q)},setTargetEyebrow(A){const V=qe(A);if(V===d)return;const K=performance.now(),Q=h.kind==="browBlend"?gn(x)??p:p;d=V,!(V===Q&&h.kind!=="browBlend")&&(h.kind==="playing"||h.kind==="doublePause"||B(K,Q,V))},snapToEmotion(A){const V=ze(A),K=it(V);c=V,u=V,d=K,p=K,E=performance.now(),h={kind:"idle",nextBlinkAt:performance.now()+pt(4e3,7e3)},w(k(K),V)},getTargetEmotion:()=>c,getTargetEyebrow:()=>d,getExpressionPath:()=>x,getDrawKey:()=>R,reset(){c="neutral",u="neutral",d=0,p=0,E=0,I(performance.now()+pt(r,n),"neutral")}}}function to(){const e=document.createElement("video");e.muted=!0,e.playsInline=!0,e.preload="auto",e.setAttribute("playsinline",""),e.loop=!0;let t=!1,r=null,n="";const s=()=>{r&&(URL.revokeObjectURL(r),r=null)};return{async load(i){t&&n===i||(s(),t=!1,n=i,i.startsWith("blob:")&&(r=i),await new Promise((o,a)=>{const l=()=>{const f=e.duration;if(!Number.isFinite(f)||f<=0){a(new Error(`Idle video has invalid duration: ${i}`));return}t=!0,e.currentTime=0,o()};e.addEventListener("loadeddata",l,{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,n=""}}}const ro=50,_s=400;function no(e,t,r){return e.filter(n=>{const s=n.queueIndex??-1;return s>=t&&s<=r})}function so(e,t){const r=[];for(const n of e){const s=no(t,n.start_word,n.end_word);if(s.length===0)continue;const i=s[0],o=s[s.length-1];r.push({sentence:n,startMs:Math.max(0,i.wtime-ro),endMs:o.wtime+o.wduration+_s})}return r.sort((n,s)=>n.startMs-s.startMs)}function io(e,t,r,n="neutral"){const s=ze(n);if(t.length===0)return s;if(r.length===0||e<r[0].wtime)return ze(t[0]?.emotion,s);const i=so(t,r);let o=null;for(const f of i)e>=f.startMs&&e<=f.endMs&&(o=f);if(o)return ze(o.sentence.emotion,s);const a=r[r.length-1],l=a.wtime+a.wduration;return e>l+_s?ze(t[t.length-1]?.emotion,s):s}function Gr(e,t,r,n,s=ms){const i=new OffscreenCanvas(r,n),o=i.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,r,n);const a=o.getImageData(0,0,r,n),l=o.createImageData(r,n);l.data.set(a.data);const c=new OffscreenCanvas(r,n).getContext("2d",{willReadFrequently:!0});for(const u of t){c.clearRect(0,0,r,n),c.drawImage(u,0,0,r,n);const d=c.getImageData(0,0,r,n);for(let p=0;p<l.data.length;p+=4){const E=d.data[p],h=d.data[p+1],x=d.data[p+2];Math.max(E,h,x)>s&&(l.data[p]=E,l.data[p+1]=h,l.data[p+2]=x,l.data[p+3]=255)}}return o.putImageData(l,0,0),i}let nt=null,_t=null;const Zt=new Map;function oo(){nt=null,_t=null,Zt.clear()}On(oo);function ao(e){return e??_t??void 0}function zr(e){return new Promise((t,r)=>{const n=new Image;n.decoding="async",n.onload=()=>t(n),n.onerror=()=>r(new Error(`Failed to load image: ${e}`)),n.src=e})}async function uo(e){return Rt()?Pi(await vt(Xt(),e??"")):zr(Xt())}async function yn(e,t,r,n){const s=r?await ds(e,n):await fetch(e).then(f=>{if(!f.ok)throw new Error(`Failed to load ${e}`);return f.json()}),i=s.sheets?.[0];if(!i?.path)throw new Error(`${e} has no sheets[0].path`);const o=r?await Ai(await vt(ps(t,i.path,r),n??"")):await zr(`${t}/${i.path}`),a=new Map;for(const f of s.cells??[])f.path&&a.set(f.path,f);const l=new Map;for(const f of s.sheets??[])l.set(f.index,f);return{atlas:o,atlasMeta:s,cellByPath:a,sheetByIndex:l,diffBase:t}}async function xr(e){const t=ao(e),r=Rt();if(r&&!t)throw new Error("Encrypted assets enabled but no key provided");if(nt&&(!r||_t===(t??null))){const i=await nt;return{...St(i),visemePaths:[...i.viseme.cellByPath.keys()]}}_t=t??null,Zt.clear();const n=Nn();nt=(async()=>{const i=await uo(t),o=await yn(Vn(),n,r,t);let a=null;try{a=await yn(Un(),n,r,t)}catch{a=null}return{sil:i,viseme:o,expression:a}})();const s=await nt;return{...St(s),visemePaths:[...s.viseme.cellByPath.keys()]}}async function jr(e){if(await xr(e),!nt)throw new Error("Assets not loaded");return nt}function St(e){return cs(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function co(e,t){const r=document.createElement("canvas");return r.width=t.w,r.height=t.h,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 er(e,t,r){const n=`${t.diffBase}::${r}`,s=Zt.get(n);if(s)return s;const i=(async()=>{const o=t.cellByPath.get(r);if(!o)throw new Error(`No atlas cell for path: ${r}`);const a=ls(t.atlasMeta,o),l=St(e);if(fs(o)){const u=co(t,o),d=document.createElement("canvas");d.width=l.width,d.height=l.height;const p=d.getContext("2d");return p.fillStyle="#000",p.fillRect(0,0,l.width,l.height),p.drawImage(u,a.x,a.y),d}const f=`${t.diffBase}/${r}`,c=await zr(f);if(c.naturalWidth===l.width&&c.naturalHeight===l.height)return c;throw new Error(`Diff PNG wrong size for ${r}`)})();return Zt.set(n,i),i}async function _r(e,t,r,n,s,i){if(s){const{width:p,height:E}=s;if(i&&!i()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const h=document.createElement("canvas");h.width=p,h.height=E;const x=h.getContext("2d");if(x.clearRect(0,0,p,E),x.drawImage(t,0,0,p,E),i&&!i())return;(e.width!==p||e.height!==E)&&(e.width=p,e.height=E);const R=e.getContext("2d");R.clearRect(0,0,p,E),R.drawImage(h,0,0);return}const o=await jr(),a=cr(o.viseme.atlasMeta),{width:l,height:f}=St(o);(e.width!==l||e.height!==f)&&(e.width=l,e.height=f);const c=[],u=e.getContext("2d");if(u.clearRect(0,0,l,f),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const p=o.sil;u.drawImage(p,0,0,l,f);return}if(c.length===0){u.drawImage(t,0,0,l,f);return}const d=Gr(t,c,l,f,a);u.drawImage(d,0,0)}async function Pr(e,t,r){const n=await jr(_t??void 0),s=cr(n.viseme.atlasMeta),{width:i,height:o}=St(n);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);const a=[];if(t.mouthPath){const c=new Set(n.viseme.cellByPath.keys()),u=Hr(t.mouthPath,c)??(c.has(t.mouthPath)?t.mouthPath:null);u&&a.push(await er(n,n.viseme,u))}if(t.expressionPath&&n.expression){const c=qr(t.expressionPath,u=>n.expression.cellByPath.has(u));c&&a.push(await er(n,n.expression,c))}const l=e.getContext("2d");if(l.clearRect(0,0,i,o),a.length===0){l.drawImage(n.sil,0,0,i,o);return}const f=Gr(n.sil,a,i,o,s);l.drawImage(f,0,0)}async function lo(e,t){const r=await jr(_t??void 0),n=cr(r.viseme.atlasMeta),{width:s,height:i}=St(r),o=[];if(e.mouthPath){const c=new Set(r.viseme.cellByPath.keys()),u=Hr(e.mouthPath,c)??(c.has(e.mouthPath)?e.mouthPath:null);u&&o.push(await er(r,r.viseme,u))}if(e.expressionPath&&r.expression){const c=qr(e.expressionPath,u=>r.expression.cellByPath.has(u));c&&o.push(await er(r,r.expression,c))}const a=new OffscreenCanvas(s,i),l=a.getContext("2d");if(o.length===0)return l.drawImage(r.sil,0,0,s,i),a.transferToImageBitmap();const f=Gr(r.sil,o,s,i,n);return l.drawImage(f,0,0),a.transferToImageBitmap()}async function fo(e){await Pr(e,{mouthPath:null,expressionPath:null})}const Gt={fontFamily:"system-ui, -apple-system, sans-serif",fontSize:"clamp(0.875rem, 2.5vw, 1.125rem)",color:"rgba(255,255,255,0.85)"};function mo(e){return e.length===0?"":e.map(t=>t.word).join(" ")}function ho(e){if(!e)return{enabled:!1,style:Gt};const t=e===!0?{}:e;return{enabled:!0,style:{fontFamily:t.fontFamily??Gt.fontFamily,fontSize:t.fontSize??Gt.fontSize,color:t.color??Gt.color}}}const po={position:"absolute",bottom:0,left:0,right:0,display:"flex",justifyContent:"center",padding:"0.75rem 1rem",pointerEvents:"none"},wo={display:"inline-block",maxWidth:"100%",padding:"0.45rem 0.85rem",borderRadius:"0.75rem",background:"rgba(0,0,0,0.55)",lineHeight:1.4,textAlign:"center"},go={display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical",overflow:"hidden",maxWidth:"100%",margin:0};function yo({text:e,style:t}){return rt.jsx("div",{style:po,"aria-live":"polite","aria-atomic":"true",children:rt.jsx("div",{style:{...wo,fontFamily:t.fontFamily,fontSize:t.fontSize,color:t.color},children:rt.jsx("p",{style:go,children:e})})})}let Ss=null;function _n(e){Ss=e}function Xe(){return Ss}function _o(e){return`${e.from}→${e.to}@${e.fromVtime}`}function Sn(){return{rafTicksSpeaking:0,rafTicksDeduped:0,drawsRequested:0,drawsDisplayed:0,queueSuperseded:0,workerStale:0,rifeFramesOmittedByGap:0,transitionFramesSampled:0,transitionFramesDisplayed:0,transitionFramesMissed:0,bufferTicks:0,bufferHits:0,bufferUnderruns:0,bufferPrefetched:0}}function So(){let e=Sn();const t=new Set,r=new Set,n=new Set;let s="",i=-1,o="";const a=()=>{e=Sn(),t.clear(),r.clear(),n.clear(),s="",i=-1,o=""},l=c=>{t.add(c)},f=c=>{c&&n.add(c)};return{reset:a,onRafTickSpeaking(){e.rafTicksSpeaking+=1},onDrawDeduped(){e.rafTicksDeduped+=1},onDrawRequested(){e.drawsRequested+=1},onQueueSuperseded(){e.queueSuperseded+=1},onWorkerStale(){e.workerStale+=1},onTransitionSample(c,u){const d=_o(c);e.transitionFramesSampled+=1,d!==s&&(s=d,i=-1,o!==d&&(o=d,e.rifeFramesOmittedByGap+=c.omittedByGap)),u&&r.add(u),i>=0&&c.frameIdx>i+1&&(e.transitionFramesMissed+=c.frameIdx-i-1),i=c.frameIdx},onBufferTick(){e.bufferTicks+=1},onBufferHit(){e.bufferHits+=1},onBufferUnderrun(){e.bufferUnderruns+=1},onBufferPrefetched(){e.bufferPrefetched+=1},onBufferReset(){},onDrawDisplayed(c,u){if(e.drawsDisplayed+=1,c.transitionMeta){e.transitionFramesDisplayed+=1;const p=c.transitionMeta;l(p.fromVtime),l(p.toVtime),f(c.mouthPath);return}if(c.label.includes("→")){e.transitionFramesDisplayed+=1;return}const d=c.label.trim();if(!(!d||d==="sil")){for(const p of u)if(p.viseme===d&&c.elapsedMs>=p.vtime&&c.elapsedMs<p.vtime+p.vduration){l(p.vtime);break}}},snapshot(c,u){let d=0,p=0;for(const P of c)if(P.viseme!=="sil"){if(P.vtime>=u)break;d+=1,t.has(P.vtime)&&(p+=1)}const E=d>0?Math.round(p/d*100):100,h=e.queueSuperseded+e.workerStale+Math.max(0,e.drawsRequested-e.drawsDisplayed),x=d-p,R=e.bufferTicks>0?Math.round(e.bufferHits/e.bufferTicks*100):100;return{...e,bufferHitRate:R,renderLoss:h,visemeSlotsTotal:d,visemeSlotsCovered:p,visemeCoveragePct:E,transitionFramesUniqueSampled:r.size,transitionFramesUniquePainted:n.size,drawsDeduped:e.rafTicksDeduped,renderSkips:e.rafTicksDeduped+h,visemesShown:p,visemesSkipped:x,rifeTransitionFramesSkipped:e.transitionFramesMissed}}}}const vs=Dr,vo=200,vn=300,bo=100,To=600,bs="__sil__";function bn(e){return e.lastDrawnKey?e.lastDrawnKey.split("|")[0]===bs:!1}function Yr(){return{lastDrawnKey:""}}function De(e){e.lastDrawnKey="",e.transitionCache=void 0}function Eo(e,t){if(t?.transitionCache&&t.transitionCache.queueLength===e.length)return t.transitionCache.transitions;const r=Zn(e);return t&&(t.transitionCache={queueLength:e.length,transitions:r}),r}function ko(e,t){let r="sil";for(const n of e)n.viseme!=="sil"&&(t!=null&&n.vtime>t||(r=n.viseme));return r}function Io(e,t,r,n=120){if(r!=="sil")return r;const s=os(e,t,n);return s!=="sil"?s:ko(e,t)}function Ao(e,t){return e==="sil"?!1:t?Fi(e,"sil",t):Kr(e,"sil").length>0}function xo(){return vs+To}function tr(e){return e??bs}function Qr(e,t,r,n,s=bo){if(e.length===0)return{diffPath:null,label:"sil"};if(t>=tt(e))return{diffPath:null,label:"sil"};const i=Eo(e,n),o=mi(i,t);if(o){const f=es(o.fromVtime,o.toVtime,o.from,o.to);if(t>=f.transStart&&t<f.transEnd){let c=Kr(o.from,o.to);r&&(c=hs(c,r));const u=Li(c,f.effectiveGapMs);if(u.length>0){const d=Oi(t,f.transStart,f.transEnd,u.length);return{diffPath:u[d]??u[u.length-1],label:`${o.from}→${o.to}`,transitionMeta:{from:o.from,to:o.to,fromVtime:o.fromVtime,toVtime:o.toVtime,frameIdx:d,frameCount:u.length,fullFrameCount:c.length,omittedByGap:Math.max(0,c.length-u.length)}}}return{diffPath:yt(o.to),label:o.to}}}const a=ir(e);let l=Yn(a,t);return l==="sil"&&(l=os(e,t,s)),l==="sil"?{diffPath:null,label:"sil"}:{diffPath:yt(l),label:l}}function Pt(e,t){const r=t??tr(e.expressionPath);return`${tr(e.mouthPath)}|${r}`}function Tn(e,t,r,n,s,i,o,a,l,f){const c=Qr(t,r,l,i),u={mouthPath:c.diffPath,expressionPath:n},d=Pt(u,s),p=Xe();if(c.transitionMeta&&p?.onTransitionSample(c.transitionMeta,c.diffPath),d===i.lastDrawnKey){p?.onDrawDeduped();return}p?.onDrawRequested(),a(c),i.lastDrawnKey=d,o(e,u,d,E=>{E===d&&(i.lastDrawnKey=d)})}function Sr(e,t,r,n,s,i,o=null,a=null){const l={mouthPath:a,expressionPath:t},f=o!=null?`|idle@${Math.round(o*1e3)}`:"",c=Pt(l,r)+f;!(o!=null)&&c===n.lastDrawnKey||(i(o!=null?t?"idle · blink":"idle":t?"blink":"sil"),s(e,l,c,d=>{d===c&&(n.lastDrawnKey=c)}))}function Ts(e,t,r,n,s=At,i){if(r<=t||e.length===0)return[];const o=i??Yr(),a=[];let l="";for(let f=t;f<r;f+=s){const c=Qr(e,f,n,o),u=tr(c.diffPath);u!==l&&(l=u,a.push({displayAtMs:f,mouthPath:c.diffPath,mouthKey:u,label:c.label}))}return a}const Po=3;function Xr(e){let t=0;const r=[];let n=!1,s=!1;const i=async()=>{if(!n){n=!0;try{for(;r.length>0;){const a=r.shift(),l=++t;await e(a.diffs,l,a)&&l===t&&a.onDisplayed?.(a.drawKey)}}finally{n=!1,r.length>0&&i()}}},o=()=>{s||(s=!0,queueMicrotask(()=>{s=!1,i()}))};return{enqueue(a,l,f){const c=r.findIndex(u=>u.drawKey===l);c>=0?r[c]={diffs:a,drawKey:l,onDisplayed:f}:(r.length>=Po&&(r.shift(),Xe()?.onQueueSuperseded()),r.push({diffs:a,drawKey:l,onDisplayed:f})),o()},reset(){t+=1,r.length=0},isLatestSeq(a){return a===t},getLatestSeq(){return t}}}function Mo(e,t=2){const r=[],n=new Set;let s=0,i=!1,o=0;const a=(f,c)=>`${f}|${c}`,l=async()=>{if(!i){i=!0;try{for(;s<t&&r.length>0;){const f=r.shift(),c=a(f.displayAtMs,f.drawKey);if(n.has(c))continue;n.add(c),s+=1;const u=o;try{await e(f)}finally{if(n.delete(c),s-=1,u!==o)return}}}finally{i=!1,r.length>0&&s<t&&l()}}};return{enqueue(f){const c=a(f.displayAtMs,f.drawKey);n.has(c)||r.some(u=>a(u.displayAtMs,u.drawKey)===c)||(r.push(f),l())},isQueuedOrInflight(f,c){const u=a(f,c);return n.has(u)?!0:r.some(d=>a(d.displayAtMs,d.drawKey)===u)},reset(){o+=1,r.length=0,n.clear(),s=0},pendingCount(){return r.length+s}}}function Ro(){const e=new Map,t=r=>{r.bitmap.close()};return{has(r,n){return e.has(n)},markRendered(r,n,s,i,o){const a=e.get(n);a&&t(a),e.set(n,{displayAtMs:r,mouthKey:n,label:s,mouthPath:i,bitmap:o})},findForMouthKey(r){return e.get(r)??null},removeThrough(r){},pruneBefore(r){},reset(){for(const r of e.values())t(r);e.clear()},size(){return e.size}}}function Co(e,t,r,n,s,i){const o=Ts(t,r,n,s,At,i);if(o.length===0)return{ready:0,total:0};let a=0;for(const l of o)e.has(l.displayAtMs,l.mouthKey)&&(a+=1);return{ready:a,total:o.length}}function Do(e){const t=Ro(),r=Yr(),n=new Set;let s="",i=null;const o=Mo(async u=>{try{if(!e.supportsBufferedPlayback)return;const d=await e.compositePrefetchFrame(u.diffs,u.mouthKey);if(!d)return;t.markRendered(u.displayAtMs,u.mouthKey,u.label,u.mouthPath,d),Xe()?.onBufferPrefetched()}finally{n.delete(u.mouthKey)}},Vs),a=u=>{for(const d of u)t.has(d.displayAtMs,d.mouthKey)||n.has(d.mouthKey)||o.isQueuedOrInflight(0,d.mouthKey)||(n.add(d.mouthKey),o.enqueue({...d,displayAtMs:0,drawKey:d.mouthKey,diffs:{mouthPath:d.mouthPath,expressionPath:null}}))},l=(u,d,p,E=wr)=>{if(!e.supportsBufferedPlayback)return;const h=Math.max(0,d-At*4),x=Ts(u,h,d+E,p,At,r);a(x)},f=(u,d)=>{s=u,i=d},c=(u,d)=>!(s!==u||i!==d);return{reset(){t.reset(),o.reset(),n.clear(),De(r),s="",i=null,Xe()?.onBufferReset()},markExternallyDisplayed(u,d){f(u.split("|")[0]??u,d)},warmupTick(u){e.supportsBufferedPlayback&&l(u.queue,0,u.availablePaths,Math.max(on,wr))},isWarmupReady(u,d=on){if(!e.supportsBufferedPlayback)return!0;const{ready:p,total:E}=Co(t,u.queue,0,d,u.availablePaths,r);return E===0?!1:p/E>=Ns},getBufferedFrameCount:()=>t.size(),tick(u){const d=Xe();d?.onBufferTick(),l(u.queue,u.elapsedMs,u.availablePaths),t.pruneBefore(u.elapsedMs-wr);const p=Qr(u.queue,u.elapsedMs,u.availablePaths,r),E=tr(p.diffPath),h=Pt({mouthPath:p.diffPath,expressionPath:u.expressionPath},u.expressionDrawKey),x=()=>(f(E,u.expressionPath),d?.onBufferHit(),{usedBuffer:!0,underrun:!1,held:!0});if(c(E,u.expressionPath)||u.playbackState.lastDrawnKey===h)return x();const R=t.findForMouthKey(E,u.elapsedMs,At);return R&&u.expressionPath!=null?(d?.onBufferHit(),{usedBuffer:!1,underrun:!1,held:!1}):R?(e.blitBitmap(u.canvas,R.bitmap),f(R.mouthKey,u.expressionPath),u.playbackState.lastDrawnKey=h,u.onStatus({diffPath:R.mouthPath,label:R.label}),u.onDisplayed(h,R.mouthPath,R.label),d?.onBufferHit(),{usedBuffer:!0,underrun:!1,held:!1}):(d?.onBufferUnderrun(),{usedBuffer:!1,underrun:!0,held:!1})}}}const Fo=`
6
6
  in vec2 aPosition;
7
7
  out vec2 vTextureCoord;
8
8
 
@@ -30,7 +30,7 @@ void main(void)
30
30
  gl_Position = filterVertexPosition();
31
31
  vTextureCoord = filterTextureCoord();
32
32
  }
33
- `,wo=`
33
+ `,Bo=`
34
34
  in vec2 vTextureCoord;
35
35
  out vec4 finalColor;
36
36
 
@@ -44,4 +44,4 @@ void main(void)
44
44
  float keep = step(uThreshold + 1e-6, peak);
45
45
  finalColor = vec4(color.rgb, keep);
46
46
  }
47
- `;function dn(e=5){const t=new ce.Filter({glProgram:ce.GlProgram.from({vertex:po,fragment:wo,name:"threshold-key-filter"}),resources:{thresholdUniforms:{uThreshold:{value:e/255,type:"f32"}}}});return t.setThreshold=r=>{t.resources.thresholdUniforms.uniforms.uThreshold=r/255},t.getThreshold=()=>Math.round(t.resources.thresholdUniforms.uniforms.uThreshold*255),t}function go(e,t,r){return e>r||t>r}function yo(e){return e?e.getParameter(e.MAX_TEXTURE_SIZE):4096}function _o(e,t){return t!=null&&t>0?Math.floor(t):yo(e)}function So(e,t){const r=document.createElement("canvas");r.width=t.w,r.height=t.h;const n=r.getContext("2d");if(!n)throw new Error("Failed to acquire 2d context for atlas cell crop");return n.drawImage(e,t.x,t.y,t.w,t.h,0,0,t.w,t.h),r}function fs(e){return fetch(e).then(t=>{if(!t.ok)throw new Error(`Failed to load image: ${e}`);return t.blob()}).then(t=>createImageBitmap(t))}async function mn(e,t,r,n,s){const i=r?await Zn(e,s):await fetch(e).then(u=>{if(!u.ok)throw new Error(`Failed to load ${e}`);return u.json()}),o=i.sheets?.[0];if(!o?.path)throw new Error(`${e} has no sheets[0].path`);const a=new Map,l=async(u,d)=>{const p=rs(t,d,r),T=r?await mi(await dt(p,s??"")):await fs(p);if(go(T.width,T.height,n))return console.info(`[PixiVisemeCompositor] Sheet ${d} (${T.width}x${T.height}) exceeds MAX_TEXTURE_SIZE=${n}; using per-cell crops.`),{mode:"cells",bitmap:T};const m=ce.Texture.from(T);return m.source.scaleMode="nearest",{mode:"full",texture:m}};a.set(o.index,await l(o.index,o.path));for(const u of i.sheets??[])u.index!==o.index&&u.path&&a.set(u.index,await l(u.index,u.path));const f=new Map;for(const u of i.cells??[])u.path&&f.set(u.path,u);const c=new Map;for(const u of i.sheets??[])c.set(u.index,u);return{atlasMeta:i,cellByPath:f,sheetByIndex:c,diffBase:t,sheets:a}}async function vo(e,t={}){const r=new ce.Application;try{await r.init({canvas:e,width:842,height:1264,preference:"webgl",autoStart:!1,antialias:!1,resolution:1,autoDensity:!1,backgroundAlpha:1,backgroundColor:0,powerPreference:"high-performance"})}catch(w){throw r.destroy({removeView:!1}),w}if(r.renderer.type!==ce.RendererType.WEBGL)throw r.destroy({removeView:!1}),new Error("Pixi did not initialize a WebGL renderer");const n=_o(r.renderer.gl,t.maxTextureSize);let s=!1,i=!1,o=!1;const a=r.canvas,l=()=>{s=!0,!i&&!o&&(i=!0,t.onWebGlContextLost?.())};a.addEventListener("webglcontextlost",l);const f=new ce.Sprite(ce.Texture.EMPTY),c=new ce.Sprite(ce.Texture.EMPTY);c.visible=!1,c.alpha=0;const u=new ce.Sprite(ce.Texture.EMPTY),d=new ce.Sprite(ce.Texture.EMPTY),p=new ce.Sprite(ce.Texture.EMPTY);p.visible=!1;const T=dn(5),m=dn(5);u.filters=[T],d.filters=[m],r.stage.addChild(f),r.stage.addChild(c),r.stage.addChild(u),r.stage.addChild(d),r.stage.addChild(p);let A=null,x=null,M=null,g=null,v=null,L=null,N=null,Q=842,fe=1264,P=null,C=5;const V=new Map;let Y=null,D=null,j=null;const $=En(()=>{V.clear(),L=null,N=null,A=null,P=null}),z=(w,_)=>{Q=w,fe=_,r.renderer.resize(w,_),(e.width!==w||e.height!==_)&&(e.width=w,e.height=_)},E=()=>{u.visible=!1,d.visible=!1,u.texture=ce.Texture.EMPTY,d.texture=ce.Texture.EMPTY,Y=null,D=null},Z=()=>{v=null,c.visible=!1,c.alpha=0},me=()=>{f.visible=!0,f.width=Q,f.height=fe,f.x=0,f.y=0},ee=()=>{c.width=Q,c.height=fe,c.x=0,c.y=0},oe=()=>{p.visible=!1,Z(),A&&(f.texture=A,me())},se=()=>{!v&&!c.visible||oe()},_e=w=>{if(o||s||!A){oe();return}const _=Math.max(0,w);if(!x||_<=0){oe();return}E(),p.visible=!1,f.texture=x,me(),c.texture=A,ee(),c.alpha=0,c.visible=!0,v={startMs:performance.now(),durationMs:_},F()},he=(w=performance.now())=>{if(!v)return!1;if(o||s)return se(),!1;const _=(w-v.startMs)/Math.max(1,v.durationMs);if(_>=1)return oe(),F(),!1;const k=Math.min(1,Math.max(0,_)),le=k*k*(3-2*k);return c.alpha=le,c.visible=!0,F(),!0},y=()=>{if(M){try{M.resource=null}catch{}M.destroy(),M=null}x&&(x.destroy(!1),x=null),g=null},B=(w,_)=>{const k=w.cellByPath.get(_);if(!k||!Jn(k))return null;const le=`${w.diffBase}::${_}`;let ie=V.get(le);if(!ie){const ge=k.sheet??0,be=w.sheets.get(ge);if(!be)return null;let Te,mt=!1;if(be.mode==="full")Te=new ce.Texture({source:be.texture.source,frame:new ce.Rectangle(k.x,k.y,k.w,k.h)});else{const rt=So(be.bitmap,k);Te=ce.Texture.from(rt),Te.source.scaleMode="nearest",mt=!0}ie={texture:Te,ownsSource:mt},V.set(le,ie)}return{texture:ie.texture,cell:k}},H=(w,_,k,le)=>{if(!le||!k)return w.visible=!1,w.texture=ce.Texture.EMPTY,!1;const ie=B(k,le);if(!ie)return w.visible=!1,w.texture=ce.Texture.EMPTY,!1;const ge=Xn(k.atlasMeta,ie.cell);return _.setThreshold(C),w.texture=ie.texture,w.x=ge.x,w.y=ge.y,w.width=ie.cell.w,w.height=ie.cell.h,w.visible=!0,!0},X=w=>{if(!w||!L)return null;const _=P??new Set(L.cellByPath.keys());return Dr(w,_)??(_.has(w)?w:null)},R=w=>{p.visible=!1,oe();const _=X(w.mouthPath),k=w.expressionPath&&N?Br(w.expressionPath,le=>N.cellByPath.has(le)):null;_!==Y?(H(u,T,L,_),Y=_):_?u.visible=!0:u.visible=!1,k!==D?(H(d,m,N,k),D=k):k?d.visible=!0:d.visible=!1},F=()=>{if(!(o||s||document.hidden))try{r.render()}catch{}};return{async loadAssets(w){if(o)return{width:Q,height:fe,visemePaths:[]};const _=bt();if(_&&!w)throw new Error("Encrypted assets enabled but no key provided");const k=_?await pi(await dt(Wt(),w??"")):await fs(Wt());A&&A.destroy(!0),A=ce.Texture.from(k),A.source.scaleMode="linear";const le=In();L=await mn(An(),le,_,n,w);try{N=await mn(Pn(),le,_,n,w)}catch(ge){console.warn("[PixiVisemeCompositor] expression atlas unavailable:",ge),N=null}const ie=Qn(L.atlasMeta,k.width,k.height);return z(ie.width,ie.height),C=Jt(L.atlasMeta),T.setThreshold(C),m.setThreshold(C),P=new Set(L.cellByPath.keys()),V.clear(),oe(),E(),F(),{width:ie.width,height:ie.height,visemePaths:[...L.cellByPath.keys()]}},getVisemeAtlasPaths(){return P},drawLiveFrame(w){o||s||!A||(se(),R(w),F())},drawIdleVideo(w,_){o||s||document.hidden||_&&!_()||w.readyState<HTMLMediaElement.HAVE_CURRENT_DATA||(Z(),E(),p.visible=!1,!x||g!==w?(y(),M=new ce.VideoSource({resource:w,autoPlay:!1,updateFPS:0}),M.autoUpdate=!1,x=new ce.Texture({source:M}),g=w):x.source.update(),f.texture=x,me(),F())},beginIdleToSilCrossfade:_e,tickIdleToSilCrossfade:he,finishIdleToSilCrossfade:se,isIdleToSilCrossfading(){return v!=null},renderSilOnly(){o||s||!A||(E(),oe(),F())},blitBitmap(w){o||s||(se(),E(),j&&(j.destroy(!0),j=null),j=ce.Texture.from(w),p.texture=j,p.visible=!0,p.x=0,p.y=0,p.width=Q,p.height=fe,f.visible=!1,F())},async compositePrefetchFrame(w){return null},renderFrame:F,isContextLost(){return s},dispose(){if(o)return;o=!0,a.removeEventListener("webglcontextlost",l),$(),y(),j&&(j.destroy(!0),j=null);for(const{texture:_,ownsSource:k}of V.values())_.destroy(k);V.clear(),A&&(A.destroy(!0),A=null);const w=_=>{_.mode==="full"?_.texture.destroy(!0):_.bitmap.close()};if(L)for(const _ of L.sheets.values())w(_);if(N)for(const _ of N.sheets.values())w(_);T.destroy(!0),m.destroy(!0),r.destroy({removeView:!1},{children:!0,texture:!0})}}}const hn=-1,Ot=1e5,pn=new Map;async function bo(e){let t=pn.get(e);if(!t){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch worker script (${r.status}): ${e}`);const n=await r.blob();t=URL.createObjectURL(n),pn.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function To(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function vr(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const r=e.getContext("2d");r&&r.drawImage(t,0,0)}function wn(e,t){let r=0,n=null;const s=Wr(async(i,o)=>(await _r(),await Sr(e,i),s.isLatestSeq(o)));return{usesWorker:!1,usesPixi:!1,async loadAssets(i){const o=await _r(i);return n=o.visemePaths?new Set(o.visemePaths):null,o},getVisemeAtlasPaths(){return n},async renderSilOnly(){r+=1,s.reset(),await Ji(e)},drawLiveFrame(i,o,a,l){s.enqueue(o,a,l)},async compositePrefetchFrame(i,o){try{return await Xi(i)}catch{return null}},blitBitmap(i,o){vr(i,o)},drawIdleVideo(){},beginIdleToSilCrossfade(){},tickIdleToSilCrossfade(){return!1},finishIdleToSilCrossfade(){},isIdleToSilCrossfading(){return!1},supportsBufferedPlayback:!0,async renderViseme(i,o){r+=1;const a=r;t.onStatus?.(o),await Sr(i,{mouthPath:_t(o),expressionPath:null})},dispose(){r+=1,s.reset()}}}function ko(e,t,r){let n=null,s=1,i=0,o=!1,a=!1,l=null;const f=new Map,c=new Map;let u=Ot;const d=Wr(async(M,g,v)=>(f.set(g,{drawKey:v.drawKey,onDisplayed:v.onDisplayed}),await A({type:"drawFrame",diffPath:M.mouthPath,expressionDiffPath:M.expressionPath,liveSeq:g},void 0,!0),!1)),p=M=>{if(M===hn){let L=-1,N;for(const[Q,fe]of f)Q>=L&&(L=Q,N=fe);L>=0&&f.delete(L),N?.onDisplayed?.(N.drawKey);return}const g=-M,v=f.get(g);f.delete(g),v?.onDisplayed?.(v.drawKey)},T=new Map,m=(M,g)=>{a||!n||n.postMessage(M,[])},A=(M,g,v=!1)=>{if(a)return Promise.resolve();const L=s++;return new Promise((N,Q)=>{T.set(L,{resolve:fe=>N(fe),reject:Q,generation:typeof M.generation=="number"?M.generation:void 0,expectRenderDone:v}),m({...M,requestId:L})})},x=(async()=>{n=await bo(r),n.onmessage=M=>{if(a)return;const g=M.data;if(g.type==="status"){t.onStatus?.(g.label);return}if(g.type==="frame"){if(g.generation<=-Ot){const N=-g.generation,Q=c.get(N);c.delete(N),Q?.resolve(g.bitmap);return}const L=g.generation<0;if(!L&&g.generation!==i){g.bitmap.close();return}if(L&&!(g.generation===hn)){const Q=-g.generation;if(!d.isLatestSeq(Q)){qe()?.onWorkerStale(),g.bitmap.close();return}}if(L&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){qe()?.onWorkerStale(),g.bitmap.close();return}vr(e,g.bitmap),g.bitmap.close(),L&&p(g.generation);return}const v=T.get(g.requestId);if(v)switch(g.type){case"ready":T.delete(g.requestId),v.resolve();break;case"loadAssetsDone":T.delete(g.requestId),l=g.visemePaths?.length?new Set(g.visemePaths):null,v.resolve({width:g.width,height:g.height,visemePaths:g.visemePaths});break;case"renderDone":if(v.expectRenderDone&&v.generation!=null&&g.generation!==v.generation)return;T.delete(g.requestId),v.resolve();break;case"renderAborted":T.delete(g.requestId),v.resolve();break;case"error":T.delete(g.requestId),t.onError?.(g.message),v.reject(new Error(g.message));break}},n.onerror=M=>{if(a)return;const g=M.message||"Worker error";t.onError?.(g);for(const[,v]of T)v.reject(new Error(g));T.clear()},await A({type:"init"}),o=!0})();return{usesWorker:!0,usesPixi:!1,async loadAssets(M){if(a)return{width:842,height:1264};if(await x,a)return{width:842,height:1264};if(!o)throw new Error("Worker init failed");return await A({type:"loadAssets",keyHex:M,urls:Ns(tt())})??{width:842,height:1264}},getVisemeAtlasPaths(){return l},async renderSilOnly(){if(a||(await x,a))return;i+=1,d.reset(),f.clear(),c.forEach(({resolve:g})=>g(null)),c.clear(),u=Ot;const M=i;await A({type:"cancelLiveDraws"}),await A({type:"renderSil",generation:M},void 0,!0)},drawLiveFrame(M,g,v,L){a||x.then(()=>{a||d.enqueue(g,v,L)})},compositePrefetchFrame(M,g){return a?Promise.resolve(null):x.then(()=>{if(a)return null;const v=++u;return new Promise(L=>{c.set(v,{resolve:L}),A({type:"drawFrame",diffPath:M.mouthPath,expressionDiffPath:M.expressionPath,liveSeq:v},void 0,!0).catch(()=>{c.delete(v),L(null)})})})},blitBitmap(M,g){vr(M,g)},drawIdleVideo(){},beginIdleToSilCrossfade(){},tickIdleToSilCrossfade(){return!1},finishIdleToSilCrossfade(){},isIdleToSilCrossfading(){return!1},supportsBufferedPlayback:!0,async renderViseme(M,g,v={}){if(a||(await x,a))return;i+=1,d.reset(),f.clear();const L=i;try{await A({type:"cancelLiveDraws"}),await A({type:"renderViseme",generation:L,to:g,from:v.from,transitionDurationMs:v.transitionDurationMs,gapMs:v.gapMs,threshold:v.threshold},void 0,!0)}catch(N){if(N?.name==="AbortError")return;throw N}},dispose(){if(!a){a=!0,i+=1,d.reset(),f.clear(),c.forEach(({resolve:M})=>M(null)),c.clear(),u=Ot;for(const[,M]of T)M.resolve();T.clear(),n?.terminate(),n=null}}}}async function Eo(e,t){const r=await vo(e,{onWebGlContextLost:t.onWebGlContextLost,maxTextureSize:t.pixiMaxTextureSize});let n=0,s=null,i=!1;const o=Wr(async(a,l)=>i?!1:t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()?(qe()?.onWorkerStale(),!1):(r.drawLiveFrame(a),o.isLatestSeq(l)));return{usesWorker:!1,usesPixi:!0,async loadAssets(a){if(i)return{width:842,height:1264};const l=await r.loadAssets(a);return s=l.visemePaths?.length?new Set(l.visemePaths):null,l},getVisemeAtlasPaths(){return s},async renderSilOnly(){i||(n+=1,o.reset(),r.renderSilOnly())},drawLiveFrame(a,l,f,c){i||o.enqueue(l,f,c)},async compositePrefetchFrame(a,l){if(i)return null;try{return await r.compositePrefetchFrame(a)}catch{return null}},blitBitmap(a,l){i||t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()||r.blitBitmap(l)},drawIdleVideo(a,l){i||r.drawIdleVideo(a,l)},beginIdleToSilCrossfade(a){i||r.beginIdleToSilCrossfade(a)},tickIdleToSilCrossfade(a){return i?!1:r.tickIdleToSilCrossfade(a)},finishIdleToSilCrossfade(){i||r.finishIdleToSilCrossfade()},isIdleToSilCrossfading(){return i?!1:r.isIdleToSilCrossfading()},supportsBufferedPlayback:!1,async renderViseme(a,l){if(i)return;n+=1;const f=n;o.reset(),t.onStatus?.(l),r.drawLiveFrame({mouthPath:_t(l),expressionPath:null})},dispose(){i||(i=!0,n+=1,o.reset(),r.dispose())}}}function gn(e,t){if(!To())return wn(e,t);const r=bs(t.workerScriptUrl);try{return ko(e,t,r)}catch{return wn(e,t)}}function Io(e,t={}){if(t.preferLegacyRenderer)return gn(e,t);let r=null,n=!1,s=null,i=!1;const o=()=>r?Promise.resolve(r):(s||(s=(async()=>{try{const a=await Eo(e,t);if(i)throw a.dispose(),new Error("Renderer disposed during Pixi init");return n=!0,r=a,a}catch(a){if(i)throw a instanceof Error?a:new Error(String(a));return console.warn("[AiTwin] Pixi WebGL renderer unavailable, using Canvas2D fallback:",a),r=gn(e,t),n=!1,r}})()),s);return{get usesWorker(){return r?.usesWorker??!1},get usesPixi(){return n},async loadAssets(a){return(await o()).loadAssets(a)},getVisemeAtlasPaths(){return r?.getVisemeAtlasPaths()??null},async renderSilOnly(){return(await o()).renderSilOnly()},drawLiveFrame(a,l,f,c){o().then(u=>{i||u.drawLiveFrame(a,l,f,c)})},async compositePrefetchFrame(a,l){return(await o()).compositePrefetchFrame(a,l)},blitBitmap(a,l){r&&r.blitBitmap(a,l)},drawIdleVideo(a,l){r?.usesPixi&&r.drawIdleVideo(a,l)},beginIdleToSilCrossfade(a){r?.beginIdleToSilCrossfade(a)},tickIdleToSilCrossfade(a){return r?.tickIdleToSilCrossfade(a)??!1},finishIdleToSilCrossfade(){r?.finishIdleToSilCrossfade()},isIdleToSilCrossfading(){return r?.isIdleToSilCrossfading()??!1},get supportsBufferedPlayback(){return r?.supportsBufferedPlayback??!1},async renderViseme(a,l,f){return(await o()).renderViseme(a,l,f)},dispose(){i=!0,s=null,r?.dispose(),r=null}}}class Ao extends Error{constructor(t,r){super(`${t} timed out after ${Math.round(r/1e3)}s`),this.name="LoadTimeoutError"}}function dr(e,t,r){return new Promise((n,s)=>{const i=globalThis.setTimeout(()=>{s(new Ao(r,t))},t);e.then(o=>{globalThis.clearTimeout(i),n(o)},o=>{globalThis.clearTimeout(i),s(o)})})}const Po={position:"relative",display:"inline-block",lineHeight:0},xo={display:"block",maxWidth:"100%",height:"auto"},Mo={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 gt(e){return As(e)?Mr:xr}function Ue(e,t){return t?.isSpeaking()?t:e.isSpeaking()?e:t?.isConnected()?t:e}const Ro=b.forwardRef(function({id:t,avatarId:r,faceId:n,assets:s,authToken:i,apiBase:o,ttsEngineId:a=Xe,voiceId:l,speakingRate:f=kt,className:c,style:u,canvasStyle:d,onStatusChange:p,onDisplayStatus:T,onError:m,onReady:A,showErrorOverlay:x=!0,onUserTranscript:M,onBotOutput:g,onRealtimeLipsyncDebug:v,onFrameRenderStats:L,lipsyncBufferedPlayback:N=!0,lipsyncPlaybackWarmupMs:Q=1500,pixiMaxTextureSize:fe},P){const C=b.useRef(null),V=b.useRef(null),Y=b.useRef(null),D=b.useRef(N);D.current=N;const j=b.useRef(Q);j.current=Q;const $=b.useRef(0),z=b.useRef(Vr()),E=b.useRef(Wi()),Z=b.useRef(!1),me=b.useRef(0),ee=b.useRef(0),oe=b.useRef(0),se=b.useRef(null),_e=b.useRef(null),he=b.useRef(!1),y=b.useRef(!1),B=b.useRef(!1),H=b.useRef(!1),X=b.useRef("sil"),R=b.useRef(0),F=b.useRef(!1),w=b.useRef(null),_=b.useRef(!1),[k,le]=b.useState("idle"),[ie,ge]=b.useState(null),[be,Te]=b.useState(!1),[mt,rt]=b.useState(!1),[er,$r]=b.useState({width:842,height:1264}),[Hr,h]=b.useState(0),I=b.useRef(!1),G=b.useRef(i??null),J=o?.trim()||vt,ye=b.useRef(null),te=b.useRef(null),de=b.useRef(!1),Ae=b.useRef({width:842,height:1264}),ke=b.useRef(null),Et=b.useRef("sil"),Ge=b.useRef(void 0),Le=b.useRef(new Map),ht=b.useRef(A),pe=b.useRef(T),It=b.useRef(m),$e=b.useRef(M),pt=b.useRef(g),ze=b.useRef(v),nt=b.useRef(L),we=b.useRef(eo());ht.current=A,pe.current=T,It.current=m,$e.current=M,pt.current=g,ze.current=v,nt.current=L,b.useEffect(()=>{const S=we.current;return an(S),()=>an(null)},[]);const Pe=b.useCallback(S=>{const O=S?.ttsEngineId??a??te.current?.ttsEngineId??Xe,W=S?.voiceId??l??te.current?.voiceId??gt(O);return{ttsEngineId:O,voiceId:W}},[a,l]),Se=b.useCallback(S=>{ge(S),It.current?.(S)},[]),Oe=b.useCallback(S=>{le(S),p?.(S)},[p]),ne=b.useRef(gr(Oe,J,{onLipsyncDebug:S=>ze.current?.(S)})),ae=b.useRef(null);b.useEffect(()=>(ne.current=gr(Oe,J,{onLipsyncDebug:S=>ze.current?.(S)}),i&&ne.current.setDeveloperToken(i),ne.current.setTtsEngineId(a),()=>{ne.current.stop();const S=ae.current;S&&(S.disconnect(),ae.current=null)}),[Oe,i,a,J]);const At=b.useCallback(()=>(ae.current||(ae.current=Yn(Oe,{speakingRate:f,onUserTranscript:(S,O)=>$e.current?.(S,O),onBotOutput:S=>pt.current?.(S),onLipsyncDebug:S=>ze.current?.(S)}),ae.current.setTtsEngineId(a)),ae.current),[Oe,f,a]),xe=b.useCallback(()=>{se.current!=null&&(clearTimeout(se.current),se.current=null)},[]),Me=b.useCallback(()=>{_e.current!=null&&(clearTimeout(_e.current),_e.current=null),B.current=!1},[]),Pt=b.useCallback(()=>{if(H.current)return;const S=V.current;if(H.current=!0,Me(),he.current=!1,y.current=!1,!_.current||!w.current){S?.renderSilOnly();return}w.current.setActive(!1);const O=w.current.getVideo();if(S?.usesPixi){S.drawIdleVideo(O),S.beginIdleToSilCrossfade(cn);return}(async()=>{const W=C.current;W&&await lr(W,O,null,void 0,Ae.current).catch(()=>{}),await new Promise(U=>setTimeout(U,cn)),H.current&&await S?.renderSilOnly()})()},[Me]),Kr=b.useCallback(()=>{!_.current||!w.current||(H.current=!1,V.current?.finishIdleToSilCrossfade(),Me(),B.current=!0,_e.current=setTimeout(()=>{if(_e.current=null,Ue(ne.current,ae.current).isSpeaking()){B.current=!1;return}if(he.current){B.current=!1;return}const S=w.current;if(!S){B.current=!1;return}S.restart(),S.setActive(!0);const O=S.getVideo();let W=!1;const U=()=>{W||(W=!0,O.removeEventListener("playing",q),B.current=!1)},q=()=>{U()};if(O.addEventListener("playing",q,{once:!0}),O.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!O.paused){U();return}setTimeout(U,300)},to))},[Me]),st=b.useCallback(()=>{if(xe(),pe.current?.("sil"),he.current=!1,H.current=!1,!_.current||!w.current){B.current=!1;return}Kr()},[xe,Kr]),tr=b.useCallback(async S=>{const O=ee.current,W=V.current,U=C.current,q=()=>{S?.fallbackOnly||(he.current=!1)};if(!W||!U||!be){q();return}if(Ue(ne.current,ae.current).isSpeaking()){q();return}if(S?.fallbackOnly){if(!he.current||(Ce(z.current),await W.renderSilOnly(),O!==ee.current)||Ue(ne.current,ae.current).isSpeaking())return;st();return}if(ln(z.current)){Ce(z.current),st();return}if(D.current&&W.supportsBufferedPlayback&&!S?.fallbackOnly){if(Ce(z.current),Y.current?.reset(),await W.renderSilOnly(),O!==ee.current||Ue(ne.current,ae.current).isSpeaking())return;st();return}const Ie=oo(Ue(ne.current,ae.current).getVisemeQueue(),R.current,X.current),Ne=ao(Ie,ke.current);if(Ce(z.current),!Ne){if(await W.renderSilOnly(),O!==ee.current||Ue(ne.current,ae.current).isSpeaking())return;st();return}try{await W.renderViseme(U,"sil",{from:Ie,transitionDurationMs:us})}catch(Ve){if(Ve?.name!=="AbortError")throw Ve}if(O!==ee.current){q();return}if(Ue(ne.current,ae.current).isSpeaking()){q();return}st()},[be,st]),it=b.useCallback(()=>{if(y.current||he.current)return;y.current=!0,he.current=!0,xe();const S=ee.current;tr().finally(()=>{y.current=!1}),se.current=setTimeout(()=>{se.current=null,S===ee.current&&tr({fallbackOnly:!0})},uo())},[xe,tr]),qr=b.useCallback(async()=>{const S=Math.floor(Date.now()/1e3);if(ye.current&&S<ye.current.expiresAt-60)return ye.current.key;const O=G.current??await Nt();G.current=O,ne.current.setDeveloperToken(O);const W=await Fs(O,J);return ye.current={key:W.value,expiresAt:W.expiresAt},W.value},[J]);b.useEffect(()=>{i&&(G.current=i,ne.current.setDeveloperToken(i))},[i]),b.useEffect(()=>{ne.current.setTtsEngineId(a),ae.current?.setTtsEngineId(a),te.current&&(te.current.ttsEngineId=a)},[a]),b.useEffect(()=>{const S=l??gt(a);te.current&&(te.current.voiceId=S)},[l,a]);const je=b.useMemo(()=>Tn(r,n),[r,n]),hs=b.useMemo(()=>s?JSON.stringify(s):je?`face:${je}`:`id:${t??""}`,[s,je,t]);return b.useEffect(()=>{if(!t&&!s&&!je){Se("AiTwin requires `assets`, `avatarId`/`faceId`, or `id`");return}let S=!1;de.current=!1,Te(!1),rt(!1),ge(null),te.current=null,w.current=null,_.current=!1;const O=C.current;if(!O)return;let W=null;return(async()=>{try{let U,q,Ee,Ie;if(s)U=s,q=a,Ee=l??gt(q);else if(je)pe.current?.("loadFace"),U=pr(je),q=a,Ee=l??gt(q);else{pe.current?.("getAiTwin");const K=await dr(bn(t),3e4,"getAiTwin");if(S)return;if(!K.faceId?.trim())throw new Error("getAiTwin returned no faceId");q=K.ttsEngineId,Ee=l??K.voiceId??gt(q),Ie=K.knowledgeContextId?.trim()||void 0,U=pr(K.faceId)}te.current={ttsEngineId:q,voiceId:Ee,personaId:Ie},kn(U),w.current=$i();const Ne=!!(s&&!et(s));W=Io(O,{preferLegacyRenderer:Ne,pixiMaxTextureSize:fe,onStatus:K=>{S||pe.current?.(K)},onError:K=>{S||Se(K)},shouldAcceptLiveFrame:()=>{if(Z.current)return!0;if(he.current)return!1;const K=Ue(ne.current,ae.current);return K.isSpeaking()||K.isWarmingUp()},onWebGlContextLost:()=>{S||I.current||(I.current=!0,queueMicrotask(()=>{S||h(K=>K+1)}))}}),V.current=W,Y.current=ho(W),I.current=!1,(K=>{const re=()=>{if(!K||j.current<=0)return!1;const ve=V.current;return ve?D.current&&ve.supportsBufferedPlayback?!0:ve.usesPixi:!1};ne.current.setLipsyncWarmupHold(K,re),ae.current?.setLipsyncWarmupHold(K,re)})(!0),pe.current?.("auth");const He=G.current??i??await dr(Nt(),3e4,"getAuthToken");if(S)return;G.current=He,ne.current.setDeveloperToken(He),ne.current.setTtsEngineId(q);const xt=bt()?await qr():void 0;if(S)return;pe.current?.("loadAssets");const ue=await dr(W.loadAssets(xt),18e4,"load face assets");if(!S&&ue.width>0&&ue.height>0&&(Ae.current=ue,$r(ue)),S)return;ke.current=W.getVisemeAtlasPaths(),de.current=!0,Te(!0),ht.current?.(),W.renderSilOnly().then(()=>{S||pe.current?.("sil")}),(async()=>{try{const K=await yi(xt);if(await w.current.load(K),S)return;_.current=!0,rt(!0),Ce(z.current),w.current.setActive(!0);const re=w.current.getVideo();W.usesPixi?W.drawIdleVideo(re):await lr(O,re,null,void 0,Ae.current),S||pe.current?.("idle")}catch(K){if(S)return;_.current=!1,rt(!1),console.warn("[AiTwin] Idle video unavailable:",K)}})()}catch(U){if(S)return;if(U instanceof Vt){Se(U.message);return}Se(U instanceof Error?U.message:"Failed to load AI twin")}})(),()=>{S=!0,w.current?.dispose(),W?.dispose(),V.current=null}},[hs,s,je,t,a,l,i,fe,qr,Se,Hr]),b.useEffect(()=>{const S={current:!1},O=()=>{const W=C.current,U=V.current,q=Ue(ne.current,ae.current);if(!W||!U||!be){me.current=requestAnimationFrame(O);return}if(document.hidden){w.current?.setActive(!1),me.current=requestAnimationFrame(O);return}const Ee=q.isSpeaking(),Ie=q.isWarmingUp(),Ne=Ee&&!F.current,Ve=F.current&&!Ee;Ne&&(xe(),Me(),he.current=!1,y.current=!1,Ce(z.current),Y.current?.reset(),we.current.reset(),Ge.current=void 0,oe.current=0,X.current="sil",R.current=0,Pt()),Ve&&(E.current.reset(),w.current?.setActive(!1),Y.current?.reset(),U.finishIdleToSilCrossfade(),it());const He=Ve||he.current||B.current||H.current,xt=N&&U.supportsBufferedPlayback;if(Ie){if(Pt(),_.current&&w.current?.setActive(!1),U.usesPixi&&U.isIdleToSilCrossfading()){U.tickIdleToSilCrossfade(performance.now()),q.getVisemeQueue().length>0&&$.current===0&&($.current=performance.now()),F.current=q.isSpeaking(),me.current=requestAnimationFrame(O);return}const ue=q.getVisemeQueue();ue.length>0&&$.current===0&&($.current=performance.now()),E.current.advance(performance.now());const K=E.current.getExpressionPath(),re=E.current.getDrawKey(),ve={queue:ue,elapsedMs:0,expressionPath:K,expressionDrawKey:re,availablePaths:ke.current};let Re=!1;xt&&Y.current?(Y.current.warmupTick(ve),Re=Y.current.isWarmupReady(ve,j.current)):U.usesPixi&&fn(W,ue,0,K,re,z.current,(ot,Mt,rr,Rt)=>{U.drawLiveFrame(ot,Mt,rr,Fe=>{Rt?.(Fe),q.isWarmingUp()&&Fe!==""&&!ln({lastDrawnKey:Fe})&&($.current=0,q.releaseWarmupHold())})},ot=>{pe.current?.(ot.label)},ke.current);const Ke=$.current>0&&performance.now()-$.current>ks;pe.current?.(Re?"buffer ready":"buffering lipsync…"),(Re||Ke)&&($.current=0,q.releaseWarmupHold()),F.current=q.isSpeaking(),me.current=requestAnimationFrame(O);return}if(Ee){Pt(),_.current&&w.current?.setActive(!1),U.usesPixi&&U.isIdleToSilCrossfading()&&U.finishIdleToSilCrossfade();const ue=q.getVisemeQueue(),K=q.getPlaybackElapsedMs();si(ue,K),R.current=K;const re=ue.length>0?Tt(ue,K):"sil";re!=="sil"&&(X.current=re);const ve=Gi(K,q.getSentenceEmotions(),q.getWordQueue(),q.getStreamMood());E.current.setTargetEmotion(ve),E.current.setTargetEyebrow(Ui(K,q.getWordQueue(),Je(ve))),E.current.advance(performance.now());const Re=E.current.getExpressionPath(),Ke=E.current.getDrawKey(),ot=(Fe,nr,wt,sr)=>{const Gr={label:Et.current,elapsedMs:K,mouthPath:nr.mouthPath,transitionMeta:Ge.current};Le.current.set(wt,Gr),Y.current?.markExternallyDisplayed(wt,Re),U.drawLiveFrame(Fe,nr,wt,Ct=>{const ps=Le.current.get(Ct)??Gr;Le.current.delete(Ct),we.current.onDrawDisplayed(ps,ue),Y.current?.markExternallyDisplayed(Ct,Re),sr?.(Ct)})},Mt=Fe=>{Et.current=Fe.label,Ge.current=Fe.transitionMeta,pe.current?.(Fe.label)},rr=N&&U.supportsBufferedPlayback;let Rt=!1;rr&&Y.current&&(Rt=Y.current.tick({canvas:W,queue:ue,elapsedMs:K,expressionPath:Re,expressionDrawKey:Ke,playbackState:z.current,availablePaths:ke.current,onStatus:Mt,onDisplayed:(nr,wt,sr)=>{we.current.onDrawDisplayed({label:sr,elapsedMs:K,mouthPath:wt,transitionMeta:Ge.current},ue)}}).usedBuffer),Rt||fn(W,ue,K,Re,Ke,z.current,ot,Mt,ke.current),we.current.onRafTickSpeaking(),nt.current?.(we.current.snapshot(ue,K))}else if(!He&&Z.current){w.current?.setActive(!1),E.current.advance(performance.now());const ue=E.current.getExpressionPath(),K=E.current.getDrawKey();fr(W,ue,K,z.current,(re,ve,Re,Ke)=>U.drawLiveFrame(re,ve,Re,Ke),re=>{const ve=E.current.getTargetEmotion();pe.current?.(re==="sil"||re==="blink"?`${ve}${re==="blink"?" · blink":""}`:re)})}else if(!He&&_.current&&w.current){const ue=w.current,K=ue.getVideo();if(K.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA){ue.setActive(!0);const re=()=>!q.isSpeaking()&&!he.current&&!B.current&&!Z.current;U.usesPixi?(U.drawIdleVideo(K,re),re()&&pe.current?.("idle")):S.current||(S.current=!0,lr(W,K,null,void 0,Ae.current,re).then(()=>pe.current?.("idle")).finally(()=>{S.current=!1}))}else fr(W,null,"__sil__",z.current,(re,ve,Re,Ke)=>U.drawLiveFrame(re,ve,Re,Ke),re=>pe.current?.(re))}else He||fr(W,null,"__sil__",z.current,(ue,K,re,ve)=>U.drawLiveFrame(ue,K,re,ve),ue=>pe.current?.(ue));F.current=Ee,me.current=requestAnimationFrame(O)};return me.current=requestAnimationFrame(O),()=>{cancelAnimationFrame(me.current),xe(),Me(),ne.current.stop(),ae.current?.stop()}},[be,mt,it,xe,Me,Pt]),b.useImperativeHandle(P,()=>({speakText:async(S,O)=>{const W=S.trim();if(!W)return;const U=V.current;if(!U||!de.current&&!be)throw new Error("AI twin is not ready");const{ttsEngineId:q,voiceId:Ee}=Pe(O);ge(null),ee.current+=1,xe(),Me(),he.current=!1,y.current=!1,X.current="sil",R.current=0,Ce(z.current),Z.current=!1,E.current.reset(),F.current=!1,H.current=!1,U.finishIdleToSilCrossfade();try{await ne.current.speak(W,{voiceId:Ee,speakingRate:O?.speakingRate??f,ttsEngineId:q})}catch(Ie){throw H.current=!1,U.finishIdleToSilCrossfade(),it(),Se(Ie instanceof Error?Ie.message:"TTS failed"),Ie}},stop:()=>{ee.current+=1,xe(),Me(),he.current=!1,y.current=!1,H.current=!1,V.current?.finishIdleToSilCrossfade(),ne.current.stop(),ae.current?.stop(),Ce(z.current),Y.current?.reset(),$.current=0,F.current=!1,it()},setTtsEngineId:S=>{ne.current.setTtsEngineId(S),ae.current?.setTtsEngineId(S),te.current&&(te.current.ttsEngineId=S)},renderViseme:async(S,O)=>{const W=V.current,U=C.current;if(!W||!U||!de.current)throw new Error("AI twin is not ready");await W.renderViseme(U,S,{from:O?.from,transitionDurationMs:O?.transitionDurationMs,gapMs:O?.gapMs,onStatus:q=>pe.current?.(q)})},setEmotion:S=>{if(!de.current)throw new Error("AI twin is not ready");const O=V.current,W=C.current;if(!O||!W)throw new Error("AI twin is not ready");ee.current+=1,xe(),Me(),he.current=!1,y.current=!1,B.current=!1,H.current=!1,ne.current.stop(),ae.current?.stop(),Ce(z.current),Y.current?.reset(),$.current=0,F.current=!1,O.finishIdleToSilCrossfade(),w.current?.setActive(!1);const U=We(S);Z.current=!0,E.current.snapToEmotion(U);const q=E.current.getExpressionPath(),Ee=E.current.getDrawKey(),Ie={mouthPath:null,expressionPath:q},Ne=Zt(Ie,Ee);z.current.lastDrawnKey="",O.drawLiveFrame(W,Ie,Ne,He=>{He===Ne&&(z.current.lastDrawnKey=Ne)});const Ve=Je(U);pe.current?.(q?`${U} · brow ${Ve>0?"+":""}${Ve}`:`${U} · brow 0 (sil base)`)},getEmotion:()=>Z.current?E.current.getTargetEmotion():null,clearEmotionPreview:()=>{Z.current=!1,E.current.reset(),Ce(z.current),pe.current?.("idle")},isReady:()=>de.current,getStatus:()=>k,connect:async(S={})=>{ge(null);try{let O=S.authToken?.trim();O||(O=G.current??i??await Nt(),G.current=O);const W=S.voiceId?.trim()??l??te.current?.voiceId,U=S.personaId?.trim()??te.current?.personaId,q=At();await q.unlockAudio(),await q.connect({...S,apiBase:S.apiBase?.trim()||J,authToken:O,voiceId:W,personaId:U,speakingRate:S.speakingRate??f})}catch(O){throw Se(O instanceof Error?O.message:"Voice connect failed"),O}},disconnect:async()=>{const S=ae.current;S&&(ee.current+=1,xe(),Me(),he.current=!1,y.current=!1,H.current=!1,V.current?.finishIdleToSilCrossfade(),Ce(z.current),Y.current?.reset(),$.current=0,F.current=!1,await S.disconnect(),it())},isConnected:()=>ae.current?.isConnected()??!1,getVoiceStream:()=>ae.current?.getVoiceStream()??ne.current?.getVoiceStream()??null}),[be,f,i,l,xe,Me,it,Se,Pe,At,k,J]),ir.jsxs("div",{className:c,style:{...Po,...u},children:[ir.jsx("canvas",{ref:C,width:er.width,height:er.height,style:{...xo,...d},"aria-label":t?`AI twin ${t}`:"AI twin face"}),x&&ie?ir.jsx("div",{style:Mo,children:ie}):null]})}),ds="thumbnail.webp";function Co(e,t=Tr){const r=e.trim();if(!r)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${r}/${ds}`}const Do=new Set([".png",".jpg",".jpeg",".webp"]);function ms(e=".png"){const t=e.toLowerCase();return Do.has(t)?`source${t}`:"source.png"}function Fo(e,t=Tr,r=".png"){const n=e.trim();if(!n)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${n}/${ms(r)}`}exports.AI_TWIN_THUMBNAIL_FILENAME=ds;exports.AiTwin=Ro;exports.AiTwinNotFoundError=Vt;exports.CONSONANT_SKIP_GROUP=Fn;exports.DEFAULT_TTS_ENGINE_ID=Xe;exports.EMOTION_TO_EYEBROW=ns;exports.EXPRESSION_ATLAS_EMOTIONS=Ri;exports.MIC_SAMPLE_RATE=Rr;exports.OCULUS_VISEME_IDS=Mn;exports.PLAYBACK_SAMPLE_RATE=Cr;exports.SPEAKING_RATE_MAX=Xt;exports.SPEAKING_RATE_MIN=Qt;exports.TTS_ENGINE_CARTESIA=Ze;exports.TTS_ENGINE_GOOGLE=Er;exports.TTS_ENGINE_INWORLD=Gt;exports.VISEME_IDS=_i;exports.VISEME_SKIP_GAP_MS=wr;exports.VISEME_TEST_CARTESIA_VOICE_ID=Mr;exports.VISEME_TEST_SPEAKING_RATE=kt;exports.VISEME_TEST_VOICE_ID=xr;exports.VOWEL_SKIP_GROUP=Dn;exports.aiTwinSourceFilename=ms;exports.buildPlaybackTransitions=Un;exports.buildPlaybackVisemeSeries=jt;exports.buildRawVisemeTransitions=Vn;exports.buildVoiceWebSocketUrl=jn;exports.compactSkippableVisemes=Ar;exports.createAvatarTtsLipsyncController=gr;exports.createRealtimeVoiceLipsyncController=Yn;exports.emotionToEyebrow=Je;exports.faceAssetUrls=pr;exports.fetchAiTwin=bn;exports.fetchDevAuthToken=Nt;exports.getAiTwinSourceUrl=Fo;exports.getAiTwinThumbnailUrl=Co;exports.listSkippedVisemeEntries=Yt;exports.listSkippedVisemes=Qs;exports.normalizeOculusViseme=Ir;exports.normalizeStreamEmotion=We;exports.resolveDirectFaceId=Tn;exports.resolveVisemeAtTime=Tt;exports.resolveVisemeFromPlaybackSeries=Ln;exports.resolveWordAtTime=ii;exports.setActiveTwinAssets=kn;
47
+ `;function En(e=5){const t=new fe.Filter({glProgram:fe.GlProgram.from({vertex:Fo,fragment:Bo,name:"threshold-key-filter"}),resources:{thresholdUniforms:{uThreshold:{value:e/255,type:"f32"}}}});return t.setThreshold=r=>{t.resources.thresholdUniforms.uniforms.uThreshold=r/255},t.getThreshold=()=>Math.round(t.resources.thresholdUniforms.uniforms.uThreshold*255),t}function Lo(e,t,r){return e>r||t>r}function Oo(e){return e?e.getParameter(e.MAX_TEXTURE_SIZE):4096}function No(e,t){return t!=null&&t>0?Math.floor(t):Oo(e)}function Vo(e,t){const r=document.createElement("canvas");r.width=t.w,r.height=t.h;const n=r.getContext("2d");if(!n)throw new Error("Failed to acquire 2d context for atlas cell crop");return n.drawImage(e,t.x,t.y,t.w,t.h,0,0,t.w,t.h),r}function Es(e){return fetch(e).then(t=>{if(!t.ok)throw new Error(`Failed to load image: ${e}`);return t.blob()}).then(t=>createImageBitmap(t))}async function kn(e,t,r,n,s){const i=r?await ds(e,s):await fetch(e).then(u=>{if(!u.ok)throw new Error(`Failed to load ${e}`);return u.json()}),o=i.sheets?.[0];if(!o?.path)throw new Error(`${e} has no sheets[0].path`);const a=new Map,l=async(u,d)=>{const p=ps(t,d,r),E=r?await Ii(await vt(p,s??"")):await Es(p);if(Lo(E.width,E.height,n))return console.info(`[PixiVisemeCompositor] Sheet ${d} (${E.width}x${E.height}) exceeds MAX_TEXTURE_SIZE=${n}; using per-cell crops.`),{mode:"cells",bitmap:E};const h=fe.Texture.from(E);return h.source.scaleMode="nearest",{mode:"full",texture:h}};a.set(o.index,await l(o.index,o.path));for(const u of i.sheets??[])u.index!==o.index&&u.path&&a.set(u.index,await l(u.index,u.path));const f=new Map;for(const u of i.cells??[])u.path&&f.set(u.path,u);const c=new Map;for(const u of i.sheets??[])c.set(u.index,u);return{atlasMeta:i,cellByPath:f,sheetByIndex:c,diffBase:t,sheets:a}}async function Uo(e,t={}){const r=new fe.Application;try{await r.init({canvas:e,width:842,height:1264,preference:"webgl",autoStart:!1,antialias:!1,resolution:1,autoDensity:!1,backgroundAlpha:1,backgroundColor:0,powerPreference:"high-performance"})}catch(_){throw r.destroy({removeView:!1}),_}if(r.renderer.type!==fe.RendererType.WEBGL)throw r.destroy({removeView:!1}),new Error("Pixi did not initialize a WebGL renderer");const n=No(r.renderer.gl,t.maxTextureSize);let s=!1,i=!1,o=!1;const a=r.canvas,l=()=>{s=!0,!i&&!o&&(i=!0,t.onWebGlContextLost?.())};a.addEventListener("webglcontextlost",l);const f=new fe.Sprite(fe.Texture.EMPTY),c=new fe.Sprite(fe.Texture.EMPTY);c.visible=!1,c.alpha=0;const u=new fe.Sprite(fe.Texture.EMPTY),d=new fe.Sprite(fe.Texture.EMPTY),p=new fe.Sprite(fe.Texture.EMPTY);p.visible=!1;const E=En(5),h=En(5);u.filters=[E],d.filters=[h],r.stage.addChild(f),r.stage.addChild(c),r.stage.addChild(u),r.stage.addChild(d),r.stage.addChild(p);let x=null,R=null,P=null,w=null,k=null,B=null,I=null,Z=842,he=1264,A=null,V=5;const K=new Map;let Q=null,j=null,me=null;const F=On(()=>{K.clear(),B=null,I=null,x=null,A=null}),J=(_,y)=>{Z=_,he=y,r.renderer.resize(_,y),(e.width!==_||e.height!==y)&&(e.width=_,e.height=y)},b=()=>{u.visible=!1,d.visible=!1,u.texture=fe.Texture.EMPTY,d.texture=fe.Texture.EMPTY,Q=null,j=null},z=()=>{k=null,c.visible=!1,c.alpha=0},ne=()=>{f.visible=!0,f.width=Z,f.height=he,f.x=0,f.y=0},ae=()=>{c.width=Z,c.height=he,c.x=0,c.y=0},te=()=>{p.visible=!1,z(),x&&(f.texture=x,ne())},ee=()=>{!k&&!c.visible||te()},ie=_=>{if(o||s||!x){te();return}const y=Math.max(0,_);if(!R||y<=0){te();return}b(),p.visible=!1,f.texture=R,ne(),c.texture=x,ae(),c.alpha=0,c.visible=!0,k={startMs:performance.now(),durationMs:y},$()},we=(_=performance.now())=>{if(!k)return!1;if(o||s)return ee(),!1;const y=(_-k.startMs)/Math.max(1,k.durationMs);if(y>=1)return te(),$(),!1;const T=Math.min(1,Math.max(0,y)),O=T*T*(3-2*T);return c.alpha=O,c.visible=!0,$(),!0},S=()=>{if(P){try{P.resource=null}catch{}P.destroy(),P=null}R&&(R.destroy(!1),R=null),w=null},U=(_,y)=>{const T=_.cellByPath.get(y);if(!T||!fs(T))return null;const O=`${_.diffBase}::${y}`;let Y=K.get(O);if(!Y){const re=T.sheet??0,Ee=_.sheets.get(re);if(!Ee)return null;let ye,Ne=!1;if(Ee.mode==="full")ye=new fe.Texture({source:Ee.texture.source,frame:new fe.Rectangle(T.x,T.y,T.w,T.h)});else{const Ve=Vo(Ee.bitmap,T);ye=fe.Texture.from(Ve),ye.source.scaleMode="nearest",Ne=!0}Y={texture:ye,ownsSource:Ne},K.set(O,Y)}return{texture:Y.texture,cell:T}},W=(_,y,T,O)=>{if(!O||!T)return _.visible=!1,_.texture=fe.Texture.EMPTY,!1;const Y=U(T,O);if(!Y)return _.visible=!1,_.texture=fe.Texture.EMPTY,!1;const re=ls(T.atlasMeta,Y.cell);return y.setThreshold(V),_.texture=Y.texture,_.x=re.x,_.y=re.y,_.width=Y.cell.w,_.height=Y.cell.h,_.visible=!0,!0},G=_=>{if(!_||!B)return null;const y=A??new Set(B.cellByPath.keys());return Hr(_,y)??(y.has(_)?_:null)},L=_=>{p.visible=!1,te();const y=G(_.mouthPath),T=_.expressionPath&&I?qr(_.expressionPath,O=>I.cellByPath.has(O)):null;y!==Q?(W(u,E,B,y),Q=y):y?u.visible=!0:u.visible=!1,T!==j?(W(d,h,I,T),j=T):T?d.visible=!0:d.visible=!1},$=()=>{if(!(o||s||document.hidden))try{r.render()}catch{}};return{async loadAssets(_){if(o)return{width:Z,height:he,visemePaths:[]};const y=Rt();if(y&&!_)throw new Error("Encrypted assets enabled but no key provided");const T=y?await xi(await vt(Xt(),_??"")):await Es(Xt());x&&x.destroy(!0),x=fe.Texture.from(T),x.source.scaleMode="linear";const O=Nn();B=await kn(Vn(),O,y,n,_);try{I=await kn(Un(),O,y,n,_)}catch(re){console.warn("[PixiVisemeCompositor] expression atlas unavailable:",re),I=null}const Y=cs(B.atlasMeta,T.width,T.height);return J(Y.width,Y.height),V=cr(B.atlasMeta),E.setThreshold(V),h.setThreshold(V),A=new Set(B.cellByPath.keys()),K.clear(),te(),b(),$(),{width:Y.width,height:Y.height,visemePaths:[...B.cellByPath.keys()]}},getVisemeAtlasPaths(){return A},drawLiveFrame(_){o||s||!x||(ee(),L(_),$())},drawIdleVideo(_,y){o||s||document.hidden||y&&!y()||_.readyState<HTMLMediaElement.HAVE_CURRENT_DATA||(z(),b(),p.visible=!1,!R||w!==_?(S(),P=new fe.VideoSource({resource:_,autoPlay:!1,updateFPS:0}),P.autoUpdate=!1,R=new fe.Texture({source:P}),w=_):R.source.update(),f.texture=R,ne(),$())},beginIdleToSilCrossfade:ie,tickIdleToSilCrossfade:we,finishIdleToSilCrossfade:ee,isIdleToSilCrossfading(){return k!=null},renderSilOnly(){o||s||!x||(b(),te(),$())},blitBitmap(_){o||s||(ee(),b(),me&&(me.destroy(!0),me=null),me=fe.Texture.from(_),p.texture=me,p.visible=!0,p.x=0,p.y=0,p.width=Z,p.height=he,f.visible=!1,$())},async compositePrefetchFrame(_){return null},renderFrame:$,isContextLost(){return s},dispose(){if(o)return;o=!0,a.removeEventListener("webglcontextlost",l),F(),S(),me&&(me.destroy(!0),me=null);for(const{texture:y,ownsSource:T}of K.values())y.destroy(T);K.clear(),x&&(x.destroy(!0),x=null);const _=y=>{y.mode==="full"?y.texture.destroy(!0):y.bitmap.close()};if(B)for(const y of B.sheets.values())_(y);if(I)for(const y of I.sheets.values())_(y);E.destroy(!0),h.destroy(!0),r.destroy({removeView:!1},{children:!0,texture:!0})}}}const In=-1,zt=1e5,An=new Map;async function Wo(e){let t=An.get(e);if(!t){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch worker script (${r.status}): ${e}`);const n=await r.blob();t=URL.createObjectURL(n),An.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function $o(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function Mr(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const r=e.getContext("2d");r&&r.drawImage(t,0,0)}function xn(e,t){let r=0,n=null;const s=Xr(async(i,o)=>(await xr(),await Pr(e,i),s.isLatestSeq(o)));return{usesWorker:!1,usesPixi:!1,async loadAssets(i){const o=await xr(i);return n=o.visemePaths?new Set(o.visemePaths):null,o},getVisemeAtlasPaths(){return n},async renderSilOnly(){r+=1,s.reset(),await fo(e)},drawLiveFrame(i,o,a,l){s.enqueue(o,a,l)},async compositePrefetchFrame(i,o){try{return await lo(i)}catch{return null}},blitBitmap(i,o){Mr(i,o)},drawIdleVideo(){},beginIdleToSilCrossfade(){},tickIdleToSilCrossfade(){return!1},finishIdleToSilCrossfade(){},isIdleToSilCrossfading(){return!1},supportsBufferedPlayback:!0,async renderViseme(i,o){r+=1;const a=r;t.onStatus?.(o),await Pr(i,{mouthPath:yt(o),expressionPath:null})},dispose(){r+=1,s.reset()}}}function Ho(e,t,r){let n=null,s=1,i=0,o=!1,a=!1,l=null;const f=new Map,c=new Map;let u=zt;const d=Xr(async(P,w,k)=>(f.set(w,{drawKey:k.drawKey,onDisplayed:k.onDisplayed}),await x({type:"drawFrame",diffPath:P.mouthPath,expressionDiffPath:P.expressionPath,liveSeq:w},void 0,!0),!1)),p=P=>{if(P===In){let B=-1,I;for(const[Z,he]of f)Z>=B&&(B=Z,I=he);B>=0&&f.delete(B),I?.onDisplayed?.(I.drawKey);return}const w=-P,k=f.get(w);f.delete(w),k?.onDisplayed?.(k.drawKey)},E=new Map,h=(P,w)=>{a||!n||n.postMessage(P,[])},x=(P,w,k=!1)=>{if(a)return Promise.resolve();const B=s++;return new Promise((I,Z)=>{E.set(B,{resolve:he=>I(he),reject:Z,generation:typeof P.generation=="number"?P.generation:void 0,expectRenderDone:k}),h({...P,requestId:B})})},R=(async()=>{n=await Wo(r),n.onmessage=P=>{if(a)return;const w=P.data;if(w.type==="status"){t.onStatus?.(w.label);return}if(w.type==="frame"){if(w.generation<=-zt){const I=-w.generation,Z=c.get(I);c.delete(I),Z?.resolve(w.bitmap);return}const B=w.generation<0;if(!B&&w.generation!==i){w.bitmap.close();return}if(B&&!(w.generation===In)){const Z=-w.generation;if(!d.isLatestSeq(Z)){Xe()?.onWorkerStale(),w.bitmap.close();return}}if(B&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){Xe()?.onWorkerStale(),w.bitmap.close();return}Mr(e,w.bitmap),w.bitmap.close(),B&&p(w.generation);return}const k=E.get(w.requestId);if(k)switch(w.type){case"ready":E.delete(w.requestId),k.resolve();break;case"loadAssetsDone":E.delete(w.requestId),l=w.visemePaths?.length?new Set(w.visemePaths):null,k.resolve({width:w.width,height:w.height,visemePaths:w.visemePaths});break;case"renderDone":if(k.expectRenderDone&&k.generation!=null&&w.generation!==k.generation)return;E.delete(w.requestId),k.resolve();break;case"renderAborted":E.delete(w.requestId),k.resolve();break;case"error":E.delete(w.requestId),t.onError?.(w.message),k.reject(new Error(w.message));break}},n.onerror=P=>{if(a)return;const w=P.message||"Worker error";t.onError?.(w);for(const[,k]of E)k.reject(new Error(w));E.clear()},await x({type:"init"}),o=!0})();return{usesWorker:!0,usesPixi:!1,async loadAssets(P){if(a)return{width:842,height:1264};if(await R,a)return{width:842,height:1264};if(!o)throw new Error("Worker init failed");return await x({type:"loadAssets",keyHex:P,urls:Xs(ut())})??{width:842,height:1264}},getVisemeAtlasPaths(){return l},async renderSilOnly(){if(a||(await R,a))return;i+=1,d.reset(),f.clear(),c.forEach(({resolve:w})=>w(null)),c.clear(),u=zt;const P=i;await x({type:"cancelLiveDraws"}),await x({type:"renderSil",generation:P},void 0,!0)},drawLiveFrame(P,w,k,B){a||R.then(()=>{a||d.enqueue(w,k,B)})},compositePrefetchFrame(P,w){return a?Promise.resolve(null):R.then(()=>{if(a)return null;const k=++u;return new Promise(B=>{c.set(k,{resolve:B}),x({type:"drawFrame",diffPath:P.mouthPath,expressionDiffPath:P.expressionPath,liveSeq:k},void 0,!0).catch(()=>{c.delete(k),B(null)})})})},blitBitmap(P,w){Mr(P,w)},drawIdleVideo(){},beginIdleToSilCrossfade(){},tickIdleToSilCrossfade(){return!1},finishIdleToSilCrossfade(){},isIdleToSilCrossfading(){return!1},supportsBufferedPlayback:!0,async renderViseme(P,w,k={}){if(a||(await R,a))return;i+=1,d.reset(),f.clear();const B=i;try{await x({type:"cancelLiveDraws"}),await x({type:"renderViseme",generation:B,to:w,from:k.from,transitionDurationMs:k.transitionDurationMs,gapMs:k.gapMs,threshold:k.threshold},void 0,!0)}catch(I){if(I?.name==="AbortError")return;throw I}},dispose(){if(!a){a=!0,i+=1,d.reset(),f.clear(),c.forEach(({resolve:P})=>P(null)),c.clear(),u=zt;for(const[,P]of E)P.resolve();E.clear(),n?.terminate(),n=null}}}}async function Ko(e,t){const r=await Uo(e,{onWebGlContextLost:t.onWebGlContextLost,maxTextureSize:t.pixiMaxTextureSize});let n=0,s=null,i=!1;const o=Xr(async(a,l)=>i?!1:t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()?(Xe()?.onWorkerStale(),!1):(r.drawLiveFrame(a),o.isLatestSeq(l)));return{usesWorker:!1,usesPixi:!0,async loadAssets(a){if(i)return{width:842,height:1264};const l=await r.loadAssets(a);return s=l.visemePaths?.length?new Set(l.visemePaths):null,l},getVisemeAtlasPaths(){return s},async renderSilOnly(){i||(n+=1,o.reset(),r.renderSilOnly())},drawLiveFrame(a,l,f,c){i||o.enqueue(l,f,c)},async compositePrefetchFrame(a,l){if(i)return null;try{return await r.compositePrefetchFrame(a)}catch{return null}},blitBitmap(a,l){i||t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()||r.blitBitmap(l)},drawIdleVideo(a,l){i||r.drawIdleVideo(a,l)},beginIdleToSilCrossfade(a){i||r.beginIdleToSilCrossfade(a)},tickIdleToSilCrossfade(a){return i?!1:r.tickIdleToSilCrossfade(a)},finishIdleToSilCrossfade(){i||r.finishIdleToSilCrossfade()},isIdleToSilCrossfading(){return i?!1:r.isIdleToSilCrossfading()},supportsBufferedPlayback:!1,async renderViseme(a,l){if(i)return;n+=1;const f=n;o.reset(),t.onStatus?.(l),r.drawLiveFrame({mouthPath:yt(l),expressionPath:null})},dispose(){i||(i=!0,n+=1,o.reset(),r.dispose())}}}function Pn(e,t){if(!$o())return xn(e,t);const r=Bs(t.workerScriptUrl);try{return Ho(e,t,r)}catch{return xn(e,t)}}function qo(e,t={}){if(t.preferLegacyRenderer)return Pn(e,t);let r=null,n=!1,s=null,i=!1;const o=()=>r?Promise.resolve(r):(s||(s=(async()=>{try{const a=await Ko(e,t);if(i)throw a.dispose(),new Error("Renderer disposed during Pixi init");return n=!0,r=a,a}catch(a){if(i)throw a instanceof Error?a:new Error(String(a));return console.warn("[AiTwin] Pixi WebGL renderer unavailable, using Canvas2D fallback:",a),r=Pn(e,t),n=!1,r}})()),s);return{get usesWorker(){return r?.usesWorker??!1},get usesPixi(){return n},async loadAssets(a){return(await o()).loadAssets(a)},getVisemeAtlasPaths(){return r?.getVisemeAtlasPaths()??null},async renderSilOnly(){return(await o()).renderSilOnly()},drawLiveFrame(a,l,f,c){o().then(u=>{i||u.drawLiveFrame(a,l,f,c)})},async compositePrefetchFrame(a,l){return(await o()).compositePrefetchFrame(a,l)},blitBitmap(a,l){r&&r.blitBitmap(a,l)},drawIdleVideo(a,l){r?.usesPixi&&r.drawIdleVideo(a,l)},beginIdleToSilCrossfade(a){r?.beginIdleToSilCrossfade(a)},tickIdleToSilCrossfade(a){return r?.tickIdleToSilCrossfade(a)??!1},finishIdleToSilCrossfade(){r?.finishIdleToSilCrossfade()},isIdleToSilCrossfading(){return r?.isIdleToSilCrossfading()??!1},get supportsBufferedPlayback(){return r?.supportsBufferedPlayback??!1},async renderViseme(a,l,f){return(await o()).renderViseme(a,l,f)},dispose(){i=!0,s=null,r?.dispose(),r=null}}}class Go extends Error{constructor(t,r){super(`${t} timed out after ${Math.round(r/1e3)}s`),this.name="LoadTimeoutError"}}function vr(e,t,r){return new Promise((n,s)=>{const i=globalThis.setTimeout(()=>{s(new Go(r,t))},t);e.then(o=>{globalThis.clearTimeout(i),n(o)},o=>{globalThis.clearTimeout(i),s(o)})})}const zo={position:"relative",display:"inline-block",lineHeight:0},jo={display:"block",maxWidth:"100%",height:"auto"},Yo={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 It(e){return Us(e)?Ur:Vr}function Ge(e,t){return t?.isSpeaking()?t:e.isSpeaking()?e:t?.isConnected()?t:e}const Qo=v.forwardRef(function({id:t,avatarId:r,faceId:n,assets:s,authToken:i,apiBase:o,ttsEngineId:a=st,voiceId:l,speakingRate:f=Dt,className:c,style:u,canvasStyle:d,onStatusChange:p,onDisplayStatus:E,onError:h,onReady:x,showErrorOverlay:R=!0,onUserTranscript:P,onBotOutput:w,onRealtimeLipsyncDebug:k,onFrameRenderStats:B,lipsyncBufferedPlayback:I=!0,lipsyncPlaybackWarmupMs:Z=1500,pixiMaxTextureSize:he,captions:A},V){const K=v.useRef(null),Q=v.useRef(null),j=v.useRef(null),me=v.useRef(I);me.current=I;const F=v.useRef(Z);F.current=Z;const J=v.useRef(0),b=v.useRef(Yr()),z=v.useRef(eo()),ne=v.useRef(!1),ae=v.useRef(null),te=v.useRef(null),ee=v.useRef(0),ie=v.useRef(0),we=v.useRef(0),S=v.useRef(null),U=v.useRef(null),W=v.useRef(!1),G=v.useRef(!1),L=v.useRef(!1),$=v.useRef(!1),_=v.useRef("sil"),y=v.useRef(0),T=v.useRef(!1),O=v.useRef(null),Y=v.useRef(!1),[re,Ee]=v.useState("idle"),[ye,Ne]=v.useState(null),[Ve,lr]=v.useState(!1),[Jr,Ft]=v.useState(!1),[m,C]=v.useState({width:842,height:1264}),[X,oe]=v.useState(null),[Se,_e]=v.useState(0),ge=v.useRef(!1),ke=v.useRef(i??null),be=o?.trim()||Mt,ct=v.useRef(null),Ie=v.useRef(null),xe=v.useRef(!1),Je=v.useRef({width:842,height:1264}),je=v.useRef(null),Bt=v.useRef("sil"),Ue=v.useRef(""),Fe=v.useMemo(()=>ho(A),[A]),Be=v.useCallback(()=>{Ue.current="",oe(null)},[]);v.useEffect(()=>{Fe.enabled||Be()},[Fe.enabled,Be]);const Ze=v.useCallback(g=>{if(!Fe.enabled)return;const D=g.isSpeaking(),N=g.isWarmingUp();if(!D||N){Ue.current&&Be();return}const M=mo(g.getWordQueue());if(!M){Ue.current&&Be();return}M!==Ue.current&&(Ue.current=M,oe(M))},[Fe.enabled,Be]),pe=v.useRef(void 0),Pe=v.useRef(new Map),We=v.useRef(x),ue=v.useRef(E),bt=v.useRef(h),Lt=v.useRef(P),Tt=v.useRef(w),lt=v.useRef(k),Zr=v.useRef(B),ft=v.useRef(So());We.current=x,ue.current=E,bt.current=h,Lt.current=P,Tt.current=w,lt.current=k,Zr.current=B,v.useEffect(()=>{const g=ft.current;return _n(g),()=>_n(null)},[]);const en=v.useCallback(g=>{const D=g?.ttsEngineId??a??Ie.current?.ttsEngineId??st,N=g?.voiceId??l??Ie.current?.voiceId??It(D);return{ttsEngineId:D,voiceId:N}},[a,l]),Ye=v.useCallback(g=>{Ne(g),bt.current?.(g)},[]),Et=v.useCallback(g=>{Ee(g),p?.(g)},[p]),ce=v.useRef(Ir(Et,be,{onLipsyncDebug:g=>lt.current?.(g)})),de=v.useRef(null);v.useEffect(()=>(ce.current=Ir(Et,be,{onLipsyncDebug:g=>lt.current?.(g)}),i&&ce.current.setDeveloperToken(i),ce.current.setTtsEngineId(a),()=>{ce.current.stop();const g=de.current;g&&(g.disconnect(),de.current=null)}),[Et,i,a,be]);const tn=v.useCallback(()=>(de.current||(de.current=us(Et,{speakingRate:f,onUserTranscript:(g,D)=>Lt.current?.(g,D),onBotOutput:g=>Tt.current?.(g),onLipsyncDebug:g=>lt.current?.(g)}),de.current.setTtsEngineId(a)),de.current),[Et,f,a]),Me=v.useCallback(()=>{S.current!=null&&(clearTimeout(S.current),S.current=null)},[]),Re=v.useCallback(()=>{U.current!=null&&(clearTimeout(U.current),U.current=null),L.current=!1},[]),Ot=v.useCallback(()=>{if($.current)return;const g=Q.current;if($.current=!0,Re(),W.current=!1,G.current=!1,!Y.current||!O.current){g?.renderSilOnly();return}O.current.setActive(!1);const D=O.current.getVideo();if(g?.usesPixi){g.drawIdleVideo(D),g.beginIdleToSilCrossfade(vn);return}(async()=>{const N=K.current;N&&await _r(N,D,null,void 0,Je.current).catch(()=>{}),await new Promise(M=>setTimeout(M,vn)),$.current&&await g?.renderSilOnly()})()},[Re]),rn=v.useCallback(()=>{!Y.current||!O.current||($.current=!1,Q.current?.finishIdleToSilCrossfade(),Re(),L.current=!0,U.current=setTimeout(()=>{if(U.current=null,Ge(ce.current,de.current).isSpeaking()){L.current=!1;return}if(W.current){L.current=!1;return}const g=O.current;if(!g){L.current=!1;return}g.restart(),g.setActive(!0);const D=g.getVideo();let N=!1;const M=()=>{N||(N=!0,D.removeEventListener("playing",H),L.current=!1)},H=()=>{M()};if(D.addEventListener("playing",H,{once:!0}),D.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!D.paused){M();return}setTimeout(M,300)},vo))},[Re]),dt=v.useCallback(()=>{if(Me(),ue.current?.("sil"),W.current=!1,$.current=!1,!Y.current||!O.current){L.current=!1;return}rn()},[Me,rn]),fr=v.useCallback(async g=>{const D=ie.current,N=Q.current,M=K.current,H=()=>{g?.fallbackOnly||(W.current=!1)};if(!N||!M||!Ve){H();return}if(Ge(ce.current,de.current).isSpeaking()){H();return}if(g?.fallbackOnly){if(!W.current||(De(b.current),await N.renderSilOnly(),D!==ie.current)||Ge(ce.current,de.current).isSpeaking())return;dt();return}if(bn(b.current)){De(b.current),dt();return}if(me.current&&N.supportsBufferedPlayback&&!g?.fallbackOnly){if(De(b.current),j.current?.reset(),await N.renderSilOnly(),D!==ie.current||Ge(ce.current,de.current).isSpeaking())return;dt();return}const Ae=Io(Ge(ce.current,de.current).getVisemeQueue(),y.current,_.current),Le=Ao(Ae,je.current);if(De(b.current),!Le){if(await N.renderSilOnly(),D!==ie.current||Ge(ce.current,de.current).isSpeaking())return;dt();return}try{await N.renderViseme(M,"sil",{from:Ae,transitionDurationMs:vs})}catch(Ce){if(Ce?.name!=="AbortError")throw Ce}if(D!==ie.current){H();return}if(Ge(ce.current,de.current).isSpeaking()){H();return}dt()},[Ve,dt]),mt=v.useCallback(()=>{if(G.current||W.current)return;G.current=!0,W.current=!0,Me();const g=ie.current;fr().finally(()=>{G.current=!1}),S.current=setTimeout(()=>{S.current=null,g===ie.current&&fr({fallbackOnly:!0})},xo())},[Me,fr]),nn=v.useCallback(async()=>{const g=Math.floor(Date.now()/1e3);if(ct.current&&g<ct.current.expiresAt-60)return ct.current.key;const D=ke.current??await jt();ke.current=D,ce.current.setDeveloperToken(D);const N=await zs(D,be);return ct.current={key:N.value,expiresAt:N.expiresAt},N.value},[be]);v.useEffect(()=>{i&&(ke.current=i,ce.current.setDeveloperToken(i))},[i]),v.useEffect(()=>{ce.current.setTtsEngineId(a),de.current?.setTtsEngineId(a),Ie.current&&(Ie.current.ttsEngineId=a)},[a]),v.useEffect(()=>{const g=l??It(a);Ie.current&&(Ie.current.voiceId=g)},[l,a]);const et=v.useMemo(()=>Bn(r,n),[r,n]),As=v.useMemo(()=>s?JSON.stringify(s):et?`face:${et}`:`id:${t??""}`,[s,et,t]);return v.useEffect(()=>{if(!t&&!s&&!et){Ye("AiTwin requires `assets`, `avatarId`/`faceId`, or `id`");return}let g=!1;xe.current=!1,lr(!1),Ft(!1),Ne(null),Ie.current=null,O.current=null,Y.current=!1;const D=K.current;if(!D)return;let N=null;return(async()=>{try{let M,H,Te,Ae;if(s)M=s,H=a,Te=l??It(H);else if(et)ue.current?.("loadFace"),M=Er(et),H=a,Te=l??It(H);else{ue.current?.("getAiTwin");const q=await vr(Fn(t),3e4,"getAiTwin");if(g)return;if(!q.faceId?.trim())throw new Error("getAiTwin returned no faceId");H=q.ttsEngineId,Te=l??q.voiceId??It(H),Ae=q.knowledgeContextId?.trim()||void 0,M=Er(q.faceId)}Ie.current={ttsEngineId:H,voiceId:Te,personaId:Ae},Ln(M),O.current=to();const Le=!!(s&&!at(s));N=qo(D,{preferLegacyRenderer:Le,pixiMaxTextureSize:he,onStatus:q=>{g||ue.current?.(q)},onError:q=>{g||Ye(q)},shouldAcceptLiveFrame:()=>{if(ne.current)return!0;if(W.current)return!1;const q=Ge(ce.current,de.current);return q.isSpeaking()||q.isWarmingUp()},onWebGlContextLost:()=>{g||ge.current||(ge.current=!0,queueMicrotask(()=>{g||_e(q=>q+1)}))}}),Q.current=N,j.current=Do(N),ge.current=!1,(q=>{const se=()=>{if(!q||F.current<=0)return!1;const ve=Q.current;return ve?me.current&&ve.supportsBufferedPlayback?!0:ve.usesPixi:!1};ce.current.setLipsyncWarmupHold(q,se),de.current?.setLipsyncWarmupHold(q,se)})(!0),ue.current?.("auth");const He=ke.current??i??await vr(jt(),3e4,"getAuthToken");if(g)return;ke.current=He,ce.current.setDeveloperToken(He),ce.current.setTtsEngineId(H);const Nt=Rt()?await nn():void 0;if(g)return;ue.current?.("loadAssets");const le=await vr(N.loadAssets(Nt),18e4,"load face assets");if(!g&&le.width>0&&le.height>0&&(Je.current=le,C(le)),g)return;je.current=N.getVisemeAtlasPaths(),xe.current=!0,lr(!0),We.current?.(),N.renderSilOnly().then(()=>{g||ue.current?.("sil")}),(async()=>{try{const q=await Ri(Nt);if(await O.current.load(q),g)return;Y.current=!0,Ft(!0),De(b.current),O.current.setActive(!0);const se=O.current.getVideo();N.usesPixi?N.drawIdleVideo(se):await _r(D,se,null,void 0,Je.current),g||ue.current?.("idle")}catch(q){if(g)return;Y.current=!1,Ft(!1),console.warn("[AiTwin] Idle video unavailable:",q)}})()}catch(M){if(g)return;if(M instanceof Yt){Ye(M.message);return}Ye(M instanceof Error?M.message:"Failed to load AI twin")}})(),()=>{g=!0,O.current?.dispose(),N?.dispose(),Q.current=null}},[As,s,et,t,a,l,i,he,nn,Ye,Se]),v.useEffect(()=>{const g={current:!1},D=()=>{const N=K.current,M=Q.current,H=Ge(ce.current,de.current);if(!N||!M||!Ve){ee.current=requestAnimationFrame(D);return}if(document.hidden){O.current?.setActive(!1),ee.current=requestAnimationFrame(D);return}const Te=H.isSpeaking(),Ae=H.isWarmingUp(),Le=Te&&!T.current,Ce=T.current&&!Te;Le&&(Me(),Re(),W.current=!1,G.current=!1,De(b.current),j.current?.reset(),ft.current.reset(),pe.current=void 0,we.current=0,_.current="sil",y.current=0,Ot()),Ce&&(z.current.reset(),O.current?.setActive(!1),j.current?.reset(),M.finishIdleToSilCrossfade(),Be(),mt());const He=Ce||W.current||L.current||$.current,Nt=I&&M.supportsBufferedPlayback;if(Ae){if(Ot(),Y.current&&O.current?.setActive(!1),M.usesPixi&&M.isIdleToSilCrossfading()){M.tickIdleToSilCrossfade(performance.now()),H.getVisemeQueue().length>0&&J.current===0&&(J.current=performance.now()),T.current=H.isSpeaking(),ee.current=requestAnimationFrame(D);return}const le=H.getVisemeQueue();le.length>0&&J.current===0&&(J.current=performance.now()),z.current.advance(performance.now());const q=z.current.getExpressionPath(),se=z.current.getDrawKey(),ve={queue:le,elapsedMs:0,expressionPath:q,expressionDrawKey:se,availablePaths:je.current};let Oe=!1;Nt&&j.current?(j.current.warmupTick(ve),Oe=j.current.isWarmupReady(ve,F.current)):M.usesPixi&&Tn(N,le,0,q,se,b.current,(ht,Vt,dr,Ut)=>{M.drawLiveFrame(ht,Vt,dr,Ke=>{Ut?.(Ke),H.isWarmingUp()&&Ke!==""&&!bn({lastDrawnKey:Ke})&&(J.current=0,H.releaseWarmupHold())})},ht=>{ue.current?.(ht.label)},je.current);const Qe=J.current>0&&performance.now()-J.current>Os;ue.current?.(Oe?"buffer ready":"buffering lipsync…"),(Oe||Qe)&&(J.current=0,H.releaseWarmupHold()),Ze(H),T.current=H.isSpeaking(),ee.current=requestAnimationFrame(D);return}if(Te){Ot(),Y.current&&O.current?.setActive(!1),M.usesPixi&&M.isIdleToSilCrossfading()&&M.finishIdleToSilCrossfade();const le=H.getVisemeQueue(),q=H.getPlaybackElapsedMs();gi(le,q),y.current=q;const se=le.length>0?Ct(le,q):"sil";se!=="sil"&&(_.current=se);const ve=io(q,H.getSentenceEmotions(),H.getWordQueue(),H.getStreamMood());z.current.setTargetEmotion(ve),z.current.setTargetEyebrow(Zi(q,H.getWordQueue(),it(ve))),z.current.advance(performance.now());const Oe=z.current.getExpressionPath(),Qe=z.current.getDrawKey(),ht=(Ke,mr,kt,hr)=>{const sn={label:Bt.current,elapsedMs:q,mouthPath:mr.mouthPath,transitionMeta:pe.current};Pe.current.set(kt,sn),j.current?.markExternallyDisplayed(kt,Oe),M.drawLiveFrame(Ke,mr,kt,Wt=>{const xs=Pe.current.get(Wt)??sn;Pe.current.delete(Wt),ft.current.onDrawDisplayed(xs,le),j.current?.markExternallyDisplayed(Wt,Oe),hr?.(Wt)})},Vt=Ke=>{Bt.current=Ke.label,pe.current=Ke.transitionMeta,ue.current?.(Ke.label)},dr=I&&M.supportsBufferedPlayback;let Ut=!1;dr&&j.current&&(Ut=j.current.tick({canvas:N,queue:le,elapsedMs:q,expressionPath:Oe,expressionDrawKey:Qe,playbackState:b.current,availablePaths:je.current,onStatus:Vt,onDisplayed:(mr,kt,hr)=>{ft.current.onDrawDisplayed({label:hr,elapsedMs:q,mouthPath:kt,transitionMeta:pe.current},le)}}).usedBuffer),Ut||Tn(N,le,q,Oe,Qe,b.current,ht,Vt,je.current),ft.current.onRafTickSpeaking(),Zr.current?.(ft.current.snapshot(le,q))}else if(!He&&ne.current){O.current?.setActive(!1),z.current.advance(performance.now());const le=z.current.getExpressionPath(),q=z.current.getDrawKey();Sr(N,le,q,b.current,(se,ve,Oe,Qe)=>M.drawLiveFrame(se,ve,Oe,Qe),se=>{if(te.current){ue.current?.(te.current);return}const ve=z.current.getTargetEmotion();ue.current?.(se==="sil"||se==="blink"?`${ve}${se==="blink"?" · blink":""}`:se)},null,ae.current)}else if(!He&&Y.current&&O.current){const le=O.current,q=le.getVideo();if(q.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA){le.setActive(!0);const se=()=>!H.isSpeaking()&&!W.current&&!L.current&&!ne.current;M.usesPixi?(M.drawIdleVideo(q,se),se()&&ue.current?.("idle")):g.current||(g.current=!0,_r(N,q,null,void 0,Je.current,se).then(()=>ue.current?.("idle")).finally(()=>{g.current=!1}))}else Sr(N,null,"__sil__",b.current,(se,ve,Oe,Qe)=>M.drawLiveFrame(se,ve,Oe,Qe),se=>ue.current?.(se))}else He||Sr(N,null,"__sil__",b.current,(le,q,se,ve)=>M.drawLiveFrame(le,q,se,ve),le=>ue.current?.(le));Fe.enabled&&Ze(H),T.current=Te,ee.current=requestAnimationFrame(D)};return ee.current=requestAnimationFrame(D),()=>{cancelAnimationFrame(ee.current),Me(),Re(),ce.current.stop(),de.current?.stop()}},[Ve,Jr,mt,Me,Re,Ot,Ze,Be,Fe.enabled]),v.useImperativeHandle(V,()=>({speakText:async(g,D)=>{const N=g.trim();if(!N)return;const M=Q.current;if(!M||!xe.current&&!Ve)throw new Error("AI twin is not ready");const{ttsEngineId:H,voiceId:Te}=en(D);Ne(null),Be(),ie.current+=1,Me(),Re(),W.current=!1,G.current=!1,_.current="sil",y.current=0,De(b.current),ne.current=!1,ae.current=null,te.current=null,z.current.reset(),T.current=!1,$.current=!1,M.finishIdleToSilCrossfade();try{await ce.current.speak(N,{voiceId:Te,speakingRate:D?.speakingRate??f,ttsEngineId:H})}catch(Ae){throw $.current=!1,M.finishIdleToSilCrossfade(),mt(),Ye(Ae instanceof Error?Ae.message:"TTS failed"),Ae}},stop:()=>{ie.current+=1,Me(),Re(),W.current=!1,G.current=!1,$.current=!1,Q.current?.finishIdleToSilCrossfade(),ce.current.stop(),de.current?.stop(),De(b.current),j.current?.reset(),J.current=0,T.current=!1,Be(),mt()},setTtsEngineId:g=>{ce.current.setTtsEngineId(g),de.current?.setTtsEngineId(g),Ie.current&&(Ie.current.ttsEngineId=g)},renderViseme:async(g,D)=>{const N=Q.current,M=K.current;if(!N||!M||!xe.current)throw new Error("AI twin is not ready");await N.renderViseme(M,g,{from:D?.from,transitionDurationMs:D?.transitionDurationMs,gapMs:D?.gapMs,onStatus:H=>ue.current?.(H)})},setEmotion:g=>{if(!xe.current)throw new Error("AI twin is not ready");const D=Q.current,N=K.current;if(!D||!N)throw new Error("AI twin is not ready");ie.current+=1,Me(),Re(),W.current=!1,G.current=!1,L.current=!1,$.current=!1,ce.current.stop(),de.current?.stop(),De(b.current),j.current?.reset(),J.current=0,T.current=!1,D.finishIdleToSilCrossfade(),O.current?.setActive(!1);const M=ze(g);ne.current=!0,ae.current=null,te.current=null,z.current.snapToEmotion(M);const H=z.current.getExpressionPath(),Te=z.current.getDrawKey(),Ae={mouthPath:null,expressionPath:H},Le=Pt(Ae,Te);b.current.lastDrawnKey="",D.drawLiveFrame(N,Ae,Le,He=>{He===Le&&(b.current.lastDrawnKey=Le)});const Ce=it(M);ue.current?.(H?`${M} · brow ${Ce>0?"+":""}${Ce}`:`${M} · brow 0 (sil base)`)},getEmotion:()=>ne.current?z.current.getTargetEmotion():null,clearEmotionPreview:()=>{ne.current=!1,ae.current=null,te.current=null,z.current.reset(),De(b.current),ue.current?.("idle")},setViseme:g=>{if(!xe.current)throw new Error("AI twin is not ready");const D=Q.current,N=K.current;if(!D||!N)throw new Error("AI twin is not ready");ie.current+=1,Me(),Re(),W.current=!1,G.current=!1,L.current=!1,$.current=!1,ce.current.stop(),de.current?.stop(),De(b.current),j.current?.reset(),J.current=0,T.current=!1,D.finishIdleToSilCrossfade(),O.current?.setActive(!1);const M=nr(g);ne.current=!0,te.current=M,ae.current=M==="sil"?null:yt(M),z.current.snapToEmotion("neutral");const H=z.current.getExpressionPath(),Te=z.current.getDrawKey(),Le={mouthPath:ae.current,expressionPath:H},Ce=Pt(Le,Te);b.current.lastDrawnKey="",D.drawLiveFrame(N,Le,Ce,He=>{He===Ce&&(b.current.lastDrawnKey=Ce)}),ue.current?.(M)},getViseme:()=>ne.current?te.current:null,isReady:()=>xe.current,getStatus:()=>re,connect:async(g={})=>{Ne(null);try{let D=g.authToken?.trim();D||(D=ke.current??i??await jt(),ke.current=D);const N=g.voiceId?.trim()??l??Ie.current?.voiceId,M=g.personaId?.trim()??Ie.current?.personaId,H=tn();await H.unlockAudio(),await H.connect({...g,apiBase:g.apiBase?.trim()||be,authToken:D,voiceId:N,personaId:M,speakingRate:g.speakingRate??f})}catch(D){throw Ye(D instanceof Error?D.message:"Voice connect failed"),D}},disconnect:async()=>{const g=de.current;g&&(ie.current+=1,Me(),Re(),W.current=!1,G.current=!1,$.current=!1,Q.current?.finishIdleToSilCrossfade(),De(b.current),j.current?.reset(),J.current=0,T.current=!1,await g.disconnect(),mt())},isConnected:()=>de.current?.isConnected()??!1,getVoiceStream:()=>de.current?.getVoiceStream()??ce.current?.getVoiceStream()??null}),[Ve,f,i,l,Me,Re,mt,Ye,en,tn,re,be]),rt.jsxs("div",{className:c,style:{...zo,...u},children:[rt.jsx("canvas",{ref:K,width:m.width,height:m.height,style:{...jo,...d},"aria-label":t?`AI twin ${t}`:"AI twin face"}),R&&ye?rt.jsx("div",{style:Yo,children:ye}):null,Fe.enabled&&X?rt.jsx(yo,{text:X,style:Fe.style},X):null]})}),ks="thumbnail.webp";function Xo(e,t=Cr){const r=e.trim();if(!r)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${r}/${ks}`}const Jo=new Set([".png",".jpg",".jpeg",".webp"]);function Is(e=".png"){const t=e.toLowerCase();return Jo.has(t)?`source${t}`:"source.png"}function Zo(e,t=Cr,r=".png"){const n=e.trim();if(!n)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${n}/${Is(r)}`}exports.AI_TWIN_THUMBNAIL_FILENAME=ks;exports.AiTwin=Qo;exports.AiTwinNotFoundError=Yt;exports.CONSONANT_SKIP_GROUP=Gn;exports.DEFAULT_TTS_ENGINE_ID=st;exports.EMOTION_TO_EYEBROW=ws;exports.EXPRESSION_ATLAS_EMOTIONS=Ki;exports.MIC_SAMPLE_RATE=Wr;exports.OCULUS_VISEME_IDS=$n;exports.PLAYBACK_SAMPLE_RATE=$r;exports.SPEAKING_RATE_MAX=ur;exports.SPEAKING_RATE_MIN=ar;exports.TTS_ENGINE_CARTESIA=ot;exports.TTS_ENGINE_GOOGLE=Fr;exports.TTS_ENGINE_INWORLD=rr;exports.VISEME_IDS=Ci;exports.VISEME_SKIP_GAP_MS=kr;exports.VISEME_TEST_CARTESIA_VOICE_ID=Ur;exports.VISEME_TEST_SPEAKING_RATE=Dt;exports.VISEME_TEST_VOICE_ID=Vr;exports.VISEME_VTIME_GRID_MS=Br;exports.VOWEL_SKIP_GROUP=qn;exports.aiTwinSourceFilename=Is;exports.buildPlaybackTransitions=Zn;exports.buildPlaybackVisemeSeries=ir;exports.buildRawVisemeTransitions=Jn;exports.buildVoiceWebSocketUrl=as;exports.compactSkippableVisemes=Or;exports.createAvatarTtsLipsyncController=Ir;exports.createRealtimeVoiceLipsyncController=us;exports.emotionToEyebrow=it;exports.faceAssetUrls=Er;exports.fetchAiTwin=Fn;exports.fetchDevAuthToken=jt;exports.getAiTwinSourceUrl=Zo;exports.getAiTwinThumbnailUrl=Xo;exports.listSkippedVisemeEntries=or;exports.listSkippedVisemes=ci;exports.normalizeOculusViseme=nr;exports.normalizeStreamEmotion=ze;exports.resolveDirectFaceId=Bn;exports.resolveVisemeAtTime=Ct;exports.resolveVisemeFromPlaybackSeries=Yn;exports.resolveWordAtTime=yi;exports.setActiveTwinAssets=Ln;exports.snapVisemeQueueToGrid=Lr;exports.snapVisemeVtimeToGrid=zn;