@streamoji/aitwin 0.1.1 → 0.1.4
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 +30 -3
- package/dist/aitwin.cjs +2 -2
- package/dist/aitwin.js +1152 -1037
- package/dist/assets/visemeDiffPreview.worker-D2ggcW5u.js +1 -0
- package/dist/index.d.ts +16 -17
- package/package.json +8 -3
- package/dist/assets/visemeDiffPreview.worker-B8Juk7ys.js +0 -1
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 De=require("react/jsx-runtime"),_=require("react"),Re="https://ai.streamoji.com",Ve="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev/custom-faces",gt="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin";class Ae extends Error{constructor(e="AI twin not found"){super(e),this.name="AiTwinNotFoundError"}}async function tt(t,e=gt){const n=new URL(e);n.searchParams.set("id",t);const r=await fetch(n.toString());if(r.status===404)throw new Ae;if(!r.ok)throw new Error(`getAiTwin failed (${r.status})`);const s=await r.json();if(!s.success)throw new Ae(s.error??"AI twin not found");return s.data}const pt="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",yt="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Et="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",St="6TNvp3SCs4Og0a1ijm9TommXLql1",kt="Swaraj Mali";async function Ce(){const t=await fetch(pt,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":yt,"Client-Secret":Et},body:JSON.stringify({userId:St,userName:kt})});if(!t.ok)throw new Error(`getAuthToken failed (${t.status})`);const e=await t.json(),n=e.authToken??e.token??"";if(!n.trim())throw new Error("getAuthToken returned no token");return n}async function vt(t,e=Re){const n=await fetch(`${e.replace(/\/$/,"")}/api/session-value`,{headers:{Authorization:`Bearer ${t}`}});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 At(t){const e=t.trim();if(e.length%2!==0)throw new Error("Invalid hex key");const n=new Uint8Array(e.length/2);for(let r=0;r<e.length;r+=2)n[r/2]=Number.parseInt(e.slice(r,r+2),16);return n}function Ee(t){return Uint8Array.from(t)}function Tt(t,e){const n=new Uint8Array(t.length+e.length);return n.set(t,0),n.set(e,t.length),n}async function It(t,e){if(!e)throw new Error("Asset key is required for encrypted assets");if(t.byteLength<28)throw new Error("Encrypted payload too small");const n=new Uint8Array(t),r=Ee(n.subarray(0,12)),s=Ee(n.subarray(12,28)),a=Ee(n.subarray(28)),i=Ee(Tt(a,s)),c=await crypto.subtle.importKey("raw",At(e),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},c,i)}let Pe=null;const Me=new Set;function Rt(t,e=Ve){const n=`${e.replace(/\/$/,"")}/${t}`;return{twinBase:n,binBase:n,encrypted:!0}}function bt(t){Pe=t;for(const e of Me)e()}function ne(){if(!Pe)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return Pe}function xt(t){return Me.add(t),()=>Me.delete(t)}function Dt(){return`${ne().twinBase}/sil.png`}function Ct(){return`${ne().twinBase}/idle.mp4`}function Pt(){const{encrypted:t,binBase:e,twinBase:n}=ne();return t?e:n}function te(){return ne().encrypted}function nt(){const{binBase:t,twinBase:e,encrypted:n}=ne();return n?`${t}/atlas.bin`:`${e}/atlas.json`}function Mt(){const{binBase:t,twinBase:e,encrypted:n}=ne();return n?`${t}/expression_atlas.bin`:`${e}/expression_atlas.json`}function Ft(t){const e=t.split(/\r?\n/);let n="",r="";for(const a of e)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 rt=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],Ot=new Map(rt.map(t=>[t.toLowerCase(),t]));function st(t){const e=(t??"").trim();return e?Ot.get(e.toLowerCase())??"sil":"sil"}const Ut={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 Lt(t){if(!t)return["sil"];const e=t.toLowerCase();return Ut[e]??["sil"]}function Vt(t,e){const n=t.word.length;if(n<=0)return 0;const r=t.wduration;if(r<=0)return 0;const s=Math.max(0,Math.min(1,(e-t.wtime)/r));return Math.min(n-1,Math.floor(s*n))}function be(t,e){const n=Math.max(0,Math.min(t.length-1,e));return t[n]??""}function Bt(t,e){const n=t.toLowerCase(),r=be(n,e),s=n.slice(Math.max(0,e-1),e+2);return r==="i"||r==="y"?"I":s.includes("ee")||r==="e"||n.includes("ea")?"E":"aa"}function $t(t,e){const n=t.toLowerCase(),r=n.slice(Math.max(0,e-1),Math.min(n.length,e+3));if(/oo|ou|uw/.test(r))return"U";const s=be(n,e);return s==="u"||s==="w"?"U":"O"}function Nt(t,e){const n=t.toLowerCase(),r=n.slice(Math.max(0,e-2),e+1);if(/qu/.test(r))return"U";const s=n.slice(Math.max(0,e-1),Math.min(n.length,e+3));return/wo|wh/.test(s)||be(n,e)==="o"?"O":"U"}const Se={d:"DD",t:"DD",n:"DD",s:"SS",z:"SS",k:"kk",g:"kk",c:"CH",x:"CH",j:"CH"};function Ht(t,e){const n=t.toLowerCase(),r=be(n,e);if(Se[r])return Se[r];for(let s=0;s<n.length;s++){const a=n[s];if(Se[a])return Se[a]}return"DD"}function Wt(t,e,n){const r=Lt(t);if(r.length===1)return r[0];const s=e.trim();if(!s)return r[0];const a=(t??"").toLowerCase(),i=Math.max(0,n);switch(a){case"aei":return Bt(s,i);case"o":return $t(s,i);case"qw":return Nt(s,i);case"cdgknstxyz":return Ht(s,i);default:return r[0]}}function Kt(t){let e=t.trim();const n=e.indexOf(",");e.startsWith("data:")&&n>=0&&(e=e.slice(n+1)),e=e.replace(/\s+/g,"").replace(/-/g,"+").replace(/_/g,"/");const r=e.length%4;return r!==0&&(e+="=".repeat(4-r)),e}const qt=24e3;function jt(t){return t.length>=4&&t[0]===82&&t[1]===73&&t[2]===70&&t[3]===70}function qe(t,e,n=qt){const r=e.byteOffset%2===0?e:e.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 i=t.createBuffer(1,a.length,n);return i.copyToChannel(a,0),i}function zt(t){const e=Kt(t),n=window.atob(e),r=new Uint8Array(n.length);for(let s=0;s<n.length;s++)r[s]=n.charCodeAt(s);return r}async function Gt(t,e){const n=zt(e);if(jt(n)){const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return t.decodeAudioData(r)}if(n.length>=2&&n.length%2===0)return qe(t,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await t.decodeAudioData(r)}catch{return qe(t,n)}}function it(t,e){let n=null;for(const r of t)e<r.vtime||e>=r.vtime+r.vduration||(!n||r.vtime>=n.vtime)&&(n=r);return n?n.viseme:"sil"}function ot(t,e){for(const n of t)if(e>=n.wtime&&e<n.wtime+n.wduration)return n;return null}function je(t){let e=0;for(const n of t)e=Math.max(e,n.vtime+n.vduration);return e}const at=.5,ct=1.5,Be="Olivia",ut="f786b574-daa5-4673-aa0c-cbe3e8534c02",$e=.85;function lt(t,e=Re){const n=`${e.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],a=[];let i="neutral";const c=[],u=[];let p=null,w=!1,o=!1,f=!1,d=!1,S=0,D=0,F=0,Z=0,N=null,h=1,y="",P="google",O="google",M=!1;const K=new Set,re=()=>{for(const m of u)m.playbackRate.value=h},C=()=>K.forEach(m=>m()),H=()=>{N!=null&&(clearTimeout(N),N=null)},se=()=>{for(const m of u)try{m.stop()}catch{}u.length=0},ue=()=>{M=!0,H(),se(),c.length=0,w=!1,o=!1,f=!1,d=!1,r.length=0,s.length=0,a.length=0,i="neutral",F=0,Z=0,D=0,t("idle"),C()},ie=()=>{!d||w||u.length>0||c.length>0||(o=!1,H(),t("done"),C())},le=()=>{const m=je(r);if(m<=0){t("done"),C();return}o=!0,S=performance.now(),t("speaking"),C(),H();const b=h>0?m/h:m;N=setTimeout(()=>{M||(o=!1,N=null,t("done"),C())},b)},z=(m,b,T)=>{m.length!==0&&(T&&(F=b),m.forEach(I=>{const R=String(I.word??"").trim();if(!R)return;const q=Math.round((I.start??0)*1e3),B=Math.round((I.duration??0)*1e3);s.push({word:R,wtime:F+q,wduration:B,queueIndex:s.length})}))},ge=(m,b,T,I)=>{m.length!==0&&(T&&(F=b),m.forEach(x=>{const R=String(x.symbol??"").trim();if(!R)return;const q=Math.round((x.start??0)*1e3),B=Math.max(1,Math.round((x.duration??0)*1e3)),A=F+q;let G;if(I==="google"||I==="cartesia")G=st(R);else{const v=ot(s,A),$=v?Vt(v,A):0;G=Wt(R,v?.word??"",$)}r.push({viseme:G,weight:1,vtime:A,vduration:B})}),C())},fe=(m,b,T,I,x)=>{z(b,T,I),ge(m,T,I,x)},oe=async(m,b,T,I)=>{if(M||!m?.trim()){(b.length>0||T.length>0)&&!f&&fe(b,T,F,I,O);return}if(w){c.push({audio:m,visemes:b,words:T,isNewSegment:I});return}w=!0;try{const x=window.AudioContext||window.webkitAudioContext,R=p??new x;R.state==="suspended"&&await R.resume(),p=R;const q=await Gt(R,m);f=!0;const B=R.currentTime;let A=Z;const G=!o;A<B&&(A=B+.1),Z=A+q.duration;const v=R.createBufferSource();if(v.buffer=q,v.playbackRate.value=h,v.connect(R.destination),u.push(v),M){u.pop();return}if(G){o=!0,t("speaking"),D=A,F=0,r.length=0,s.length=0;const V=(A-B)*1e3;S=performance.now()+V}v.onended=()=>{const V=u.indexOf(v);V>=0&&u.splice(V,1),ie(),C()},v.start(A);const $=(A-D)*1e3;fe(b,T,$,I,O)}catch(x){console.error("[avatarTtsLipsync] audio chunk failed:",x)}finally{w=!1;const x=c.shift();x?await oe(x.audio,x.visemes,x.words,x.isNewSegment):ie()}},pe=()=>{if(d=!0,!f&&r.length>0){le();return}ie()};return{setDeveloperToken:m=>{y=m},setTtsProvider:m=>{P=m},getTtsProvider:()=>P,speak:async(m,b)=>{ue(),M=!1,t("loading");const T=y.trim();if(!T)throw new Error("Developer token required for avatar_ttsWithPoses");const I=b?.tts??P;O=I;const x=b?.voiceId??Be,R=b?.speakingRate??$e,q=Math.max(at,Math.min(ct,R)),B={user_query:m,tts:I,speakingRate:q};(I==="inworld"||I==="cartesia")&&(B.voice_id=x);try{const A=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${T}`},body:JSON.stringify(B)});if(!A.ok)throw new Error(`avatar_ttsWithPoses failed (${A.status})`);const G=A.body;if(!G)throw new Error("No response body");const v=G.getReader(),$=new TextDecoder;let V="";const J=async ae=>{const U=Ft(ae);if(U){if(U.event==="audio"){const l=U.data.chunk,E=U.data.visemes??[],k=U.data.words??[],g=U.data.is_new_segment??!1;if(l)await oe(l,E,k,g);else if(E.length>0||k.length>0){const L=g&&r.length>0?je(r):F;fe(E,k,L,g,I)}}else if(U.event==="metadata"){const l=U.data.mood;typeof l=="string"&&l.trim()&&(i=l.trim());const E=U.data.sentence_emotions;if(Array.isArray(E)){a.length=0;for(const k of E){if(!k||typeof k!="object")continue;const g=k;a.push({sentence_index:Number(g.sentence_index??0),text:String(g.text??""),sentiment:typeof g.sentiment=="string"?g.sentiment:void 0,emotion:String(g.emotion??i),start_word:Number(g.start_word??0),end_word:Number(g.end_word??0)})}}C()}else if(U.event==="error")throw new Error(String(U.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:ae,value:U}=await v.read();U&&(V+=$.decode(U,{stream:!0}));const l=V.split(`
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Me=require("react/jsx-runtime"),_=require("react"),At="https://pub-607ad1fc22e2400eb57d17240aab857c.r2.dev",ot={publicBase:At},vt="0.1.4",Rt={version:vt},Ae="https://ai.streamoji.com",bt=`${ot.publicBase}/custom-faces`,Dt=`${ot.publicBase}/aitwin-workers/v${Rt.version}/visemeDiffPreview.worker.js`,xt="https://us-central1-streamoji-265f4.cloudfunctions.net/getAiTwin",te="eng_a7f2b9c1",we="eng_d4e8f3a2",ge="eng_c9b1e6d4",He=we,Ct=new Set([we,ge]),at={[te]:"oculus_direct",[ge]:"oculus_direct",[we]:"inworld_word"},je={google:te,inworld:we,cartesia:ge};function Mt(e){return at[e]}function Pt(e){return e===ge}function Ft(e){if(e.ttsEngineId&&e.ttsEngineId in at)return e.ttsEngineId;const t=e.tts?.toLowerCase();return t&&t in je?je[t]:He}class ve extends Error{constructor(t="AI twin not found"){super(t),this.name="AiTwinNotFoundError"}}async function ct(e,t=xt){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:o,...a}=s.data;return{...a,ttsEngineId:Ft(s.data)}}const Ot="https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken",Lt="client_6TNvp3SCs4Og0a1ijm9TommXLql1",Nt="kk8Fq8EmexzP10jMIEwY3R44M5RKUEm1",Ut="6TNvp3SCs4Og0a1ijm9TommXLql1",Bt="Swaraj Mali";async function Fe(){const e=await fetch(Ot,{method:"POST",headers:{"Content-Type":"application/json","Client-Id":Lt,"Client-Secret":Nt},body:JSON.stringify({userId:Ut,userName:Bt})});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 Vt(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 $t(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 Ht(e,t){const n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}async function Wt(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)),o=Se(n.subarray(28)),a=Se(Ht(o,s)),c=await crypto.subtle.importKey("raw",$t(t),{name:"AES-GCM"},!1,["decrypt"]);return crypto.subtle.decrypt({name:"AES-GCM",iv:r,tagLength:128},c,a)}let Oe=null;const Le=new Set;function Kt(e,t=bt){const n=`${t.replace(/\/$/,"")}/${e}`;return{twinBase:n,binBase:n,encrypted:!0}}function qt(e){Oe=e;for(const t of Le)t()}function ne(){if(!Oe)throw new Error("Twin assets not configured; call setActiveTwinAssets first");return Oe}function Gt(e){return Le.add(e),()=>Le.delete(e)}function jt(){return`${ne().twinBase}/sil.png`}function zt(){return`${ne().twinBase}/idle.mp4`}function Qt(){const{encrypted:e,binBase:t,twinBase:n}=ne();return e?t:n}function ut(){return ne().encrypted}function Jt(){const{binBase:e,twinBase:t,encrypted:n}=ne();return n?`${e}/atlas.bin`:`${t}/atlas.json`}function Xt(){const{binBase:e,twinBase:t,encrypted:n}=ne();return n?`${e}/expression_atlas.bin`:`${t}/expression_atlas.json`}function Yt(e){const t=e.split(/\r?\n/);let n="",r="";for(const o of t)o.startsWith("event:")?n=o.slice(6).trim():o.startsWith("data:")&&(r=o.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 lt=["aa","CH","DD","E","FF","I","O","PP","RR","SS","TH","U","kk","nn","sil"],Zt=new Map(lt.map(e=>[e.toLowerCase(),e]));function ft(e){const t=(e??"").trim();return t?Zt.get(t.toLowerCase())??"sil":"sil"}const en={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 tn(e){if(!e)return["sil"];const t=e.toLowerCase();return en[t]??["sil"]}function nn(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 rn(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 sn(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 on(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 an(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 o=n[s];if(Te[o])return Te[o]}return"DD"}function cn(e,t,n){const r=tn(e);if(r.length===1)return r[0];const s=t.trim();if(!s)return r[0];const o=(e??"").toLowerCase(),a=Math.max(0,n);switch(o){case"aei":return rn(s,a);case"o":return sn(s,a);case"qw":return on(s,a);case"cdgknstxyz":return an(s,a);default:return r[0]}}function un(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 ln=24e3;function fn(e){return e.length>=4&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70}function ze(e,t,n=ln){const r=t.byteOffset%2===0?t:t.slice(),s=new Int16Array(r.buffer,r.byteOffset,r.byteLength/2),o=new Float32Array(s.length);for(let c=0;c<s.length;c++)o[c]=s[c]>=32768?-(65536-s[c])/32768:s[c]/32767;const a=e.createBuffer(1,o.length,n);return a.copyToChannel(o,0),a}function dn(e){const t=un(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 hn(e,t){const n=dn(t);if(fn(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 ze(e,n);try{const r=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);return await e.decodeAudioData(r)}catch{return ze(e,n)}}function dt(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 ht(e,t){for(const n of e)if(t>=n.wtime&&t<n.wtime+n.wduration)return n;return null}function Qe(e){let t=0;for(const n of e)t=Math.max(t,n.vtime+n.vduration);return t}const _t=.5,mt=1.5,We="Olivia",wt="f786b574-daa5-4673-aa0c-cbe3e8534c02",Ke=.85;function gt(e,t=Ae){const n=`${t.replace(/\/$/,"")}/avatar_ttsWithPoses`,r=[],s=[],o=[];let a="neutral";const c=[],u=[];let m=null,d=!1,T=!1,f=!1,i=!1,g=0,v=0,E=0,$=0,F=null,B=1,Q="",w=te,y=te,b=!1;const R=new Set,H=()=>{for(const h of u)h.playbackRate.value=B},W=()=>R.forEach(h=>h()),ie=()=>{F!=null&&(clearTimeout(F),F=null)},Ee=()=>{for(const h of u)try{h.stop()}catch{}u.length=0},Y=()=>{b=!0,ie(),Ee(),c.length=0,d=!1,T=!1,f=!1,i=!1,r.length=0,s.length=0,o.length=0,a="neutral",E=0,$=0,v=0,e("idle"),W()},K=()=>{!i||d||u.length>0||c.length>0||(T=!1,ie(),e("done"),W())},ye=()=>{const h=Qe(r);if(h<=0){e("done"),W();return}T=!0,g=performance.now(),e("speaking"),W(),ie();const D=B>0?h/B:h;F=setTimeout(()=>{b||(T=!1,F=null,e("done"),W())},D)},xe=(h,D,N)=>{h.length!==0&&(N&&(E=D),h.forEach(M=>{const O=String(M.word??"").trim();if(!O)return;const z=Math.round((M.start??0)*1e3),U=Math.round((M.duration??0)*1e3);s.push({word:O,wtime:E+z,wduration:U,queueIndex:s.length})}))},oe=(h,D,N,M)=>{h.length!==0&&(N&&(E=D),h.forEach(S=>{const O=String(S.symbol??"").trim();if(!O)return;const z=Math.round((S.start??0)*1e3),U=Math.max(1,Math.round((S.duration??0)*1e3)),P=E+z;let x;if(Mt(M)==="oculus_direct")x=ft(O);else{const C=ht(s,P),J=C?nn(C,P):0;x=cn(O,C?.word??"",J)}r.push({viseme:x,weight:1,vtime:P,vduration:U})}),W())},re=(h,D,N,M,S)=>{xe(D,N,M),oe(h,N,M,S)},pe=async(h,D,N,M)=>{if(b||!h?.trim()){(D.length>0||N.length>0)&&!f&&re(D,N,E,M,y);return}if(d){c.push({audio:h,visemes:D,words:N,isNewSegment:M});return}d=!0;try{const S=window.AudioContext||window.webkitAudioContext,O=m??new S;O.state==="suspended"&&await O.resume(),m=O;const z=await hn(O,h);f=!0;const U=O.currentTime;let P=$;const x=!T;P<U&&(P=U+.1),$=P+z.duration;const C=O.createBufferSource();if(C.buffer=z,C.playbackRate.value=B,C.connect(O.destination),u.push(C),b){u.pop();return}if(x){T=!0,e("speaking"),v=P,E=0,r.length=0,s.length=0;const V=(P-U)*1e3;g=performance.now()+V}C.onended=()=>{const V=u.indexOf(C);V>=0&&u.splice(V,1),K(),W()},C.start(P);const J=(P-v)*1e3;re(D,N,J,M,y)}catch(S){console.error("[avatarTtsLipsync] audio chunk failed:",S)}finally{d=!1;const S=c.shift();S?await pe(S.audio,S.visemes,S.words,S.isNewSegment):K()}},Z=()=>{if(i=!0,!f&&r.length>0){ye();return}K()};return{setDeveloperToken:h=>{Q=h},setTtsEngineId:h=>{w=h},getTtsEngineId:()=>w,speak:async(h,D)=>{Y(),b=!1,e("loading");const N=Q.trim();if(!N)throw new Error("Developer token required for avatar_ttsWithPoses");const M=D?.ttsEngineId??w??He;y=M;const S=D?.voiceId??We,O=D?.speakingRate??Ke,z=Math.max(_t,Math.min(mt,O)),U={user_query:h,ttsEngineId:M,speakingRate:z};Ct.has(M)&&(U.voice_id=S);try{const P=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${N}`},body:JSON.stringify(U)});if(!P.ok)throw new Error(`avatar_ttsWithPoses failed (${P.status})`);const x=P.body;if(!x)throw new Error("No response body");const C=x.getReader(),J=new TextDecoder;let V="";const ce=async ue=>{const l=Yt(ue);if(l){if(l.event==="audio"){const p=l.data.chunk,k=l.data.visemes??[],I=l.data.words??[],A=l.data.is_new_segment??!1;if(p)await pe(p,k,I,A);else if(k.length>0||I.length>0){const q=A&&r.length>0?Qe(r):E;re(k,I,q,A,M)}}else if(l.event==="metadata"){const p=l.data.mood;typeof p=="string"&&p.trim()&&(a=p.trim());const k=l.data.sentence_emotions;if(Array.isArray(k)){o.length=0;for(const I of k){if(!I||typeof I!="object")continue;const A=I;o.push({sentence_index:Number(A.sentence_index??0),text:String(A.text??""),sentiment:typeof A.sentiment=="string"?A.sentiment:void 0,emotion:String(A.emotion??a),start_word:Number(A.start_word??0),end_word:Number(A.end_word??0)})}}W()}else if(l.event==="error")throw new Error(String(l.data.message??"avatar_ttsWithPoses stream error"))}};for(;;){const{done:ue,value:l}=await C.read();l&&(V+=J.decode(l,{stream:!0}));const p=V.split(`
|
|
2
2
|
|
|
3
|
-
`);V=l.pop()??"";for(const E of l)await J(E);if(ae){V.trim()&&await J(V.trim()),M||pe();break}}}catch(A){throw console.error("[avatarTtsLipsync]",A),ue(),t("error"),A}},stop:ue,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>a,getStreamMood:()=>i,getPlaybackElapsedMs:()=>o?f&&p&&D>0?Math.max(0,(p.currentTime-D)*1e3):Math.max(0,(performance.now()-S)*h):0,isSpeaking:()=>o,setPlaybackSpeed:m=>{h=Math.max(.1,Math.min(1,m)),re()},getPlaybackSpeed:()=>h,subscribe:m=>(K.add(m),()=>K.delete(m))}}function Qt(t,e,n){return{width:t.crop?.source_width??e,height:t.crop?.source_height??n}}function Jt(t,e){const n=t.crop;return{x:e?.source_x??n?.x??0,y:e?.source_y??n?.y??0,width:e?.source_w??n?.width??t.frame_width??0,height:e?.source_h??n?.height??t.frame_height??0}}function Xt(t){return t?(t.w??0)>0&&(t.h??0)>0:!1}async function ft(t,e){const n=await fetch(t);if(!n.ok)throw new Error(`Failed to load encrypted asset: ${t}`);const r=await n.arrayBuffer();return It(r,e)}async function dt(t,e){if(!e)throw new Error("Missing asset key for encrypted JSON");const n=await ft(t,e),r=new TextDecoder().decode(n);return JSON.parse(r)}async function Yt(t){const e=new Blob([t],{type:"image/webp"}),n=URL.createObjectURL(e);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 ht(t,e){return`${t}/${e}_minus_sil.png`}const Ne=15,Zt=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],Fe=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],en=Fe.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 Ge(t,e){return`${t}__${e}`}function Qe(t,e){return`pairs/${t}/${e}_minus_sil.png`}function Oe(t){return`${t}_minus_sil.png`}function tn(t,e){if(t===e)return[];const n=[],r=Ge(t,e);if(ze.has(r)){for(const a of Fe)n.push(Qe(r,a));return n}const s=Ge(e,t);if(ze.has(s)){for(let a=en-1;a>=0;a--)n.push(Qe(s,Fe[a]));return n}return[]}function nn(t){return t<16?[]:t<35?[2]:t<51?[0,4]:t<67?[0,2,4]:t<83?[0,1,2,3]:[0,1,2,3,4]}function rn(t,e){return nn(e).map(r=>t[r]).filter(r=>r!=null)}function sn(t,e,n,r){if(r<=0)return 0;const a=Math.max(1,n-e)/r,i=t-e,c=Math.floor(i/a);return Math.min(r-1,Math.max(0,c))}function on(t,e){if(!te())return`${t}/${e}`;const n=e.split("/").pop()??e,r=n.includes(".")?n.slice(0,n.lastIndexOf(".")):n;return`${t}/${r}.bin`}async function an(t){const e=nt(),n=te()?await dt(e,t):await fetch(e).then(r=>{if(!r.ok)throw new Error(`Failed to load ${e}`);return r.json()});return{width:n.crop?.source_width??n.frame_width??842,height:n.crop?.source_height??n.frame_height??1264}}const cn=[.32,.24,.16,.08,0],xe=cn.map(t=>`eyes_${t.toFixed(2)}`),un=["eyes_0.32","eyes_0.16","eyes_0.00"],ln=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],fn={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 ee(t,e="neutral"){const n=String(t??"").trim().toLowerCase();return n?fn[n]??e:e}function W(t){return t==="neutral"?null:ht(t,"eyes_standard")}function Ue(t,e){return!e||e==="eyes_standard"?W(t):t==="neutral"&&e==="eyes_standard"?null:ht(t,e)}function dn(t){if(!t)return"open";const e=t.match(/\/(eyes_[^/]+)_minus_sil\.png$/);if(!e?.[1])return"open";const n=e[1];return xe.includes(n)?n:"open"}function hn(t){return t==="open"?-1:xe.indexOf(t)}function _n(t,e){const n=[];for(const r of e)n.push(Ue(t,r));for(let r=e.length-2;r>=0;r--)n.push(Ue(t,e[r]));return n.push(W(t)),n}function mn(t,e){if(e==="open")return[W(t)];const n=hn(e);if(n<0)return[W(t)];const r=[];for(let s=n;s>=0;s--)r.push(Ue(t,xe[s]));return r.push(W(t)),r}const wn=["t01_0.20","t02_0.40","t03_0.60","t04_0.80"];function gn(t,e){return`${t}__${e}`}function pn(t,e,n){return`pairs/${gn(t,e)}/${n}_minus_sil.png`}function yn(t,e){return wn.map(n=>pn(t,e,n))}function _e(t,e){return t+Math.random()*(e-t)}function ke(){return _e(14,32)}function En(t={}){const{initialDelayMs:e=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:a=120,doubleBlinkGapMaxMs:i=220,fullBlinkChance:c=.75,postEmotionChangeDelayMs:u=380}=t;let p="neutral",w="neutral",o={kind:"idle",nextBlinkAt:performance.now()+e},f=null,d="__eyes_open__|neutral";function S(h,y){f=h,d=`${h??"__eyes_open__"}|${y}`}function D(h,y){o={kind:"idle",nextBlinkAt:h+_e(n,r)},w=y,S(W(y),y)}function F(h,y,P){const O=P?un:Math.random()<c?xe:ln;o={kind:"playing",paths:_n(y,O),index:0,holdUntil:h+ke(),after:"idle",blinkEmotion:y},w=y,S(o.paths[0]??null,y)}function Z(h,y,P){const O=dn(f),M=[];if(O!=="open"){const K=mn(y,O);M.push(...K.slice(0,-1))}M.push(...yn(y,P)),M.push(W(P)),o={kind:"emotionBlend",paths:M,index:0,holdUntil:h+ke(),thenEmotion:P},S(M[0]??null,y)}function N(h){if(o.kind==="emotionBlend"){for(;h>=o.holdUntil&&o.index<o.paths.length-1;)o.index+=1,o.holdUntil=h+ke();const y=o.paths[o.index]??null;S(y,o.thenEmotion),h>=o.holdUntil&&o.index>=o.paths.length-1&&(p=o.thenEmotion,w=o.thenEmotion,S(W(o.thenEmotion),o.thenEmotion),o={kind:"idle",nextBlinkAt:h+u});return}if(o.kind==="idle"){S(W(w),w),h>=o.nextBlinkAt&&F(h,w,!1);return}if(o.kind==="doublePause"){S(W(o.blinkEmotion),o.blinkEmotion),h>=o.resumeAt&&F(h,o.blinkEmotion,!0);return}if(o.kind==="playing"){for(;h>=o.holdUntil&&o.index<o.paths.length-1;)o.index+=1,o.holdUntil=h+ke();if(S(o.paths[o.index]??null,o.blinkEmotion),h>=o.holdUntil&&o.index>=o.paths.length-1){if(o.after==="resumeIdle"){D(h+u,p);return}if(o.after==="doublePause"){o={kind:"doublePause",resumeAt:h+_e(a,i),blinkEmotion:o.blinkEmotion},S(W(o.blinkEmotion),o.blinkEmotion);return}if(o.after==="idle"&&Math.random()<s){o={kind:"doublePause",resumeAt:h+_e(a,i),blinkEmotion:o.blinkEmotion},S(W(o.blinkEmotion),o.blinkEmotion);return}D(h,o.blinkEmotion)}}}return{advance:N,setTargetEmotion(h){const y=ee(h,p);if(y===p&&o.kind!=="emotionBlend")return;const P=performance.now(),O=p;if(p=y,o.kind==="playing"){Z(P,w,y);return}if(o.kind==="emotionBlend"){o={...o,thenEmotion:y};return}Z(P,O,y)},getTargetEmotion:()=>p,getExpressionPath:()=>f,getDrawKey:()=>d,reset(){p="neutral",w="neutral",D(performance.now()+_e(n,r),"neutral")}}}function Sn(t){const e=document.createElement("video");e.muted=!0,e.playsInline=!0,e.preload="auto",e.setAttribute("playsinline",""),e.loop=!0;let n=!1;return{async load(){n||await new Promise((r,s)=>{const a=()=>{const i=e.duration;if(!Number.isFinite(i)||i<=0){s(new Error(`Idle video has invalid duration: ${t}`));return}n=!0,e.currentTime=0,r()};e.addEventListener("loadeddata",a,{once:!0}),e.addEventListener("error",()=>s(new Error(`Failed to load idle video: ${t}`)),{once:!0}),e.src=t,e.load()})},isReady(){return n},getVideo(){return e},setActive(r){n&&(r?e.paused&&e.play().catch(()=>{}):e.paused||e.pause())},restart(){n&&(e.currentTime=0)}}}function kn(t,e){for(const n of t)if(e>=n.wtime&&e<n.wtime+n.wduration)return n;return null}function vn(t,e,n,r="neutral"){const s=ee(r);if(e.length===0)return s;if(n.length===0)return ee(e[0]?.emotion,s);const a=kn(n,t);let i;if(a)i=a.queueIndex??n.findIndex(c=>c===a),i<0&&(i=0);else{if(t<n[0].wtime)return ee(e[0]?.emotion,s);i=n[n.length-1].queueIndex??n.length-1}for(const c of e)if(i>=c.start_word&&i<=c.end_word)return ee(c.emotion,s);for(const c of e)if(i<c.start_word)return ee(c.emotion,s);return ee(e[e.length-1]?.emotion,s)}function _t(t,e,n,r,s=Ne){const a=new OffscreenCanvas(n,r),i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(t,0,0,n,r);const c=i.getImageData(0,0,n,r),u=i.createImageData(n,r);u.data.set(c.data);const w=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const o of e){w.clearRect(0,0,n,r),w.drawImage(o,0,0,n,r);const f=w.getImageData(0,0,n,r);for(let d=0;d<u.data.length;d+=4){const S=f.data[d],D=f.data[d+1],F=f.data[d+2];Math.max(S,D,F)>s&&(u.data[d]=S,u.data[d+1]=D,u.data[d+2]=F,u.data[d+3]=255)}}return i.putImageData(u,0,0),a}let me=null,we=null;const Te=new Map;function An(){me=null,we=null,Te.clear()}xt(An);function Tn(t){return t??we??void 0}function He(t){return new Promise((e,n)=>{const r=new Image;r.decoding="async",r.onload=()=>e(r),r.onerror=()=>n(new Error(`Failed to load image: ${t}`)),r.src=t})}async function Je(t,e,n){const r=te()?await dt(t,n):await fetch(t).then(u=>{if(!u.ok)throw new Error(`Failed to load ${t}`);return u.json()}),s=r.sheets?.[0];if(!s?.path)throw new Error(`${t} has no sheets[0].path`);const a=te()?await Yt(await ft(on(e,s.path),n??"")):await He(`${e}/${s.path}`),i=new Map;for(const u of r.cells??[])u.path&&i.set(u.path,u);const c=new Map;for(const u of r.sheets??[])c.set(u.index,u);return{atlas:a,atlasMeta:r,cellByPath:i,sheetByIndex:c,diffBase:e}}async function Ie(t){const e=Tn(t);if(te()&&!e)throw new Error("Encrypted assets enabled but no key provided");if(me&&(!te()||we===(e??null)))return me;we=e??null,Te.clear();const n=Pt();return me=(async()=>{const r=await He(Dt()),s=await Je(nt(),n,e);let a=null;try{a=await Je(Mt(),n,e)}catch{a=null}return{sil:r,viseme:s,expression:a}})(),me}function We(t){return Qt(t.viseme.atlasMeta,t.sil.naturalWidth,t.sil.naturalHeight)}function In(t,e){const n=document.createElement("canvas");return n.width=e.w,n.height=e.h,n.getContext("2d",{willReadFrequently:!0}).drawImage(t.atlas,e.x,e.y,e.w,e.h,0,0,e.w,e.h),n}async function Xe(t,e,n){const r=`${e.diffBase}::${n}`,s=Te.get(r);if(s)return s;const a=(async()=>{const i=e.cellByPath.get(n);if(!i)throw new Error(`No atlas cell for path: ${n}`);const c=Jt(e.atlasMeta,i),u=We(t);if(Xt(i)){const o=In(e,i),f=document.createElement("canvas");f.width=u.width,f.height=u.height;const d=f.getContext("2d");return d.fillStyle="#000",d.fillRect(0,0,u.width,u.height),d.drawImage(o,c.x,c.y),f}const p=`${e.diffBase}/${n}`,w=await He(p);if(w.naturalWidth===u.width&&w.naturalHeight===u.height)return w;throw new Error(`Diff PNG wrong size for ${n}`)})();return Te.set(r,a),a}async function Ye(t,e,n,r=Ne){const s=await Ie(),{width:a,height:i}=We(s);(t.width!==a||t.height!==i)&&(t.width=a,t.height=i);const c=[],u=t.getContext("2d");if(u.clearRect(0,0,a,i),e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const w=s.sil;u.drawImage(w,0,0,a,i);return}if(c.length===0){u.drawImage(e,0,0,a,i);return}const p=_t(e,c,a,i,r);u.drawImage(p,0,0)}async function Le(t,e,n=Ne){const r=await Ie(we??void 0),{width:s,height:a}=We(r);(t.width!==s||t.height!==a)&&(t.width=s,t.height=a);const i=[];e.mouthPath&&i.push(await Xe(r,r.viseme,e.mouthPath)),e.expressionPath&&r.expression&&i.push(await Xe(r,r.expression,e.expressionPath));const c=t.getContext("2d");if(c.clearRect(0,0,s,a),i.length===0){c.drawImage(r.sil,0,0,s,a);return}const u=_t(r.sil,i,s,a,n);c.drawImage(u,0,0)}async function Rn(t){await Le(t,{mouthPath:null,expressionPath:null})}function bn(t){const e=new Map;for(const n of t)e.has(n.vtime)||e.set(n.vtime,n);return[...e.values()].sort((n,r)=>n.vtime-r.vtime)}function xn(t){const e=bn(t),n=[];for(let r=0;r<e.length-1;r++){const s=e[r].viseme,a=e[r+1].viseme,i=e[r].vtime,c=e[r+1].vtime;s===a||c<=i||n.push({from:s,to:a,fromVtime:i,toVtime:c})}return n}function Dn(t,e){let n=null;for(const r of t)e>=r.fromVtime&&e<r.toVtime&&(n=r);return n}function Cn(t,e){return{gapMs:Math.max(0,e-t),holdEnd:t,transStart:t}}const Pn=500,Mn="__sil__";function Fn(){return{lastDrawnKey:""}}function he(t){t.lastDrawnKey=""}function Ze(t){return t??Mn}function On(t,e){if(t.length===0)return{diffPath:null,label:"sil"};const n=xn(t),r=Dn(n,e);if(r){const a=r.toVtime-r.fromVtime,i=Cn(r.fromVtime,r.toVtime);if(e>=i.transStart&&e<r.toVtime){const c=tn(r.from,r.to),u=rn(c,a);if(u.length>0){const p=sn(e,i.transStart,r.toVtime,u.length);return{diffPath:u[p]??u[u.length-1],label:`${r.from}→${r.to}`}}return{diffPath:Oe(r.to),label:r.to}}}const s=it(t,e);return s==="sil"?{diffPath:null,label:"sil"}:{diffPath:Oe(s),label:s}}function mt(t,e){const n=e??Ze(t.expressionPath);return`${Ze(t.mouthPath)}|${n}`}function Un(t,e,n,r,s,a,i,c){const u=On(e,n),p={mouthPath:u.diffPath,expressionPath:r},w=mt(p,s);w!==a.lastDrawnKey&&(a.lastDrawnKey=w,c(u.label),i(t,p))}function Ln(t,e,n,r,s,a,i=null){const c={mouthPath:null,expressionPath:e},u=i!=null?`|idle@${Math.round(i*1e3)}`:"",p=mt(c,n)+u;!(i!=null)&&p===r.lastDrawnKey||(r.lastDrawnKey=p,a(i!=null?"idle":"sil"),s(t,c))}function Vn(t){return new Worker("/assets/visemeDiffPreview.worker-B8Juk7ys.js",{name:t?.name})}function Bn(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function $n(t,e){(t.width!==e.width||t.height!==e.height)&&(t.width=e.width,t.height=e.height);const n=t.getContext("2d");n&&n.drawImage(e,0,0)}function et(t,e){let n=0;return{usesWorker:!1,async loadAssets(r){await Ie(r)},async renderSilOnly(){n+=1,await Rn(t)},async drawLiveFrame(r,s){await Ie(),await Le(r,s)},async renderViseme(r,s){n+=1;const a=n;e.onStatus?.(s),await Le(r,{mouthPath:Oe(s),expressionPath:null})},dispose(){n+=1}}}function Nn(t,e){const n=new Vn;let r=1,s=0,a=!1,i=!1;const c=new Map,u=(o,f)=>{i||n.postMessage(o,[])},p=(o,f,d=!1)=>{if(i)return Promise.resolve();const S=r++;return new Promise((D,F)=>{c.set(S,{resolve:()=>D(),reject:F,generation:typeof o.generation=="number"?o.generation:void 0,expectRenderDone:d}),u({...o,requestId:S})})};n.onmessage=o=>{if(i)return;const f=o.data;if(f.type==="status"){e.onStatus?.(f.label);return}if(f.type==="frame"){const S=f.generation<0;if(!S&&f.generation!==s){f.bitmap.close();return}if(S&&e.shouldAcceptLiveFrame&&!e.shouldAcceptLiveFrame()){f.bitmap.close();return}$n(t,f.bitmap),f.bitmap.close();return}const d=c.get(f.requestId);if(d)switch(f.type){case"ready":case"loadAssetsDone":c.delete(f.requestId),d.resolve();break;case"renderDone":if(d.expectRenderDone&&d.generation!=null&&f.generation!==d.generation)return;c.delete(f.requestId),d.resolve();break;case"renderAborted":c.delete(f.requestId),d.resolve();break;case"error":c.delete(f.requestId),e.onError?.(f.message),d.reject(new Error(f.message));break}},n.onerror=o=>{if(i)return;const f=o.message||"Worker error";e.onError?.(f);for(const[,d]of c)d.reject(new Error(f));c.clear()};const w=(async()=>{await p({type:"init"}),a=!0})();return{usesWorker:!0,async loadAssets(o){if(!i&&(await w,!i)){if(!a)throw new Error("Worker init failed");await p({type:"loadAssets",keyHex:o,urls:ne()})}},async renderSilOnly(){if(i||(await w,i))return;s+=1;const o=s;await p({type:"renderSil",generation:o},void 0,!0)},async drawLiveFrame(o,f){i||(await w,!i&&await p({type:"drawFrame",diffPath:f.mouthPath,expressionDiffPath:f.expressionPath},void 0,!0))},async renderViseme(o,f,d={}){if(i||(await w,i))return;s+=1;const S=s;try{await p({type:"renderViseme",generation:S,to:f,from:d.from,transitionDurationMs:d.transitionDurationMs,gapMs:d.gapMs,threshold:d.threshold},void 0,!0)}catch(D){if(D?.name==="AbortError")return;throw D}},dispose(){if(!i){i=!0,s+=1;for(const[,o]of c)o.resolve();c.clear(),n.terminate()}}}}function Hn(t,e={}){if(!Bn())return et(t,e);try{return Nn(t,e)}catch{return et(t,e)}}const Wn={position:"relative",display:"inline-block",lineHeight:0},Kn={display:"block",maxWidth:"100%",height:"auto"},qn={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(t){return t==="cartesia"?ut:Be}const jn=_.forwardRef(function({id:e,assets:n,authToken:r,apiBase:s=Re,facesCdnBase:a=Ve,tts:i="google",voiceId:c,speakingRate:u=$e,className:p,style:w,canvasStyle:o,onStatusChange:f,onDisplayStatus:d,onError:S,onReady:D,showErrorOverlay:F=!0},Z){const N=_.useRef(null),h=_.useRef(null),y=_.useRef(Fn()),P=_.useRef(En()),O=_.useRef(0),M=_.useRef(0),K=_.useRef(null),re=_.useRef(!1),C=_.useRef(null),H=_.useRef(!1),[se,ue]=_.useState("idle"),[ie,le]=_.useState(null),[z,ge]=_.useState(!1),[fe,oe]=_.useState(!1),[pe,Ke]=_.useState({width:842,height:1264}),m=_.useRef(r??null),b=_.useRef(null),T=_.useRef(null),I=_.useRef(!1),x=_.useRef(D),R=_.useRef(d),q=_.useRef(S);x.current=D,R.current=d,q.current=S;const B=_.useCallback(l=>{const E=l?.tts??i??T.current?.tts??"google",k=l?.voiceId??c??T.current?.voiceId??ve(E);return{tts:E,voiceId:k}},[i,c]),A=_.useCallback(l=>{le(l),q.current?.(l)},[]),G=_.useCallback(l=>{ue(l),f?.(l)},[f]),v=_.useRef(lt(G,s)),$=_.useCallback(()=>{K.current!=null&&(clearTimeout(K.current),K.current=null)},[]),V=_.useCallback(async()=>{const l=M.current,E=h.current;!E||!z||v.current.isSpeaking()||(he(y.current),!H.current&&(await E.renderSilOnly(),l!==M.current||v.current.isSpeaking()))},[z]),J=_.useCallback(()=>{$();const l=M.current;V(),K.current=setTimeout(()=>{K.current=null,l===M.current&&V()},Pn)},[$,V]),ae=_.useCallback(async()=>{const l=Math.floor(Date.now()/1e3);if(b.current&&l<b.current.expiresAt-60)return b.current.key;const E=m.current??await Ce();m.current=E,v.current.setDeveloperToken(E);const k=await vt(E,s);return b.current={key:k.value,expiresAt:k.expiresAt},k.value},[s]);_.useEffect(()=>{r&&(m.current=r,v.current.setDeveloperToken(r))},[r]),_.useEffect(()=>{v.current.setTtsProvider(i),T.current&&(T.current.tts=i)},[i]),_.useEffect(()=>{const l=c??ve(i);T.current&&(T.current.voiceId=l)},[c,i]);const U=_.useMemo(()=>n?JSON.stringify(n):`id:${e??""}:${a}`,[n,e,a]);return _.useEffect(()=>{if(!e&&!n){A("AiTwin requires either `id` or `assets`");return}let l=!1;I.current=!1,ge(!1),oe(!1),le(null),T.current=null,C.current=null,H.current=!1;const E=N.current;if(!E)return;const k=Hn(E,{onStatus:g=>{l||R.current?.(g)},onError:g=>{l||A(g)},shouldAcceptLiveFrame:()=>v.current.isSpeaking()});return h.current=k,(async()=>{try{let g,L,Q;if(n)g=n,L=i,Q=c??ve(L);else{const Y=await tt(e);if(l)return;L=Y.tts,Q=c??Y.voiceId??ve(L),g=Rt(Y.faceId,a)}T.current={tts:L,voiceId:Q},bt(g),C.current=Sn(Ct());const X=m.current??r??await Ce();if(l)return;m.current=X,v.current.setDeveloperToken(X),v.current.setTtsProvider(L);const ye=te()?await ae():void 0;if(l)return;const j=await an(ye);if(!l&&j.width>0&&j.height>0&&Ke(j),await k.loadAssets(ye),l)return;let ce=!1;try{await C.current.load(),ce=!0,H.current=!0,oe(!0)}catch(Y){H.current=!1,oe(!1),console.warn("[AiTwin] Idle video unavailable:",Y)}if(!ce)await k.renderSilOnly(),R.current?.("sil");else{he(y.current);const Y=C.current.getVideo();C.current.setActive(!0);try{await Ye(E,Y,null),R.current?.("idle")}catch(de){console.warn("[AiTwin] Initial idle frame paint failed:",de)}}if(l)return;I.current=!0,ge(!0),x.current?.()}catch(g){if(l)return;if(g instanceof Ae){A(g.message);return}A(g instanceof Error?g.message:"Failed to load AI twin")}})(),()=>{l=!0,k.dispose(),h.current=null}},[U,e,a,r,s,ae,A]),_.useEffect(()=>{se==="done"&&z&&!v.current.isSpeaking()&&J()},[se,z,J]),_.useEffect(()=>{const l=v.current,E={current:!1},k=()=>{const g=N.current,L=h.current;if(!g||!L||!z){O.current=requestAnimationFrame(k);return}const Q=l.isSpeaking(),X=l.getPlaybackElapsedMs(),ye=Q?vn(X,l.getSentenceEmotions(),l.getWordQueue(),l.getStreamMood()):l.getStreamMood();if(Q){if(H.current&&C.current?.setActive(!1),l.getVisemeQueue().length===0){O.current=requestAnimationFrame(k);return}P.current.setTargetEmotion(ye),P.current.advance(performance.now());const ce=P.current.getExpressionPath(),Y=P.current.getDrawKey();Un(g,l.getVisemeQueue(),X,ce,Y,y.current,(de,wt)=>L.drawLiveFrame(de,wt),de=>R.current?.(de))}else if(H.current&&C.current){const j=C.current;j.setActive(!0),E.current||(E.current=!0,Ye(g,j.getVideo()).then(()=>R.current?.("idle")).finally(()=>{E.current=!1}))}else Ln(g,null,"__sil__",y.current,(j,ce)=>L.drawLiveFrame(j,ce),j=>R.current?.(j));re.current&&!Q&&(P.current.reset(),H.current&&C.current?.restart(),J()),re.current=Q,O.current=requestAnimationFrame(k)};return O.current=requestAnimationFrame(k),()=>{cancelAnimationFrame(O.current),$(),l.stop()}},[z,fe,J,$]),_.useImperativeHandle(Z,()=>({speakText:async(l,E)=>{const k=l.trim();if(!k)return;const g=h.current;if(!g||!I.current&&!z)throw new Error("AI twin is not ready");const{tts:L,voiceId:Q}=B(E);le(null),M.current+=1,$(),he(y.current),P.current.reset(),re.current=!1;try{H.current?(C.current?.setActive(!1),C.current?.restart(),he(y.current)):await g.renderSilOnly(),await v.current.speak(k,{voiceId:Q,speakingRate:E?.speakingRate??u,tts:L})}catch(X){throw A(X instanceof Error?X.message:"TTS failed"),X}},stop:()=>{M.current+=1,$(),v.current.stop(),he(y.current),re.current=!1,J()},setTtsProvider:l=>{v.current.setTtsProvider(l),T.current&&(T.current.tts=l)},renderViseme:async(l,E)=>{const k=h.current,g=N.current;if(!k||!g||!I.current)throw new Error("AI twin is not ready");await k.renderViseme(g,l,{from:E?.from,transitionDurationMs:E?.transitionDurationMs,gapMs:E?.gapMs,onStatus:L=>R.current?.(L)})},isReady:()=>I.current,getStatus:()=>se}),[z,u,$,J,A,B,se]),De.jsxs("div",{className:p,style:{...Wn,...w},children:[De.jsx("canvas",{ref:N,width:pe.width,height:pe.height,style:{...Kn,...o},"aria-label":e?`AI twin ${e}`:"AI twin face"}),F&&ie?De.jsx("div",{style:qn,children:ie}):null]})});exports.AiTwin=jn;exports.AiTwinNotFoundError=Ae;exports.DEFAULT_API_BASE=Re;exports.DEFAULT_FACES_CDN_BASE=Ve;exports.OCULUS_VISEME_IDS=rt;exports.SPEAKING_RATE_MAX=ct;exports.SPEAKING_RATE_MIN=at;exports.VISEME_IDS=Zt;exports.VISEME_TEST_CARTESIA_VOICE_ID=ut;exports.VISEME_TEST_SPEAKING_RATE=$e;exports.VISEME_TEST_VOICE_ID=Be;exports.createAvatarTtsLipsyncController=lt;exports.fetchAiTwin=tt;exports.fetchDevAuthToken=Ce;exports.normalizeOculusViseme=st;exports.resolveVisemeAtTime=it;exports.resolveWordAtTime=ot;
|
|
3
|
+
`);V=p.pop()??"";for(const k of p)await ce(k);if(ue){V.trim()&&await ce(V.trim()),b||Z();break}}}catch(P){throw console.error("[avatarTtsLipsync]",P),Y(),e("error"),P}},stop:Y,getVisemeQueue:()=>r,getWordQueue:()=>s,getSentenceEmotions:()=>o,getStreamMood:()=>a,getPlaybackElapsedMs:()=>T?f&&m&&v>0?Math.max(0,(m.currentTime-v)*1e3):Math.max(0,(performance.now()-g)*B):0,isSpeaking:()=>T,setPlaybackSpeed:h=>{B=Math.max(.1,Math.min(1,h)),H()},getPlaybackSpeed:()=>B,subscribe:h=>(R.add(h),()=>R.delete(h))}}function _n(e,t,n){return{width:e.crop?.source_width??t,height:e.crop?.source_height??n}}function mn(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 wn(e){return e?(e.w??0)>0&&(e.h??0)>0:!1}async function Et(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 Wt(r,t)}async function gn(e,t){if(!t)throw new Error("Missing asset key for encrypted JSON");const n=await Et(e,t),r=new TextDecoder().decode(n);return JSON.parse(r)}async function En(e){const t=new Blob([e],{type:"image/webp"}),n=URL.createObjectURL(t);return new Promise((r,s)=>{const o=new Image;o.decoding="async",o.onload=()=>{URL.revokeObjectURL(n),r(o)},o.onerror=()=>{URL.revokeObjectURL(n),s(new Error("Failed to decode decrypted image"))},o.src=n})}function yt(e,t){return`${e}/${t}_minus_sil.png`}const qe=15,yn=["aa","E","I","O","U","PP","FF","DD","SS","TH","CH","RR","kk","nn"],Ne=["t01_0.17","t02_0.33","t03_0.50","t04_0.67","t05_0.83"],pn=Ne.length,Je=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 Xe(e,t){return`${e}__${t}`}function Ye(e,t){return`pairs/${e}/${t}_minus_sil.png`}function Ue(e){return`${e}_minus_sil.png`}function Sn(e,t){if(e===t)return[];const n=[],r=Xe(e,t);if(Je.has(r)){for(const o of Ne)n.push(Ye(r,o));return n}const s=Xe(t,e);if(Je.has(s)){for(let o=pn-1;o>=0;o--)n.push(Ye(s,Ne[o]));return n}return[]}function Tn(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 kn(e,t){return Tn(t).map(r=>e[r]).filter(r=>r!=null)}function In(e,t,n,r){if(r<=0)return 0;const o=Math.max(1,n-t)/r,a=e-t,c=Math.floor(a/o);return Math.min(r-1,Math.max(0,c))}function An(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 vn=[.32,.24,.16,.08,0],De=vn.map(e=>`eyes_${e.toFixed(2)}`),Rn=["eyes_0.32","eyes_0.16","eyes_0.00"],bn=["eyes_0.32","eyes_0.16","eyes_0.08","eyes_0.00"],Dn={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 se(e,t="neutral"){const n=String(e??"").trim().toLowerCase();return n?Dn[n]??t:t}function j(e){return e==="neutral"?null:yt(e,"eyes_standard")}function Be(e,t){return!t||t==="eyes_standard"?j(e):e==="neutral"&&t==="eyes_standard"?null:yt(e,t)}function xn(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 Cn(e){return e==="open"?-1:De.indexOf(e)}function Mn(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(j(e)),n}function Pn(e,t){if(t==="open")return[j(e)];const n=Cn(t);if(n<0)return[j(e)];const r=[];for(let s=n;s>=0;s--)r.push(Be(e,De[s]));return r.push(j(e)),r}const Fn=["t01_0.20","t02_0.40","t03_0.60","t04_0.80"];function On(e,t){return`${e}__${t}`}function Ln(e,t,n){return`pairs/${On(e,t)}/${n}_minus_sil.png`}function Nn(e,t){return Fn.map(n=>Ln(e,t,n))}function he(e,t){return e+Math.random()*(t-e)}function ke(){return he(14,32)}function Un(e={}){const{initialDelayMs:t=800+Math.random()*1200,intervalMinMs:n=2200,intervalMaxMs:r=5800,doubleBlinkChance:s=.18,doubleBlinkGapMinMs:o=120,doubleBlinkGapMaxMs:a=220,fullBlinkChance:c=.75,postEmotionChangeDelayMs:u=380,minDwellMs:m=550}=e;let d="neutral",T="neutral",f=0,i={kind:"idle",nextBlinkAt:performance.now()+t},g=null,v="__eyes_open__|neutral";function E(w,y){g=w,v=`${w??"__eyes_open__"}|${y}`}function $(w,y){i={kind:"idle",nextBlinkAt:w+he(n,r)},T=y,E(j(y),y)}function F(w,y,b){const R=b?Rn:Math.random()<c?De:bn;i={kind:"playing",paths:Mn(y,R),index:0,holdUntil:w+ke(),after:"idle",blinkEmotion:y},T=y,E(i.paths[0]??null,y)}function B(w,y,b){const R=xn(g),H=[];if(R!=="open"){const W=Pn(y,R);H.push(...W.slice(0,-1))}H.push(...Nn(y,b)),H.push(j(b)),i={kind:"emotionBlend",paths:H,index:0,holdUntil:w+ke(),thenEmotion:b},E(H[0]??null,y)}function Q(w){if(i.kind==="emotionBlend"){for(;w>=i.holdUntil&&i.index<i.paths.length-1;)i.index+=1,i.holdUntil=w+ke();const y=i.paths[i.index]??null;E(y,i.thenEmotion),w>=i.holdUntil&&i.index>=i.paths.length-1&&(d=i.thenEmotion,T=i.thenEmotion,E(j(i.thenEmotion),i.thenEmotion),i={kind:"idle",nextBlinkAt:w+u});return}if(i.kind==="idle"){E(j(T),T),w>=i.nextBlinkAt&&F(w,T,!1);return}if(i.kind==="doublePause"){E(j(i.blinkEmotion),i.blinkEmotion),w>=i.resumeAt&&F(w,i.blinkEmotion,!0);return}if(i.kind==="playing"){for(;w>=i.holdUntil&&i.index<i.paths.length-1;)i.index+=1,i.holdUntil=w+ke();if(E(i.paths[i.index]??null,i.blinkEmotion),w>=i.holdUntil&&i.index>=i.paths.length-1){if(i.after==="resumeIdle"){$(w+u,d);return}if(i.after==="doublePause"){i={kind:"doublePause",resumeAt:w+he(o,a),blinkEmotion:i.blinkEmotion},E(j(i.blinkEmotion),i.blinkEmotion);return}if(i.after==="idle"&&Math.random()<s){i={kind:"doublePause",resumeAt:w+he(o,a),blinkEmotion:i.blinkEmotion},E(j(i.blinkEmotion),i.blinkEmotion);return}$(w,i.blinkEmotion)}}}return{advance:Q,setTargetEmotion(w){const y=se(w,d);if(y===d&&i.kind!=="emotionBlend")return;const b=performance.now();if(m>0&&y!==d&&i.kind!=="emotionBlend"&&!(d==="neutral"&&y!=="neutral")&&b-f<m)return;const R=d;if(d=y,f=b,i.kind==="playing"){B(b,T,y);return}if(i.kind==="emotionBlend"){i={...i,thenEmotion:y};return}B(b,R,y)},getTargetEmotion:()=>d,getExpressionPath:()=>g,getDrawKey:()=>v,reset(){d="neutral",T="neutral",f=0,$(performance.now()+he(n,r),"neutral")}}}function Bn(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 o=()=>{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",o,{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 Vn=50,pt=400;function $n(e,t,n){return e.filter(r=>{const s=r.queueIndex??-1;return s>=t&&s<=n})}function Hn(e,t){const n=[];for(const r of e){const s=$n(t,r.start_word,r.end_word);if(s.length===0)continue;const o=s[0],a=s[s.length-1];n.push({sentence:r,startMs:Math.max(0,o.wtime-Vn),endMs:a.wtime+a.wduration+pt})}return n.sort((r,s)=>r.startMs-s.startMs)}function Wn(e,t,n,r="neutral"){const s=se(r);if(t.length===0)return s;if(n.length===0||e<n[0].wtime)return se(t[0]?.emotion,s);const o=Hn(t,n);let a=null;for(const m of o)e>=m.startMs&&e<=m.endMs&&(a=m);if(a)return se(a.sentence.emotion,s);const c=n[n.length-1],u=c.wtime+c.wduration;return e>u+pt?se(t[t.length-1]?.emotion,s):s}function St(e,t,n,r,s=qe){const o=new OffscreenCanvas(n,r),a=o.getContext("2d",{willReadFrequently:!0});a.drawImage(e,0,0,n,r);const c=a.getImageData(0,0,n,r),u=a.createImageData(n,r);u.data.set(c.data);const d=new OffscreenCanvas(n,r).getContext("2d",{willReadFrequently:!0});for(const T of t){d.clearRect(0,0,n,r),d.drawImage(T,0,0,n,r);const f=d.getImageData(0,0,n,r);for(let i=0;i<u.data.length;i+=4){const g=f.data[i],v=f.data[i+1],E=f.data[i+2];Math.max(g,v,E)>s&&(u.data[i]=g,u.data[i+1]=v,u.data[i+2]=E,u.data[i+3]=255)}}return a.putImageData(u,0,0),o}let ee=null,_e=null;const Re=new Map;function Kn(){ee=null,_e=null,Re.clear()}Gt(Kn);function qn(e){return e??_e??void 0}function Ge(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 Ze(e,t,n,r){const s=n?await gn(e,r):await fetch(e).then(m=>{if(!m.ok)throw new Error(`Failed to load ${e}`);return m.json()}),o=s.sheets?.[0];if(!o?.path)throw new Error(`${e} has no sheets[0].path`);const a=n?await En(await Et(An(t,o.path,n),r??"")):await Ge(`${t}/${o.path}`),c=new Map;for(const m of s.cells??[])m.path&&c.set(m.path,m);const u=new Map;for(const m of s.sheets??[])u.set(m.index,m);return{atlas:a,atlasMeta:s,cellByPath:c,sheetByIndex:u,diffBase:t}}async function Ve(e){const t=qn(e),n=ut();if(n&&!t)throw new Error("Encrypted assets enabled but no key provided");if(ee&&(!n||_e===(t??null))){const o=await ee;return me(o)}_e=t??null,Re.clear();const r=Qt();ee=(async()=>{const o=await Ge(jt()),a=await Ze(Jt(),r,n,t);let c=null;try{c=await Ze(Xt(),r,n,t)}catch{c=null}return{sil:o,viseme:a,expression:c}})();const s=await ee;return me(s)}async function Tt(e){if(await Ve(e),!ee)throw new Error("Assets not loaded");return ee}function me(e){return _n(e.viseme.atlasMeta,e.sil.naturalWidth,e.sil.naturalHeight)}function Gn(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 et(e,t,n){const r=`${t.diffBase}::${n}`,s=Re.get(r);if(s)return s;const o=(async()=>{const a=t.cellByPath.get(n);if(!a)throw new Error(`No atlas cell for path: ${n}`);const c=mn(t.atlasMeta,a),u=me(e);if(wn(a)){const T=Gn(t,a),f=document.createElement("canvas");f.width=u.width,f.height=u.height;const i=f.getContext("2d");return i.fillStyle="#000",i.fillRect(0,0,u.width,u.height),i.drawImage(T,c.x,c.y),f}const m=`${t.diffBase}/${n}`,d=await Ge(m);if(d.naturalWidth===u.width&&d.naturalHeight===u.height)return d;throw new Error(`Diff PNG wrong size for ${n}`)})();return Re.set(r,o),o}async function tt(e,t,n,r=qe,s,o){if(s){const{width:f,height:i}=s;if(o&&!o()||t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)return;const g=document.createElement("canvas");g.width=f,g.height=i;const v=g.getContext("2d");if(v.clearRect(0,0,f,i),v.drawImage(t,0,0,f,i),o&&!o())return;(e.width!==f||e.height!==i)&&(e.width=f,e.height=i);const E=e.getContext("2d");E.clearRect(0,0,f,i),E.drawImage(g,0,0);return}const a=await Tt(),{width:c,height:u}=me(a);(e.width!==c||e.height!==u)&&(e.width=c,e.height=u);const m=[],d=e.getContext("2d");if(d.clearRect(0,0,c,u),t.readyState<HTMLMediaElement.HAVE_CURRENT_DATA){const f=a.sil;d.drawImage(f,0,0,c,u);return}if(m.length===0){d.drawImage(t,0,0,c,u);return}const T=St(t,m,c,u,r);d.drawImage(T,0,0)}async function $e(e,t,n=qe){const r=await Tt(_e??void 0),{width:s,height:o}=me(r);(e.width!==s||e.height!==o)&&(e.width=s,e.height=o);const a=[];t.mouthPath&&a.push(await et(r,r.viseme,t.mouthPath)),t.expressionPath&&r.expression&&a.push(await et(r,r.expression,t.expressionPath));const c=e.getContext("2d");if(c.clearRect(0,0,s,o),a.length===0){c.drawImage(r.sil,0,0,s,o);return}const u=St(r.sil,a,s,o,n);c.drawImage(u,0,0)}async function jn(e){await $e(e,{mouthPath:null,expressionPath:null})}function zn(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=zn(e),n=[];for(let r=0;r<t.length-1;r++){const s=t[r].viseme,o=t[r+1].viseme,a=t[r].vtime,c=t[r+1].vtime;s===o||c<=a||n.push({from:s,to:o,fromVtime:a,toVtime:c})}return n}function Jn(e,t){let n=null;for(const r of e)t>=r.fromVtime&&t<r.toVtime&&(n=r);return n}function Xn(e,t){return{gapMs:Math.max(0,t-e),holdEnd:e,transStart:e}}const Yn=500,Zn="__sil__";function er(){return{lastDrawnKey:""}}function de(e){e.lastDrawnKey=""}function nt(e){return e??Zn}function tr(e,t){if(e.length===0)return{diffPath:null,label:"sil"};const n=Qn(e),r=Jn(n,t);if(r){const o=r.toVtime-r.fromVtime,a=Xn(r.fromVtime,r.toVtime);if(t>=a.transStart&&t<r.toVtime){const c=Sn(r.from,r.to),u=kn(c,o);if(u.length>0){const m=In(t,a.transStart,r.toVtime,u.length);return{diffPath:u[m]??u[u.length-1],label:`${r.from}→${r.to}`}}return{diffPath:Ue(r.to),label:r.to}}}const s=dt(e,t);return s==="sil"?{diffPath:null,label:"sil"}:{diffPath:Ue(s),label:s}}function kt(e,t){const n=t??nt(e.expressionPath);return`${nt(e.mouthPath)}|${n}`}function nr(e,t,n,r,s,o,a,c){const u=tr(t,n),m={mouthPath:u.diffPath,expressionPath:r},d=kt(m,s);d!==o.lastDrawnKey&&(o.lastDrawnKey=d,c(u.label),a(e,m))}function rt(e,t,n,r,s,o,a=null){const c={mouthPath:null,expressionPath:t},u=a!=null?`|idle@${Math.round(a*1e3)}`:"",m=kt(c,n)+u;!(a!=null)&&m===r.lastDrawnKey||(r.lastDrawnKey=m,o(a!=null?"idle":"sil"),s(e,c))}const st=new Map;async function rr(e){let t=st.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),st.set(e,t)}return new Worker(t,{name:"visemeDiffPreview"})}function sr(){return typeof Worker<"u"&&typeof OffscreenCanvas<"u"}function ir(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 it(e,t){let n=0;return{usesWorker:!1,async loadAssets(r){return await Ve(r)},async renderSilOnly(){n+=1,await jn(e)},async drawLiveFrame(r,s){await Ve(),await $e(r,s)},async renderViseme(r,s){n+=1;const o=n;t.onStatus?.(s),await $e(r,{mouthPath:Ue(s),expressionPath:null})},dispose(){n+=1}}}function or(e,t,n){let r=null,s=1,o=0,a=!1,c=!1;const u=new Map,m=(f,i)=>{c||!r||r.postMessage(f,[])},d=(f,i,g=!1)=>{if(c)return Promise.resolve();const v=s++;return new Promise((E,$)=>{u.set(v,{resolve:F=>E(F),reject:$,generation:typeof f.generation=="number"?f.generation:void 0,expectRenderDone:g}),m({...f,requestId:v})})},T=(async()=>{r=await rr(n),r.onmessage=f=>{if(c)return;const i=f.data;if(i.type==="status"){t.onStatus?.(i.label);return}if(i.type==="frame"){const v=i.generation<0;if(!v&&i.generation!==o){i.bitmap.close();return}if(v&&t.shouldAcceptLiveFrame&&!t.shouldAcceptLiveFrame()){i.bitmap.close();return}ir(e,i.bitmap),i.bitmap.close();return}const g=u.get(i.requestId);if(g)switch(i.type){case"ready":u.delete(i.requestId),g.resolve();break;case"loadAssetsDone":u.delete(i.requestId),g.resolve({width:i.width,height:i.height});break;case"renderDone":if(g.expectRenderDone&&g.generation!=null&&i.generation!==g.generation)return;u.delete(i.requestId),g.resolve();break;case"renderAborted":u.delete(i.requestId),g.resolve();break;case"error":u.delete(i.requestId),t.onError?.(i.message),g.reject(new Error(i.message));break}},r.onerror=f=>{if(c)return;const i=f.message||"Worker error";t.onError?.(i);for(const[,g]of u)g.reject(new Error(i));u.clear()},await d({type:"init"}),a=!0})();return{usesWorker:!0,async loadAssets(f){if(c)return{width:842,height:1264};if(await T,c)return{width:842,height:1264};if(!a)throw new Error("Worker init failed");return await d({type:"loadAssets",keyHex:f,urls:ne()})??{width:842,height:1264}},async renderSilOnly(){if(c||(await T,c))return;o+=1;const f=o;await d({type:"renderSil",generation:f},void 0,!0)},async drawLiveFrame(f,i){c||(await T,!c&&await d({type:"drawFrame",diffPath:i.mouthPath,expressionDiffPath:i.expressionPath},void 0,!0))},async renderViseme(f,i,g={}){if(c||(await T,c))return;o+=1;const v=o;try{await d({type:"renderViseme",generation:v,to:i,from:g.from,transitionDurationMs:g.transitionDurationMs,gapMs:g.gapMs,threshold:g.threshold},void 0,!0)}catch(E){if(E?.name==="AbortError")return;throw E}},dispose(){if(!c){c=!0,o+=1;for(const[,f]of u)f.resolve();u.clear(),r?.terminate(),r=null}}}}function ar(e,t={}){if(!sr())return it(e,t);const n=t.workerScriptUrl??Dt;try{return or(e,t,n)}catch{return it(e,t)}}class cr 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 o=globalThis.setTimeout(()=>{s(new cr(n,t))},t);e.then(a=>{globalThis.clearTimeout(o),r(a)},a=>{globalThis.clearTimeout(o),s(a)})})}const ur={position:"relative",display:"inline-block",lineHeight:0},lr={display:"block",maxWidth:"100%",height:"auto"},fr={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 Ie(e){return Pt(e)?wt:We}const dr=_.forwardRef(function({id:t,assets:n,authToken:r,ttsEngineId:s=te,voiceId:o,speakingRate:a=Ke,className:c,style:u,canvasStyle:m,onStatusChange:d,onDisplayStatus:T,onError:f,onReady:i,showErrorOverlay:g=!0},v){const E=_.useRef(null),$=_.useRef(null),F=_.useRef(er()),B=_.useRef(Un()),Q=_.useRef(0),w=_.useRef(0),y=_.useRef(null),b=_.useRef(!1),R=_.useRef(null),H=_.useRef(!1),[W,ie]=_.useState("idle"),[Ee,Y]=_.useState(null),[K,ye]=_.useState(!1),[xe,oe]=_.useState(!1),[re,pe]=_.useState({width:842,height:1264}),Z=_.useRef(r??null),ae=_.useRef(null),h=_.useRef(null),D=_.useRef(!1),N=_.useRef({width:842,height:1264}),M=_.useRef(i),S=_.useRef(T),O=_.useRef(f);M.current=i,S.current=T,O.current=f;const z=_.useCallback(l=>{const p=l?.ttsEngineId??s??h.current?.ttsEngineId??te,k=l?.voiceId??o??h.current?.voiceId??Ie(p);return{ttsEngineId:p,voiceId:k}},[s,o]),U=_.useCallback(l=>{Y(l),O.current?.(l)},[]),P=_.useCallback(l=>{ie(l),d?.(l)},[d]),x=_.useRef(gt(P,Ae)),C=_.useCallback(()=>{y.current!=null&&(clearTimeout(y.current),y.current=null)},[]),J=_.useCallback(async()=>{const l=w.current,p=$.current;!p||!K||x.current.isSpeaking()||(de(F.current),await p.renderSilOnly(),l===w.current&&(x.current.isSpeaking()||(S.current?.("sil"),H.current&&R.current&&(R.current.restart(),R.current.setActive(!0)))))},[K]),V=_.useCallback(()=>{C();const l=w.current;J(),y.current=setTimeout(()=>{y.current=null,l===w.current&&J()},Yn)},[C,J]),ce=_.useCallback(async()=>{const l=Math.floor(Date.now()/1e3);if(ae.current&&l<ae.current.expiresAt-60)return ae.current.key;const p=Z.current??await Fe();Z.current=p,x.current.setDeveloperToken(p);const k=await Vt(p,Ae);return ae.current={key:k.value,expiresAt:k.expiresAt},k.value},[]);_.useEffect(()=>{r&&(Z.current=r,x.current.setDeveloperToken(r))},[r]),_.useEffect(()=>{x.current.setTtsEngineId(s),h.current&&(h.current.ttsEngineId=s)},[s]),_.useEffect(()=>{const l=o??Ie(s);h.current&&(h.current.voiceId=l)},[o,s]);const ue=_.useMemo(()=>n?JSON.stringify(n):`id:${t??""}`,[n,t]);return _.useEffect(()=>{if(!t&&!n){U("AiTwin requires either `id` or `assets`");return}let l=!1;D.current=!1,ye(!1),oe(!1),Y(null),h.current=null,R.current=null,H.current=!1;const p=E.current;if(!p)return;let k=null;return(async()=>{try{let I,A,q;if(n)I=n,A=s,q=o??Ie(A);else{S.current?.("getAiTwin");const L=await Pe(ct(t),3e4,"getAiTwin");if(l)return;if(!L.faceId?.trim())throw new Error("getAiTwin returned no faceId");A=L.ttsEngineId,q=o??L.voiceId??Ie(A),I=Kt(L.faceId)}h.current={ttsEngineId:A,voiceId:q},qt(I),R.current=Bn(zt()),k=ar(p,{onStatus:L=>{l||S.current?.(L)},onError:L=>{l||U(L)},shouldAcceptLiveFrame:()=>x.current.isSpeaking()}),$.current=k,S.current?.("auth");const X=Z.current??r??await Pe(Fe(),3e4,"getAuthToken");if(l)return;Z.current=X,x.current.setDeveloperToken(X),x.current.setTtsEngineId(A);const Ce=ut()?await ce():void 0;if(l)return;S.current?.("loadAssets");const G=await Pe(k.loadAssets(Ce),18e4,"load face assets");if(!l&&G.width>0&&G.height>0&&(N.current=G,pe(G)),l)return;D.current=!0,ye(!0),M.current?.(),k.renderSilOnly().then(()=>{l||S.current?.("sil")}),(async()=>{try{if(await R.current.load(),l)return;H.current=!0,oe(!0),de(F.current),R.current.setActive(!0);const L=R.current.getVideo();await tt(p,L,null,void 0,N.current),l||S.current?.("idle")}catch(L){if(l)return;H.current=!1,oe(!1),console.warn("[AiTwin] Idle video unavailable:",L)}})()}catch(I){if(l)return;if(I instanceof ve){U(I.message);return}U(I instanceof Error?I.message:"Failed to load AI twin")}})(),()=>{l=!0,k?.dispose(),$.current=null}},[ue,n,s,o,r,ce,U]),_.useEffect(()=>{W==="done"&&K&&!x.current.isSpeaking()&&V()},[W,K,V]),_.useEffect(()=>{const l=x.current,p={current:!1},k=()=>{const I=E.current,A=$.current;if(!I||!A||!K){Q.current=requestAnimationFrame(k);return}const q=l.isSpeaking(),X=l.getPlaybackElapsedMs(),Ce=q?Wn(X,l.getSentenceEmotions(),l.getWordQueue(),l.getStreamMood()):l.getStreamMood();if(q){if(H.current&&R.current?.setActive(!1),l.getVisemeQueue().length===0){Q.current=requestAnimationFrame(k);return}B.current.setTargetEmotion(Ce),B.current.advance(performance.now());const L=B.current.getExpressionPath(),le=B.current.getDrawKey();nr(I,l.getVisemeQueue(),X,L,le,F.current,(fe,It)=>A.drawLiveFrame(fe,It),fe=>S.current?.(fe))}else if(H.current&&R.current){const G=R.current,L=G.getVideo();L.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?(G.setActive(!0),p.current||(p.current=!0,tt(I,L,null,void 0,N.current,()=>!l.isSpeaking()).then(()=>S.current?.("idle")).finally(()=>{p.current=!1}))):rt(I,null,"__sil__",F.current,(le,fe)=>A.drawLiveFrame(le,fe),le=>S.current?.(le))}else rt(I,null,"__sil__",F.current,(G,L)=>A.drawLiveFrame(G,L),G=>S.current?.(G));b.current&&!q&&(B.current.reset(),R.current?.setActive(!1),V()),b.current=q,Q.current=requestAnimationFrame(k)};return Q.current=requestAnimationFrame(k),()=>{cancelAnimationFrame(Q.current),C(),l.stop()}},[K,xe,V,C]),_.useImperativeHandle(v,()=>({speakText:async(l,p)=>{const k=l.trim();if(!k)return;const I=$.current;if(!I||!D.current&&!K)throw new Error("AI twin is not ready");const{ttsEngineId:A,voiceId:q}=z(p);Y(null),w.current+=1,C(),de(F.current),B.current.reset(),b.current=!1;try{H.current?(R.current?.setActive(!1),R.current?.restart(),de(F.current)):await I.renderSilOnly(),await x.current.speak(k,{voiceId:q,speakingRate:p?.speakingRate??a,ttsEngineId:A})}catch(X){throw U(X instanceof Error?X.message:"TTS failed"),X}},stop:()=>{w.current+=1,C(),x.current.stop(),de(F.current),b.current=!1,V()},setTtsEngineId:l=>{x.current.setTtsEngineId(l),h.current&&(h.current.ttsEngineId=l)},renderViseme:async(l,p)=>{const k=$.current,I=E.current;if(!k||!I||!D.current)throw new Error("AI twin is not ready");await k.renderViseme(I,l,{from:p?.from,transitionDurationMs:p?.transitionDurationMs,gapMs:p?.gapMs,onStatus:A=>S.current?.(A)})},isReady:()=>D.current,getStatus:()=>W}),[K,a,C,V,U,z,W]),Me.jsxs("div",{className:c,style:{...ur,...u},children:[Me.jsx("canvas",{ref:E,width:re.width,height:re.height,style:{...lr,...m},"aria-label":t?`AI twin ${t}`:"AI twin face"}),g&&Ee?Me.jsx("div",{style:fr,children:Ee}):null]})});exports.AiTwin=dr;exports.AiTwinNotFoundError=ve;exports.DEFAULT_TTS_ENGINE_ID=He;exports.OCULUS_VISEME_IDS=lt;exports.SPEAKING_RATE_MAX=mt;exports.SPEAKING_RATE_MIN=_t;exports.TTS_ENGINE_CARTESIA=ge;exports.TTS_ENGINE_GOOGLE=te;exports.TTS_ENGINE_INWORLD=we;exports.VISEME_IDS=yn;exports.VISEME_TEST_CARTESIA_VOICE_ID=wt;exports.VISEME_TEST_SPEAKING_RATE=Ke;exports.VISEME_TEST_VOICE_ID=We;exports.createAvatarTtsLipsyncController=gt;exports.fetchAiTwin=ct;exports.fetchDevAuthToken=Fe;exports.normalizeOculusViseme=ft;exports.resolveVisemeAtTime=dt;exports.resolveWordAtTime=ht;
|