@streamoji/aitwin 0.1.2 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -53,9 +53,7 @@ Provide **`id`** (cloud twin) **or** **`assets`** (fixed URLs). One is required.
|
|
|
53
53
|
| `id` | Twin id for `getAiTwin` (e.g. `olivia`) |
|
|
54
54
|
| `assets` | `{ twinBase, binBase, encrypted }` — skip `getAiTwin` (lab / custom CDN) |
|
|
55
55
|
| `authToken` | Bearer for TTS + encrypted assets; omitted → dev `getAuthToken` |
|
|
56
|
-
| `
|
|
57
|
-
| `facesCdnBase` | R2 base when using `id` (default pub `custom-faces`) |
|
|
58
|
-
| `tts` | Provider when using `assets` (default `google`) |
|
|
56
|
+
| `ttsEngineId` | TTS engine when using `assets` (default Google) |
|
|
59
57
|
| `voiceId` | Override TTS voice |
|
|
60
58
|
| `speakingRate` | Default `0.85` |
|
|
61
59
|
| `showErrorOverlay` | Canvas error overlay (default `true`) |
|
|
@@ -87,6 +85,33 @@ Use **stable** `useCallback` handlers for `onReady` / `onError` / `onDisplayStat
|
|
|
87
85
|
|
|
88
86
|
Do not duplicate renderer code under `frontend/src/lib`; extend the package instead.
|
|
89
87
|
|
|
88
|
+
## Worker CDN
|
|
89
|
+
|
|
90
|
+
The viseme diff Web Worker is hosted on R2 and loaded at runtime via `fetch` + blob URL (cross-origin `new Worker(cdnUrl)` is blocked by browsers).
|
|
91
|
+
|
|
92
|
+
The worker URL is baked into each release from `package.json` version (`config/defaults.ts`) and uploaded to the matching versioned R2 path via `npm run upload:worker` (see below). This avoids CDN cache mismatches between the npm bundle and the worker script.
|
|
93
|
+
|
|
94
|
+
Example URL for `0.1.4`: `https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev/aitwin-workers/v0.1.4/visemeDiffPreview.worker.js`
|
|
95
|
+
|
|
96
|
+
### Upload worker after build
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
export R2_ACCESS_KEY_ID=...
|
|
100
|
+
export R2_SECRET_ACCESS_KEY=...
|
|
101
|
+
npm run build
|
|
102
|
+
npm run upload:worker
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### R2 CORS (required for browser fetch)
|
|
106
|
+
|
|
107
|
+
Allow `GET` and `HEAD` from your app origins (`https://aitwin.me`, `http://localhost:3000`). Use Cloudflare dashboard → R2 → `aitwin` bucket → Settings → CORS, or:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx wrangler r2 bucket cors set aitwin --file scripts/r2-cors.json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
(`scripts/r2-cors.json` is included; needs a Cloudflare API token with R2 Admin permissions.)
|
|
114
|
+
|
|
90
115
|
## Publishing
|
|
91
116
|
|
|
92
117
|
From `packages/aitwin`:
|
|
@@ -97,6 +122,8 @@ npm version patch
|
|
|
97
122
|
npm publish --access public
|
|
98
123
|
```
|
|
99
124
|
|
|
125
|
+
`prepublishOnly` runs `build` and `upload:worker` automatically.
|
|
126
|
+
|
|
100
127
|
The published tarball includes only **`dist/`** (bundled JS + `.d.ts` + worker chunk). **No `src/` and no source maps.**
|
|
101
128
|
|
|
102
129
|
Scoped packages need `--access public` on the free npm plan.
|
package/dist/aitwin.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Fe=require("react/jsx-runtime"),m=require("react"),De="https://ai.streamoji.com",We="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev/custom-faces",vt="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",re="eng_a7f2b9c1",ye="eng_d4e8f3a2",pe="eng_c9b1e6d4",Ke=ye,Rt=new Set([ye,pe]),ct={[re]:"oculus_direct",[pe]:"oculus_direct",[ye]:"inworld_word"},Je={google:re,inworld:ye,cartesia:pe};function bt(e){return ct[e]}function Dt(e){return e===pe}function xt(e){if(e.ttsEngineId&&e.ttsEngineId in ct)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in Je?Je[t]:Ke}class Re extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function ut(e,t=vt){const n=new URL(t);n.searchParams.set("id",e);const r=await fetch(n.toString());if(r.status===404)throw new Re;if(!r.ok)throw new Error(`getAiTwin failed (${r.status})`);const s=await r.json();if(!s.success)throw new Re(s.error??"AI twin not found");const{tts:a,...o}=s.data;return{...o,ttsEngineId:xt(s.data)}}const Ct="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",Mt="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Ft="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Pt="6TNvp3SCs4Og0a1ijm9TommXLql1",Ot="Swaraj Mali";async function Oe(){const e=await fetch(Ct,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":Mt,"Client-Secret":Ft},body:JSON.stringify({userId:Pt,userName:Ot})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),n=t.authToken??t.token??"";if(!n.trim())throw new Error("getAuthToken returned no token");return n}async function Lt(e,t=De){const n=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!n.ok)throw new Error(`/api/session-value failed (${n.status})`);const r=await n.json();if(!r.value)throw new Error("Session value response missing value");return r}function Nt(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function Ie(e){return Uint8Array.from(e)}function Ut(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function Vt(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=Ie(n.subarray(0,12)),s=Ie(n.subarray(12,28)),a=Ie(n.subarray(28)),o=Ie(Ut(a,s)),c=await crypto.subtle.importKey("raw",Nt(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},c,o)}let Le=null;const Ne=new Set;function Bt(e,t=We){const n=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:n,binBase:n,encrypted:!0}}function $t(e){Le=e;for(const t of Ne)t()}function se(){if(!Le)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return Le}function Ht(e){return Ne.add(e),()=>Ne.delete(e)}function Wt(){return`${se().twinBase}/sil.png`}function Kt(){return`${se().twinBase}/idle.mp4`}function qt(){const{encrypted:e,binBase:t,twinBase:n}=se();return e?t:n}function lt(){return se().encrypted}function Gt(){const{binBase:e,twinBase:t,encrypted:n}=se();return n?`${e}/atlas.bin`:`${t}/atlas.json`}function jt(){const{binBase:e,twinBase:t,encrypted:n}=se();return n?`${e}/expression_atlas.bin`:`${t}/expression_atlas.json`}function zt(e){const t=e.split(/\r?\n/);let n="",r="";for(const a of t)a.startsWith("event:")?n=a.slice(6).trim():a.startsWith("data:")&&(r=a.slice(5).trim());if(!n)return null;let s={};if(r)try{s=JSON.parse(r)}catch{s={raw:r}}return{event:n,data:s}}const ft=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],Qt=new Map(ft.map(e=>[e.toLowerCase(),e]));function dt(e){const t=(e??"").trim();return t?Qt.get(t.toLowerCase())??"sil":"sil"}const Jt={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 Xt(e){if(!e)return["sil"];const t=e.toLowerCase();return Jt[t]??["sil"]}function Yt(e,t){const n=e.word.length;if(n<=0)return 0;const r=e.wduration;if(r<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/r));return Math.min(n-1,Math.floor(s*n))}function xe(e,t){const n=Math.max(0,Math.min(e.length-1,t));return e[n]??""}function Zt(e,t){const n=e.toLowerCase(),r=xe(n,t),s=n.slice(Math.max(0,t-1),t+2);return r==="i"||r==="y"?"I":s.includes("ee")||r==="e"||n.includes("ea")?"E":"aa"}function en(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));if(/oo|ou|uw/.test(r))return"U";const s=xe(n,t);return s==="u"||s==="w"?"U":"O"}function tn(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-2),t+1);if(/qu/.test(r))return"U";const s=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));return/wo|wh/.test(s)||xe(n,t)==="o"?"O":"U"}const Ae={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function nn(e,t){const n=e.toLowerCase(),r=xe(n,t);if(Ae[r])return Ae[r];for(let s=0;s<n.length;s++){const a=n[s];if(Ae[a])return Ae[a]}return"DD"}function rn(e,t,n){const r=Xt(e);if(r.length===1)return r[0];const s=t.trim();if(!s)return r[0];const a=(e??"").toLowerCase(),o=Math.max(0,n);switch(a){case"aei":return Zt(s,o);case"o":return en(s,o);case"qw":return tn(s,o);case"cdgknstxyz":return nn(s,o);default:return r[0]}}function sn(e){let t=e.trim();const n=t.indexOf(",");t.startsWith("data:")&&n>=0&&(t=t.slice(n+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const r=t.length%4;return r!==0&&(t+="=".repeat(4-r)),t}const on=24e3;function an(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function Xe(e,t,n=on){const r=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(r.buffer,r.byteOffset,r.byteLength/2),a=new Float32Array(s.length);for(let c=0;c<s.length;c++)a[c]=s[c]>=32768?-(65536-s[c])/32768:s[c]/32767;const o=e.createBuffer(1,a.length,n);return o.copyToChannel(a,0),o}function cn(e){const t=sn(e),n=window.atob(t),r=new Uint8Array(n.length);for(let s=0;s<n.length;s++)r[s]=n.charCodeAt(s);return r}async function un(e,t){const n=cn(t);if(an(n)){const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(r)}if(n.length>=2&&n.length%2===0)return Xe(e,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(r)}catch{return Xe(e,n)}}function ht(e,t){let n=null;for(const r of e)t<r.vtime||t>=r.vtime+r.vduration||(!n||r.vtime>=n.vtime)&&(n=r);return n?n.viseme:"sil"}function _t(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function Ye(e){let t=0;for(const n of e)t=Math.max(t,n.vtime+n.vduration);return t}const mt=.5,wt=1.5,qe="Olivia",gt="f786b574-daa5-4673-aa0c-cbe3e8534c02",Ge=.85;function Et(e,t=De){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],a=[];let o="neutral";const c=[],f=[];let h=null,_=!1,d=!1,u=!1,i=!1,T=0,R=0,S=0,Q=0,W=null,N=1,K="",g=re,E=re,k=!1;const V=new Set,q=()=>{for(const w of f)w.playbackRate.value=N},I=()=>V.forEach(w=>w()),J=()=>{W!=null&&(clearTimeout(W),W=null)},ie=()=>{for(const w of f)try{w.stop()}catch{}f.length=0},le=()=>{k=!0,J(),ie(),c.length=0,_=!1,d=!1,u=!1,i=!1,r.length=0,s.length=0,a.length=0,o="neutral",S=0,Q=0,R=0,e("idle"),I()},oe=()=>{!i||_||f.length>0||c.length>0||(d=!1,J(),e("done"),I())},fe=()=>{const w=Ye(r);if(w<=0){e("done"),I();return}d=!0,T=performance.now(),e("speaking"),I(),J();const C=N>0?w/N:w;W=setTimeout(()=>{k||(d=!1,W=null,e("done"),I())},C)},Y=(w,C,A)=>{w.length!==0&&(A&&(S=C),w.forEach(v=>{const F=String(v.word??"").trim();if(!F)return;const D=Math.round((v.start??0)*1e3),G=Math.round((v.duration??0)*1e3);s.push({word:F,wtime:S+D,wduration:G,queueIndex:s.length})}))},Se=(w,C,A,v)=>{w.length!==0&&(A&&(S=C),w.forEach(b=>{const F=String(b.symbol??"").trim();if(!F)return;const D=Math.round((b.start??0)*1e3),G=Math.max(1,Math.round((b.duration??0)*1e3)),M=S+D;let B;if(bt(v)==="oculus_direct")B=dt(F);else{const $=_t(s,M),P=$?Yt($,M):0;B=rn(F,$?.word??"",P)}r.push({viseme:B,weight:1,vtime:M,vduration:G})}),I())},de=(w,C,A,v,b)=>{Y(C,A,v),Se(w,A,v,b)},ae=async(w,C,A,v)=>{if(k||!w?.trim()){(C.length>0||A.length>0)&&!u&&de(C,A,S,v,E);return}if(_){c.push({audio:w,visemes:C,words:A,isNewSegment:v});return}_=!0;try{const b=window.AudioContext||window.webkitAudioContext,F=h??new b;F.state==="suspended"&&await F.resume(),h=F;const D=await un(F,w);u=!0;const G=F.currentTime;let M=Q;const B=!d;M<G&&(M=G+.1),Q=M+D.duration;const $=F.createBufferSource();if($.buffer=D,$.playbackRate.value=N,$.connect(F.destination),f.push($),k){f.pop();return}if(B){d=!0,e("speaking"),R=M,S=0,r.length=0,s.length=0;const L=(M-G)*1e3;T=performance.now()+L}$.onended=()=>{const L=f.indexOf($);L>=0&&f.splice(L,1),oe(),I()},$.start(M);const P=(M-R)*1e3;de(C,A,P,v,E)}catch(b){console.error("[avatarTtsLipsync] audio chunk failed:",b)}finally{_=!1;const b=c.shift();b?await ae(b.audio,b.visemes,b.words,b.isNewSegment):oe()}},Te=()=>{if(i=!0,!u&&r.length>0){fe();return}oe()};return{setDeveloperToken:w=>{K=w},setTtsEngineId:w=>{g=w},getTtsEngineId:()=>g,speak:async(w,C)=>{le(),k=!1,e("loading");const A=K.trim();if(!A)throw new Error("Developer token required for avatar_ttsWithPoses");const v=C?.ttsEngineId??g??Ke;E=v;const b=C?.voiceId??qe,F=C?.speakingRate??Ge,D=Math.max(mt,Math.min(wt,F)),G={user_query:w,ttsEngineId:v,speakingRate:D};Rt.has(v)&&(G.voice_id=b);try{const M=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${A}`},body:JSON.stringify(G)});if(!M.ok)throw new Error(`avatar_ttsWithPoses failed (${M.status})`);const B=M.body;if(!B)throw new Error("No response body");const $=B.getReader(),P=new TextDecoder;let L="";const ce=async Z=>{const U=zt(Z);if(U){if(U.event==="audio"){const X=U.data.chunk,l=U.data.visemes??[],p=U.data.words??[],y=U.data.is_new_segment??!1;if(X)await ae(X,l,p,y);else if(l.length>0||p.length>0){const x=y&&r.length>0?Ye(r):S;de(l,p,x,y,v)}}else if(U.event==="metadata"){const X=U.data.mood;typeof X=="string"&&X.trim()&&(o=X.trim());const l=U.data.sentence_emotions;if(Array.isArray(l)){a.length=0;for(const p of l){if(!p||typeof p!="object")continue;const y=p;a.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??o),start_word:Number(y.start_word??0),end_word:Number(y.end_word??0)})}}I()}else if(U.event==="error")throw new Error(String(U.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:Z,value:U}=await $.read();U&&(L+=P.decode(U,{stream:!0}));const X=L.split(`
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Oe=require("react/jsx-runtime"),p=require("react");var Pe=typeof document<"u"?document.currentScript:null;const xt="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev",lt={publicBase:xt},Ct="0.1.5",Mt={version:Ct},Ft={},Ae="https://ai.streamoji.com",Ot=`${lt.publicBase}/custom-faces`,Pt=`${lt.publicBase}/aitwin-workers/v${Mt.version}/visemeDiffPreview.worker.js`;function Lt(e){if(e)return e;const t=typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:Pe&&Pe.tagName.toUpperCase()==="SCRIPT"&&Pe.src||new URL("aitwin.cjs",document.baseURI).href}<"u"?Ft?.VITE_AITWIN_WORKER_URL:void 0;return t?.trim()?t.trim():Pt}const Ut="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",re="eng_a7f2b9c1",we="eng_d4e8f3a2",ge="eng_c9b1e6d4",Ke=we,Nt=new Set([we,ge]),ft={[re]:"oculus_direct",[ge]:"oculus_direct",[we]:"inworld_word"},Je={google:re,inworld:we,cartesia:ge};function Bt(e){return ft[e]}function Vt(e){return e===ge}function $t(e){if(e.ttsEngineId&&e.ttsEngineId in ft)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in Je?Je[t]:Ke}class ve extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function dt(e,t=Ut){const n=new URL(t);n.searchParams.set("id",e);const r=await fetch(n.toString());if(r.status===404)throw new ve;if(!r.ok)throw new Error(`getAiTwin failed (${r.status})`);const s=await r.json();if(!s.success)throw new ve(s.error??"AI twin not found");const{tts:i,...a}=s.data;return{...a,ttsEngineId:$t(s.data)}}const Ht="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",qt="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Wt="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Kt="6TNvp3SCs4Og0a1ijm9TommXLql1",Gt="Swaraj Mali";async function Ue(){const e=await fetch(Ht,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":qt,"Client-Secret":Wt},body:JSON.stringify({userId:Kt,userName:Gt})});if(!e.ok)throw new Error(`getAuthToken failed (${e.status})`);const t=await e.json(),n=t.authToken??t.token??"";if(!n.trim())throw new Error("getAuthToken returned no token");return n}async function jt(e,t=Ae){const n=await fetch(`${t.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${e}`}});if(!n.ok)throw new Error(`/api/session-value failed (${n.status})`);const r=await n.json();if(!r.value)throw new Error("Session value response missing value");return r}function zt(e){const t=e.trim();if(t.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)n[r/2]=Number.parseInt(t.slice(r,r+2),16);return n}function Se(e){return Uint8Array.from(e)}function Qt(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function Jt(e,t){if(!t)throw new Error("Asset key is required for encrypted assets");if(e.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(e),r=Se(n.subarray(0,12)),s=Se(n.subarray(12,28)),i=Se(n.subarray(28)),a=Se(Qt(i,s)),c=await crypto.subtle.importKey("raw",zt(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},c,a)}let Ne=null;const Be=new Set;function Yt(e,t=Ot){const n=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:n,binBase:n,encrypted:!0}}function Xt(e){Ne=e;for(const t of Be)t()}function Zt(e,t){const n=typeof globalThis.location<"u"?globalThis.location.href:void 0;if(!n)return e;const r=s=>{try{return new URL(s,n).href.replace(/\/$/,"")}catch{return s}};return{twinBase:r(e.twinBase),binBase:r(e.binBase),encrypted:e.encrypted}}function se(){if(!Ne)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return Ne}function en(e){return Be.add(e),()=>Be.delete(e)}function tn(){return`${se().twinBase}/sil.png`}function nn(){return`${se().twinBase}/idle.mp4`}function rn(){const{encrypted:e,binBase:t,twinBase:n}=se();return e?t:n}function ht(){return se().encrypted}function sn(){const{binBase:e,twinBase:t,encrypted:n}=se();return n?`${e}/atlas.bin`:`${t}/atlas.json`}function on(){const{binBase:e,twinBase:t,encrypted:n}=se();return n?`${e}/expression_atlas.bin`:`${t}/expression_atlas.json`}function an(e){const t=e.split(/\r?\n/);let n="",r="";for(const i of t)i.startsWith("event:")?n=i.slice(6).trim():i.startsWith("data:")&&(r=i.slice(5).trim());if(!n)return null;let s={};if(r)try{s=JSON.parse(r)}catch{s={raw:r}}return{event:n,data:s}}const _t=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],cn=new Map(_t.map(e=>[e.toLowerCase(),e]));function mt(e){const t=(e??"").trim();return t?cn.get(t.toLowerCase())??"sil":"sil"}const un={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 ln(e){if(!e)return["sil"];const t=e.toLowerCase();return un[t]??["sil"]}function fn(e,t){const n=e.word.length;if(n<=0)return 0;const r=e.wduration;if(r<=0)return 0;const s=Math.max(0,Math.min(1,(t-e.wtime)/r));return Math.min(n-1,Math.floor(s*n))}function be(e,t){const n=Math.max(0,Math.min(e.length-1,t));return e[n]??""}function dn(e,t){const n=e.toLowerCase(),r=be(n,t),s=n.slice(Math.max(0,t-1),t+2);return r==="i"||r==="y"?"I":s.includes("ee")||r==="e"||n.includes("ea")?"E":"aa"}function hn(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));if(/oo|ou|uw/.test(r))return"U";const s=be(n,t);return s==="u"||s==="w"?"U":"O"}function _n(e,t){const n=e.toLowerCase(),r=n.slice(Math.max(0,t-2),t+1);if(/qu/.test(r))return"U";const s=n.slice(Math.max(0,t-1),Math.min(n.length,t+3));return/wo|wh/.test(s)||be(n,t)==="o"?"O":"U"}const Te={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function mn(e,t){const n=e.toLowerCase(),r=be(n,t);if(Te[r])return Te[r];for(let s=0;s<n.length;s++){const i=n[s];if(Te[i])return Te[i]}return"DD"}function wn(e,t,n){const r=ln(e);if(r.length===1)return r[0];const s=t.trim();if(!s)return r[0];const i=(e??"").toLowerCase(),a=Math.max(0,n);switch(i){case"aei":return dn(s,a);case"o":return hn(s,a);case"qw":return _n(s,a);case"cdgknstxyz":return mn(s,a);default:return r[0]}}function gn(e){let t=e.trim();const n=t.indexOf(",");t.startsWith("data:")&&n>=0&&(t=t.slice(n+1)),t=t.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const r=t.length%4;return r!==0&&(t+="=".repeat(4-r)),t}const yn=24e3;function pn(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function Ye(e,t,n=yn){const r=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(r.buffer,r.byteOffset,r.byteLength/2),i=new Float32Array(s.length);for(let c=0;c<s.length;c++)i[c]=s[c]>=32768?-(65536-s[c])/32768:s[c]/32767;const a=e.createBuffer(1,i.length,n);return a.copyToChannel(i,0),a}function En(e){const t=gn(e),n=window.atob(t),r=new Uint8Array(n.length);for(let s=0;s<n.length;s++)r[s]=n.charCodeAt(s);return r}async function Sn(e,t){const n=En(t);if(pn(n)){const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return e.decodeAudioData(r)}if(n.length>=2&&n.length%2===0)return Ye(e,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(r)}catch{return Ye(e,n)}}function wt(e,t){let n=null;for(const r of e)t<r.vtime||t>=r.vtime+r.vduration||(!n||r.vtime>=n.vtime)&&(n=r);return n?n.viseme:"sil"}function gt(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function Xe(e){let t=0;for(const n of e)t=Math.max(t,n.vtime+n.vduration);return t}const yt=.5,pt=1.5,Ge="Olivia",Et="f786b574-daa5-4673-aa0c-cbe3e8534c02",je=.85;function St(e,t=Ae){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],i=[];let a="neutral";const c=[],l=[];let d=null,m=!1,_=!1,I=!1,o=!1,x=0,w=0,u=0,g=0,E=null,R=1,$="",y=re,S=re,C=!1;const D=new Set,q=()=>{for(const h of l)h.playbackRate.value=R},W=()=>D.forEach(h=>h()),ae=()=>{E!=null&&(clearTimeout(E),E=null)},ye=()=>{for(const h of l)try{h.stop()}catch{}l.length=0},X=()=>{C=!0,ae(),ye(),c.length=0,m=!1,_=!1,I=!1,o=!1,r.length=0,s.length=0,i.length=0,a="neutral",u=0,g=0,w=0,e("idle"),W()},K=()=>{!o||m||l.length>0||c.length>0||(_=!1,ae(),e("done"),W())},pe=()=>{const h=Xe(r);if(h<=0){e("done"),W();return}_=!0,x=performance.now(),e("speaking"),W(),ae();const M=R>0?h/R:h;E=setTimeout(()=>{C||(_=!1,E=null,e("done"),W())},M)},xe=(h,M,B)=>{h.length!==0&&(B&&(u=M),h.forEach(P=>{const U=String(P.word??"").trim();if(!U)return;const Q=Math.round((P.start??0)*1e3),V=Math.round((P.duration??0)*1e3);s.push({word:U,wtime:u+Q,wduration:V,queueIndex:s.length})}))},ce=(h,M,B,P)=>{h.length!==0&&(B&&(u=M),h.forEach(k=>{const U=String(k.symbol??"").trim();if(!U)return;const Q=Math.round((k.start??0)*1e3),V=Math.max(1,Math.round((k.duration??0)*1e3)),L=u+Q;let F;if(Bt(P)==="oculus_direct")F=mt(U);else{const O=gt(s,L),J=O?fn(O,L):0;F=wn(U,O?.word??"",J)}r.push({viseme:F,weight:1,vtime:L,vduration:V})}),W())},ie=(h,M,B,P,k)=>{xe(M,B,P),ce(h,B,P,k)},Ee=async(h,M,B,P)=>{if(C||!h?.trim()){(M.length>0||B.length>0)&&!I&&ie(M,B,u,P,S);return}if(m){c.push({audio:h,visemes:M,words:B,isNewSegment:P});return}m=!0;try{const k=window.AudioContext||window.webkitAudioContext,U=d??new k;U.state==="suspended"&&await U.resume(),d=U;const Q=await Sn(U,h);I=!0;const V=U.currentTime;let L=g;const F=!_;L<V&&(L=V+.1),g=L+Q.duration;const O=U.createBufferSource();if(O.buffer=Q,O.playbackRate.value=R,O.connect(U.destination),l.push(O),C){l.pop();return}if(F){_=!0,e("speaking"),w=L,u=0,r.length=0,s.length=0;const H=(L-V)*1e3;x=performance.now()+H}O.onended=()=>{const H=l.indexOf(O);H>=0&&l.splice(H,1),K(),W()},O.start(L);const J=(L-w)*1e3;ie(M,B,J,P,S)}catch(k){console.error("[avatarTtsLipsync] audio chunk failed:",k)}finally{m=!1;const k=c.shift();k?await Ee(k.audio,k.visemes,k.words,k.isNewSegment):K()}},Z=()=>{if(o=!0,!I&&r.length>0){pe();return}K()};return{setDeveloperToken:h=>{$=h},setTtsEngineId:h=>{y=h},getTtsEngineId:()=>y,speak:async(h,M)=>{X(),C=!1,e("loading");const B=$.trim();if(!B)throw new Error("Developer token required for avatar_ttsWithPoses");const P=M?.ttsEngineId??y??Ke;S=P;const k=M?.voiceId??Ge,U=M?.speakingRate??je,Q=Math.max(yt,Math.min(pt,U)),V={user_query:h,ttsEngineId:P,speakingRate:Q};Nt.has(P)&&(V.voice_id=k);try{const L=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${B}`},body:JSON.stringify(V)});if(!L.ok)throw new Error(`avatar_ttsWithPoses failed (${L.status})`);const F=L.body;if(!F)throw new Error("No response body");const O=F.getReader(),J=new TextDecoder;let H="";const le=async fe=>{const f=an(fe);if(f){if(f.event==="audio"){const T=f.data.chunk,A=f.data.visemes??[],v=f.data.words??[],b=f.data.is_new_segment??!1;if(T)await Ee(T,A,v,b);else if(A.length>0||v.length>0){const G=b&&r.length>0?Xe(r):u;ie(A,v,G,b,P)}}else if(f.event==="metadata"){const T=f.data.mood;typeof T=="string"&&T.trim()&&(a=T.trim());const A=f.data.sentence_emotions;if(Array.isArray(A)){i.length=0;for(const v of A){if(!v||typeof v!="object")continue;const b=v;i.push({sentence_index:Number(b.sentence_index??0),text:String(b.text??""),sentiment:typeof b.sentiment=="string"?b.sentiment:void 0,emotion:String(b.emotion??a),start_word:Number(b.start_word??0),end_word:Number(b.end_word??0)})}}W()}else if(f.event==="error")throw new Error(String(f.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:fe,value:f}=await O.read();f&&(H+=J.decode(f,{stream:!0}));const T=H.split(`
|
|
2
2
|
|
|
3
|
-
`);L=X.pop()??"";for(const l of X)await ce(l);if(Z){L.trim()&&await ce(L.trim()),k||Te();break}}}catch(M){throw console.error("[avatarTtsLipsync]",M),le(),e("error"),M}},stop:le,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>a,getStreamMood:()=>o,getPlaybackElapsedMs:()=>d?u&&h&&R>0?Math.max(0,(h.currentTime-R)*1e3):Math.max(0,(performance.now()-T)*N):0,isSpeaking:()=>d,setPlaybackSpeed:w=>{N=Math.max(.1,Math.min(1,w)),q()},getPlaybackSpeed:()=>N,subscribe:w=>(V.add(w),()=>V.delete(w))}}function ln(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function fn(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function dn(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function yt(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return Vt(r,t)}async function hn(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await yt(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function _n(e){const t=new Blob([e],{type:"image/webp"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const a=new Image;a.decoding="async",a.onload=()=>{URL.revokeObjectURL(n),r(a)},a.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted image"))},a.src=n})}function pt(e,t){return`${e}/${t}_minus_sil.png`}const je=15,mn=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],Ue=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],wn=Ue.length,Ze=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 et(e,t){return`${e}__${t}`}function tt(e,t){return`pairs/${e}/${t}_minus_sil.png`}function Ve(e){return`${e}_minus_sil.png`}function gn(e,t){if(e===t)return[];const n=[],r=et(e,t);if(Ze.has(r)){for(const a of Ue)n.push(tt(r,a));return n}const s=et(t,e);if(Ze.has(s)){for(let a=wn-1;a>=0;a--)n.push(tt(s,Ue[a]));return n}return[]}function En(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function yn(e,t){return En(t).map(r=>e[r]).filter(r=>r!=null)}function pn(e,t,n,r){if(r<=0)return 0;const a=Math.max(1,n-t)/r,o=e-t,c=Math.floor(o/a);return Math.min(r-1,Math.max(0,c))}function Sn(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}const Tn=[.32,.24,.16,.08,0],Ce=Tn.map(e=>`eyes_${e.toFixed(2)}`),In=["eyes_0.32","eyes_0.16","eyes_0.00"],An=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],kn={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 ue(e,t="neutral"){const n=String(e??"").trim().toLowerCase();return n?kn[n]??t:t}function z(e){return e==="neutral"?null:pt(e,"eyes_standard")}function Be(e,t){return!t||t==="eyes_standard"?z(e):e==="neutral"&&t==="eyes_standard"?null:pt(e,t)}function vn(e){if(!e)return"open";const t=e.match(/\/(eyes_[^/]+)_minus_sil\.png$/);if(!t?.[1])return"open";const n=t[1];return Ce.includes(n)?n:"open"}function Rn(e){return e==="open"?-1:Ce.indexOf(e)}function bn(e,t){const n=[];for(const r of t)n.push(Be(e,r));for(let r=t.length-2;r>=0;r--)n.push(Be(e,t[r]));return n.push(z(e)),n}function Dn(e,t){if(t==="open")return[z(e)];const n=Rn(t);if(n<0)return[z(e)];const r=[];for(let s=n;s>=0;s--)r.push(Be(e,Ce[s]));return r.push(z(e)),r}const xn=["t01_0.20","t02_0.40","t03_0.60","t04_0.80"];function Cn(e,t){return`${e}__${t}`}function Mn(e,t,n){return`pairs/${Cn(e,t)}/${n}_minus_sil.png`}function Fn(e,t){return xn.map(n=>Mn(e,t,n))}function we(e,t){return e+Math.random()*(t-e)}function ke(){return we(14,32)}function Pn(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:a=120,doubleBlinkGapMaxMs:o=220,fullBlinkChance:c=.75,postEmotionChangeDelayMs:f=380,minDwellMs:h=550}=e;let _="neutral",d="neutral",u=0,i={kind:"idle",nextBlinkAt:performance.now()+t},T=null,R="__eyes_open__|neutral";function S(g,E){T=g,R=`${g??"__eyes_open__"}|${E}`}function Q(g,E){i={kind:"idle",nextBlinkAt:g+we(n,r)},d=E,S(z(E),E)}function W(g,E,k){const V=k?In:Math.random()<c?Ce:An;i={kind:"playing",paths:bn(E,V),index:0,holdUntil:g+ke(),after:"idle",blinkEmotion:E},d=E,S(i.paths[0]??null,E)}function N(g,E,k){const V=vn(T),q=[];if(V!=="open"){const I=Dn(E,V);q.push(...I.slice(0,-1))}q.push(...Fn(E,k)),q.push(z(k)),i={kind:"emotionBlend",paths:q,index:0,holdUntil:g+ke(),thenEmotion:k},S(q[0]??null,E)}function K(g){if(i.kind==="emotionBlend"){for(;g>=i.holdUntil&&i.index<i.paths.length-1;)i.index+=1,i.holdUntil=g+ke();const E=i.paths[i.index]??null;S(E,i.thenEmotion),g>=i.holdUntil&&i.index>=i.paths.length-1&&(_=i.thenEmotion,d=i.thenEmotion,S(z(i.thenEmotion),i.thenEmotion),i={kind:"idle",nextBlinkAt:g+f});return}if(i.kind==="idle"){S(z(d),d),g>=i.nextBlinkAt&&W(g,d,!1);return}if(i.kind==="doublePause"){S(z(i.blinkEmotion),i.blinkEmotion),g>=i.resumeAt&&W(g,i.blinkEmotion,!0);return}if(i.kind==="playing"){for(;g>=i.holdUntil&&i.index<i.paths.length-1;)i.index+=1,i.holdUntil=g+ke();if(S(i.paths[i.index]??null,i.blinkEmotion),g>=i.holdUntil&&i.index>=i.paths.length-1){if(i.after==="resumeIdle"){Q(g+f,_);return}if(i.after==="doublePause"){i={kind:"doublePause",resumeAt:g+we(a,o),blinkEmotion:i.blinkEmotion},S(z(i.blinkEmotion),i.blinkEmotion);return}if(i.after==="idle"&&Math.random()<s){i={kind:"doublePause",resumeAt:g+we(a,o),blinkEmotion:i.blinkEmotion},S(z(i.blinkEmotion),i.blinkEmotion);return}Q(g,i.blinkEmotion)}}}return{advance:K,setTargetEmotion(g){const E=ue(g,_);if(E===_&&i.kind!=="emotionBlend")return;const k=performance.now();if(h>0&&E!==_&&i.kind!=="emotionBlend"&&!(_==="neutral"&&E!=="neutral")&&k-u<h)return;const V=_;if(_=E,u=k,i.kind==="playing"){N(k,d,E);return}if(i.kind==="emotionBlend"){i={...i,thenEmotion:E};return}N(k,V,E)},getTargetEmotion:()=>_,getExpressionPath:()=>T,getDrawKey:()=>R,reset(){_="neutral",d="neutral",u=0,Q(performance.now()+we(n,r),"neutral")}}}function On(e){const t=document.createElement("video");t.muted=!0,t.playsInline=!0,t.preload="auto",t.setAttribute("playsinline",""),t.loop=!0;let n=!1;return{async load(){n||await new Promise((r,s)=>{const a=()=>{const o=t.duration;if(!Number.isFinite(o)||o<=0){s(new Error(`Idle video has invalid duration: ${e}`));return}n=!0,t.currentTime=0,r()};t.addEventListener("loadeddata",a,{once:!0}),t.addEventListener("error",()=>s(new Error(`Failed to load idle video: ${e}`)),{once:!0}),t.src=e,t.load()})},isReady(){return n},getVideo(){return t},setActive(r){n&&(r?t.paused&&t.play().catch(()=>{}):t.paused||t.pause())},restart(){n&&(t.currentTime=0)}}}const Ln=50,St=400;function Nn(e,t,n){return e.filter(r=>{const s=r.queueIndex??-1;return s>=t&&s<=n})}function Un(e,t){const n=[];for(const r of e){const s=Nn(t,r.start_word,r.end_word);if(s.length===0)continue;const a=s[0],o=s[s.length-1];n.push({sentence:r,startMs:Math.max(0,a.wtime-Ln),endMs:o.wtime+o.wduration+St})}return n.sort((r,s)=>r.startMs-s.startMs)}function Vn(e,t,n,r="neutral"){const s=ue(r);if(t.length===0)return s;if(n.length===0||e<n[0].wtime)return ue(t[0]?.emotion,s);const a=Un(t,n);let o=null;for(const h of a)e>=h.startMs&&e<=h.endMs&&(o=h);if(o)return ue(o.sentence.emotion,s);const c=n[n.length-1],f=c.wtime+c.wduration;return e>f+St?ue(t[t.length-1]?.emotion,s):s}function Tt(e,t,n,r,s=je){const a=new OffscreenCanvas(n,r),o=a.getContext("2d",{willReadFrequently:!0});o.drawImage(e,0,0,n,r);const c=o.getImageData(0,0,n,r),f=o.createImageData(n,r);f.data.set(c.data);const _=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const d of t){_.clearRect(0,0,n,r),_.drawImage(d,0,0,n,r);const u=_.getImageData(0,0,n,r);for(let i=0;i<f.data.length;i+=4){const T=u.data[i],R=u.data[i+1],S=u.data[i+2];Math.max(T,R,S)>s&&(f.data[i]=T,f.data[i+1]=R,f.data[i+2]=S,f.data[i+3]=255)}}return o.putImageData(f,0,0),a}let ne=null,ge=null;const be=new Map;function Bn(){ne=null,ge=null,be.clear()}Ht(Bn);function $n(e){return e??ge??void 0}function ze(e){return new Promise((t,n)=>{const r=new Image;r.decoding="async",r.onload=()=>t(r),r.onerror=()=>n(new Error(`Failed to load image: ${e}`)),r.src=e})}async function nt(e,t,n,r){const s=n?await hn(e,r):await fetch(e).then(h=>{if(!h.ok)throw new Error(`Failed to load ${e}`);return h.json()}),a=s.sheets?.[0];if(!a?.path)throw new Error(`${e} has no sheets[0].path`);const o=n?await _n(await yt(Sn(t,a.path,n),r??"")):await ze(`${t}/${a.path}`),c=new Map;for(const h of s.cells??[])h.path&&c.set(h.path,h);const f=new Map;for(const h of s.sheets??[])f.set(h.index,h);return{atlas:o,atlasMeta:s,cellByPath:c,sheetByIndex:f,diffBase:t}}async function $e(e){const t=$n(e),n=lt();if(n&&!t)throw new Error("Encrypted assets enabled but no key provided");if(ne&&(!n||ge===(t??null))){const a=await ne;return Ee(a)}ge=t??null,be.clear();const r=qt();ne=(async()=>{const a=await ze(Wt()),o=await nt(Gt(),r,n,t);let c=null;try{c=await nt(jt(),r,n,t)}catch{c=null}return{sil:a,viseme:o,expression:c}})();const s=await ne;return Ee(s)}async function It(e){if(await $e(e),!ne)throw new Error("Assets not loaded");return ne}function Ee(e){return ln(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function Hn(e,t){const n=document.createElement("canvas");return n.width=t.w,n.height=t.h,n.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),n}async function rt(e,t,n){const r=`${t.diffBase}::${n}`,s=be.get(r);if(s)return s;const a=(async()=>{const o=t.cellByPath.get(n);if(!o)throw new Error(`No atlas cell for path: ${n}`);const c=fn(t.atlasMeta,o),f=Ee(e);if(dn(o)){const d=Hn(t,o),u=document.createElement("canvas");u.width=f.width,u.height=f.height;const i=u.getContext("2d");return i.fillStyle="#000",i.fillRect(0,0,f.width,f.height),i.drawImage(d,c.x,c.y),u}const h=`${t.diffBase}/${n}`,_=await ze(h);if(_.naturalWidth===f.width&&_.naturalHeight===f.height)return _;throw new Error(`Diff PNG wrong size for ${n}`)})();return be.set(r,a),a}async function st(e,t,n,r=je,s,a){if(s){const{width:u,height:i}=s;if(a&&!a()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const T=document.createElement("canvas");T.width=u,T.height=i;const R=T.getContext("2d");if(R.clearRect(0,0,u,i),R.drawImage(t,0,0,u,i),a&&!a())return;(e.width!==u||e.height!==i)&&(e.width=u,e.height=i);const S=e.getContext("2d");S.clearRect(0,0,u,i),S.drawImage(T,0,0);return}const o=await It(),{width:c,height:f}=Ee(o);(e.width!==c||e.height!==f)&&(e.width=c,e.height=f);const h=[],_=e.getContext("2d");if(_.clearRect(0,0,c,f),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const u=o.sil;_.drawImage(u,0,0,c,f);return}if(h.length===0){_.drawImage(t,0,0,c,f);return}const d=Tt(t,h,c,f,r);_.drawImage(d,0,0)}async function He(e,t,n=je){const r=await It(ge??void 0),{width:s,height:a}=Ee(r);(e.width!==s||e.height!==a)&&(e.width=s,e.height=a);const o=[];t.mouthPath&&o.push(await rt(r,r.viseme,t.mouthPath)),t.expressionPath&&r.expression&&o.push(await rt(r,r.expression,t.expressionPath));const c=e.getContext("2d");if(c.clearRect(0,0,s,a),o.length===0){c.drawImage(r.sil,0,0,s,a);return}const f=Tt(r.sil,o,s,a,n);c.drawImage(f,0,0)}async function Wn(e){await He(e,{mouthPath:null,expressionPath:null})}function Kn(e){const t=new Map;for(const n of e)t.has(n.vtime)||t.set(n.vtime,n);return[...t.values()].sort((n,r)=>n.vtime-r.vtime)}function qn(e){const t=Kn(e),n=[];for(let r=0;r<t.length-1;r++){const s=t[r].viseme,a=t[r+1].viseme,o=t[r].vtime,c=t[r+1].vtime;s===a||c<=o||n.push({from:s,to:a,fromVtime:o,toVtime:c})}return n}function Gn(e,t){let n=null;for(const r of e)t>=r.fromVtime&&t<r.toVtime&&(n=r);return n}function jn(e,t){return{gapMs:Math.max(0,t-e),holdEnd:e,transStart:e}}const zn=500,Qn="__sil__";function Jn(){return{lastDrawnKey:""}}function me(e){e.lastDrawnKey=""}function it(e){return e??Qn}function Xn(e,t){if(e.length===0)return{diffPath:null,label:"sil"};const n=qn(e),r=Gn(n,t);if(r){const a=r.toVtime-r.fromVtime,o=jn(r.fromVtime,r.toVtime);if(t>=o.transStart&&t<r.toVtime){const c=gn(r.from,r.to),f=yn(c,a);if(f.length>0){const h=pn(t,o.transStart,r.toVtime,f.length);return{diffPath:f[h]??f[f.length-1],label:`${r.from}→${r.to}`}}return{diffPath:Ve(r.to),label:r.to}}}const s=ht(e,t);return s==="sil"?{diffPath:null,label:"sil"}:{diffPath:Ve(s),label:s}}function At(e,t){const n=t??it(e.expressionPath);return`${it(e.mouthPath)}|${n}`}function Yn(e,t,n,r,s,a,o,c){const f=Xn(t,n),h={mouthPath:f.diffPath,expressionPath:r},_=At(h,s);_!==a.lastDrawnKey&&(a.lastDrawnKey=_,c(f.label),o(e,h))}function ot(e,t,n,r,s,a,o=null){const c={mouthPath:null,expressionPath:t},f=o!=null?`|idle@${Math.round(o*1e3)}`:"",h=At(c,n)+f;!(o!=null)&&h===r.lastDrawnKey||(r.lastDrawnKey=h,a(o!=null?"idle":"sil"),s(e,c))}function Zn(e){return new Worker("/assets/visemeDiffPreview.worker-D2ggcW5u.js",{name:e?.name})}function er(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function tr(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const n=e.getContext("2d");n&&n.drawImage(t,0,0)}function at(e,t){let n=0;return{usesWorker:!1,async loadAssets(r){return await $e(r)},async renderSilOnly(){n+=1,await Wn(e)},async drawLiveFrame(r,s){await $e(),await He(r,s)},async renderViseme(r,s){n+=1;const a=n;t.onStatus?.(s),await He(r,{mouthPath:Ve(s),expressionPath:null})},dispose(){n+=1}}}function nr(e,t){const n=new Zn;let r=1,s=0,a=!1,o=!1;const c=new Map,f=(d,u)=>{o||n.postMessage(d,[])},h=(d,u,i=!1)=>{if(o)return Promise.resolve();const T=r++;return new Promise((R,S)=>{c.set(T,{resolve:Q=>R(Q),reject:S,generation:typeof d.generation=="number"?d.generation:void 0,expectRenderDone:i}),f({...d,requestId:T})})};n.onmessage=d=>{if(o)return;const u=d.data;if(u.type==="status"){t.onStatus?.(u.label);return}if(u.type==="frame"){const T=u.generation<0;if(!T&&u.generation!==s){u.bitmap.close();return}if(T&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){u.bitmap.close();return}tr(e,u.bitmap),u.bitmap.close();return}const i=c.get(u.requestId);if(i)switch(u.type){case"ready":c.delete(u.requestId),i.resolve();break;case"loadAssetsDone":c.delete(u.requestId),i.resolve({width:u.width,height:u.height});break;case"renderDone":if(i.expectRenderDone&&i.generation!=null&&u.generation!==i.generation)return;c.delete(u.requestId),i.resolve();break;case"renderAborted":c.delete(u.requestId),i.resolve();break;case"error":c.delete(u.requestId),t.onError?.(u.message),i.reject(new Error(u.message));break}},n.onerror=d=>{if(o)return;const u=d.message||"Worker error";t.onError?.(u);for(const[,i]of c)i.reject(new Error(u));c.clear()};const _=(async()=>{await h({type:"init"}),a=!0})();return{usesWorker:!0,async loadAssets(d){if(o)return{width:842,height:1264};if(await _,o)return{width:842,height:1264};if(!a)throw new Error("Worker init failed");return await h({type:"loadAssets",keyHex:d,urls:se()})??{width:842,height:1264}},async renderSilOnly(){if(o||(await _,o))return;s+=1;const d=s;await h({type:"renderSil",generation:d},void 0,!0)},async drawLiveFrame(d,u){o||(await _,!o&&await h({type:"drawFrame",diffPath:u.mouthPath,expressionDiffPath:u.expressionPath},void 0,!0))},async renderViseme(d,u,i={}){if(o||(await _,o))return;s+=1;const T=s;try{await h({type:"renderViseme",generation:T,to:u,from:i.from,transitionDurationMs:i.transitionDurationMs,gapMs:i.gapMs,threshold:i.threshold},void 0,!0)}catch(R){if(R?.name==="AbortError")return;throw R}},dispose(){if(!o){o=!0,s+=1;for(const[,d]of c)d.resolve();c.clear(),n.terminate()}}}}function rr(e,t={}){if(!er())return at(e,t);try{return nr(e,t)}catch{return at(e,t)}}class sr extends Error{constructor(t,n){super(`${t} timed out after ${Math.round(n/1e3)}s`),this.name="LoadTimeoutError"}}function Pe(e,t,n){return new Promise((r,s)=>{const a=globalThis.setTimeout(()=>{s(new sr(n,t))},t);e.then(o=>{globalThis.clearTimeout(a),r(o)},o=>{globalThis.clearTimeout(a),s(o)})})}const ir={position:"relative",display:"inline-block",lineHeight:0},or={display:"block",maxWidth:"100%",height:"auto"},ar={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 ve(e){return Dt(e)?gt:qe}const cr=m.forwardRef(function({id:t,assets:n,authToken:r,apiBase:s=De,facesCdnBase:a=We,ttsEngineId:o=re,voiceId:c,speakingRate:f=Ge,className:h,style:_,canvasStyle:d,onStatusChange:u,onDisplayStatus:i,onError:T,onReady:R,showErrorOverlay:S=!0},Q){const W=m.useRef(null),N=m.useRef(null),K=m.useRef(Jn()),g=m.useRef(Pn()),E=m.useRef(0),k=m.useRef(0),V=m.useRef(null),q=m.useRef(!1),I=m.useRef(null),J=m.useRef(!1),[ie,le]=m.useState("idle"),[oe,fe]=m.useState(null),[Y,Se]=m.useState(!1),[de,ae]=m.useState(!1),[Te,Qe]=m.useState({width:842,height:1264}),w=m.useRef(r??null),C=m.useRef(null),A=m.useRef(null),v=m.useRef(!1),b=m.useRef({width:842,height:1264}),F=m.useRef(R),D=m.useRef(i),G=m.useRef(T);F.current=R,D.current=i,G.current=T;const M=m.useCallback(l=>{const p=l?.ttsEngineId??o??A.current?.ttsEngineId??re,y=l?.voiceId??c??A.current?.voiceId??ve(p);return{ttsEngineId:p,voiceId:y}},[o,c]),B=m.useCallback(l=>{fe(l),G.current?.(l)},[]),$=m.useCallback(l=>{le(l),u?.(l)},[u]),P=m.useRef(Et($,s)),L=m.useCallback(()=>{V.current!=null&&(clearTimeout(V.current),V.current=null)},[]),ce=m.useCallback(async()=>{const l=k.current,p=N.current;!p||!Y||P.current.isSpeaking()||(me(K.current),await p.renderSilOnly(),l===k.current&&(P.current.isSpeaking()||(D.current?.("sil"),J.current&&I.current&&(I.current.restart(),I.current.setActive(!0)))))},[Y]),Z=m.useCallback(()=>{L();const l=k.current;ce(),V.current=setTimeout(()=>{V.current=null,l===k.current&&ce()},zn)},[L,ce]),U=m.useCallback(async()=>{const l=Math.floor(Date.now()/1e3);if(C.current&&l<C.current.expiresAt-60)return C.current.key;const p=w.current??await Oe();w.current=p,P.current.setDeveloperToken(p);const y=await Lt(p,s);return C.current={key:y.value,expiresAt:y.expiresAt},y.value},[s]);m.useEffect(()=>{r&&(w.current=r,P.current.setDeveloperToken(r))},[r]),m.useEffect(()=>{P.current.setTtsEngineId(o),A.current&&(A.current.ttsEngineId=o)},[o]),m.useEffect(()=>{const l=c??ve(o);A.current&&(A.current.voiceId=l)},[c,o]);const X=m.useMemo(()=>n?JSON.stringify(n):`id:${t??""}:${a}`,[n,t,a]);return m.useEffect(()=>{if(!t&&!n){B("AiTwin requires either `id` or `assets`");return}let l=!1;v.current=!1,Se(!1),ae(!1),fe(null),A.current=null,I.current=null,J.current=!1;const p=W.current;if(!p)return;let y=null;return(async()=>{try{let x,H,ee;if(n)x=n,H=o,ee=c??ve(H);else{D.current?.("getAiTwin");const O=await Pe(ut(t),3e4,"getAiTwin");if(l)return;if(!O.faceId?.trim())throw new Error("getAiTwin returned no faceId");H=O.ttsEngineId,ee=c??O.voiceId??ve(H),x=Bt(O.faceId,a)}A.current={ttsEngineId:H,voiceId:ee},$t(x),I.current=On(Kt()),y=rr(p,{onStatus:O=>{l||D.current?.(O)},onError:O=>{l||B(O)},shouldAcceptLiveFrame:()=>P.current.isSpeaking()}),N.current=y,D.current?.("auth");const te=w.current??r??await Pe(Oe(),3e4,"getAuthToken");if(l)return;w.current=te,P.current.setDeveloperToken(te),P.current.setTtsEngineId(H);const Me=lt()?await U():void 0;if(l)return;D.current?.("loadAssets");const j=await Pe(y.loadAssets(Me),18e4,"load face assets");if(!l&&j.width>0&&j.height>0&&(b.current=j,Qe(j)),l)return;v.current=!0,Se(!0),F.current?.(),y.renderSilOnly().then(()=>{l||D.current?.("sil")}),(async()=>{try{if(await I.current.load(),l)return;J.current=!0,ae(!0),me(K.current),I.current.setActive(!0);const O=I.current.getVideo();await st(p,O,null,void 0,b.current),l||D.current?.("idle")}catch(O){if(l)return;J.current=!1,ae(!1),console.warn("[AiTwin] Idle video unavailable:",O)}})()}catch(x){if(l)return;if(x instanceof Re){B(x.message);return}B(x instanceof Error?x.message:"Failed to load AI twin")}})(),()=>{l=!0,y?.dispose(),N.current=null}},[X,n,o,c,a,r,s,U,B]),m.useEffect(()=>{ie==="done"&&Y&&!P.current.isSpeaking()&&Z()},[ie,Y,Z]),m.useEffect(()=>{const l=P.current,p={current:!1},y=()=>{const x=W.current,H=N.current;if(!x||!H||!Y){E.current=requestAnimationFrame(y);return}const ee=l.isSpeaking(),te=l.getPlaybackElapsedMs(),Me=ee?Vn(te,l.getSentenceEmotions(),l.getWordQueue(),l.getStreamMood()):l.getStreamMood();if(ee){if(J.current&&I.current?.setActive(!1),l.getVisemeQueue().length===0){E.current=requestAnimationFrame(y);return}g.current.setTargetEmotion(Me),g.current.advance(performance.now());const O=g.current.getExpressionPath(),he=g.current.getDrawKey();Yn(x,l.getVisemeQueue(),te,O,he,K.current,(_e,kt)=>H.drawLiveFrame(_e,kt),_e=>D.current?.(_e))}else if(J.current&&I.current){const j=I.current,O=j.getVideo();O.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?(j.setActive(!0),p.current||(p.current=!0,st(x,O,null,void 0,b.current,()=>!l.isSpeaking()).then(()=>D.current?.("idle")).finally(()=>{p.current=!1}))):ot(x,null,"__sil__",K.current,(he,_e)=>H.drawLiveFrame(he,_e),he=>D.current?.(he))}else ot(x,null,"__sil__",K.current,(j,O)=>H.drawLiveFrame(j,O),j=>D.current?.(j));q.current&&!ee&&(g.current.reset(),I.current?.setActive(!1),Z()),q.current=ee,E.current=requestAnimationFrame(y)};return E.current=requestAnimationFrame(y),()=>{cancelAnimationFrame(E.current),L(),l.stop()}},[Y,de,Z,L]),m.useImperativeHandle(Q,()=>({speakText:async(l,p)=>{const y=l.trim();if(!y)return;const x=N.current;if(!x||!v.current&&!Y)throw new Error("AI twin is not ready");const{ttsEngineId:H,voiceId:ee}=M(p);fe(null),k.current+=1,L(),me(K.current),g.current.reset(),q.current=!1;try{J.current?(I.current?.setActive(!1),I.current?.restart(),me(K.current)):await x.renderSilOnly(),await P.current.speak(y,{voiceId:ee,speakingRate:p?.speakingRate??f,ttsEngineId:H})}catch(te){throw B(te instanceof Error?te.message:"TTS failed"),te}},stop:()=>{k.current+=1,L(),P.current.stop(),me(K.current),q.current=!1,Z()},setTtsEngineId:l=>{P.current.setTtsEngineId(l),A.current&&(A.current.ttsEngineId=l)},renderViseme:async(l,p)=>{const y=N.current,x=W.current;if(!y||!x||!v.current)throw new Error("AI twin is not ready");await y.renderViseme(x,l,{from:p?.from,transitionDurationMs:p?.transitionDurationMs,gapMs:p?.gapMs,onStatus:H=>D.current?.(H)})},isReady:()=>v.current,getStatus:()=>ie}),[Y,f,L,Z,B,M,ie]),Fe.jsxs("div",{className:h,style:{...ir,..._},children:[Fe.jsx("canvas",{ref:W,width:Te.width,height:Te.height,style:{...or,...d},"aria-label":t?`AI twin ${t}`:"AI twin face"}),S&&oe?Fe.jsx("div",{style:ar,children:oe}):null]})});exports.AiTwin=cr;exports.AiTwinNotFoundError=Re;exports.DEFAULT_API_BASE=De;exports.DEFAULT_FACES_CDN_BASE=We;exports.DEFAULT_TTS_ENGINE_ID=Ke;exports.OCULUS_VISEME_IDS=ft;exports.SPEAKING_RATE_MAX=wt;exports.SPEAKING_RATE_MIN=mt;exports.TTS_ENGINE_CARTESIA=pe;exports.TTS_ENGINE_GOOGLE=re;exports.TTS_ENGINE_INWORLD=ye;exports.VISEME_IDS=mn;exports.VISEME_TEST_CARTESIA_VOICE_ID=gt;exports.VISEME_TEST_SPEAKING_RATE=Ge;exports.VISEME_TEST_VOICE_ID=qe;exports.createAvatarTtsLipsyncController=Et;exports.fetchAiTwin=ut;exports.fetchDevAuthToken=Oe;exports.normalizeOculusViseme=dt;exports.resolveVisemeAtTime=ht;exports.resolveWordAtTime=_t;
|
|
3
|
+
`);H=T.pop()??"";for(const A of T)await le(A);if(fe){H.trim()&&await le(H.trim()),C||Z();break}}}catch(L){throw console.error("[avatarTtsLipsync]",L),X(),e("error"),L}},stop:X,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>i,getStreamMood:()=>a,getPlaybackElapsedMs:()=>_?I&&d&&w>0?Math.max(0,(d.currentTime-w)*1e3):Math.max(0,(performance.now()-x)*R):0,isSpeaking:()=>_,setPlaybackSpeed:h=>{R=Math.max(.1,Math.min(1,h)),q()},getPlaybackSpeed:()=>R,subscribe:h=>(D.add(h),()=>D.delete(h))}}function Tn(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function In(e,t){const n=e.crop;return{x:t?.source_x??n?.x??0,y:t?.source_y??n?.y??0,width:t?.source_w??n?.width??e.frame_width??0,height:t?.source_h??n?.height??e.frame_height??0}}function kn(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function Tt(e,t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${e}`);const r=await n.arrayBuffer();return Jt(r,t)}async function An(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await Tt(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function vn(e){const t=new Blob([e],{type:"image/webp"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const i=new Image;i.decoding="async",i.onload=()=>{URL.revokeObjectURL(n),r(i)},i.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted image"))},i.src=n})}function It(e,t){return`${e}/${t}_minus_sil.png`}const ze=15,Rn=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],Ve=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],bn=Ve.length,Ze=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 et(e,t){return`${e}__${t}`}function tt(e,t){return`pairs/${e}/${t}_minus_sil.png`}function $e(e){return`${e}_minus_sil.png`}function Dn(e,t){if(e===t)return[];const n=[],r=et(e,t);if(Ze.has(r)){for(const i of Ve)n.push(tt(r,i));return n}const s=et(t,e);if(Ze.has(s)){for(let i=bn-1;i>=0;i--)n.push(tt(s,Ve[i]));return n}return[]}function xn(e){return e<16?[]:e<35?[2]:e<51?[0,4]:e<67?[0,2,4]:e<83?[0,1,2,3]:[0,1,2,3,4]}function Cn(e,t){return xn(t).map(r=>e[r]).filter(r=>r!=null)}function Mn(e,t,n,r){if(r<=0)return 0;const i=Math.max(1,n-t)/r,a=e-t,c=Math.floor(a/i);return Math.min(r-1,Math.max(0,c))}function Fn(e,t,n){if(!n)return`${e}/${t}`;const r=t.split("/").pop()??t,s=r.includes(".")?r.slice(0,r.lastIndexOf(".")):r;return`${e}/${s}.bin`}const On=[.32,.24,.16,.08,0],De=On.map(e=>`eyes_${e.toFixed(2)}`),Pn=["eyes_0.32","eyes_0.16","eyes_0.00"],Ln=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],Un={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 oe(e,t="neutral"){const n=String(e??"").trim().toLowerCase();return n?Un[n]??t:t}function z(e){return e==="neutral"?null:It(e,"eyes_standard")}function He(e,t){return!t||t==="eyes_standard"?z(e):e==="neutral"&&t==="eyes_standard"?null:It(e,t)}function Nn(e){if(!e)return"open";const t=e.match(/\/(eyes_[^/]+)_minus_sil\.png$/);if(!t?.[1])return"open";const n=t[1];return De.includes(n)?n:"open"}function Bn(e){return e==="open"?-1:De.indexOf(e)}function Vn(e,t){const n=[];for(const r of t)n.push(He(e,r));for(let r=t.length-2;r>=0;r--)n.push(He(e,t[r]));return n.push(z(e)),n}function $n(e,t){if(t==="open")return[z(e)];const n=Bn(t);if(n<0)return[z(e)];const r=[];for(let s=n;s>=0;s--)r.push(He(e,De[s]));return r.push(z(e)),r}const Hn=["t01_0.20","t02_0.40","t03_0.60","t04_0.80"];function qn(e,t){return`${e}__${t}`}function Wn(e,t,n){return`pairs/${qn(e,t)}/${n}_minus_sil.png`}function Kn(e,t){return Hn.map(n=>Wn(e,t,n))}function he(e,t){return e+Math.random()*(t-e)}function Ie(){return he(14,32)}function Gn(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:i=120,doubleBlinkGapMaxMs:a=220,fullBlinkChance:c=.75,postEmotionChangeDelayMs:l=380,minDwellMs:d=550}=e;let m="neutral",_="neutral",I=0,o={kind:"idle",nextBlinkAt:performance.now()+t},x=null,w="__eyes_open__|neutral";function u(y,S){x=y,w=`${y??"__eyes_open__"}|${S}`}function g(y,S){o={kind:"idle",nextBlinkAt:y+he(n,r)},_=S,u(z(S),S)}function E(y,S,C){const D=C?Pn:Math.random()<c?De:Ln;o={kind:"playing",paths:Vn(S,D),index:0,holdUntil:y+Ie(),after:"idle",blinkEmotion:S},_=S,u(o.paths[0]??null,S)}function R(y,S,C){const D=Nn(x),q=[];if(D!=="open"){const W=$n(S,D);q.push(...W.slice(0,-1))}q.push(...Kn(S,C)),q.push(z(C)),o={kind:"emotionBlend",paths:q,index:0,holdUntil:y+Ie(),thenEmotion:C},u(q[0]??null,S)}function $(y){if(o.kind==="emotionBlend"){for(;y>=o.holdUntil&&o.index<o.paths.length-1;)o.index+=1,o.holdUntil=y+Ie();const S=o.paths[o.index]??null;u(S,o.thenEmotion),y>=o.holdUntil&&o.index>=o.paths.length-1&&(m=o.thenEmotion,_=o.thenEmotion,u(z(o.thenEmotion),o.thenEmotion),o={kind:"idle",nextBlinkAt:y+l});return}if(o.kind==="idle"){u(z(_),_),y>=o.nextBlinkAt&&E(y,_,!1);return}if(o.kind==="doublePause"){u(z(o.blinkEmotion),o.blinkEmotion),y>=o.resumeAt&&E(y,o.blinkEmotion,!0);return}if(o.kind==="playing"){for(;y>=o.holdUntil&&o.index<o.paths.length-1;)o.index+=1,o.holdUntil=y+Ie();if(u(o.paths[o.index]??null,o.blinkEmotion),y>=o.holdUntil&&o.index>=o.paths.length-1){if(o.after==="resumeIdle"){g(y+l,m);return}if(o.after==="doublePause"){o={kind:"doublePause",resumeAt:y+he(i,a),blinkEmotion:o.blinkEmotion},u(z(o.blinkEmotion),o.blinkEmotion);return}if(o.after==="idle"&&Math.random()<s){o={kind:"doublePause",resumeAt:y+he(i,a),blinkEmotion:o.blinkEmotion},u(z(o.blinkEmotion),o.blinkEmotion);return}g(y,o.blinkEmotion)}}}return{advance:$,setTargetEmotion(y){const S=oe(y,m);if(S===m&&o.kind!=="emotionBlend")return;const C=performance.now();if(d>0&&S!==m&&o.kind!=="emotionBlend"&&!(m==="neutral"&&S!=="neutral")&&C-I<d)return;const D=m;if(m=S,I=C,o.kind==="playing"){R(C,_,S);return}if(o.kind==="emotionBlend"){o={...o,thenEmotion:S};return}R(C,D,S)},getTargetEmotion:()=>m,getExpressionPath:()=>x,getDrawKey:()=>w,reset(){m="neutral",_="neutral",I=0,g(performance.now()+he(n,r),"neutral")}}}function jn(e){const t=document.createElement("video");t.muted=!0,t.playsInline=!0,t.preload="auto",t.setAttribute("playsinline",""),t.loop=!0;let n=!1;return{async load(){n||await new Promise((r,s)=>{const i=()=>{const a=t.duration;if(!Number.isFinite(a)||a<=0){s(new Error(`Idle video has invalid duration: ${e}`));return}n=!0,t.currentTime=0,r()};t.addEventListener("loadeddata",i,{once:!0}),t.addEventListener("error",()=>s(new Error(`Failed to load idle video: ${e}`)),{once:!0}),t.src=e,t.load()})},isReady(){return n},getVideo(){return t},setActive(r){n&&(r?t.paused&&t.play().catch(()=>{}):t.paused||t.pause())},restart(){n&&(t.currentTime=0)}}}const zn=50,kt=400;function Qn(e,t,n){return e.filter(r=>{const s=r.queueIndex??-1;return s>=t&&s<=n})}function Jn(e,t){const n=[];for(const r of e){const s=Qn(t,r.start_word,r.end_word);if(s.length===0)continue;const i=s[0],a=s[s.length-1];n.push({sentence:r,startMs:Math.max(0,i.wtime-zn),endMs:a.wtime+a.wduration+kt})}return n.sort((r,s)=>r.startMs-s.startMs)}function Yn(e,t,n,r="neutral"){const s=oe(r);if(t.length===0)return s;if(n.length===0||e<n[0].wtime)return oe(t[0]?.emotion,s);const i=Jn(t,n);let a=null;for(const d of i)e>=d.startMs&&e<=d.endMs&&(a=d);if(a)return oe(a.sentence.emotion,s);const c=n[n.length-1],l=c.wtime+c.wduration;return e>l+kt?oe(t[t.length-1]?.emotion,s):s}function At(e,t,n,r,s=ze){const i=new OffscreenCanvas(n,r),a=i.getContext("2d",{willReadFrequently:!0});a.drawImage(e,0,0,n,r);const c=a.getImageData(0,0,n,r),l=a.createImageData(n,r);l.data.set(c.data);const m=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const _ of t){m.clearRect(0,0,n,r),m.drawImage(_,0,0,n,r);const I=m.getImageData(0,0,n,r);for(let o=0;o<l.data.length;o+=4){const x=I.data[o],w=I.data[o+1],u=I.data[o+2];Math.max(x,w,u)>s&&(l.data[o]=x,l.data[o+1]=w,l.data[o+2]=u,l.data[o+3]=255)}}return a.putImageData(l,0,0),i}let ne=null,_e=null;const Re=new Map;function Xn(){ne=null,_e=null,Re.clear()}en(Xn);function Zn(e){return e??_e??void 0}function Qe(e){return new Promise((t,n)=>{const r=new Image;r.decoding="async",r.onload=()=>t(r),r.onerror=()=>n(new Error(`Failed to load image: ${e}`)),r.src=e})}async function nt(e,t,n,r){const s=n?await An(e,r):await fetch(e).then(d=>{if(!d.ok)throw new Error(`Failed to load ${e}`);return d.json()}),i=s.sheets?.[0];if(!i?.path)throw new Error(`${e} has no sheets[0].path`);const a=n?await vn(await Tt(Fn(t,i.path,n),r??"")):await Qe(`${t}/${i.path}`),c=new Map;for(const d of s.cells??[])d.path&&c.set(d.path,d);const l=new Map;for(const d of s.sheets??[])l.set(d.index,d);return{atlas:a,atlasMeta:s,cellByPath:c,sheetByIndex:l,diffBase:t}}async function qe(e){const t=Zn(e),n=ht();if(n&&!t)throw new Error("Encrypted assets enabled but no key provided");if(ne&&(!n||_e===(t??null))){const i=await ne;return me(i)}_e=t??null,Re.clear();const r=rn();ne=(async()=>{const i=await Qe(tn()),a=await nt(sn(),r,n,t);let c=null;try{c=await nt(on(),r,n,t)}catch{c=null}return{sil:i,viseme:a,expression:c}})();const s=await ne;return me(s)}async function vt(e){if(await qe(e),!ne)throw new Error("Assets not loaded");return ne}function me(e){return Tn(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function er(e,t){const n=document.createElement("canvas");return n.width=t.w,n.height=t.h,n.getContext("2d",{willReadFrequently:!0}).drawImage(e.atlas,t.x,t.y,t.w,t.h,0,0,t.w,t.h),n}async function rt(e,t,n){const r=`${t.diffBase}::${n}`,s=Re.get(r);if(s)return s;const i=(async()=>{const a=t.cellByPath.get(n);if(!a)throw new Error(`No atlas cell for path: ${n}`);const c=In(t.atlasMeta,a),l=me(e);if(kn(a)){const _=er(t,a),I=document.createElement("canvas");I.width=l.width,I.height=l.height;const o=I.getContext("2d");return o.fillStyle="#000",o.fillRect(0,0,l.width,l.height),o.drawImage(_,c.x,c.y),I}const d=`${t.diffBase}/${n}`,m=await Qe(d);if(m.naturalWidth===l.width&&m.naturalHeight===l.height)return m;throw new Error(`Diff PNG wrong size for ${n}`)})();return Re.set(r,i),i}async function st(e,t,n,r=ze,s,i){if(s){const{width:I,height:o}=s;if(i&&!i()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const x=document.createElement("canvas");x.width=I,x.height=o;const w=x.getContext("2d");if(w.clearRect(0,0,I,o),w.drawImage(t,0,0,I,o),i&&!i())return;(e.width!==I||e.height!==o)&&(e.width=I,e.height=o);const u=e.getContext("2d");u.clearRect(0,0,I,o),u.drawImage(x,0,0);return}const a=await vt(),{width:c,height:l}=me(a);(e.width!==c||e.height!==l)&&(e.width=c,e.height=l);const d=[],m=e.getContext("2d");if(m.clearRect(0,0,c,l),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const I=a.sil;m.drawImage(I,0,0,c,l);return}if(d.length===0){m.drawImage(t,0,0,c,l);return}const _=At(t,d,c,l,r);m.drawImage(_,0,0)}async function We(e,t,n=ze){const r=await vt(_e??void 0),{width:s,height:i}=me(r);(e.width!==s||e.height!==i)&&(e.width=s,e.height=i);const a=[];t.mouthPath&&a.push(await rt(r,r.viseme,t.mouthPath)),t.expressionPath&&r.expression&&a.push(await rt(r,r.expression,t.expressionPath));const c=e.getContext("2d");if(c.clearRect(0,0,s,i),a.length===0){c.drawImage(r.sil,0,0,s,i);return}const l=At(r.sil,a,s,i,n);c.drawImage(l,0,0)}async function tr(e){await We(e,{mouthPath:null,expressionPath:null})}function nr(e){const t=new Map;for(const n of e)t.has(n.vtime)||t.set(n.vtime,n);return[...t.values()].sort((n,r)=>n.vtime-r.vtime)}function rr(e){const t=nr(e),n=[];for(let r=0;r<t.length-1;r++){const s=t[r].viseme,i=t[r+1].viseme,a=t[r].vtime,c=t[r+1].vtime;s===i||c<=a||n.push({from:s,to:i,fromVtime:a,toVtime:c})}return n}function sr(e,t){let n=null;for(const r of e)t>=r.fromVtime&&t<r.toVtime&&(n=r);return n}function ir(e,t){return{gapMs:Math.max(0,t-e),holdEnd:e,transStart:e}}const or=500,ar="__sil__";function cr(){return{lastDrawnKey:""}}function de(e){e.lastDrawnKey=""}function it(e){return e??ar}function ur(e,t){if(e.length===0)return{diffPath:null,label:"sil"};const n=rr(e),r=sr(n,t);if(r){const i=r.toVtime-r.fromVtime,a=ir(r.fromVtime,r.toVtime);if(t>=a.transStart&&t<r.toVtime){const c=Dn(r.from,r.to),l=Cn(c,i);if(l.length>0){const d=Mn(t,a.transStart,r.toVtime,l.length);return{diffPath:l[d]??l[l.length-1],label:`${r.from}→${r.to}`}}return{diffPath:$e(r.to),label:r.to}}}const s=wt(e,t);return s==="sil"?{diffPath:null,label:"sil"}:{diffPath:$e(s),label:s}}function Rt(e,t){const n=t??it(e.expressionPath);return`${it(e.mouthPath)}|${n}`}function lr(e,t,n,r,s,i,a,c){const l=ur(t,n),d={mouthPath:l.diffPath,expressionPath:r},m=Rt(d,s);m!==i.lastDrawnKey&&(c(l.label),a(e,d,m,_=>{_===m&&(i.lastDrawnKey=m)}))}function ot(e,t,n,r,s,i,a=null){const c={mouthPath:null,expressionPath:t},l=a!=null?`|idle@${Math.round(a*1e3)}`:"",d=Rt(c,n)+l;!(a!=null)&&d===r.lastDrawnKey||(i(a!=null?"idle":"sil"),s(e,c,d,_=>{_===d&&(r.lastDrawnKey=d)}))}function bt(e){let t=0,n=null,r=!1,s=!1;const i=async()=>{if(!r){r=!0;try{for(;n;){const c=n;n=null;const l=++t,d=await e(c.diffs,l,c);n||d&&l===t&&c.onDisplayed?.(c.drawKey)}}finally{r=!1,n&&i()}}},a=()=>{s||(s=!0,queueMicrotask(()=>{s=!1,i()}))};return{enqueue(c,l,d){n={diffs:c,drawKey:l,onDisplayed:d},a()},reset(){t+=1,n=null},isLatestSeq(c){return c===t},getLatestSeq(){return t}}}const at=-1,ct=new Map;async function fr(e){let t=ct.get(e);if(!t){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to fetch worker script (${n.status}): ${e}`);const r=await n.blob();t=URL.createObjectURL(r),ct.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function dr(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function hr(e,t){(e.width!==t.width||e.height!==t.height)&&(e.width=t.width,e.height=t.height);const n=e.getContext("2d");n&&n.drawImage(t,0,0)}function ut(e,t){let n=0;const r=bt(async(s,i)=>(await qe(),await We(e,s),r.isLatestSeq(i)));return{usesWorker:!1,async loadAssets(s){return await qe(s)},async renderSilOnly(){n+=1,r.reset(),await tr(e)},drawLiveFrame(s,i,a,c){r.enqueue(i,a,c)},async renderViseme(s,i){n+=1;const a=n;t.onStatus?.(i),await We(s,{mouthPath:$e(i),expressionPath:null})},dispose(){n+=1,r.reset()}}}function _r(e,t,n){let r=null,s=1,i=0,a=!1,c=!1;const l=new Map,d=bt(async(w,u,g)=>(l.set(u,{drawKey:g.drawKey,onDisplayed:g.onDisplayed}),await o({type:"drawFrame",diffPath:w.mouthPath,expressionDiffPath:w.expressionPath,liveSeq:u},void 0,!0),!1)),m=w=>{if(w===at){let E=-1,R;for(const[$,y]of l)$>=E&&(E=$,R=y);E>=0&&l.delete(E),R?.onDisplayed?.(R.drawKey);return}const u=-w,g=l.get(u);l.delete(u),g?.onDisplayed?.(g.drawKey)},_=new Map,I=(w,u)=>{c||!r||r.postMessage(w,[])},o=(w,u,g=!1)=>{if(c)return Promise.resolve();const E=s++;return new Promise((R,$)=>{_.set(E,{resolve:y=>R(y),reject:$,generation:typeof w.generation=="number"?w.generation:void 0,expectRenderDone:g}),I({...w,requestId:E})})},x=(async()=>{r=await fr(n),r.onmessage=w=>{if(c)return;const u=w.data;if(u.type==="status"){t.onStatus?.(u.label);return}if(u.type==="frame"){const E=u.generation<0;if(!E&&u.generation!==i){u.bitmap.close();return}if(E&&!(u.generation===at)){const $=-u.generation;if(!d.isLatestSeq($)){u.bitmap.close();return}}if(E&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){u.bitmap.close();return}hr(e,u.bitmap),u.bitmap.close(),E&&m(u.generation);return}const g=_.get(u.requestId);if(g)switch(u.type){case"ready":_.delete(u.requestId),g.resolve();break;case"loadAssetsDone":_.delete(u.requestId),g.resolve({width:u.width,height:u.height});break;case"renderDone":if(g.expectRenderDone&&g.generation!=null&&u.generation!==g.generation)return;_.delete(u.requestId),g.resolve();break;case"renderAborted":_.delete(u.requestId),g.resolve();break;case"error":_.delete(u.requestId),t.onError?.(u.message),g.reject(new Error(u.message));break}},r.onerror=w=>{if(c)return;const u=w.message||"Worker error";t.onError?.(u);for(const[,g]of _)g.reject(new Error(u));_.clear()},await o({type:"init"}),a=!0})();return{usesWorker:!0,async loadAssets(w){if(c)return{width:842,height:1264};if(await x,c)return{width:842,height:1264};if(!a)throw new Error("Worker init failed");return await o({type:"loadAssets",keyHex:w,urls:Zt(se())})??{width:842,height:1264}},async renderSilOnly(){if(c||(await x,c))return;i+=1,d.reset(),l.clear();const w=i;await o({type:"renderSil",generation:w},void 0,!0)},drawLiveFrame(w,u,g,E){c||x.then(()=>{c||d.enqueue(u,g,E)})},async renderViseme(w,u,g={}){if(c||(await x,c))return;i+=1;const E=i;try{await o({type:"renderViseme",generation:E,to:u,from:g.from,transitionDurationMs:g.transitionDurationMs,gapMs:g.gapMs,threshold:g.threshold},void 0,!0)}catch(R){if(R?.name==="AbortError")return;throw R}},dispose(){if(!c){c=!0,i+=1,d.reset(),l.clear();for(const[,w]of _)w.resolve();_.clear(),r?.terminate(),r=null}}}}function mr(e,t={}){if(!dr())return ut(e,t);const n=Lt(t.workerScriptUrl);try{return _r(e,t,n)}catch{return ut(e,t)}}class wr extends Error{constructor(t,n){super(`${t} timed out after ${Math.round(n/1e3)}s`),this.name="LoadTimeoutError"}}function Le(e,t,n){return new Promise((r,s)=>{const i=globalThis.setTimeout(()=>{s(new wr(n,t))},t);e.then(a=>{globalThis.clearTimeout(i),r(a)},a=>{globalThis.clearTimeout(i),s(a)})})}const gr={position:"relative",display:"inline-block",lineHeight:0},yr={display:"block",maxWidth:"100%",height:"auto"},pr={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 ke(e){return Vt(e)?Et:Ge}const Er=p.forwardRef(function({id:t,assets:n,authToken:r,ttsEngineId:s=re,voiceId:i,speakingRate:a=je,className:c,style:l,canvasStyle:d,onStatusChange:m,onDisplayStatus:_,onError:I,onReady:o,showErrorOverlay:x=!0},w){const u=p.useRef(null),g=p.useRef(null),E=p.useRef(cr()),R=p.useRef(Gn()),$=p.useRef(0),y=p.useRef(0),S=p.useRef(null),C=p.useRef(!1),D=p.useRef(null),q=p.useRef(!1),[W,ae]=p.useState("idle"),[ye,X]=p.useState(null),[K,pe]=p.useState(!1),[xe,ce]=p.useState(!1),[ie,Ee]=p.useState({width:842,height:1264}),Z=p.useRef(r??null),ue=p.useRef(null),h=p.useRef(null),M=p.useRef(!1),B=p.useRef({width:842,height:1264}),P=p.useRef(o),k=p.useRef(_),U=p.useRef(I);P.current=o,k.current=_,U.current=I;const Q=p.useCallback(f=>{const T=f?.ttsEngineId??s??h.current?.ttsEngineId??re,A=f?.voiceId??i??h.current?.voiceId??ke(T);return{ttsEngineId:T,voiceId:A}},[s,i]),V=p.useCallback(f=>{X(f),U.current?.(f)},[]),L=p.useCallback(f=>{ae(f),m?.(f)},[m]),F=p.useRef(St(L,Ae)),O=p.useCallback(()=>{S.current!=null&&(clearTimeout(S.current),S.current=null)},[]),J=p.useCallback(async()=>{const f=y.current,T=g.current;!T||!K||F.current.isSpeaking()||(de(E.current),await T.renderSilOnly(),f===y.current&&(F.current.isSpeaking()||(k.current?.("sil"),q.current&&D.current&&(D.current.restart(),D.current.setActive(!0)))))},[K]),H=p.useCallback(()=>{O();const f=y.current;J(),S.current=setTimeout(()=>{S.current=null,f===y.current&&J()},or)},[O,J]),le=p.useCallback(async()=>{const f=Math.floor(Date.now()/1e3);if(ue.current&&f<ue.current.expiresAt-60)return ue.current.key;const T=Z.current??await Ue();Z.current=T,F.current.setDeveloperToken(T);const A=await jt(T,Ae);return ue.current={key:A.value,expiresAt:A.expiresAt},A.value},[]);p.useEffect(()=>{r&&(Z.current=r,F.current.setDeveloperToken(r))},[r]),p.useEffect(()=>{F.current.setTtsEngineId(s),h.current&&(h.current.ttsEngineId=s)},[s]),p.useEffect(()=>{const f=i??ke(s);h.current&&(h.current.voiceId=f)},[i,s]);const fe=p.useMemo(()=>n?JSON.stringify(n):`id:${t??""}`,[n,t]);return p.useEffect(()=>{if(!t&&!n){V("AiTwin requires either `id` or `assets`");return}let f=!1;M.current=!1,pe(!1),ce(!1),X(null),h.current=null,D.current=null,q.current=!1;const T=u.current;if(!T)return;let A=null;return(async()=>{try{let v,b,G;if(n)v=n,b=s,G=i??ke(b);else{k.current?.("getAiTwin");const N=await Le(dt(t),3e4,"getAiTwin");if(f)return;if(!N.faceId?.trim())throw new Error("getAiTwin returned no faceId");b=N.ttsEngineId,G=i??N.voiceId??ke(b),v=Yt(N.faceId)}h.current={ttsEngineId:b,voiceId:G},Xt(v),D.current=jn(nn()),A=mr(T,{onStatus:N=>{f||k.current?.(N)},onError:N=>{f||V(N)},shouldAcceptLiveFrame:()=>F.current.isSpeaking()}),g.current=A,k.current?.("auth");const Y=Z.current??r??await Le(Ue(),3e4,"getAuthToken");if(f)return;Z.current=Y,F.current.setDeveloperToken(Y),F.current.setTtsEngineId(b);const Ce=ht()?await le():void 0;if(f)return;k.current?.("loadAssets");const j=await Le(A.loadAssets(Ce),18e4,"load face assets");if(!f&&j.width>0&&j.height>0&&(B.current=j,Ee(j)),f)return;M.current=!0,pe(!0),P.current?.(),A.renderSilOnly().then(()=>{f||k.current?.("sil")}),(async()=>{try{if(await D.current.load(),f)return;q.current=!0,ce(!0),de(E.current),D.current.setActive(!0);const N=D.current.getVideo();await st(T,N,null,void 0,B.current),f||k.current?.("idle")}catch(N){if(f)return;q.current=!1,ce(!1),console.warn("[AiTwin] Idle video unavailable:",N)}})()}catch(v){if(f)return;if(v instanceof ve){V(v.message);return}V(v instanceof Error?v.message:"Failed to load AI twin")}})(),()=>{f=!0,A?.dispose(),g.current=null}},[fe,n,s,i,r,le,V]),p.useEffect(()=>{W==="done"&&K&&!F.current.isSpeaking()&&H()},[W,K,H]),p.useEffect(()=>{const f=F.current,T={current:!1},A=()=>{const v=u.current,b=g.current;if(!v||!b||!K){$.current=requestAnimationFrame(A);return}const G=f.isSpeaking(),Y=f.getPlaybackElapsedMs(),Ce=G?Yn(Y,f.getSentenceEmotions(),f.getWordQueue(),f.getStreamMood()):f.getStreamMood();if(G){if(q.current&&D.current?.setActive(!1),f.getVisemeQueue().length===0){$.current=requestAnimationFrame(A);return}R.current.setTargetEmotion(Ce),R.current.advance(performance.now());const N=R.current.getExpressionPath(),ee=R.current.getDrawKey();lr(v,f.getVisemeQueue(),Y,N,ee,E.current,(te,Me,Fe,Dt)=>b.drawLiveFrame(te,Me,Fe,Dt),te=>k.current?.(te))}else if(q.current&&D.current){const j=D.current,N=j.getVideo();N.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?(j.setActive(!0),T.current||(T.current=!0,st(v,N,null,void 0,B.current,()=>!f.isSpeaking()).then(()=>k.current?.("idle")).finally(()=>{T.current=!1}))):ot(v,null,"__sil__",E.current,(ee,te,Me,Fe)=>b.drawLiveFrame(ee,te,Me,Fe),ee=>k.current?.(ee))}else ot(v,null,"__sil__",E.current,(j,N,ee,te)=>b.drawLiveFrame(j,N,ee,te),j=>k.current?.(j));C.current&&!G&&(R.current.reset(),D.current?.setActive(!1),H()),C.current=G,$.current=requestAnimationFrame(A)};return $.current=requestAnimationFrame(A),()=>{cancelAnimationFrame($.current),O(),f.stop()}},[K,xe,H,O]),p.useImperativeHandle(w,()=>({speakText:async(f,T)=>{const A=f.trim();if(!A)return;const v=g.current;if(!v||!M.current&&!K)throw new Error("AI twin is not ready");const{ttsEngineId:b,voiceId:G}=Q(T);X(null),y.current+=1,O(),de(E.current),R.current.reset(),C.current=!1;try{q.current?(D.current?.setActive(!1),D.current?.restart(),de(E.current)):await v.renderSilOnly(),await F.current.speak(A,{voiceId:G,speakingRate:T?.speakingRate??a,ttsEngineId:b})}catch(Y){throw V(Y instanceof Error?Y.message:"TTS failed"),Y}},stop:()=>{y.current+=1,O(),F.current.stop(),de(E.current),C.current=!1,H()},setTtsEngineId:f=>{F.current.setTtsEngineId(f),h.current&&(h.current.ttsEngineId=f)},renderViseme:async(f,T)=>{const A=g.current,v=u.current;if(!A||!v||!M.current)throw new Error("AI twin is not ready");await A.renderViseme(v,f,{from:T?.from,transitionDurationMs:T?.transitionDurationMs,gapMs:T?.gapMs,onStatus:b=>k.current?.(b)})},isReady:()=>M.current,getStatus:()=>W}),[K,a,O,H,V,Q,W]),Oe.jsxs("div",{className:c,style:{...gr,...l},children:[Oe.jsx("canvas",{ref:u,width:ie.width,height:ie.height,style:{...yr,...d},"aria-label":t?`AI twin ${t}`:"AI twin face"}),x&&ye?Oe.jsx("div",{style:pr,children:ye}):null]})});exports.AiTwin=Er;exports.AiTwinNotFoundError=ve;exports.DEFAULT_TTS_ENGINE_ID=Ke;exports.OCULUS_VISEME_IDS=_t;exports.SPEAKING_RATE_MAX=pt;exports.SPEAKING_RATE_MIN=yt;exports.TTS_ENGINE_CARTESIA=ge;exports.TTS_ENGINE_GOOGLE=re;exports.TTS_ENGINE_INWORLD=we;exports.VISEME_IDS=Rn;exports.VISEME_TEST_CARTESIA_VOICE_ID=Et;exports.VISEME_TEST_SPEAKING_RATE=je;exports.VISEME_TEST_VOICE_ID=Ge;exports.createAvatarTtsLipsyncController=St;exports.fetchAiTwin=dt;exports.fetchDevAuthToken=Ue;exports.normalizeOculusViseme=mt;exports.resolveVisemeAtTime=wt;exports.resolveWordAtTime=gt;
|