@streamoji/aitwin 0.5.2 → 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 +23 -0
- package/dist/aitwin.cjs +5 -5
- package/dist/aitwin.js +2114 -2003
- package/dist/index.d.ts +11 -0
- package/package.json +1 -1
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 ur=require("react/jsx-runtime"),T=require("react"),vs=require("@pipecat-ai/client-js"),kr=require("@pipecat-ai/websocket-transport"),ue=require("pixi.js");var cr=typeof document<"u"?document.currentScript:null;const bs="https://aitwin.bubu.social",bn={publicBase:bs},Ts="0.5.2",Es={version:Ts},ks={},kt="https://ai.aitwin.me",Ir=`${bn.publicBase}/custom-faces`,Is=`${bn.publicBase}/aitwin-workers/v${Es.version}/visemeDiffPreview.worker.js`;function As(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:cr&&cr.tagName.toUpperCase()==="SCRIPT"&&cr.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?ks?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():Is}const Ps="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",lr=3e3,bt=16,Xr=3e3,xs=4e3,Ms=.85,Rs=8,Ar=400,Pr="eng_a7f2b9c1",Yt="eng_d4e8f3a2",et="eng_c9b1e6d4",Je=et,Tn=new Set([Yt,et]),En={[Pr]:"oculus_direct",[et]:"oculus_direct",[Yt]:"inworld_word"},Jr={google:Pr,inworld:Yt,cartesia:et};function kn(e){return En[e]}function Cs(e){return e===et}function Ds(e){if(e.ttsEngineId&&e.ttsEngineId in En)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in Jr?Jr[t]:Je}class $t extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function In(e,t=Ps){const r=new URL(t);r.searchParams.set("id",e);const n=await fetch(r.toString());if(n.status===404)throw new $t;if(!n.ok)throw new Error(`getAiTwin failed (${n.status})`);const s=await n.json();if(!s.success)throw new $t(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:Ds(s.data),authToken:l}}const Fs="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",Bs="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Ls="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Os="6TNvp3SCs4Og0a1ijm9TommXLql1",Ns="Swaraj Mali";async function Wt(){const e=await fetch(Fs,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":Bs,"Client-Secret":Ls},body:JSON.stringify({userId:Os,userName:Ns})});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 Vs(e,t=kt){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 Us(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 Lt(e){return Uint8Array.from(e)}function Ws(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e,0),r.set(t,e.length),r}async function $s(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=Lt(r.subarray(0,12)),s=Lt(r.subarray(12,28)),i=Lt(r.subarray(28)),o=Lt(Ws(i,s)),a=await crypto.subtle.importKey("raw",Us(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:n,tagLength:128},a,o)}let wr=null;const gr=new Set;function Ot(e){return e.includes("/custom-faces/")}function Ht(e){const t=e.binBase.trim().replace(/\/$/,""),r=e.twinBase.trim().replace(/\/$/,"");if(!(e.encrypted||Ot(t)||Ot(r)))return{twinBase:r,binBase:t,encrypted:!1};const s=Ot(t)?t:Ot(r)?r:t;return{twinBase:s,binBase:s,encrypted:!0}}function tt(e){return Ht(e).encrypted}function yr(e,t=Ir){const r=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:r,binBase:r,encrypted:!0}}function An(e,t){return(e??t)?.trim()||void 0}function Pn(e){wr=Ht(e);for(const t of gr)t()}function Hs(e,t){const r=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!r)return Ht(e);const n=s=>{try{return new URL(s,r).href.replace(/\/$/,"")}catch{return s}};return Ht({twinBase:n(e.twinBase),binBase:n(e.binBase),encrypted:e.encrypted})}function rt(){if(!wr)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return wr}function xn(e){return gr.add(e),()=>gr.delete(e)}function Kt(){const e=rt();return tt(e)?`${e.binBase}/sil.bin`:`${e.twinBase}/sil.png`}function Ks(){const e=rt();return tt(e)?`${e.binBase}/idle.bin`:`${e.twinBase}/idle.mp4`}function Mn(){const e=rt();return tt(e)?e.binBase:e.twinBase}function It(){return tt(rt())}function Rn(){const e=rt();return tt(e)?`${e.binBase}/atlas.bin`:`${e.twinBase}/atlas.json`}function Cn(){const e=rt();return tt(e)?`${e.binBase}/expression_atlas.bin`:`${e.twinBase}/expression_atlas.json`}function Dn(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 Fn=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],qs=new Map(Fn.map(e=>[e.toLowerCase(),e]));function Qt(e){const t=(e??"").trim();return t?qs.get(t.toLowerCase())??"sil":"sil"}const Gs={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 zs(e){if(!e)return["sil"];const t=e.toLowerCase();return Gs[t]??["sil"]}function Bn(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 ft(e,t){const r=Math.max(0,Math.min(e.length-1,t));return e[r]??""}function Xt(e,t){const r=e.toLowerCase().replace(/[^a-z']/g,"");return!r||ft(r,t)!=="w"||t>0&&ft(r,t-1)==="q"?!1:t===0&&/^w[aeiouy]/.test(r)}function js(e,t){const r=e.toLowerCase(),n=ft(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 Ys(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(Xt(r,t))return"FF";const s=ft(r,t);return s==="u"||s==="w"?"U":"O"}function Qs(e,t){const r=e.toLowerCase();if(Xt(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)||ft(r,t)==="o"?"O":"U"}const Nt={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function Xs(e,t){const r=e.toLowerCase(),n=ft(r,t);if(Nt[n])return Nt[n];for(let s=0;s<r.length;s++){const i=r[s];if(Nt[i])return Nt[i]}return"DD"}function Ln(e,t,r){const n=zs(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 js(s,o);case"o":return Ys(s,o);case"qw":return Qs(s,o);case"cdgknstxyz":return Xs(s,o);default:return n[0]}}const xr=16,_r=47,On=new Set(["aa","E","I"]),Nn=new Set(["DD","SS","kk","CH"]),Js=[On,Nn];function Zs(e){for(const t of Js)if(t.has(e))return t;return null}function ei(e){return Zs(e)!=null}function Vn(e,t=xr){return t<=0?e:Math.round(e/t)*t}function Mr(e,t=xr){return e.map(r=>({...r,vtime:Vn(r.vtime,t)}))}function Un(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 Rr(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(!ei(n.viseme)){t.push(n);continue}const o=n.vtime-i.vtime,a=s.vtime-n.vtime;o<_r||a<_r||t.push(n)}return t.push(e[e.length-1]),t}function Jt(e){return Rr(Un(Mr(e)))}function Wn(e,t){let r="sil";for(const n of e){if(n.vtime>t)break;n.viseme!=="sil"&&(r=n.viseme)}return r}function Zt(e){const t=Un(Mr(e)),r=Rr(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 ti(e){return Zt(e).map(t=>t.viseme)}const ri=32,Zr=48;function $n(e){let t=0;for(const r of e)t=Math.max(t,r.vtime+r.vduration);return t}function Hn(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 ni(e){return Jt(e).find(r=>r.viseme!=="sil")??null}function Qe(e){const t=$n(e);return Hn(e)?t+Ar:t}function si(e){const t=[],r=ni(e);r&&r.vtime>0&&t.push({from:"sil",to:r.viseme,fromVtime:0,toVtime:r.vtime});const n=Hn(e),s=$n(e);return n&&t.push({from:n.viseme,to:"sil",fromVtime:s,toVtime:s+Ar}),t}function Kn(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 qn(e){return e.length===0?[]:[...si(e),...Kn(e)]}function ii(e,t){let r=null;for(const n of e){const{transStart:s,transEnd:i}=Gn(n.fromVtime,n.toVtime,n.from,n.to);t>=s&&t<i&&(r=n)}return r}function Gn(e,t,r,n){const s=Math.max(0,t-e);if((r==="PP"||n==="PP")&&s>Zr){const f=Zr,c=t,u=c-f;return{gapMs:s,effectiveGapMs:f,holdEnd:u,transStart:u,transEnd:c}}const o=s>0?Math.max(s,ri):0,a=e,l=e+o;return{gapMs:s,effectiveGapMs:o,holdEnd:e,transStart:a,transEnd:l}}function zn(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?Bn(u,c):0;let p;kn(i)==="oculus_direct"?(p=Qt(a),p==="U"&&u&&Xt(u.word,d)&&(p="FF")):p=Ln(a,u?.word??"",d),e.push({viseme:p,weight:1,vtime:c,vduration:f})})}function jn(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 Cr=16e3;function Yn(e){const t=Number(e);return Number.isFinite(t)&&t>0?t:Cr}function oi(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 ai(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function en(e,t,r=Cr){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 ui(e){const t=oi(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 tn(e,t,r=Cr){const n=ui(t),s=Yn(r);if(ai(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 en(e,n,s);try{const i=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(i)}catch{return en(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 Xn(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,m=!1,A=null,x=0,C=!1,w=!1,b=0,O=null,V=null,X=null;const fe=()=>({visemes:[],words:[],segmentOffsetMs:0}),P=()=>{w=!1,b=0},D=()=>{d!=null&&(clearTimeout(d),d=null)},N=()=>{for(const S of n){try{S.onended=null,S.stop(0)}catch{}try{S.disconnect()}catch{}}n.length=0},j=()=>{for(const S of n)S.playbackRate.value=p},F=()=>{if(!(m||!l||i||w||n.length>0||r.length>0)){if(a&&s&&c>0){const S=(s.currentTime-c)*1e3;if(S<x){D();const M=p>0?(x-S)/p:x-S;d=setTimeout(()=>{m||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},Math.max(0,M));return}}o=!1,D(),e.onSpeakingDone(),e.onNotify()}},Y=S=>{const M=Qe(S);if(M<=0){e.onSpeakingDone(),e.onNotify();return}o=!0,f=performance.now(),e.onSpeakingStart(),e.onNotify(),D();const G=p>0?M/p:M;d=setTimeout(()=>{m||(o=!1,d=null,e.onSpeakingDone(),e.onNotify())},G)},$=(S,M,G,z)=>{M.length!==0&&(u=G,M.forEach(I=>{const g=String(I.word??"").trim();if(!g)return;const _=Math.round((I.start??0)*1e3),y=I.duration!=null?Math.round(I.duration*1e3):I.end!=null?Math.max(1,Math.round((I.end-(I.start??0))*1e3)):0;S.push({word:g,wtime:u+_,wduration:y,queueIndex:S.length,gesture:I.gesture??null,emphasis:I.emphasis===!0})}))},q=(S,M,G,z,I,W)=>{G.length!==0&&(u=z,G.forEach(g=>{const _=String(g.symbol??g.viseme??"").trim();if(!_)return;const y=Math.round((g.start??0)*1e3),Z=Math.max(1,Math.round((g.duration??0)*1e3)),oe=u+y,ye=Qn(M,oe),Re=ye?Bn(ye,oe):0;let _e;kn(W)==="oculus_direct"?(_e=Qt(_),_e==="U"&&ye&&Xt(ye.word,Re)&&(_e="FF")):_e=Ln(_,ye?.word??"",Re),S.push({viseme:_e,weight:1,vtime:oe,vduration:Z})}),e.onNotify())},k=(S,M,G,z,I,W,g)=>{$(M,z,I),q(S,M,G,I,W,g)},ee=(S,M,G)=>{X===S&&S.visemes===M||(M.length=0,G.length=0,S.visemes=M,S.words=G,S.segmentOffsetMs=0,X=S,u=0,x=0)},he=async()=>{const S=window.AudioContext||window.webkitAudioContext;return s||(s=new S),s.state==="suspended"&&await s.resume(),A||(A=s.createMediaStreamDestination()),s},ce=(S,M)=>{if(!s)return 0;const G=s,z=G.currentTime,I=z+.02;if(M.isFirstAudio){c<=0&&(c=I);const g=(I-z)*1e3;f=performance.now()+g}M.onBeforeStart?.(I);const W=G.createBufferSource();return W.buffer=S,W.playbackRate.value=p,W.connect(G.destination),A&&W.connect(A),n.push(W),m?(n.pop(),I):(M.isFirstAudio&&(o=!0,e.onSpeakingStart()),W.onended=()=>{const g=n.indexOf(W);g>=0&&n.splice(g,1),e.onNotify(),!m&&(O&&V?te(O,V):F())},W.start(I),I)},te=async(S,M)=>{if(m||i||n.length>0)return;if(r.length===0){F();return}i=!0;let G=!1;try{const z=await he();if(m)return;const I=r[0];if(!I)return;const W=!o;if(C&&(e.shouldHoldForWarmup?.()??!1)&&(w||W&&I.newStream)||w){ee(I.utterance,S,M);for(const y of r){if(m)return;y.audioBuffer||(y.audioBuffer=await tn(z,y.audio,y.sampleRate)),a=!0,w||(w=!0,b=0,y.newStream&&y.onNewStream?.()),y.visemesQueued||(k(S,M,y.visemes,y.words,b,!0,E),y.visemesQueued=!0,b+=Math.round(y.audioBuffer.duration*1e3),x=Math.max(x,Qe(S)))}e.onNotify();return}const _=r.shift();if(!_||(_.audioBuffer||(_.audioBuffer=await tn(z,_.audio,_.sampleRate)),m))return;a=!0,D(),ee(_.utterance,S,M),_.visemesQueued=!1,_.newStream&&_.onNewStream?.(),ce(_.audioBuffer,{isFirstAudio:!o,isFirstOfStream:!1,onBeforeStart:y=>{c=y,u=0,f=performance.now()+(y-z.currentTime)*1e3,k(S,M,_.visemes,_.words,0,!0,E),_.visemesQueued=!0,_.utterance.segmentOffsetMs=0}}),G=n.length>0,x=Qe(S),e.onNotify()}catch(z){console.error("[avatarChunkPlayback] audio chunk failed:",z)}finally{if(i=!1,m)return;if(w){r.some(I=>!I.audioBuffer||!I.visemesQueued)&&te(S,M);return}!G&&n.length===0&&r.length>0?te(S,M):n.length===0&&r.length===0&&F()}};return{setStreamTtsEngineId:S=>{E=S},setPlaybackSpeed:S=>{p=Math.max(.1,Math.min(1,S)),j()},getPlaybackSpeed:()=>p,setLipsyncWarmupHoldEnabled:S=>{C=S,S||P()},isWarmingUp:()=>w,releaseWarmupHold:()=>{if(w){w=!1,b=0;for(const S of r)S.newStream=!1;O&&V&&te(O,V),e.onNotify()}},resetForNewStream:S=>{m=!1,l=!1,E=S,x=0},resumeBotStream:()=>{m=!1,l=!1},clearAborted:()=>{m=!1},primeAudioContext:async()=>{const S=window.AudioContext||window.webkitAudioContext;s||(s=new S),s.state==="suspended"&&await s.resume(),s&&!A&&(A=s.createMediaStreamDestination())},stop:()=>{m=!0,D(),N(),r.length=0,i=!1,o=!1,a=!1,l=!1,u=0,c=0,f=0,x=0,O=null,V=null,X=null,P()},markStreamComplete:S=>{if(x=Math.max(x,Qe(S)),l=!0,!a&&S.length>0){Y(S);return}F()},playAudioChunk:async(S,M,G,z,I,W,g)=>{if(m)return;O=S,V=M;const _=Yn(g?.sampleRate),y=g?.newStream===!0,Z=fe();if(!G?.trim()){if(z.length===0&&I.length===0)return;ee(Z,S,M),y&&g?.onNewStream?.(),k(S,M,z,I,0,!0,E),x=Qe(S),!o&&x>0&&Y(S),e.onNotify();return}r.push({audio:G,audioBuffer:null,utterance:Z,visemes:z,words:I,isNewSegment:W,sampleRate:_,newStream:y,onNewStream:g?.onNewStream,visemesQueued:!1}),await te(S,M)},queueTimingsOnly:(S,M,G,z,I)=>{if(m)return;const W=fe();ee(W,S,M),k(S,M,G,z,0,!0,E),W.segmentOffsetMs=0,x=Qe(S)},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,D(),N(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,x=0,O=null,V=null,X=null,P()},interrupt:()=>{m=!0,l=!1,D(),N(),r.length=0,i=!1,o=!1,a=!1,u=0,c=0,f=0,x=0,O=null,V=null,X=null,P()}}}function At(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 Jn(e,t,r=120){const n=At(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 ci(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 li(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function rn(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 er=.5,tr=1.5,Dr="Olivia",Fr="f786b574-daa5-4673-aa0c-cbe3e8534c02",Pt=.85;function Sr(e,t=kt,r={}){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,s=[],i=[],o=[],a=[];let l=0;const f=[],c=[];let u="",d="neutral",p="",E=Je;const m=new Set;let A=0,x=null;const C=()=>m.forEach(F=>F());let w=()=>!1;const b=Xn({onSpeakingStart:()=>e("speaking"),onSpeakingDone:()=>e("done"),onNotify:C,shouldHoldForWarmup:()=>w()},Je),O=()=>b.getPlaybackElapsedMs(),V=(F,Y=u)=>{if(!r.onLipsyncDebug)return;const $=O(),q=s,k=Zt(o).map(ee=>({viseme:ee.viseme,vtimeMs:ee.vtime}));r.onLipsyncDebug({event:F,spokenText:Y,mood:d,playbackAnchorPerf:b.getPlaybackStartPerf(),playbackElapsedMs:$,queueLength:q.length,activeViseme:At(q,$),segments:c.map(ee=>({...ee,visemes:[...ee.visemes]})),fullQueueLength:o.length,compactionSkipped:k})},X=(F,Y)=>{r.onLipsyncDebug&&(x={event:F,spokenText:Y},A===0&&(A=requestAnimationFrame(()=>{A=0;const $=x;x=null,$&&V($.event,$.spokenText)})))},fe=(F,Y,$,q,k)=>{if(!r.onLipsyncDebug)return;const ee=s.slice(Y),he=$.filter(ce=>String(ce.symbol??ce.viseme??"").trim());c.push({spokenText:F,audioOffsetMs:ee[0]?.vtime??0,isNewSegment:q,chunkDurationMs:k,receivedAtMs:performance.now(),visemes:ee.map((ce,te)=>{const J=he[te];return{rawSymbol:String(J?.symbol??J?.viseme??ce.viseme).trim(),viseme:ce.viseme,startSec:Number(J?.start??0),durationSec:Number(J?.duration??0),vtimeMs:ce.vtime,vdurationMs:ce.vduration}})})},P=()=>{o.length=0,a.length=0,l=0},D=(F,Y,$,q,k)=>{if(F.length===0&&Y.length===0)return;zn(o,a,F,Y,l,k);const ee=jn(F,l),he=$?rn($,q):null;l=Math.max(ee,he!=null?l+he:l)},N=()=>{b.stop(),s.length=0,i.length=0,P(),f.length=0,d="neutral",c.length=0,u="",e("idle"),X("reset"),C()};return{setDeveloperToken:F=>{p=F},setTtsEngineId:F=>{E=F},getTtsEngineId:()=>E,speak:async(F,Y)=>{N(),e("loading"),u=F,c.length=0,P();const $=p.trim();if(!$)throw new Error("Developer token required for avatar_ttsWithPoses");const q=Y?.ttsEngineId??E??Je;b.resetForNewStream(q),b.setStreamTtsEngineId(q);const k=Y?.voiceId??Dr,ee=Y?.speakingRate??Pt,he=Math.max(er,Math.min(tr,ee)),ce={user_query:F,ttsEngineId:q,speakingRate:he,tenant:"aiTwin"};Tn.has(q)&&(ce.voice_id=k);try{const te=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${$}`},body:JSON.stringify(ce)});if(!te.ok)throw new Error(`avatar_ttsWithPoses failed (${te.status})`);const J=te.body;if(!J)throw new Error("No response body");const ke=J.getReader(),Me=new TextDecoder;let S="",M=!0;const G=async z=>{const I=Dn(z);if(I){if(I.event==="audio"){const W=I.data.chunk,g=I.data.visemes??[],_=I.data.words??[],y=I.data.is_new_segment??!1,Z=I.data.sample_rate,oe=s.length;if(D(g,_,W,Z,q),W){const ye=M;M=!1,await b.playAudioChunk(s,i,W,g,_,y,{sampleRate:Z,newStream:ye}),fe(F,oe,g,y,rn(W,Z)),X("audio_chunk",F)}else(g.length>0||_.length>0)&&(b.queueTimingsOnly(s,i,g,_,y),fe(F,oe,g,y,null),X("audio_chunk",F))}else if(I.event==="metadata"){const W=I.data.mood;typeof W=="string"&&W.trim()&&(d=W.trim());const g=I.data.sentence_emotions;if(Array.isArray(g)){f.length=0;for(const _ of g){if(!_||typeof _!="object")continue;const y=_;f.push({sentence_index:Number(y.sentence_index??0),text:String(y.text??""),sentiment:typeof y.sentiment=="string"?y.sentiment:void 0,emotion:String(y.emotion??d),start_word:Number(y.start_word??0),end_word:Number(y.end_word??0)})}}C(),X("metadata",F)}else if(I.event==="error")throw new Error(String(I.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:z,value:I}=await ke.read();I&&(S+=Me.decode(I,{stream:!0}));const W=S.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&>(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
|
-
`);
|
|
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
|
-
`);se=Ce.pop()??"";for(const Te of Ce)await _t(Te);if(st){se.trim()&&await _t(se.trim()),me===D&&!re.signal.aborted&&V&&k.markStreamComplete(r);break}}}catch(Ke){if(re.signal.aborted||!V)return;console.error("[realtimeVoice] avatar_speak failed:",Ke),e("error"),$()}finally{P===re&&(P=null)}},Re=h=>{const R=wi(h);if(!R){console.warn("[realtimeVoice] server message with no payload",h);return}const Q=String(R.type??"unknown");switch(Q!=="avatar_metadata"&&Q!=="avatar_audio_chunk"&&Q!=="avatar_lipsync"&&Q!=="avatar_interrupt"&&Q!=="error"&&console.debug("[realtimeVoice] server message",Q,R),Q){case"avatar_metadata":g(R);break;case"avatar_audio_chunk":Z(R);break;case"avatar_lipsync":oe(R);break;case"avatar_interrupt":M(R);break;case"error":{const re=String(R.message??"Voice session error");console.error("[realtimeVoice] server error:",re),e("error"),$();break}case"avatar_speak":ye(R);break}},_e=()=>p||(E=new di,p=new vs.PipecatClient({transport:new kr.WebSocketTransport({mediaManager:E,serializer:new fi,recorderSampleRate:Br,playerSampleRate:Lr}),enableCam:!1,enableMic:!0,callbacks:{onBotReady:()=>{d=!0,e("idle"),$()},onDisconnected:()=>{d=!1,I()},onError:h=>{const R=typeof h=="object"&&h!==null&&"data"in h?String(h.data??h):String(h);console.error("[realtimeVoice]",R,h),e("error"),$()},onTransportStateChanged:h=>{h==="error"&&(e("error"),$())},onServerMessage:h=>{he(h)},onUserStartedSpeaking:()=>{},onBotStartedSpeaking:()=>{ke(),J("bot_started")},onBotStoppedSpeaking:()=>{V&&(Y(),j=setTimeout(()=>{j=null,V&&(k.markStreamComplete(r),J("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,k.setStreamTtsEngineId(h)},getTtsEngineId:()=>u,speak:async(h,R)=>{const Q=h.trim();if(Q){if(!p||!d)throw new Error("Realtime voice is not connected. Call connect() first.");z(),await p.sendText(Q)}},stop:()=>{P?.abort(),P=null,D+=1,ke(),z(),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 R=_e();await R.initDevices();const Q=Zn(m);console.info("[realtimeVoice] connecting (avatar_audio_chunk RTVI):",Q);try{await R.connect({wsUrl:Q})}catch(re){d=!1,e("error"),$();const me=re instanceof Error?re.message:"Realtime voice connect failed (WebSocket 101 only means upgrade OK — check authToken/origin/RTVI handshake)";throw new Error(`${me} — ${Q}`)}d=!0,e("idle"),$()},disconnect:async()=>{if(Y(),N=!1,w.length=0,b=!1,P?.abort(),P=null,D+=1,ke(),p)try{await p.disconnect()}catch(h){console.error("[realtimeVoice] disconnect failed:",h)}d=!1,I()},isConnected:()=>d,unlockAudio:async()=>{await k.primeAudioContext()},getVisemeQueue:()=>r,getWordQueue:()=>n,getFullVisemeQueue:()=>s,getFullWordQueue:()=>i,getSentenceEmotions:()=>a,getStreamMood:()=>c,getPlaybackElapsedMs:ce,isSpeaking:()=>k.isSpeaking(),isWarmingUp:()=>k.isWarmingUp(),releaseWarmupHold:()=>k.releaseWarmupHold(),setLipsyncWarmupHold:(h,R)=>{R&&(q=R),k.setLipsyncWarmupHoldEnabled(h)},getVoiceStream:()=>k.getVoiceStream(),setPlaybackSpeed:h=>{k.setPlaybackSpeed(h)},getPlaybackSpeed:()=>k.getPlaybackSpeed(),subscribe:h=>(C.add(h),()=>C.delete(h))}}function ts(e,t,r){return{width:e.crop?.source_width??t,height:e.crop?.source_height??r}}function rs(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 ns(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function pt(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 $s(n,t)}async function ss(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const r=await pt(e,t),n=new TextDecoder().decode(r);return JSON.parse(n)}async function yi(e){const t=new Blob([e],{type:"image/webp"});return createImageBitmap(t)}async function _i(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 Si(e){const t=new Blob([e],{type:"image/png"});return createImageBitmap(t)}async function vi(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 bi(e){const t=new Blob([e],{type:"video/mp4"});return URL.createObjectURL(t)}async function Ti(e){const t=Ks();if(!It())return t;if(!e)throw new Error("Missing asset key for encrypted idle video");const r=await pt(t,e);return bi(r)}function lt(e,t){return`${e}/${t}_minus_sil.png`}const is=5;function rr(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:is}const Ei=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],vr=["t01_0.25","t02_0.50","t03_0.75"],ki=vr.length,nn=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 sn(e,t){return`${e}__${t}`}function on(e,t){return`pairs/${e}/${t}_minus_sil.png`}function dt(e){return`${e}_minus_sil.png`}function Or(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=dt(n);return t.has(s)?s:null}function os(e,t){return e.filter(r=>t.has(r))}function Ii(e,t,r){const n=Nr(e,t);return os(n,r).length>0}function Nr(e,t){if(e===t)return[];const r=[],n=sn(e,t);if(nn.has(n)){for(const i of vr)r.push(on(n,i));return r}const s=sn(t,e);if(nn.has(s)){for(let i=ki-1;i>=0;i--)r.push(on(s,vr[i]));return r}return[]}function Ai(e){return e<16?[1]:e<24?[1]:e<35?[0,1,2]:[0,1,2]}function Pi(e,t){return Ai(t).map(n=>e[n]).filter(n=>n!=null)}function xi(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 as(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 Mi=[.32,.24,.16,.08,0],Ri=Mi.map(e=>`eyes_${e.toFixed(2)}`),Ci=["eyes_0.32","eyes_0.16","eyes_0.00"],Di=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],Le=2,Fi=-20,qt=16,us={fear:-20,anger:-20,sad:-10,neutral:0,love:0,happy:0,disgust:10};function Tt(e){return`brow_${e}`}function Ze(e){return us[e]}function Ue(e){return Math.max(Fi,Math.min(qt,e))}function Bi(e,t){if(e===t)return[];const r=e<t,n=r?Le:-Le,s=[];let i=e;if(i%Le!==0){const o=r?Math.ceil(i/Le)*Le:Math.floor(i/Le)*Le,a=Ue(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 an(e){const t=Ue(e);if(t%Le===0)return[t];const r=Math.floor(t/Le)*Le,n=Math.min(qt,r+Le),s=Ue(r);return t-s<=n-t?s===n?[s]:[s,n]:s===n?[n]:[n,s]}const Li=["fear","anger","sad","neutral","love","happy","disgust"],Oi={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 He(e,t="neutral"){const r=String(e??"").trim().toLowerCase();return r?Oi[r]??t:t}function cs(e){return e===0?null:lt(Tt(e),"eyes_standard")}function ls(e){return cs(Ze(e))}const Ni={[-20]:"anger",[-10]:"sad",0:"neutral",10:"disgust"};function Vr(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 an(s)){if(a===s)continue;const l=lt(Tt(a),i);if(t(l))return l}const o=Ni[s];if(o){const a=lt(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=Ze(s);for(const a of[o,...an(o)]){const l=lt(Tt(a),i);if(t(l))return l}}return null}function un(e,t){const r=Tt(Ze(e));return!t||t==="eyes_standard"?ls(e):e==="neutral"&&t==="eyes_standard"?null:lt(r,t)}function Vi(e,t){const r=[];for(const n of t)r.push(un(e,n));for(let n=t.length-2;n>=0;n--)r.push(un(e,t[n]));return r.push(ls(e)),r}function Ui(e){return lt(Tt(e),"eyes_standard")}function Wi(e,t){return Bi(e,t).map(Ui)}const $i=6;function Hi(e,t){for(const r of e)if(t>=r.wtime&&t<r.wtime+r.wduration)return r;return null}function Ki(e){const t=Ue(e);return t>=qt?qt:Ue(t+$i)}function qi(e,t,r){const n=Ue(r);return Hi(t,e)?.emphasis?Ki(n):n}function cn(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)?Ue(r):null}function ct(e,t){return e+Math.random()*(t-e)}function Vt(){return ct(14,32)}function Gi(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,m={kind:"idle",nextBlinkAt:performance.now()+t},A=null,x="__eyes_open__|neutral|brow:0";function C(P=c){x=`${A??"__eyes_open__"}|${P}|brow:${p}`}function w(P,D=c){A=P;const N=cn(P);N!=null?p=N:P==null&&(p=0),C(D)}function b(P){return cs(P)}function O(P,D,N){const j=Ue(D),F=Ue(N);if(d=F,j===F){w(b(F));return}const Y=[...Wi(j,F),b(F)];m={kind:"browBlend",paths:Y,index:0,holdUntil:P+Vt(),targetBrow:F},w(Y[0]??null)}function V(P,D){m={kind:"idle",nextBlinkAt:P+ct(r,n)},u=D,w(b(d),D)}function X(P,D,N){const j=N?Ci:Math.random()<a?Ri:Di;m={kind:"playing",paths:Vi(D,j),index:0,holdUntil:P+Vt(),after:"idle",blinkEmotion:D},u=D,w(m.paths[0]??null,D)}function fe(P){if(m.kind==="browBlend"){for(;P>=m.holdUntil&&m.index<m.paths.length-1;)m.index+=1,m.holdUntil=P+Vt();const D=m.paths[m.index]??null;w(D),P>=m.holdUntil&&m.index>=m.paths.length-1&&(p=m.targetBrow,d=m.targetBrow,w(b(m.targetBrow)),m={kind:"idle",nextBlinkAt:P+l});return}if(m.kind==="idle"){w(b(d),u),P>=m.nextBlinkAt&&X(P,u,!1);return}if(m.kind==="doublePause"){w(b(d),m.blinkEmotion),P>=m.resumeAt&&X(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+Vt();if(w(m.paths[m.index]??null,m.blinkEmotion),P>=m.holdUntil&&m.index>=m.paths.length-1){if(m.after==="resumeIdle"){V(P+l,c);return}if(m.after==="doublePause"){m={kind:"doublePause",resumeAt:P+ct(i,o),blinkEmotion:m.blinkEmotion},w(b(d),m.blinkEmotion);return}if(m.after==="idle"&&Math.random()<s){m={kind:"doublePause",resumeAt:P+ct(i,o),blinkEmotion:m.blinkEmotion},w(b(d),m.blinkEmotion);return}V(P,m.blinkEmotion)}}}return{advance:fe,setTargetEmotion(P,D){const N=He(P,c);if(N===c)return;const j=performance.now();!D?.force&&f>0&&!(c==="neutral"&&N!=="neutral")&&j-E<f||(c=N,u=N,E=j)},setTargetEyebrow(P){const D=Ue(P);if(D===d)return;const N=performance.now(),j=m.kind==="browBlend"?cn(A)??p:p;d=D,!(D===j&&m.kind!=="browBlend")&&(m.kind==="playing"||m.kind==="doublePause"||O(N,j,D))},snapToEmotion(P){const D=He(P),N=Ze(D);c=D,u=D,d=N,p=N,E=performance.now(),m={kind:"idle",nextBlinkAt:performance.now()+ct(4e3,7e3)},w(b(N),D)},getTargetEmotion:()=>c,getTargetEyebrow:()=>d,getExpressionPath:()=>A,getDrawKey:()=>x,reset(){c="neutral",u="neutral",d=0,p=0,E=0,V(performance.now()+ct(r,n),"neutral")}}}function zi(){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 ji=50,fs=400;function Yi(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=Yi(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-ji),endMs:o.wtime+o.wduration+fs})}return r.sort((n,s)=>n.startMs-s.startMs)}function Xi(e,t,r,n="neutral"){const s=He(n);if(t.length===0)return s;if(r.length===0||e<r[0].wtime)return He(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 He(o.sentence.emotion,s);const a=r[r.length-1],l=a.wtime+a.wduration;return e>l+fs?He(t[t.length-1]?.emotion,s):s}function Ur(e,t,r,n,s=is){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],m=d.data[p+1],A=d.data[p+2];Math.max(E,m,A)>s&&(l.data[p]=E,l.data[p+1]=m,l.data[p+2]=A,l.data[p+3]=255)}}return o.putImageData(l,0,0),i}let Xe=null,mt=null;const Gt=new Map;function Ji(){Xe=null,mt=null,Gt.clear()}xn(Ji);function Zi(e){return e??mt??void 0}function Wr(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 eo(e){return It()?vi(await pt(Kt(),e??"")):Wr(Kt())}async function ln(e,t,r,n){const s=r?await ss(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 _i(await pt(as(t,i.path,r),n??"")):await Wr(`${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 br(e){const t=Zi(e),r=It();if(r&&!t)throw new Error("Encrypted assets enabled but no key provided");if(Xe&&(!r||mt===(t??null))){const i=await Xe;return{...ht(i),visemePaths:[...i.viseme.cellByPath.keys()]}}mt=t??null,Gt.clear();const n=Mn();Xe=(async()=>{const i=await eo(t),o=await ln(Rn(),n,r,t);let a=null;try{a=await ln(Cn(),n,r,t)}catch{a=null}return{sil:i,viseme:o,expression:a}})();const s=await Xe;return{...ht(s),visemePaths:[...s.viseme.cellByPath.keys()]}}async function $r(e){if(await br(e),!Xe)throw new Error("Assets not loaded");return Xe}function ht(e){return ts(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function to(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 zt(e,t,r){const n=`${t.diffBase}::${r}`,s=Gt.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=rs(t.atlasMeta,o),l=ht(e);if(ns(o)){const u=to(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 Wr(f);if(c.naturalWidth===l.width&&c.naturalHeight===l.height)return c;throw new Error(`Diff PNG wrong size for ${r}`)})();return Gt.set(n,i),i}async function mr(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 m=document.createElement("canvas");m.width=p,m.height=E;const A=m.getContext("2d");if(A.clearRect(0,0,p,E),A.drawImage(t,0,0,p,E),i&&!i())return;(e.width!==p||e.height!==E)&&(e.width=p,e.height=E);const x=e.getContext("2d");x.clearRect(0,0,p,E),x.drawImage(m,0,0);return}const o=await $r(),a=rr(o.viseme.atlasMeta),{width:l,height:f}=ht(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=Ur(t,c,l,f,a);u.drawImage(d,0,0)}async function Tr(e,t,r){const n=await $r(mt??void 0),s=rr(n.viseme.atlasMeta),{width:i,height:o}=ht(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=Or(t.mouthPath,c)??(c.has(t.mouthPath)?t.mouthPath:null);u&&a.push(await zt(n,n.viseme,u))}if(t.expressionPath&&n.expression){const c=Vr(t.expressionPath,u=>n.expression.cellByPath.has(u));c&&a.push(await zt(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=Ur(n.sil,a,i,o,s);l.drawImage(f,0,0)}async function ro(e,t){const r=await $r(mt??void 0),n=rr(r.viseme.atlasMeta),{width:s,height:i}=ht(r),o=[];if(e.mouthPath){const c=new Set(r.viseme.cellByPath.keys()),u=Or(e.mouthPath,c)??(c.has(e.mouthPath)?e.mouthPath:null);u&&o.push(await zt(r,r.viseme,u))}if(e.expressionPath&&r.expression){const c=Vr(e.expressionPath,u=>r.expression.cellByPath.has(u));c&&o.push(await zt(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=Ur(r.sil,o,s,i,n);return l.drawImage(f,0,0),a.transferToImageBitmap()}async function no(e){await Tr(e,{mouthPath:null,expressionPath:null})}let ds=null;function fn(e){ds=e}function Ge(){return ds}function so(e){return`${e.from}→${e.to}@${e.fromVtime}`}function dn(){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 io(){let e=dn();const t=new Set,r=new Set,n=new Set;let s="",i=-1,o="";const a=()=>{e=dn(),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=so(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 C of c)if(C.viseme!=="sil"){if(C.vtime>=u)break;d+=1,t.has(C.vtime)&&(p+=1)}const E=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:E,transitionFramesUniqueSampled:r.size,transitionFramesUniquePainted:n.size,drawsDeduped:e.rafTicksDeduped,renderSkips:e.rafTicksDeduped+m,visemesShown:p,visemesSkipped:A,rifeTransitionFramesSkipped:e.transitionFramesMissed}}}}const ms=Ar,oo=200,mn=300,ao=100,uo=600,hs="__sil__";function hn(e){return e.lastDrawnKey?e.lastDrawnKey.split("|")[0]===hs:!1}function Hr(){return{lastDrawnKey:""}}function xe(e){e.lastDrawnKey="",e.transitionCache=void 0}function co(e,t){if(t?.transitionCache&&t.transitionCache.queueLength===e.length)return t.transitionCache.transitions;const r=qn(e);return t&&(t.transitionCache={queueLength:e.length,transitions:r}),r}function lo(e,t){let r="sil";for(const n of e)n.viseme!=="sil"&&(t!=null&&n.vtime>t||(r=n.viseme));return r}function fo(e,t,r,n=120){if(r!=="sil")return r;const s=Jn(e,t,n);return s!=="sil"?s:lo(e,t)}function mo(e,t){return e==="sil"?!1:t?Ii(e,"sil",t):Nr(e,"sil").length>0}function ho(){return ms+uo}function jt(e){return e??hs}function Kr(e,t,r,n,s=ao){if(e.length===0)return{diffPath:null,label:"sil"};if(t>=Qe(e))return{diffPath:null,label:"sil"};const i=co(e,n),o=ii(i,t);if(o){const f=Gn(o.fromVtime,o.toVtime,o.from,o.to);if(t>=f.transStart&&t<f.transEnd){let c=Nr(o.from,o.to);r&&(c=os(c,r));const u=Pi(c,f.effectiveGapMs);if(u.length>0){const d=xi(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:dt(o.to),label:o.to}}}const a=Jt(e);let l=Wn(a,t);return l==="sil"&&(l=Jn(e,t,s)),l==="sil"?{diffPath:null,label:"sil"}:{diffPath:dt(l),label:l}}function Et(e,t){const r=t??jt(e.expressionPath);return`${jt(e.mouthPath)}|${r}`}function pn(e,t,r,n,s,i,o,a,l,f){const c=Kr(t,r,l,i),u={mouthPath:c.diffPath,expressionPath:n},d=Et(u,s),p=Ge();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 hr(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=Et(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 ps(e,t,r,n,s=bt,i){if(r<=t||e.length===0)return[];const o=i??Hr(),a=[];let l="";for(let f=t;f<r;f+=s){const c=Kr(e,f,n,o),u=jt(c.diffPath);u!==l&&(l=u,a.push({displayAtMs:f,mouthPath:c.diffPath,mouthKey:u,label:c.label}))}return a}const po=3;function qr(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(),Ge()?.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 wo(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 go(){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 yo(e,t,r,n,s,i){const o=ps(t,r,n,s,bt,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 _o(e){const t=go(),r=Hr(),n=new Set;let s="",i=null;const o=wo(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),Ge()?.onBufferPrefetched()}finally{n.delete(u.mouthKey)}},Rs),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=lr)=>{if(!e.supportsBufferedPlayback)return;const m=Math.max(0,d-bt*4),A=ps(u,m,d+E,p,bt,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(),xe(r),s="",i=null,Ge()?.onBufferReset()},markExternallyDisplayed(u,d){f(u.split("|")[0]??u,d)},warmupTick(u){e.supportsBufferedPlayback&&l(u.queue,0,u.availablePaths,Math.max(Xr,lr))},isWarmupReady(u,d=Xr){if(!e.supportsBufferedPlayback)return!0;const{ready:p,total:E}=yo(t,u.queue,0,d,u.availablePaths,r);return E===0?!1:p/E>=Ms},getBufferedFrameCount:()=>t.size(),tick(u){const d=Ge();d?.onBufferTick(),l(u.queue,u.elapsedMs,u.availablePaths),t.pruneBefore(u.elapsedMs-lr);const p=Kr(u.queue,u.elapsedMs,u.availablePaths,r),E=jt(p.diffPath),m=Et({mouthPath:p.diffPath,expressionPath:u.expressionPath},u.expressionDrawKey),A=()=>(f(E,u.expressionPath),d?.onBufferHit(),{usedBuffer:!0,underrun:!1,held:!0});if(c(E,u.expressionPath)||u.playbackState.lastDrawnKey===m)return A();const x=t.findForMouthKey(E,u.elapsedMs,bt);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 So=`
|
|
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
|
-
`,
|
|
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 wn(e=5){const t=new ue.Filter({glProgram:ue.GlProgram.from({vertex:So,fragment:vo,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 bo(e,t,r){return e>r||t>r}function To(e){return e?e.getParameter(e.MAX_TEXTURE_SIZE):4096}function Eo(e,t){return t!=null&&t>0?Math.floor(t):To(e)}function ko(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 ws(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 gn(e,t,r,n,s){const i=r?await ss(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=as(t,d,r),E=r?await yi(await pt(p,s??"")):await ws(p);if(bo(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 m=ue.Texture.from(E);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 Io(e,t={}){const r=new ue.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(g){throw r.destroy({removeView:!1}),g}if(r.renderer.type!==ue.RendererType.WEBGL)throw r.destroy({removeView:!1}),new Error("Pixi did not initialize a WebGL renderer");const n=Eo(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 ue.Sprite(ue.Texture.EMPTY),c=new ue.Sprite(ue.Texture.EMPTY);c.visible=!1,c.alpha=0;const u=new ue.Sprite(ue.Texture.EMPTY),d=new ue.Sprite(ue.Texture.EMPTY),p=new ue.Sprite(ue.Texture.EMPTY);p.visible=!1;const E=wn(5),m=wn(5);u.filters=[E],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,C=null,w=null,b=null,O=null,V=null,X=842,fe=1264,P=null,D=5;const N=new Map;let j=null,F=null,Y=null;const $=xn(()=>{N.clear(),O=null,V=null,A=null,P=null}),q=(g,_)=>{X=g,fe=_,r.renderer.resize(g,_),(e.width!==g||e.height!==_)&&(e.width=g,e.height=_)},k=()=>{u.visible=!1,d.visible=!1,u.texture=ue.Texture.EMPTY,d.texture=ue.Texture.EMPTY,j=null,F=null},ee=()=>{b=null,c.visible=!1,c.alpha=0},he=()=>{f.visible=!0,f.width=X,f.height=fe,f.x=0,f.y=0},ce=()=>{c.width=X,c.height=fe,c.x=0,c.y=0},te=()=>{p.visible=!1,ee(),A&&(f.texture=A,he())},J=()=>{!b&&!c.visible||te()},ke=g=>{if(o||s||!A){te();return}const _=Math.max(0,g);if(!x||_<=0){te();return}k(),p.visible=!1,f.texture=x,he(),c.texture=A,ce(),c.alpha=0,c.visible=!0,b={startMs:performance.now(),durationMs:_},W()},Me=(g=performance.now())=>{if(!b)return!1;if(o||s)return J(),!1;const _=(g-b.startMs)/Math.max(1,b.durationMs);if(_>=1)return te(),W(),!1;const y=Math.min(1,Math.max(0,_)),Z=y*y*(3-2*y);return c.alpha=Z,c.visible=!0,W(),!0},S=()=>{if(C){try{C.resource=null}catch{}C.destroy(),C=null}x&&(x.destroy(!1),x=null),w=null},M=(g,_)=>{const y=g.cellByPath.get(_);if(!y||!ns(y))return null;const Z=`${g.diffBase}::${_}`;let oe=N.get(Z);if(!oe){const ye=y.sheet??0,Re=g.sheets.get(ye);if(!Re)return null;let _e,Oe=!1;if(Re.mode==="full")_e=new ue.Texture({source:Re.texture.source,frame:new ue.Rectangle(y.x,y.y,y.w,y.h)});else{const wt=ko(Re.bitmap,y);_e=ue.Texture.from(wt),_e.source.scaleMode="nearest",Oe=!0}oe={texture:_e,ownsSource:Oe},N.set(Z,oe)}return{texture:oe.texture,cell:y}},G=(g,_,y,Z)=>{if(!Z||!y)return g.visible=!1,g.texture=ue.Texture.EMPTY,!1;const oe=M(y,Z);if(!oe)return g.visible=!1,g.texture=ue.Texture.EMPTY,!1;const ye=rs(y.atlasMeta,oe.cell);return _.setThreshold(D),g.texture=oe.texture,g.x=ye.x,g.y=ye.y,g.width=oe.cell.w,g.height=oe.cell.h,g.visible=!0,!0},z=g=>{if(!g||!O)return null;const _=P??new Set(O.cellByPath.keys());return Or(g,_)??(_.has(g)?g:null)},I=g=>{p.visible=!1,te();const _=z(g.mouthPath),y=g.expressionPath&&V?Vr(g.expressionPath,Z=>V.cellByPath.has(Z)):null;_!==j?(G(u,E,O,_),j=_):_?u.visible=!0:u.visible=!1,y!==F?(G(d,m,V,y),F=y):y?d.visible=!0:d.visible=!1},W=()=>{if(!(o||s||document.hidden))try{r.render()}catch{}};return{async loadAssets(g){if(o)return{width:X,height:fe,visemePaths:[]};const _=It();if(_&&!g)throw new Error("Encrypted assets enabled but no key provided");const y=_?await Si(await pt(Kt(),g??"")):await ws(Kt());A&&A.destroy(!0),A=ue.Texture.from(y),A.source.scaleMode="linear";const Z=Mn();O=await gn(Rn(),Z,_,n,g);try{V=await gn(Cn(),Z,_,n,g)}catch(ye){console.warn("[PixiVisemeCompositor] expression atlas unavailable:",ye),V=null}const oe=ts(O.atlasMeta,y.width,y.height);return q(oe.width,oe.height),D=rr(O.atlasMeta),E.setThreshold(D),m.setThreshold(D),P=new Set(O.cellByPath.keys()),N.clear(),te(),k(),W(),{width:oe.width,height:oe.height,visemePaths:[...O.cellByPath.keys()]}},getVisemeAtlasPaths(){return P},drawLiveFrame(g){o||s||!A||(J(),I(g),W())},drawIdleVideo(g,_){o||s||document.hidden||_&&!_()||g.readyState<HTMLMediaElement.HAVE_CURRENT_DATA||(ee(),k(),p.visible=!1,!x||w!==g?(S(),C=new ue.VideoSource({resource:g,autoPlay:!1,updateFPS:0}),C.autoUpdate=!1,x=new ue.Texture({source:C}),w=g):x.source.update(),f.texture=x,he(),W())},beginIdleToSilCrossfade:ke,tickIdleToSilCrossfade:Me,finishIdleToSilCrossfade:J,isIdleToSilCrossfading(){return b!=null},renderSilOnly(){o||s||!A||(k(),te(),W())},blitBitmap(g){o||s||(J(),k(),Y&&(Y.destroy(!0),Y=null),Y=ue.Texture.from(g),p.texture=Y,p.visible=!0,p.x=0,p.y=0,p.width=X,p.height=fe,f.visible=!1,W())},async compositePrefetchFrame(g){return null},renderFrame:W,isContextLost(){return s},dispose(){if(o)return;o=!0,a.removeEventListener("webglcontextlost",l),$(),S(),Y&&(Y.destroy(!0),Y=null);for(const{texture:_,ownsSource:y}of N.values())_.destroy(y);N.clear(),A&&(A.destroy(!0),A=null);const g=_=>{_.mode==="full"?_.texture.destroy(!0):_.bitmap.close()};if(O)for(const _ of O.sheets.values())g(_);if(V)for(const _ of V.sheets.values())g(_);E.destroy(!0),m.destroy(!0),r.destroy({removeView:!1},{children:!0,texture:!0})}}}const yn=-1,Ut=1e5,_n=new Map;async function Ao(e){let t=_n.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),_n.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function Po(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function Er(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 Sn(e,t){let r=0,n=null;const s=qr(async(i,o)=>(await br(),await Tr(e,i),s.isLatestSeq(o)));return{usesWorker:!1,usesPixi:!1,async loadAssets(i){const o=await br(i);return n=o.visemePaths?new Set(o.visemePaths):null,o},getVisemeAtlasPaths(){return n},async renderSilOnly(){r+=1,s.reset(),await no(e)},drawLiveFrame(i,o,a,l){s.enqueue(o,a,l)},async compositePrefetchFrame(i,o){try{return await ro(i)}catch{return null}},blitBitmap(i,o){Er(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 Tr(i,{mouthPath:dt(o),expressionPath:null})},dispose(){r+=1,s.reset()}}}function xo(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=Ut;const d=qr(async(C,w,b)=>(f.set(w,{drawKey:b.drawKey,onDisplayed:b.onDisplayed}),await A({type:"drawFrame",diffPath:C.mouthPath,expressionDiffPath:C.expressionPath,liveSeq:w},void 0,!0),!1)),p=C=>{if(C===yn){let O=-1,V;for(const[X,fe]of f)X>=O&&(O=X,V=fe);O>=0&&f.delete(O),V?.onDisplayed?.(V.drawKey);return}const w=-C,b=f.get(w);f.delete(w),b?.onDisplayed?.(b.drawKey)},E=new Map,m=(C,w)=>{a||!n||n.postMessage(C,[])},A=(C,w,b=!1)=>{if(a)return Promise.resolve();const O=s++;return new Promise((V,X)=>{E.set(O,{resolve:fe=>V(fe),reject:X,generation:typeof C.generation=="number"?C.generation:void 0,expectRenderDone:b}),m({...C,requestId:O})})},x=(async()=>{n=await Ao(r),n.onmessage=C=>{if(a)return;const w=C.data;if(w.type==="status"){t.onStatus?.(w.label);return}if(w.type==="frame"){if(w.generation<=-Ut){const V=-w.generation,X=c.get(V);c.delete(V),X?.resolve(w.bitmap);return}const O=w.generation<0;if(!O&&w.generation!==i){w.bitmap.close();return}if(O&&!(w.generation===yn)){const X=-w.generation;if(!d.isLatestSeq(X)){Ge()?.onWorkerStale(),w.bitmap.close();return}}if(O&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){Ge()?.onWorkerStale(),w.bitmap.close();return}Er(e,w.bitmap),w.bitmap.close(),O&&p(w.generation);return}const b=E.get(w.requestId);if(b)switch(w.type){case"ready":E.delete(w.requestId),b.resolve();break;case"loadAssetsDone":E.delete(w.requestId),l=w.visemePaths?.length?new Set(w.visemePaths):null,b.resolve({width:w.width,height:w.height,visemePaths:w.visemePaths});break;case"renderDone":if(b.expectRenderDone&&b.generation!=null&&w.generation!==b.generation)return;E.delete(w.requestId),b.resolve();break;case"renderAborted":E.delete(w.requestId),b.resolve();break;case"error":E.delete(w.requestId),t.onError?.(w.message),b.reject(new Error(w.message));break}},n.onerror=C=>{if(a)return;const w=C.message||"Worker error";t.onError?.(w);for(const[,b]of E)b.reject(new Error(w));E.clear()},await A({type:"init"}),o=!0})();return{usesWorker:!0,usesPixi:!1,async loadAssets(C){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:C,urls:Hs(rt())})??{width:842,height:1264}},getVisemeAtlasPaths(){return l},async renderSilOnly(){if(a||(await x,a))return;i+=1,d.reset(),f.clear(),c.forEach(({resolve:w})=>w(null)),c.clear(),u=Ut;const C=i;await A({type:"cancelLiveDraws"}),await A({type:"renderSil",generation:C},void 0,!0)},drawLiveFrame(C,w,b,O){a||x.then(()=>{a||d.enqueue(w,b,O)})},compositePrefetchFrame(C,w){return a?Promise.resolve(null):x.then(()=>{if(a)return null;const b=++u;return new Promise(O=>{c.set(b,{resolve:O}),A({type:"drawFrame",diffPath:C.mouthPath,expressionDiffPath:C.expressionPath,liveSeq:b},void 0,!0).catch(()=>{c.delete(b),O(null)})})})},blitBitmap(C,w){Er(C,w)},drawIdleVideo(){},beginIdleToSilCrossfade(){},tickIdleToSilCrossfade(){return!1},finishIdleToSilCrossfade(){},isIdleToSilCrossfading(){return!1},supportsBufferedPlayback:!0,async renderViseme(C,w,b={}){if(a||(await x,a))return;i+=1,d.reset(),f.clear();const O=i;try{await A({type:"cancelLiveDraws"}),await A({type:"renderViseme",generation:O,to:w,from:b.from,transitionDurationMs:b.transitionDurationMs,gapMs:b.gapMs,threshold:b.threshold},void 0,!0)}catch(V){if(V?.name==="AbortError")return;throw V}},dispose(){if(!a){a=!0,i+=1,d.reset(),f.clear(),c.forEach(({resolve:C})=>C(null)),c.clear(),u=Ut;for(const[,C]of E)C.resolve();E.clear(),n?.terminate(),n=null}}}}async function Mo(e,t){const r=await Io(e,{onWebGlContextLost:t.onWebGlContextLost,maxTextureSize:t.pixiMaxTextureSize});let n=0,s=null,i=!1;const o=qr(async(a,l)=>i?!1:t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()?(Ge()?.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:dt(l),expressionPath:null})},dispose(){i||(i=!0,n+=1,o.reset(),r.dispose())}}}function vn(e,t){if(!Po())return Sn(e,t);const r=As(t.workerScriptUrl);try{return xo(e,t,r)}catch{return Sn(e,t)}}function Ro(e,t={}){if(t.preferLegacyRenderer)return vn(e,t);let r=null,n=!1,s=null,i=!1;const o=()=>r?Promise.resolve(r):(s||(s=(async()=>{try{const a=await Mo(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=vn(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 Co extends Error{constructor(t,r){super(`${t} timed out after ${Math.round(r/1e3)}s`),this.name="LoadTimeoutError"}}function pr(e,t,r){return new Promise((n,s)=>{const i=globalThis.setTimeout(()=>{s(new Co(r,t))},t);e.then(o=>{globalThis.clearTimeout(i),n(o)},o=>{globalThis.clearTimeout(i),s(o)})})}const Do={position:"relative",display:"inline-block",lineHeight:0},Fo={display:"block",maxWidth:"100%",height:"auto"},Bo={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 vt(e){return Cs(e)?Fr:Dr}function $e(e,t){return t?.isSpeaking()?t:e.isSpeaking()?e:t?.isConnected()?t:e}const Lo=T.forwardRef(function({id:t,avatarId:r,faceId:n,assets:s,authToken:i,apiBase:o,ttsEngineId:a=Je,voiceId:l,speakingRate:f=Pt,className:c,style:u,canvasStyle:d,onStatusChange:p,onDisplayStatus:E,onError:m,onReady:A,showErrorOverlay:x=!0,onUserTranscript:C,onBotOutput:w,onRealtimeLipsyncDebug:b,onFrameRenderStats:O,lipsyncBufferedPlayback:V=!0,lipsyncPlaybackWarmupMs:X=1500,pixiMaxTextureSize:fe},P){const D=T.useRef(null),N=T.useRef(null),j=T.useRef(null),F=T.useRef(V);F.current=V;const Y=T.useRef(X);Y.current=X;const $=T.useRef(0),q=T.useRef(Hr()),k=T.useRef(Gi()),ee=T.useRef(!1),he=T.useRef(null),ce=T.useRef(null),te=T.useRef(0),J=T.useRef(0),ke=T.useRef(0),Me=T.useRef(null),S=T.useRef(null),M=T.useRef(!1),G=T.useRef(!1),z=T.useRef(!1),I=T.useRef(!1),W=T.useRef("sil"),g=T.useRef(0),_=T.useRef(!1),y=T.useRef(null),Z=T.useRef(!1),[oe,ye]=T.useState("idle"),[Re,_e]=T.useState(null),[Oe,wt]=T.useState(!1),[Gr,xt]=T.useState(!1),[nr,h]=T.useState({width:842,height:1264}),[R,Q]=T.useState(0),re=T.useRef(!1),me=T.useRef(i??null),de=o?.trim()||kt,pe=T.useRef(null),we=T.useRef(null),ve=T.useRef(!1),nt=T.useRef({width:842,height:1264}),We=T.useRef(null),Ke=T.useRef("sil"),ze=T.useRef(void 0),gt=T.useRef(new Map),Mt=T.useRef(A),se=T.useRef(E),yt=T.useRef(m),_t=T.useRef(C),st=T.useRef(w),ge=T.useRef(b),Ce=T.useRef(O),Te=T.useRef(io());Mt.current=A,se.current=E,yt.current=m,_t.current=C,st.current=w,ge.current=b,Ce.current=O,T.useEffect(()=>{const v=Te.current;return fn(v),()=>fn(null)},[]);const it=T.useCallback(v=>{const B=v?.ttsEngineId??a??we.current?.ttsEngineId??Je,U=v?.voiceId??l??we.current?.voiceId??vt(B);return{ttsEngineId:B,voiceId:U}},[a,l]),Be=T.useCallback(v=>{_e(v),yt.current?.(v)},[]),je=T.useCallback(v=>{ye(v),p?.(v)},[p]),ie=T.useRef(Sr(je,de,{onLipsyncDebug:v=>ge.current?.(v)})),le=T.useRef(null);T.useEffect(()=>(ie.current=Sr(je,de,{onLipsyncDebug:v=>ge.current?.(v)}),i&&ie.current.setDeveloperToken(i),ie.current.setTtsEngineId(a),()=>{ie.current.stop();const v=le.current;v&&(v.disconnect(),le.current=null)}),[je,i,a,de]);const zr=T.useCallback(()=>(le.current||(le.current=es(je,{speakingRate:f,onUserTranscript:(v,B)=>_t.current?.(v,B),onBotOutput:v=>st.current?.(v),onLipsyncDebug:v=>ge.current?.(v)}),le.current.setTtsEngineId(a)),le.current),[je,f,a]),Ie=T.useCallback(()=>{Me.current!=null&&(clearTimeout(Me.current),Me.current=null)},[]),Ae=T.useCallback(()=>{S.current!=null&&(clearTimeout(S.current),S.current=null),z.current=!1},[]),Rt=T.useCallback(()=>{if(I.current)return;const v=N.current;if(I.current=!0,Ae(),M.current=!1,G.current=!1,!Z.current||!y.current){v?.renderSilOnly();return}y.current.setActive(!1);const B=y.current.getVideo();if(v?.usesPixi){v.drawIdleVideo(B),v.beginIdleToSilCrossfade(mn);return}(async()=>{const U=D.current;U&&await mr(U,B,null,void 0,nt.current).catch(()=>{}),await new Promise(L=>setTimeout(L,mn)),I.current&&await v?.renderSilOnly()})()},[Ae]),jr=T.useCallback(()=>{!Z.current||!y.current||(I.current=!1,N.current?.finishIdleToSilCrossfade(),Ae(),z.current=!0,S.current=setTimeout(()=>{if(S.current=null,$e(ie.current,le.current).isSpeaking()){z.current=!1;return}if(M.current){z.current=!1;return}const v=y.current;if(!v){z.current=!1;return}v.restart(),v.setActive(!0);const B=v.getVideo();let U=!1;const L=()=>{U||(U=!0,B.removeEventListener("playing",H),z.current=!1)},H=()=>{L()};if(B.addEventListener("playing",H,{once:!0}),B.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&!B.paused){L();return}setTimeout(L,300)},oo))},[Ae]),ot=T.useCallback(()=>{if(Ie(),se.current?.("sil"),M.current=!1,I.current=!1,!Z.current||!y.current){z.current=!1;return}jr()},[Ie,jr]),sr=T.useCallback(async v=>{const B=J.current,U=N.current,L=D.current,H=()=>{v?.fallbackOnly||(M.current=!1)};if(!U||!L||!Oe){H();return}if($e(ie.current,le.current).isSpeaking()){H();return}if(v?.fallbackOnly){if(!M.current||(xe(q.current),await U.renderSilOnly(),B!==J.current)||$e(ie.current,le.current).isSpeaking())return;ot();return}if(hn(q.current)){xe(q.current),ot();return}if(F.current&&U.supportsBufferedPlayback&&!v?.fallbackOnly){if(xe(q.current),j.current?.reset(),await U.renderSilOnly(),B!==J.current||$e(ie.current,le.current).isSpeaking())return;ot();return}const Ee=fo($e(ie.current,le.current).getVisemeQueue(),g.current,W.current),De=mo(Ee,We.current);if(xe(q.current),!De){if(await U.renderSilOnly(),B!==J.current||$e(ie.current,le.current).isSpeaking())return;ot();return}try{await U.renderViseme(L,"sil",{from:Ee,transitionDurationMs:ms})}catch(Pe){if(Pe?.name!=="AbortError")throw Pe}if(B!==J.current){H();return}if($e(ie.current,le.current).isSpeaking()){H();return}ot()},[Oe,ot]),at=T.useCallback(()=>{if(G.current||M.current)return;G.current=!0,M.current=!0,Ie();const v=J.current;sr().finally(()=>{G.current=!1}),Me.current=setTimeout(()=>{Me.current=null,v===J.current&&sr({fallbackOnly:!0})},ho())},[Ie,sr]),Yr=T.useCallback(async()=>{const v=Math.floor(Date.now()/1e3);if(pe.current&&v<pe.current.expiresAt-60)return pe.current.key;const B=me.current??await Wt();me.current=B,ie.current.setDeveloperToken(B);const U=await Vs(B,de);return pe.current={key:U.value,expiresAt:U.expiresAt},U.value},[de]);T.useEffect(()=>{i&&(me.current=i,ie.current.setDeveloperToken(i))},[i]),T.useEffect(()=>{ie.current.setTtsEngineId(a),le.current?.setTtsEngineId(a),we.current&&(we.current.ttsEngineId=a)},[a]),T.useEffect(()=>{const v=l??vt(a);we.current&&(we.current.voiceId=v)},[l,a]);const Ye=T.useMemo(()=>An(r,n),[r,n]),_s=T.useMemo(()=>s?JSON.stringify(s):Ye?`face:${Ye}`:`id:${t??""}`,[s,Ye,t]);return T.useEffect(()=>{if(!t&&!s&&!Ye){Be("AiTwin requires `assets`, `avatarId`/`faceId`, or `id`");return}let v=!1;ve.current=!1,wt(!1),xt(!1),_e(null),we.current=null,y.current=null,Z.current=!1;const B=D.current;if(!B)return;let U=null;return(async()=>{try{let L,H,be,Ee;if(s)L=s,H=a,be=l??vt(H);else if(Ye)se.current?.("loadFace"),L=yr(Ye),H=a,be=l??vt(H);else{se.current?.("getAiTwin");const K=await pr(In(t),3e4,"getAiTwin");if(v)return;if(!K.faceId?.trim())throw new Error("getAiTwin returned no faceId");H=K.ttsEngineId,be=l??K.voiceId??vt(H),Ee=K.knowledgeContextId?.trim()||void 0,L=yr(K.faceId)}we.current={ttsEngineId:H,voiceId:be,personaId:Ee},Pn(L),y.current=zi();const De=!!(s&&!tt(s));U=Ro(B,{preferLegacyRenderer:De,pixiMaxTextureSize:fe,onStatus:K=>{v||se.current?.(K)},onError:K=>{v||Be(K)},shouldAcceptLiveFrame:()=>{if(ee.current)return!0;if(M.current)return!1;const K=$e(ie.current,le.current);return K.isSpeaking()||K.isWarmingUp()},onWebGlContextLost:()=>{v||re.current||(re.current=!0,queueMicrotask(()=>{v||Q(K=>K+1)}))}}),N.current=U,j.current=_o(U),re.current=!1,(K=>{const ne=()=>{if(!K||Y.current<=0)return!1;const Se=N.current;return Se?F.current&&Se.supportsBufferedPlayback?!0:Se.usesPixi:!1};ie.current.setLipsyncWarmupHold(K,ne),le.current?.setLipsyncWarmupHold(K,ne)})(!0),se.current?.("auth");const Ne=me.current??i??await pr(Wt(),3e4,"getAuthToken");if(v)return;me.current=Ne,ie.current.setDeveloperToken(Ne),ie.current.setTtsEngineId(H);const Ct=It()?await Yr():void 0;if(v)return;se.current?.("loadAssets");const ae=await pr(U.loadAssets(Ct),18e4,"load face assets");if(!v&&ae.width>0&&ae.height>0&&(nt.current=ae,h(ae)),v)return;We.current=U.getVisemeAtlasPaths(),ve.current=!0,wt(!0),Mt.current?.(),U.renderSilOnly().then(()=>{v||se.current?.("sil")}),(async()=>{try{const K=await Ti(Ct);if(await y.current.load(K),v)return;Z.current=!0,xt(!0),xe(q.current),y.current.setActive(!0);const ne=y.current.getVideo();U.usesPixi?U.drawIdleVideo(ne):await mr(B,ne,null,void 0,nt.current),v||se.current?.("idle")}catch(K){if(v)return;Z.current=!1,xt(!1),console.warn("[AiTwin] Idle video unavailable:",K)}})()}catch(L){if(v)return;if(L instanceof $t){Be(L.message);return}Be(L instanceof Error?L.message:"Failed to load AI twin")}})(),()=>{v=!0,y.current?.dispose(),U?.dispose(),N.current=null}},[_s,s,Ye,t,a,l,i,fe,Yr,Be,R]),T.useEffect(()=>{const v={current:!1},B=()=>{const U=D.current,L=N.current,H=$e(ie.current,le.current);if(!U||!L||!Oe){te.current=requestAnimationFrame(B);return}if(document.hidden){y.current?.setActive(!1),te.current=requestAnimationFrame(B);return}const be=H.isSpeaking(),Ee=H.isWarmingUp(),De=be&&!_.current,Pe=_.current&&!be;De&&(Ie(),Ae(),M.current=!1,G.current=!1,xe(q.current),j.current?.reset(),Te.current.reset(),ze.current=void 0,ke.current=0,W.current="sil",g.current=0,Rt()),Pe&&(k.current.reset(),y.current?.setActive(!1),j.current?.reset(),L.finishIdleToSilCrossfade(),at());const Ne=Pe||M.current||z.current||I.current,Ct=V&&L.supportsBufferedPlayback;if(Ee){if(Rt(),Z.current&&y.current?.setActive(!1),L.usesPixi&&L.isIdleToSilCrossfading()){L.tickIdleToSilCrossfade(performance.now()),H.getVisemeQueue().length>0&&$.current===0&&($.current=performance.now()),_.current=H.isSpeaking(),te.current=requestAnimationFrame(B);return}const ae=H.getVisemeQueue();ae.length>0&&$.current===0&&($.current=performance.now()),k.current.advance(performance.now());const K=k.current.getExpressionPath(),ne=k.current.getDrawKey(),Se={queue:ae,elapsedMs:0,expressionPath:K,expressionDrawKey:ne,availablePaths:We.current};let Fe=!1;Ct&&j.current?(j.current.warmupTick(Se),Fe=j.current.isWarmupReady(Se,Y.current)):L.usesPixi&&pn(U,ae,0,K,ne,q.current,(ut,Dt,ir,Ft)=>{L.drawLiveFrame(ut,Dt,ir,Ve=>{Ft?.(Ve),H.isWarmingUp()&&Ve!==""&&!hn({lastDrawnKey:Ve})&&($.current=0,H.releaseWarmupHold())})},ut=>{se.current?.(ut.label)},We.current);const qe=$.current>0&&performance.now()-$.current>xs;se.current?.(Fe?"buffer ready":"buffering lipsync…"),(Fe||qe)&&($.current=0,H.releaseWarmupHold()),_.current=H.isSpeaking(),te.current=requestAnimationFrame(B);return}if(be){Rt(),Z.current&&y.current?.setActive(!1),L.usesPixi&&L.isIdleToSilCrossfading()&&L.finishIdleToSilCrossfade();const ae=H.getVisemeQueue(),K=H.getPlaybackElapsedMs();ci(ae,K),g.current=K;const ne=ae.length>0?At(ae,K):"sil";ne!=="sil"&&(W.current=ne);const Se=Xi(K,H.getSentenceEmotions(),H.getWordQueue(),H.getStreamMood());k.current.setTargetEmotion(Se),k.current.setTargetEyebrow(qi(K,H.getWordQueue(),Ze(Se))),k.current.advance(performance.now());const Fe=k.current.getExpressionPath(),qe=k.current.getDrawKey(),ut=(Ve,or,St,ar)=>{const Qr={label:Ke.current,elapsedMs:K,mouthPath:or.mouthPath,transitionMeta:ze.current};gt.current.set(St,Qr),j.current?.markExternallyDisplayed(St,Fe),L.drawLiveFrame(Ve,or,St,Bt=>{const Ss=gt.current.get(Bt)??Qr;gt.current.delete(Bt),Te.current.onDrawDisplayed(Ss,ae),j.current?.markExternallyDisplayed(Bt,Fe),ar?.(Bt)})},Dt=Ve=>{Ke.current=Ve.label,ze.current=Ve.transitionMeta,se.current?.(Ve.label)},ir=V&&L.supportsBufferedPlayback;let Ft=!1;ir&&j.current&&(Ft=j.current.tick({canvas:U,queue:ae,elapsedMs:K,expressionPath:Fe,expressionDrawKey:qe,playbackState:q.current,availablePaths:We.current,onStatus:Dt,onDisplayed:(or,St,ar)=>{Te.current.onDrawDisplayed({label:ar,elapsedMs:K,mouthPath:St,transitionMeta:ze.current},ae)}}).usedBuffer),Ft||pn(U,ae,K,Fe,qe,q.current,ut,Dt,We.current),Te.current.onRafTickSpeaking(),Ce.current?.(Te.current.snapshot(ae,K))}else if(!Ne&&ee.current){y.current?.setActive(!1),k.current.advance(performance.now());const ae=k.current.getExpressionPath(),K=k.current.getDrawKey();hr(U,ae,K,q.current,(ne,Se,Fe,qe)=>L.drawLiveFrame(ne,Se,Fe,qe),ne=>{if(ce.current){se.current?.(ce.current);return}const Se=k.current.getTargetEmotion();se.current?.(ne==="sil"||ne==="blink"?`${Se}${ne==="blink"?" · blink":""}`:ne)},null,he.current)}else if(!Ne&&Z.current&&y.current){const ae=y.current,K=ae.getVideo();if(K.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA){ae.setActive(!0);const ne=()=>!H.isSpeaking()&&!M.current&&!z.current&&!ee.current;L.usesPixi?(L.drawIdleVideo(K,ne),ne()&&se.current?.("idle")):v.current||(v.current=!0,mr(U,K,null,void 0,nt.current,ne).then(()=>se.current?.("idle")).finally(()=>{v.current=!1}))}else hr(U,null,"__sil__",q.current,(ne,Se,Fe,qe)=>L.drawLiveFrame(ne,Se,Fe,qe),ne=>se.current?.(ne))}else Ne||hr(U,null,"__sil__",q.current,(ae,K,ne,Se)=>L.drawLiveFrame(ae,K,ne,Se),ae=>se.current?.(ae));_.current=be,te.current=requestAnimationFrame(B)};return te.current=requestAnimationFrame(B),()=>{cancelAnimationFrame(te.current),Ie(),Ae(),ie.current.stop(),le.current?.stop()}},[Oe,Gr,at,Ie,Ae,Rt]),T.useImperativeHandle(P,()=>({speakText:async(v,B)=>{const U=v.trim();if(!U)return;const L=N.current;if(!L||!ve.current&&!Oe)throw new Error("AI twin is not ready");const{ttsEngineId:H,voiceId:be}=it(B);_e(null),J.current+=1,Ie(),Ae(),M.current=!1,G.current=!1,W.current="sil",g.current=0,xe(q.current),ee.current=!1,he.current=null,ce.current=null,k.current.reset(),_.current=!1,I.current=!1,L.finishIdleToSilCrossfade();try{await ie.current.speak(U,{voiceId:be,speakingRate:B?.speakingRate??f,ttsEngineId:H})}catch(Ee){throw I.current=!1,L.finishIdleToSilCrossfade(),at(),Be(Ee instanceof Error?Ee.message:"TTS failed"),Ee}},stop:()=>{J.current+=1,Ie(),Ae(),M.current=!1,G.current=!1,I.current=!1,N.current?.finishIdleToSilCrossfade(),ie.current.stop(),le.current?.stop(),xe(q.current),j.current?.reset(),$.current=0,_.current=!1,at()},setTtsEngineId:v=>{ie.current.setTtsEngineId(v),le.current?.setTtsEngineId(v),we.current&&(we.current.ttsEngineId=v)},renderViseme:async(v,B)=>{const U=N.current,L=D.current;if(!U||!L||!ve.current)throw new Error("AI twin is not ready");await U.renderViseme(L,v,{from:B?.from,transitionDurationMs:B?.transitionDurationMs,gapMs:B?.gapMs,onStatus:H=>se.current?.(H)})},setEmotion:v=>{if(!ve.current)throw new Error("AI twin is not ready");const B=N.current,U=D.current;if(!B||!U)throw new Error("AI twin is not ready");J.current+=1,Ie(),Ae(),M.current=!1,G.current=!1,z.current=!1,I.current=!1,ie.current.stop(),le.current?.stop(),xe(q.current),j.current?.reset(),$.current=0,_.current=!1,B.finishIdleToSilCrossfade(),y.current?.setActive(!1);const L=He(v);ee.current=!0,he.current=null,ce.current=null,k.current.snapToEmotion(L);const H=k.current.getExpressionPath(),be=k.current.getDrawKey(),Ee={mouthPath:null,expressionPath:H},De=Et(Ee,be);q.current.lastDrawnKey="",B.drawLiveFrame(U,Ee,De,Ne=>{Ne===De&&(q.current.lastDrawnKey=De)});const Pe=Ze(L);se.current?.(H?`${L} · brow ${Pe>0?"+":""}${Pe}`:`${L} · brow 0 (sil base)`)},getEmotion:()=>ee.current?k.current.getTargetEmotion():null,clearEmotionPreview:()=>{ee.current=!1,he.current=null,ce.current=null,k.current.reset(),xe(q.current),se.current?.("idle")},setViseme:v=>{if(!ve.current)throw new Error("AI twin is not ready");const B=N.current,U=D.current;if(!B||!U)throw new Error("AI twin is not ready");J.current+=1,Ie(),Ae(),M.current=!1,G.current=!1,z.current=!1,I.current=!1,ie.current.stop(),le.current?.stop(),xe(q.current),j.current?.reset(),$.current=0,_.current=!1,B.finishIdleToSilCrossfade(),y.current?.setActive(!1);const L=Qt(v);ee.current=!0,ce.current=L,he.current=L==="sil"?null:dt(L),k.current.snapToEmotion("neutral");const H=k.current.getExpressionPath(),be=k.current.getDrawKey(),De={mouthPath:he.current,expressionPath:H},Pe=Et(De,be);q.current.lastDrawnKey="",B.drawLiveFrame(U,De,Pe,Ne=>{Ne===Pe&&(q.current.lastDrawnKey=Pe)}),se.current?.(L)},getViseme:()=>ee.current?ce.current:null,isReady:()=>ve.current,getStatus:()=>oe,connect:async(v={})=>{_e(null);try{let B=v.authToken?.trim();B||(B=me.current??i??await Wt(),me.current=B);const U=v.voiceId?.trim()??l??we.current?.voiceId,L=v.personaId?.trim()??we.current?.personaId,H=zr();await H.unlockAudio(),await H.connect({...v,apiBase:v.apiBase?.trim()||de,authToken:B,voiceId:U,personaId:L,speakingRate:v.speakingRate??f})}catch(B){throw Be(B instanceof Error?B.message:"Voice connect failed"),B}},disconnect:async()=>{const v=le.current;v&&(J.current+=1,Ie(),Ae(),M.current=!1,G.current=!1,I.current=!1,N.current?.finishIdleToSilCrossfade(),xe(q.current),j.current?.reset(),$.current=0,_.current=!1,await v.disconnect(),at())},isConnected:()=>le.current?.isConnected()??!1,getVoiceStream:()=>le.current?.getVoiceStream()??ie.current?.getVoiceStream()??null}),[Oe,f,i,l,Ie,Ae,at,Be,it,zr,oe,de]),ur.jsxs("div",{className:c,style:{...Do,...u},children:[ur.jsx("canvas",{ref:D,width:nr.width,height:nr.height,style:{...Fo,...d},"aria-label":t?`AI twin ${t}`:"AI twin face"}),x&&Re?ur.jsx("div",{style:Bo,children:Re}):null]})}),gs="thumbnail.webp";function Oo(e,t=Ir){const r=e.trim();if(!r)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${r}/${gs}`}const No=new Set([".png",".jpg",".jpeg",".webp"]);function ys(e=".png"){const t=e.toLowerCase();return No.has(t)?`source${t}`:"source.png"}function Vo(e,t=Ir,r=".png"){const n=e.trim();if(!n)throw new Error("faceId is required");return`${t.replace(/\/$/,"")}/${n}/${ys(r)}`}exports.AI_TWIN_THUMBNAIL_FILENAME=gs;exports.AiTwin=Lo;exports.AiTwinNotFoundError=$t;exports.CONSONANT_SKIP_GROUP=Nn;exports.DEFAULT_TTS_ENGINE_ID=Je;exports.EMOTION_TO_EYEBROW=us;exports.EXPRESSION_ATLAS_EMOTIONS=Li;exports.MIC_SAMPLE_RATE=Br;exports.OCULUS_VISEME_IDS=Fn;exports.PLAYBACK_SAMPLE_RATE=Lr;exports.SPEAKING_RATE_MAX=tr;exports.SPEAKING_RATE_MIN=er;exports.TTS_ENGINE_CARTESIA=et;exports.TTS_ENGINE_GOOGLE=Pr;exports.TTS_ENGINE_INWORLD=Yt;exports.VISEME_IDS=Ei;exports.VISEME_SKIP_GAP_MS=_r;exports.VISEME_TEST_CARTESIA_VOICE_ID=Fr;exports.VISEME_TEST_SPEAKING_RATE=Pt;exports.VISEME_TEST_VOICE_ID=Dr;exports.VISEME_VTIME_GRID_MS=xr;exports.VOWEL_SKIP_GROUP=On;exports.aiTwinSourceFilename=ys;exports.buildPlaybackTransitions=qn;exports.buildPlaybackVisemeSeries=Jt;exports.buildRawVisemeTransitions=Kn;exports.buildVoiceWebSocketUrl=Zn;exports.compactSkippableVisemes=Rr;exports.createAvatarTtsLipsyncController=Sr;exports.createRealtimeVoiceLipsyncController=es;exports.emotionToEyebrow=Ze;exports.faceAssetUrls=yr;exports.fetchAiTwin=In;exports.fetchDevAuthToken=Wt;exports.getAiTwinSourceUrl=Vo;exports.getAiTwinThumbnailUrl=Oo;exports.listSkippedVisemeEntries=Zt;exports.listSkippedVisemes=ti;exports.normalizeOculusViseme=Qt;exports.normalizeStreamEmotion=He;exports.resolveDirectFaceId=An;exports.resolveVisemeAtTime=At;exports.resolveVisemeFromPlaybackSeries=Wn;exports.resolveWordAtTime=li;exports.setActiveTwinAssets=Pn;exports.snapVisemeQueueToGrid=Mr;exports.snapVisemeVtimeToGrid=Vn;
|
|
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;
|