@streamoji/aitwin 0.1.8 → 0.2.0

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