@zeroweight/react 0.2.23 → 0.2.24

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarControls.d.ts","sourceRoot":"","sources":["../src/AvatarControls.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAQxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,UAAU,mBAAmB;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,gDAAgD;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AA+BD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiIxD,CAAC"}
1
+ {"version":3,"file":"AvatarControls.d.ts","sourceRoot":"","sources":["../src/AvatarControls.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAQxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,UAAU,mBAAmB;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,gDAAgD;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AA+BD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAoIxD,CAAC"}
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),_=require("@zeroweight/renderer"),t=require("react/jsx-runtime"),T=require("@livekit/components-react");require("@livekit/components-styles");const C=require("lucide-react"),be=3e4,ye=120,Se="wss://prod-project-pazuyq69.livekit.cloud",Z="https://api.zeroweight.ai",ke=()=>{const f=["Happy","Swift","Bright","Cool","Smart"],o=["User","Guest","Visitor","Agent","Caller"],r=Math.floor(Math.random()*1e3);return`${f[Math.floor(Math.random()*f.length)]}${o[Math.floor(Math.random()*o.length)]}${r}`};function X(f){const{avatarId:o,apiKey:r=null,api:g,livekitUrl:m=Se,sessionDuration:u=ye,inactivityTimeout:x=be}=f,R=m,p=e.useMemo(()=>{if(g)return g;const n=r?{"X-ZW-Api-Key":r}:void 0;return{getBundle:async l=>{const y=await fetch(`${Z}/avatars/bundle/${encodeURIComponent(l)}`,{headers:n});if(!y.ok)throw new Error(`Failed to fetch avatar bundle (${y.status} ${y.statusText})`);return y.json()},getLiveKitToken:async(l,y)=>{const h=new URLSearchParams({avatar_id:l,name:y}),j=await fetch(`${Z}/livekit/token?${h.toString()}`,{headers:n});if(!j.ok)throw new Error(`Failed to fetch LiveKit token (${j.status} ${j.statusText})`);return j.json()}}},[g,r]),v=e.useRef(null),b=e.useRef(null),a=e.useRef(null),c=e.useRef(null),[A,i]=e.useState("idle"),[s,d]=e.useState(null),[E,M]=e.useState(new Set(["listening"])),[L,q]=e.useState({listening:{kind:"looped"},speaking:{kind:"looped"}}),[re,B]=e.useState(!1),[se,N]=e.useState(null),[D,z]=e.useState(!1),[$,O]=e.useState(!1),[ae,F]=e.useState(u),k=e.useRef(null),P=e.useRef(()=>{}),[ie,oe]=e.useState(!1),[ce,V]=e.useState(.8),w=e.useRef(null),U=A==="ready",W=e.useCallback(()=>{const n=v.current;if(!n)return null;let l=n.querySelector("canvas");return l||(l=document.createElement("canvas"),l.style.width="100%",l.style.height="100%",l.style.display="block",n.appendChild(l)),b.current=l,l},[]);e.useEffect(()=>{let n=!1;return(async()=>{const y=W();if(!y)return;const h=new _.ZeroWeightRenderer;a.current=h;const j=new _.ActionQueue((S,I)=>{h.play(S,I)});c.current=j,h.on("stateChanged",S=>{n||i(S)}),h.on("dimensions",(S,I)=>{n||d({width:S,height:I})}),h.on("actionLoaded",S=>{n||M(I=>{const Q=new Set(I);return Q.add(S),Q})}),h.on("allActionsLoaded",()=>{n||B(!1)}),h.on("ready",()=>{n||(q(h.getActionMetadata()),j.setActionMetadata(h.getActionMetadata()))});try{B(!0);const S=await p.getBundle(o);if(n||(await h.init(y,{payload:S.payload}),n))return;q(h.getActionMetadata()),j.setActionMetadata(h.getActionMetadata())}catch(S){console.error("[useAvatarSession] Init failed:",S)}})(),()=>{n=!0,a.current?.destroy(),a.current=null,c.current=null}},[o,W,p]);const H=e.useRef(!1);e.useEffect(()=>{U&&a.current&&E.has("wave_hand")&&!H.current&&(a.current.play("wave_hand","listening"),H.current=!0)},[U,E]);const le=e.useCallback(async()=>{if(!(D||$)){z(!0);try{await navigator.mediaDevices.getUserMedia({audio:!0});const n=ke(),l=await p.getLiveKitToken(o,n);N(l.token)}catch(n){console.error("[useAvatarSession] Failed to connect:",n),z(!1)}}},[D,$,o,p]),K=e.useCallback(()=>{k.current&&(clearInterval(k.current),k.current=null),F(u),w.current&&(clearTimeout(w.current),w.current=null),N(null),O(!1),z(!1),c.current?.forceListening()},[u]);e.useEffect(()=>{P.current=K},[K]);const ue=e.useCallback(()=>{O(!0),z(!1)},[]),de=e.useCallback(()=>{k.current&&clearInterval(k.current),F(u),k.current=setInterval(()=>{F(n=>n<=1?(clearInterval(k.current),k.current=null,setTimeout(()=>P.current(),0),0):n-1)},1e3)},[u]),fe=e.useCallback(n=>{const l=Math.floor(n/60).toString().padStart(2,"0"),y=(n%60).toString().padStart(2,"0");return`${l}:${y}`},[]);e.useEffect(()=>()=>{k.current&&clearInterval(k.current)},[]);const pe=e.useCallback(n=>{if(!n){w.current&&(clearTimeout(w.current),w.current=null);return}w.current||(w.current=setTimeout(()=>{w.current=null,P.current()},x))},[x]),ge=e.useCallback(()=>{oe(n=>!n)},[]),he=e.useCallback(n=>{V(n)},[]),me=e.useCallback(()=>{V(n=>n>0?0:1)},[]),ve=e.useCallback(()=>{a.current?.interrupt()},[]),xe=e.useCallback(n=>{c.current?c.current.dispatch(n):a.current?.play(n,"listening")},[]);return{containerRef:v,renderer:a.current,actionQueue:c.current,rendererState:A,avatarDimensions:s,loadedActions:E,actionMetadata:L,isLoadingActions:re,isEngineReady:U,token:se,isConnecting:D,isConnected:$,livekitUrl:R,timeRemaining:ae,formatTime:fe,micMuted:ie,volume:ce,connect:le,disconnect:K,toggleMic:ge,setVolume:he,toggleVolume:me,interrupt:ve,runAction:xe,startSessionTimer:de,markConnected:ue,setInactivityActive:pe}}const Y=({session:f,style:o,loadingContent:r})=>t.jsxs("div",{style:{position:"absolute",inset:0,width:"100%",height:"100%",zIndex:0,pointerEvents:"auto",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",...o},children:[t.jsx("style",{children:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react"),_=require("@zeroweight/renderer"),t=require("react/jsx-runtime"),T=require("@livekit/components-react");require("@livekit/components-styles");const C=require("lucide-react"),be=3e4,ye=120,Se="wss://prod-project-pazuyq69.livekit.cloud",Z="https://api.zeroweight.ai",ke=()=>{const f=["Happy","Swift","Bright","Cool","Smart"],o=["User","Guest","Visitor","Agent","Caller"],r=Math.floor(Math.random()*1e3);return`${f[Math.floor(Math.random()*f.length)]}${o[Math.floor(Math.random()*o.length)]}${r}`};function X(f){const{avatarId:o,apiKey:r=null,api:g,livekitUrl:m=Se,sessionDuration:u=ye,inactivityTimeout:x=be}=f,j=m,p=e.useMemo(()=>{if(g)return g;const n=r?{"X-ZW-Api-Key":r}:void 0;return{getBundle:async l=>{const y=await fetch(`${Z}/avatars/bundle/${encodeURIComponent(l)}`,{headers:n});if(!y.ok)throw new Error(`Failed to fetch avatar bundle (${y.status} ${y.statusText})`);return y.json()},getLiveKitToken:async(l,y)=>{const h=new URLSearchParams({avatar_id:l,name:y}),R=await fetch(`${Z}/livekit/token?${h.toString()}`,{headers:n});if(!R.ok)throw new Error(`Failed to fetch LiveKit token (${R.status} ${R.statusText})`);return R.json()}}},[g,r]),v=e.useRef(null),b=e.useRef(null),a=e.useRef(null),c=e.useRef(null),[A,i]=e.useState("idle"),[s,d]=e.useState(null),[E,M]=e.useState(new Set(["listening"])),[L,q]=e.useState({listening:{kind:"looped"},speaking:{kind:"looped"}}),[re,B]=e.useState(!1),[se,N]=e.useState(null),[D,z]=e.useState(!1),[$,O]=e.useState(!1),[ae,F]=e.useState(u),k=e.useRef(null),P=e.useRef(()=>{}),[ie,oe]=e.useState(!1),[ce,V]=e.useState(.8),w=e.useRef(null),U=A==="ready",W=e.useCallback(()=>{const n=v.current;if(!n)return null;let l=n.querySelector("canvas");return l||(l=document.createElement("canvas"),l.style.width="100%",l.style.height="100%",l.style.display="block",n.appendChild(l)),b.current=l,l},[]);e.useEffect(()=>{let n=!1;return(async()=>{const y=W();if(!y)return;const h=new _.ZeroWeightRenderer;a.current=h;const R=new _.ActionQueue((S,I)=>{h.play(S,I)});c.current=R,h.on("stateChanged",S=>{n||i(S)}),h.on("dimensions",(S,I)=>{n||d({width:S,height:I})}),h.on("actionLoaded",S=>{n||M(I=>{const Q=new Set(I);return Q.add(S),Q})}),h.on("allActionsLoaded",()=>{n||B(!1)}),h.on("ready",()=>{n||(q(h.getActionMetadata()),R.setActionMetadata(h.getActionMetadata()))});try{B(!0);const S=await p.getBundle(o);if(n||(await h.init(y,{payload:S.payload}),n))return;q(h.getActionMetadata()),R.setActionMetadata(h.getActionMetadata())}catch(S){console.error("[useAvatarSession] Init failed:",S)}})(),()=>{n=!0,a.current?.destroy(),a.current=null,c.current=null}},[o,W,p]);const H=e.useRef(!1);e.useEffect(()=>{U&&a.current&&E.has("wave_hand")&&!H.current&&(a.current.play("wave_hand","listening"),H.current=!0)},[U,E]);const le=e.useCallback(async()=>{if(!(D||$)){z(!0);try{await navigator.mediaDevices.getUserMedia({audio:!0});const n=ke(),l=await p.getLiveKitToken(o,n);N(l.token)}catch(n){console.error("[useAvatarSession] Failed to connect:",n),z(!1)}}},[D,$,o,p]),K=e.useCallback(()=>{k.current&&(clearInterval(k.current),k.current=null),F(u),w.current&&(clearTimeout(w.current),w.current=null),N(null),O(!1),z(!1),c.current?.forceListening()},[u]);e.useEffect(()=>{P.current=K},[K]);const ue=e.useCallback(()=>{O(!0),z(!1)},[]),de=e.useCallback(()=>{k.current&&clearInterval(k.current),F(u),k.current=setInterval(()=>{F(n=>n<=1?(clearInterval(k.current),k.current=null,setTimeout(()=>P.current(),0),0):n-1)},1e3)},[u]),fe=e.useCallback(n=>{const l=Math.floor(n/60).toString().padStart(2,"0"),y=(n%60).toString().padStart(2,"0");return`${l}:${y}`},[]);e.useEffect(()=>()=>{k.current&&clearInterval(k.current)},[]);const pe=e.useCallback(n=>{if(!n){w.current&&(clearTimeout(w.current),w.current=null);return}w.current||(w.current=setTimeout(()=>{w.current=null,P.current()},x))},[x]),ge=e.useCallback(()=>{oe(n=>!n)},[]),he=e.useCallback(n=>{V(n)},[]),me=e.useCallback(()=>{V(n=>n>0?0:1)},[]),ve=e.useCallback(()=>{a.current?.interrupt()},[]),xe=e.useCallback(n=>{c.current?c.current.dispatch(n):a.current?.play(n,"listening")},[]);return{containerRef:v,renderer:a.current,actionQueue:c.current,rendererState:A,avatarDimensions:s,loadedActions:E,actionMetadata:L,isLoadingActions:re,isEngineReady:U,token:se,isConnecting:D,isConnected:$,livekitUrl:j,timeRemaining:ae,formatTime:fe,micMuted:ie,volume:ce,connect:le,disconnect:K,toggleMic:ge,setVolume:he,toggleVolume:me,interrupt:ve,runAction:xe,startSessionTimer:de,markConnected:ue,setInactivityActive:pe}}const Y=({session:f,style:o,loadingContent:r})=>t.jsxs("div",{style:{position:"absolute",inset:0,width:"100%",height:"100%",zIndex:0,pointerEvents:"auto",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",...o},children:[t.jsx("style",{children:`
2
2
  @keyframes zwr-spin {
3
3
  from { transform: rotate(0deg); }
4
4
  to { transform: rotate(360deg); }
@@ -7,7 +7,7 @@
7
7
  0%, 100% { opacity: 1; }
8
8
  50% { opacity: 0.5; }
9
9
  }
10
- `}),t.jsx("div",{ref:f.containerRef,style:{position:"relative",width:"100%",height:"100%",transition:"all 0.5s ease-in-out"},children:!f.isEngineReady&&t.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,0.4)",backdropFilter:"blur(4px)",zIndex:10},children:r||t.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:16},children:[t.jsx(C.Loader2,{style:{width:40,height:40,color:"rgba(255,255,255,0.5)",animation:"zwr-spin 1s linear infinite"}}),t.jsx("div",{style:{fontSize:14,color:"rgba(255,255,255,0.5)",fontWeight:500,letterSpacing:"0.1em",textTransform:"uppercase",animation:"zwr-pulse 2s ease-in-out infinite"},children:"Initializing Neural Engine..."})]})})})]}),G={flexShrink:0,borderRadius:9999,padding:16,transition:"all 0.3s",border:"none",cursor:"pointer",display:"inline-flex",alignItems:"center",justifyContent:"center"},J={position:"relative",display:"inline-flex",alignItems:"center",justifyContent:"center",gap:12,borderRadius:9999,padding:"16px 24px",fontSize:16,fontWeight:600,color:"#fff",whiteSpace:"nowrap",transition:"all 0.3s",border:"none",cursor:"pointer"},ee=({session:f,style:o})=>{const{micMuted:r,toggleMic:g,isConnected:m,isConnecting:u,isEngineReady:x,isLoadingActions:R,connect:p,disconnect:v}=f,[b,a]=e.useState(!1),[c,A]=e.useState(!1),i=x&&!R,s=r?{...G,background:"rgba(239,68,68,0.1)",color:"#f87171",boxShadow:"inset 0 0 0 1px rgba(239,68,68,0.3)",...b?{background:"rgba(239,68,68,0.2)"}:{}}:{...G,background:b?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.4)",color:"#fff",boxShadow:"inset 0 0 0 1px rgba(255,255,255,0.1)"},d=m?{...J,background:c?"rgba(239,68,68,0.22)":"rgba(239,68,68,0.14)",boxShadow:"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)"}:{...J,background:"linear-gradient(to right, #7c3aed, #db2777, #f97316)",boxShadow:"0 0 30px rgba(236,72,153,0.3)",opacity:c?.9:1};return(u||!i)&&(d.opacity=.5,d.cursor="not-allowed"),t.jsxs("div",{style:{width:"100%",marginTop:"auto",display:"flex",flexDirection:"column",alignItems:"center",gap:16,paddingBottom:16,...o},children:[t.jsx("style",{children:`
10
+ `}),t.jsx("div",{ref:f.containerRef,style:{position:"relative",width:"100%",height:"100%",transition:"all 0.5s ease-in-out"},children:!f.isEngineReady&&t.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,0.4)",backdropFilter:"blur(4px)",zIndex:10},children:r||t.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:16},children:[t.jsx(C.Loader2,{style:{width:40,height:40,color:"rgba(255,255,255,0.5)",animation:"zwr-spin 1s linear infinite"}}),t.jsx("div",{style:{fontSize:14,color:"rgba(255,255,255,0.5)",fontWeight:500,letterSpacing:"0.1em",textTransform:"uppercase",animation:"zwr-pulse 2s ease-in-out infinite"},children:"Initializing Neural Engine..."})]})})})]}),G={flexShrink:0,borderRadius:9999,padding:16,transition:"all 0.3s",border:"none",cursor:"pointer",display:"inline-flex",alignItems:"center",justifyContent:"center"},J={position:"relative",display:"inline-flex",alignItems:"center",justifyContent:"center",gap:12,borderRadius:9999,padding:"16px 24px",fontSize:16,fontWeight:600,color:"#fff",whiteSpace:"nowrap",transition:"all 0.3s",border:"none",cursor:"pointer"},ee=({session:f,style:o})=>{const{micMuted:r,toggleMic:g,isConnected:m,isConnecting:u,isEngineReady:x,loadedActions:j,connect:p,disconnect:v}=f,[b,a]=e.useState(!1),[c,A]=e.useState(!1),i=x&&j.has("listening")&&j.has("speaking"),s=r?{...G,background:"rgba(239,68,68,0.1)",color:"#f87171",boxShadow:"inset 0 0 0 1px rgba(239,68,68,0.3)",...b?{background:"rgba(239,68,68,0.2)"}:{}}:{...G,background:b?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.4)",color:"#fff",boxShadow:"inset 0 0 0 1px rgba(255,255,255,0.1)"},d=m?{...J,background:c?"rgba(239,68,68,0.22)":"rgba(239,68,68,0.14)",boxShadow:"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)"}:{...J,background:"linear-gradient(to right, #7c3aed, #db2777, #f97316)",boxShadow:"0 0 30px rgba(236,72,153,0.3)",opacity:c?.9:1};return(u||!i)&&(d.opacity=.5,d.cursor="not-allowed"),t.jsxs("div",{style:{width:"100%",marginTop:"auto",display:"flex",flexDirection:"column",alignItems:"center",gap:16,paddingBottom:16,...o},children:[t.jsx("style",{children:`
11
11
  @keyframes zwr-spin {
12
12
  from { transform: rotate(0deg); }
13
13
  to { transform: rotate(360deg); }
@@ -17,5 +17,5 @@
17
17
  0%, 100% { opacity: 1; }
18
18
  50% { opacity: 0.5; }
19
19
  }
20
- `}),t.jsxs("div",{style:x,children:[t.jsx("div",{style:{height:8,width:8,borderRadius:"50%",...o?{background:"#22c55e",boxShadow:"0 0 10px rgba(34,197,94,0.5)"}:{background:"rgba(239,68,68,0.5)"}}}),t.jsx("span",{style:{fontSize:10,fontWeight:700,letterSpacing:"0.05em",color:"rgba(255,255,255,0.7)",textTransform:"uppercase"},children:o?`Online ${g(r)}`:"Offline"})]})]})},ne=({session:f})=>{const{renderer:o,actionQueue:r,micMuted:g,volume:m,setInactivityActive:u,loadedActions:x}=f,R=e.useRef(!1),{state:p,audioTrack:v}=T.useVoiceAssistant(),b=T.useLocalParticipant(),a=T.useIsSpeaking(b.localParticipant),c=e.useRef(null),A=e.useRef(x);e.useEffect(()=>{A.current=x},[x]),e.useEffect(()=>{if(!r)return;const s=new _.VoiceActivityDetector({threshold:.008,analyseIntervalMs:30,speechStartFrames:1,speechPauseFrames:30,turnEndFrames:50});return c.current=s,s.on("speechStart",()=>{r.setTurnActive(!0),r.setSpeechState("speaking")}),s.on("turnEnd",()=>{r.setTurnActive(!1)}),()=>{s.stop(),c.current=null}},[r]),e.useEffect(()=>{const s=c.current;if(s)if(v?.publication?.track){const d=v.publication.track.mediaStreamTrack;d&&s.start(d)}else s.stop()},[v?.publication?.track]);const i=e.useRef(null);return e.useEffect(()=>{if(!r)return;const s=p;s==="speaking"?(i.current&&(clearTimeout(i.current),i.current=null),r.setTurnActive(!0),r.setSpeechState("speaking")):s==="listening"||s==="idle"?(i.current&&(clearTimeout(i.current),i.current=null),c.current?.endTurn(),r.setTurnActive(!1)):s==="thinking"&&(i.current||(i.current=setTimeout(()=>{i.current=null,c.current?.endTurn(),r.setTurnActive(!1)},500)))},[p,r]),e.useEffect(()=>()=>{i.current&&clearTimeout(i.current)},[]),T.useDataChannel(s=>{try{const E=new TextDecoder().decode(s.payload),M=JSON.parse(E);if(M.type==="AVATAR_UPDATE"){const L=M.action;if(!A.current.has(L))return;r?.dispatch(L)}}catch(d){console.error("[LiveKitAvatarProvider] Failed to parse data message:",d)}}),e.useEffect(()=>{u(!(p==="speaking")&&!a)},[a,u,p]),e.useEffect(()=>{if(!r)return;const s=!!v;(!s||p==="disconnected")&&(c.current?.endTurn(),r.forceListening()),R.current=s},[v,p,r]),e.useEffect(()=>{const s=b.localParticipant;s&&s.setMicrophoneEnabled(!g).catch(d=>{console.error("[LiveKitAvatarProvider] Failed to set mic state:",d)})},[g,b.localParticipant]),t.jsx("div",{style:{position:"absolute",bottom:80,left:8,right:8,display:"flex",flexDirection:"column",gap:8},children:t.jsx(T.RoomAudioRenderer,{volume:m})})},Ae=({avatarId:f,apiKey:o,api:r,livekitUrl:g,sessionDuration:m,inactivityTimeout:u,style:x,className:R,loadingContent:p,customControls:v,customStatusBadge:b})=>{const a=X({avatarId:f,apiKey:o,api:r,livekitUrl:g,sessionDuration:m,inactivityTimeout:u}),{token:c,isConnected:A,avatarDimensions:i,disconnect:s,startSessionTimer:d}=a;return t.jsxs("section",{className:R,style:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",borderRadius:16,border:"1px solid rgba(255,255,255,0.1)",boxShadow:"0 25px 50px -12px rgba(0,0,0,0.5)",height:"80vh",width:"auto",maxWidth:"100%",aspectRatio:i?`${i.width} / ${i.height}`:"3 / 4",...x},children:[t.jsx(Y,{session:a,loadingContent:p}),t.jsxs("div",{style:{position:"absolute",inset:0,zIndex:20,pointerEvents:"none",display:"flex",flexDirection:"column",justifyContent:"space-between",padding:16},children:[t.jsxs("div",{style:{display:"flex",width:"100%",alignItems:"flex-start",justifyContent:"space-between"},children:[b?b(a):t.jsx(te,{session:a}),t.jsx("div",{})]}),v?v(a):t.jsx(ee,{session:a})]}),t.jsx("div",{style:{display:"none"},children:c&&g&&t.jsx(T.LiveKitRoom,{serverUrl:g,token:c,connect:!0,video:!1,audio:!0,onConnected:()=>{a.markConnected(),d()},onDisconnected:s,children:t.jsx(ne,{session:a})})})]})};exports.AvatarCanvas=Y;exports.AvatarControls=ee;exports.AvatarStatusBadge=te;exports.LiveKitAvatarProvider=ne;exports.LiveKitAvatarSession=Ae;exports.useAvatarSession=X;
20
+ `}),t.jsxs("div",{style:x,children:[t.jsx("div",{style:{height:8,width:8,borderRadius:"50%",...o?{background:"#22c55e",boxShadow:"0 0 10px rgba(34,197,94,0.5)"}:{background:"rgba(239,68,68,0.5)"}}}),t.jsx("span",{style:{fontSize:10,fontWeight:700,letterSpacing:"0.05em",color:"rgba(255,255,255,0.7)",textTransform:"uppercase"},children:o?`Online ${g(r)}`:"Offline"})]})]})},ne=({session:f})=>{const{renderer:o,actionQueue:r,micMuted:g,volume:m,setInactivityActive:u,loadedActions:x}=f,j=e.useRef(!1),{state:p,audioTrack:v}=T.useVoiceAssistant(),b=T.useLocalParticipant(),a=T.useIsSpeaking(b.localParticipant),c=e.useRef(null),A=e.useRef(x);e.useEffect(()=>{A.current=x},[x]),e.useEffect(()=>{if(!r)return;const s=new _.VoiceActivityDetector({threshold:.008,analyseIntervalMs:30,speechStartFrames:1,speechPauseFrames:30,turnEndFrames:50});return c.current=s,s.on("speechStart",()=>{r.setTurnActive(!0),r.setSpeechState("speaking")}),s.on("turnEnd",()=>{r.setTurnActive(!1)}),()=>{s.stop(),c.current=null}},[r]),e.useEffect(()=>{const s=c.current;if(s)if(v?.publication?.track){const d=v.publication.track.mediaStreamTrack;d&&s.start(d)}else s.stop()},[v?.publication?.track]);const i=e.useRef(null);return e.useEffect(()=>{if(!r)return;const s=p;s==="speaking"?(i.current&&(clearTimeout(i.current),i.current=null),r.setTurnActive(!0),r.setSpeechState("speaking")):s==="listening"||s==="idle"?(i.current&&(clearTimeout(i.current),i.current=null),c.current?.endTurn(),r.setTurnActive(!1)):s==="thinking"&&(i.current||(i.current=setTimeout(()=>{i.current=null,c.current?.endTurn(),r.setTurnActive(!1)},500)))},[p,r]),e.useEffect(()=>()=>{i.current&&clearTimeout(i.current)},[]),T.useDataChannel(s=>{try{const E=new TextDecoder().decode(s.payload),M=JSON.parse(E);if(M.type==="AVATAR_UPDATE"){const L=M.action;if(!A.current.has(L))return;r?.dispatch(L)}}catch(d){console.error("[LiveKitAvatarProvider] Failed to parse data message:",d)}}),e.useEffect(()=>{u(!(p==="speaking")&&!a)},[a,u,p]),e.useEffect(()=>{if(!r)return;const s=!!v;(!s||p==="disconnected")&&(c.current?.endTurn(),r.forceListening()),j.current=s},[v,p,r]),e.useEffect(()=>{const s=b.localParticipant;s&&s.setMicrophoneEnabled(!g).catch(d=>{console.error("[LiveKitAvatarProvider] Failed to set mic state:",d)})},[g,b.localParticipant]),t.jsx("div",{style:{position:"absolute",bottom:80,left:8,right:8,display:"flex",flexDirection:"column",gap:8},children:t.jsx(T.RoomAudioRenderer,{volume:m})})},Ae=({avatarId:f,apiKey:o,api:r,livekitUrl:g,sessionDuration:m,inactivityTimeout:u,style:x,className:j,loadingContent:p,customControls:v,customStatusBadge:b})=>{const a=X({avatarId:f,apiKey:o,api:r,livekitUrl:g,sessionDuration:m,inactivityTimeout:u}),{token:c,isConnected:A,avatarDimensions:i,disconnect:s,startSessionTimer:d}=a;return t.jsxs("section",{className:j,style:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",borderRadius:16,border:"1px solid rgba(255,255,255,0.1)",boxShadow:"0 25px 50px -12px rgba(0,0,0,0.5)",height:"80vh",width:"auto",maxWidth:"100%",aspectRatio:i?`${i.width} / ${i.height}`:"3 / 4",...x},children:[t.jsx(Y,{session:a,loadingContent:p}),t.jsxs("div",{style:{position:"absolute",inset:0,zIndex:20,pointerEvents:"none",display:"flex",flexDirection:"column",justifyContent:"space-between",padding:16},children:[t.jsxs("div",{style:{display:"flex",width:"100%",alignItems:"flex-start",justifyContent:"space-between"},children:[b?b(a):t.jsx(te,{session:a}),t.jsx("div",{})]}),v?v(a):t.jsx(ee,{session:a})]}),t.jsx("div",{style:{display:"none"},children:c&&g&&t.jsx(T.LiveKitRoom,{serverUrl:g,token:c,connect:!0,video:!1,audio:!0,onConnected:()=>{a.markConnected(),d()},onDisconnected:s,children:t.jsx(ne,{session:a})})})]})};exports.AvatarCanvas=Y;exports.AvatarControls=ee;exports.AvatarStatusBadge=te;exports.LiveKitAvatarProvider=ne;exports.LiveKitAvatarSession=Ae;exports.useAvatarSession=X;
21
21
  //# sourceMappingURL=zeroweight-renderer-react.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zeroweight-renderer-react.cjs.js","sources":["../src/useAvatarSession.ts","../src/AvatarCanvas.tsx","../src/AvatarControls.tsx","../src/AvatarStatusBadge.tsx","../src/LiveKitAvatarProvider.tsx","../src/LiveKitAvatarSession.tsx"],"sourcesContent":["/**\n * useAvatarSession — Main React hook for avatar rendering.\n *\n * Wraps ZeroWeightRenderer + ActionQueue in React lifecycle.\n * Returns reactive state and imperative methods.\n *\n * Usage:\n * const session = useAvatarSession({\n * avatarId: 'abc123',\n * apiKey: 'optional-api-key',\n * });\n */\n\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ZeroWeightRenderer, ActionQueue } from \"@zeroweight/renderer\";\nimport type { ActionMetadata, RendererState } from \"@zeroweight/renderer\";\nimport type { ZeroWeightApi } from \"./types\";\n\nconst INACTIVITY_TIMEOUT_MS = 30000;\nconst SESSION_DURATION_SECONDS = 120;\nconst DEFAULT_LIVEKIT_URL = \"wss://prod-project-pazuyq69.livekit.cloud\";\nconst DEFAULT_API_BASE_URL = \"https://api.zeroweight.ai\";\n\nconst generateRandomName = () => {\n const adjectives = [\"Happy\", \"Swift\", \"Bright\", \"Cool\", \"Smart\"];\n const nouns = [\"User\", \"Guest\", \"Visitor\", \"Agent\", \"Caller\"];\n const randomNum = Math.floor(Math.random() * 1000);\n return `${adjectives[Math.floor(Math.random() * adjectives.length)]}${\n nouns[Math.floor(Math.random() * nouns.length)]\n }${randomNum}`;\n};\n\nexport interface AvatarSessionConfig {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL. */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n}\n\nexport interface AvatarSessionReturn {\n // Refs\n containerRef: React.RefObject<HTMLDivElement | null>;\n\n // Renderer instance (for advanced use)\n renderer: ZeroWeightRenderer | null;\n actionQueue: ActionQueue | null;\n\n // Reactive state\n rendererState: RendererState;\n avatarDimensions: { width: number; height: number } | null;\n loadedActions: Set<string>;\n actionMetadata: Record<string, ActionMetadata>;\n isLoadingActions: boolean;\n isEngineReady: boolean;\n\n // Connection state\n token: string | null;\n isConnecting: boolean;\n isConnected: boolean;\n livekitUrl: string;\n\n // Session\n timeRemaining: number;\n formatTime: (seconds: number) => string;\n\n // Controls\n micMuted: boolean;\n volume: number;\n\n // Methods\n connect: () => Promise<void>;\n disconnect: () => void;\n toggleMic: () => void;\n setVolume: (v: number) => void;\n toggleVolume: () => void;\n interrupt: () => void;\n runAction: (actionId: string) => void;\n startSessionTimer: () => void;\n /** Called by LiveKitRoom onConnected */\n markConnected: () => void;\n\n // For LiveKitAvatarProvider internal use\n setInactivityActive: (isInactive: boolean) => void;\n}\n\nexport function useAvatarSession(config: AvatarSessionConfig): AvatarSessionReturn {\n const {\n avatarId,\n apiKey = null,\n api,\n livekitUrl: livekitUrlProp = DEFAULT_LIVEKIT_URL,\n sessionDuration = SESSION_DURATION_SECONDS,\n inactivityTimeout = INACTIVITY_TIMEOUT_MS,\n } = config;\n\n const livekitUrl = livekitUrlProp;\n const resolvedApi = useMemo<ZeroWeightApi>(() => {\n if (api) return api;\n\n const headers = apiKey ? { \"X-ZW-Api-Key\": apiKey } : undefined;\n\n return {\n getBundle: async (resolvedAvatarId: string) => {\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/avatars/bundle/${encodeURIComponent(\n resolvedAvatarId\n )}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch avatar bundle (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n getLiveKitToken: async (resolvedAvatarId: string, userName: string) => {\n const params = new URLSearchParams({\n avatar_id: resolvedAvatarId,\n name: userName,\n });\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/livekit/token?${params.toString()}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch LiveKit token (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n };\n }, [api, apiKey]);\n\n // Refs\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rendererRef = useRef<ZeroWeightRenderer | null>(null);\n const actionQueueRef = useRef<ActionQueue | null>(null);\n\n // Engine state\n const [rendererState, setRendererState] = useState<RendererState>(\"idle\");\n const [avatarDimensions, setAvatarDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const [loadedActions, setLoadedActions] = useState<Set<string>>(\n new Set([\"listening\"])\n );\n const [actionMetadata, setActionMetadata] = useState<\n Record<string, ActionMetadata>\n >({\n listening: { kind: \"looped\" },\n speaking: { kind: \"looped\" },\n });\n const [isLoadingActions, setIsLoadingActions] = useState(false);\n\n // Connection state\n const [token, setToken] = useState<string | null>(null);\n const [isConnecting, setIsConnecting] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n\n // Session timer\n const [timeRemaining, setTimeRemaining] = useState(sessionDuration);\n const sessionTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const handleDisconnectRef = useRef<() => void>(() => {});\n\n // Controls\n const [micMuted, setMicMuted] = useState(false);\n const [volume, setVolumeState] = useState(0.8);\n\n // Inactivity\n const inactivityTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isEngineReady = rendererState === \"ready\";\n\n // ─── Ensure Canvas ──────────────────────────────────────────────\n\n const ensureCanvas = useCallback(() => {\n const container = containerRef.current;\n if (!container) return null;\n\n let canvas = container.querySelector(\"canvas\") as HTMLCanvasElement | null;\n if (!canvas) {\n canvas = document.createElement(\"canvas\");\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n container.appendChild(canvas);\n }\n canvasRef.current = canvas;\n return canvas;\n }, []);\n\n // ─── Init Renderer ──────────────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n const initRenderer = async () => {\n const canvas = ensureCanvas();\n if (!canvas) return;\n\n // Create renderer\n const renderer = new ZeroWeightRenderer();\n rendererRef.current = renderer;\n\n // Create action queue\n const queue = new ActionQueue((actionId, fallback) => {\n renderer.play(actionId, fallback);\n });\n actionQueueRef.current = queue;\n\n // Wire up renderer events\n renderer.on(\"stateChanged\", (state) => {\n if (!cancelled) setRendererState(state);\n });\n\n renderer.on(\"dimensions\", (w, h) => {\n if (!cancelled) setAvatarDimensions({ width: w, height: h });\n });\n\n renderer.on(\"actionLoaded\", (actionId) => {\n if (!cancelled) {\n setLoadedActions((prev) => {\n const next = new Set(prev);\n next.add(actionId);\n return next;\n });\n }\n });\n\n renderer.on(\"allActionsLoaded\", () => {\n if (!cancelled) setIsLoadingActions(false);\n });\n\n renderer.on(\"ready\", () => {\n // Update action metadata from renderer\n if (!cancelled) {\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n }\n });\n\n // Fetch bundle and init\n try {\n setIsLoadingActions(true);\n const data = await resolvedApi.getBundle(avatarId);\n if (cancelled) return;\n\n await renderer.init(canvas, { payload: data.payload });\n if (cancelled) return;\n\n // After init, update metadata again with all loaded data\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n } catch (e) {\n console.error(\"[useAvatarSession] Init failed:\", e);\n }\n };\n\n initRenderer();\n\n return () => {\n cancelled = true;\n rendererRef.current?.destroy();\n rendererRef.current = null;\n actionQueueRef.current = null;\n };\n }, [avatarId, ensureCanvas, resolvedApi]);\n\n // ─── Auto-wave on load ──────────────────────────────────────────\n\n const hasWavedRef = useRef(false);\n useEffect(() => {\n if (\n isEngineReady &&\n rendererRef.current &&\n loadedActions.has(\"wave_hand\") &&\n !hasWavedRef.current\n ) {\n rendererRef.current.play(\"wave_hand\", \"listening\");\n hasWavedRef.current = true;\n }\n }, [isEngineReady, loadedActions]);\n\n // ─── Connection ─────────────────────────────────────────────────\n\n const connect = useCallback(async () => {\n if (isConnecting || isConnected) return;\n\n setIsConnecting(true);\n try {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n const userName = generateRandomName();\n const data = await resolvedApi.getLiveKitToken(avatarId, userName);\n setToken(data.token);\n } catch (error) {\n console.error(\"[useAvatarSession] Failed to connect:\", error);\n setIsConnecting(false);\n }\n }, [isConnecting, isConnected, avatarId, resolvedApi]);\n\n const disconnect = useCallback(() => {\n // Clear session timer\n if (sessionTimerRef.current) {\n clearInterval(sessionTimerRef.current);\n sessionTimerRef.current = null;\n }\n setTimeRemaining(sessionDuration);\n\n // Clear inactivity timer\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n\n setToken(null);\n setIsConnected(false);\n setIsConnecting(false);\n\n // Reset avatar to listening\n actionQueueRef.current?.forceListening();\n }, [sessionDuration]);\n\n // Keep disconnect ref current for timer\n useEffect(() => {\n handleDisconnectRef.current = disconnect;\n }, [disconnect]);\n\n const markConnected = useCallback(() => {\n setIsConnected(true);\n setIsConnecting(false);\n }, []);\n\n // ─── Session Timer ──────────────────────────────────────────────\n\n const startSessionTimer = useCallback(() => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n setTimeRemaining(sessionDuration);\n\n sessionTimerRef.current = setInterval(() => {\n setTimeRemaining((prev) => {\n if (prev <= 1) {\n clearInterval(sessionTimerRef.current!);\n sessionTimerRef.current = null;\n setTimeout(() => handleDisconnectRef.current(), 0);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n }, [sessionDuration]);\n\n const formatTime = useCallback((seconds: number) => {\n const m = Math.floor(seconds / 60)\n .toString()\n .padStart(2, \"0\");\n const s = (seconds % 60).toString().padStart(2, \"0\");\n return `${m}:${s}`;\n }, []);\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n };\n }, []);\n\n // ─── Inactivity ─────────────────────────────────────────────────\n\n const setInactivityActive = useCallback(\n (isInactive: boolean) => {\n if (!isInactive) {\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n return;\n }\n\n if (inactivityTimeoutRef.current) return;\n\n inactivityTimeoutRef.current = setTimeout(() => {\n inactivityTimeoutRef.current = null;\n handleDisconnectRef.current();\n }, inactivityTimeout);\n },\n [inactivityTimeout]\n );\n\n // ─── Controls ───────────────────────────────────────────────────\n\n const toggleMic = useCallback(() => {\n setMicMuted((v) => !v);\n }, []);\n\n const setVolume = useCallback((v: number) => {\n setVolumeState(v);\n }, []);\n\n const toggleVolume = useCallback(() => {\n setVolumeState((v) => (v > 0 ? 0 : 1));\n }, []);\n\n const interrupt = useCallback(() => {\n rendererRef.current?.interrupt();\n }, []);\n\n const runAction = useCallback((actionId: string) => {\n if (actionQueueRef.current) {\n actionQueueRef.current.dispatch(actionId);\n } else {\n rendererRef.current?.play(actionId, \"listening\");\n }\n }, []);\n\n return {\n containerRef,\n renderer: rendererRef.current,\n actionQueue: actionQueueRef.current,\n rendererState,\n avatarDimensions,\n loadedActions,\n actionMetadata,\n isLoadingActions,\n isEngineReady,\n token,\n isConnecting,\n isConnected,\n livekitUrl,\n timeRemaining,\n formatTime,\n micMuted,\n volume,\n connect,\n disconnect,\n toggleMic,\n setVolume,\n toggleVolume,\n interrupt,\n runAction,\n startSessionTimer,\n markConnected,\n setInactivityActive,\n };\n}\n","/**\n * AvatarCanvas — Canvas container for the avatar renderer.\n *\n * Provides the div container where the renderer creates and manages a <canvas>.\n * Shows loading overlay when the engine is initializing.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarCanvasProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the outer container. */\n style?: React.CSSProperties;\n /** Custom loading component. */\n loadingContent?: React.ReactNode;\n}\n\nexport const AvatarCanvas: React.FC<AvatarCanvasProps> = ({\n session,\n style,\n loadingContent,\n}) => {\n return (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: 0,\n pointerEvents: \"auto\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div\n ref={session.containerRef}\n style={{\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n transition: \"all 0.5s ease-in-out\",\n }}\n >\n {/* Canvas is injected here by the renderer */}\n {!session.isEngineReady && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"rgba(0,0,0,0.4)\",\n backdropFilter: \"blur(4px)\",\n zIndex: 10,\n }}\n >\n {loadingContent || (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n }}\n >\n <Loader2\n style={{\n width: 40,\n height: 40,\n color: \"rgba(255,255,255,0.5)\",\n animation: \"zwr-spin 1s linear infinite\",\n }}\n />\n <div\n style={{\n fontSize: 14,\n color: \"rgba(255,255,255,0.5)\",\n fontWeight: 500,\n letterSpacing: \"0.1em\",\n textTransform: \"uppercase\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }}\n >\n Initializing Neural Engine...\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n","/**\n * AvatarControls — Default control bar for the avatar session.\n *\n * Mic toggle, connect/disconnect button.\n * Can be replaced entirely by a custom UI.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React, { useState } from \"react\";\nimport {\n Mic,\n MicOff,\n Power,\n Activity,\n Loader2,\n} from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarControlsProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the wrapper. */\n style?: React.CSSProperties;\n}\n\nconst btnBase: React.CSSProperties = {\n flexShrink: 0,\n borderRadius: 9999,\n padding: 16,\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst connectBtnBase: React.CSSProperties = {\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n borderRadius: 9999,\n padding: \"16px 24px\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n};\n\nexport const AvatarControls: React.FC<AvatarControlsProps> = ({\n session,\n style,\n}) => {\n const {\n micMuted,\n toggleMic,\n isConnected,\n isConnecting,\n isEngineReady,\n isLoadingActions,\n connect,\n disconnect,\n } = session;\n\n const [micHover, setMicHover] = useState(false);\n const [connectHover, setConnectHover] = useState(false);\n\n const isSessionReady = isEngineReady && !isLoadingActions;\n\n const micStyle: React.CSSProperties = micMuted\n ? {\n ...btnBase,\n background: \"rgba(239,68,68,0.1)\",\n color: \"#f87171\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.3)\",\n ...(micHover ? { background: \"rgba(239,68,68,0.2)\" } : {}),\n }\n : {\n ...btnBase,\n background: micHover ? \"rgba(255,255,255,0.1)\" : \"rgba(0,0,0,0.4)\",\n color: \"#fff\",\n boxShadow: \"inset 0 0 0 1px rgba(255,255,255,0.1)\",\n };\n\n const connectStyle: React.CSSProperties = isConnected\n ? {\n ...connectBtnBase,\n background: connectHover ? \"rgba(239,68,68,0.22)\" : \"rgba(239,68,68,0.14)\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)\",\n }\n : {\n ...connectBtnBase,\n background: \"linear-gradient(to right, #7c3aed, #db2777, #f97316)\",\n boxShadow: \"0 0 30px rgba(236,72,153,0.3)\",\n opacity: connectHover ? 0.9 : 1,\n };\n\n if (isConnecting || !isSessionReady) {\n connectStyle.opacity = 0.5;\n connectStyle.cursor = \"not-allowed\";\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n marginTop: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n paddingBottom: 16,\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}</style>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 16,\n width: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Mic Toggle */}\n <button\n type=\"button\"\n onClick={toggleMic}\n onMouseEnter={() => setMicHover(true)}\n onMouseLeave={() => setMicHover(false)}\n style={micStyle}\n title={micMuted ? \"Unmute mic\" : \"Mute mic\"}\n >\n {micMuted ? <MicOff size={24} /> : <Mic size={24} />}\n </button>\n\n {/* Main Connect Button */}\n <button\n type=\"button\"\n onClick={isConnected ? disconnect : connect}\n onMouseEnter={() => setConnectHover(true)}\n onMouseLeave={() => setConnectHover(false)}\n disabled={!isConnected && (isConnecting || !isSessionReady)}\n style={connectStyle}\n >\n {isConnecting ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Connecting...</span>\n </>\n ) : !isConnected && !isSessionReady ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Loading Avatar...</span>\n </>\n ) : isConnected ? (\n <>\n <Power size={20} />\n <span>End Session</span>\n </>\n ) : (\n <>\n <Activity size={20} />\n <span>Start Session</span>\n </>\n )}\n </button>\n </div>\n </div>\n );\n};\n","/**\n * AvatarStatusBadge — Connection status indicator with session timer.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarStatusBadgeProps {\n session: AvatarSessionReturn;\n}\n\nexport const AvatarStatusBadge: React.FC<AvatarStatusBadgeProps> = ({\n session,\n}) => {\n const { isConnected, timeRemaining, formatTime } = session;\n\n const isUrgent = isConnected && timeRemaining <= 30;\n const isWarning = isConnected && timeRemaining > 30 && timeRemaining <= 60;\n\n const wrapperStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n backdropFilter: \"blur(12px)\",\n padding: \"6px 12px\",\n borderRadius: 9999,\n border: \"1px solid\",\n pointerEvents: \"auto\",\n transition: \"all 0.3s\",\n ...(isUrgent\n ? {\n background: \"rgba(239,68,68,0.3)\",\n borderColor: \"rgba(239,68,68,0.4)\",\n boxShadow: \"0 0 15px rgba(239,68,68,0.3)\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }\n : isWarning\n ? {\n background: \"rgba(249,115,22,0.2)\",\n borderColor: \"rgba(249,115,22,0.3)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }\n : {\n background: \"rgba(0,0,0,0.4)\",\n borderColor: \"rgba(255,255,255,0.1)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }),\n };\n\n return (\n <>\n <style>{`\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div style={wrapperStyle}>\n <div\n style={{\n height: 8,\n width: 8,\n borderRadius: \"50%\",\n ...(isConnected\n ? {\n background: \"#22c55e\",\n boxShadow: \"0 0 10px rgba(34,197,94,0.5)\",\n }\n : {\n background: \"rgba(239,68,68,0.5)\",\n }),\n }}\n />\n <span\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n color: \"rgba(255,255,255,0.7)\",\n textTransform: \"uppercase\",\n }}\n >\n {isConnected ? `Online ${formatTime(timeRemaining)}` : \"Offline\"}\n </span>\n </div>\n </>\n );\n};\n","/**\n * LiveKitAvatarProvider — Bridge between LiveKit hooks and the ActionQueue/VAD.\n * Uses inline styles only — no CSS framework dependency.\n *\n * This component MUST be rendered inside a <LiveKitRoom>.\n */\n\nimport React, { useEffect, useRef } from \"react\";\nimport {\n useVoiceAssistant,\n RoomAudioRenderer,\n useDataChannel,\n useIsSpeaking,\n useLocalParticipant,\n} from \"@livekit/components-react\";\nimport { VoiceActivityDetector } from \"@zeroweight/renderer\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface LiveKitAvatarProviderProps {\n session: AvatarSessionReturn;\n}\n\nexport const LiveKitAvatarProvider: React.FC<LiveKitAvatarProviderProps> = ({\n session,\n}) => {\n const { renderer, actionQueue, micMuted, volume, setInactivityActive, loadedActions } =\n session;\n\n const prevAudioTrackRef = useRef<boolean>(false);\n const { state, audioTrack } = useVoiceAssistant();\n const localParticipant = useLocalParticipant();\n const isUserSpeaking = useIsSpeaking(localParticipant.localParticipant);\n\n // VAD instance\n const vadRef = useRef<VoiceActivityDetector | null>(null);\n\n // Ref to latest loadedActions so data channel callback doesn't go stale\n const loadedActionsRef = useRef(loadedActions);\n useEffect(() => {\n loadedActionsRef.current = loadedActions;\n }, [loadedActions]);\n\n // ─── VAD Setup ──────────────────────────────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const vad = new VoiceActivityDetector({\n threshold: 0.008,\n analyseIntervalMs: 30,\n speechStartFrames: 1,\n speechPauseFrames: 30,\n turnEndFrames: 50,\n });\n vadRef.current = vad;\n\n vad.on(\"speechStart\", () => {\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n });\n\n vad.on(\"turnEnd\", () => {\n actionQueue.setTurnActive(false);\n });\n\n return () => {\n vad.stop();\n vadRef.current = null;\n };\n }, [actionQueue]);\n\n // ─── Connect/disconnect VAD to audio track ──────────────────────\n\n useEffect(() => {\n const vad = vadRef.current;\n if (!vad) return;\n\n if (audioTrack?.publication?.track) {\n const mediaTrack = audioTrack.publication.track.mediaStreamTrack;\n if (mediaTrack) {\n vad.start(mediaTrack);\n }\n } else {\n vad.stop();\n }\n }, [audioTrack?.publication?.track]);\n\n // ─── LiveKit state → turn management ─────────────────────────────\n\n const turnEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const stateStr = state as string;\n\n if (stateStr === \"speaking\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n } else if (stateStr === \"listening\" || stateStr === \"idle\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n } else if (stateStr === \"thinking\") {\n if (!turnEndTimerRef.current) {\n turnEndTimerRef.current = setTimeout(() => {\n turnEndTimerRef.current = null;\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n }, 500);\n }\n }\n }, [state, actionQueue]);\n\n // Cleanup turn-end timer on unmount\n useEffect(() => {\n return () => {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n }\n };\n }, []);\n\n // ─── Data channel (backend-triggered actions) ───────────────────\n\n useDataChannel((msg) => {\n try {\n const decoder = new TextDecoder();\n const strData = decoder.decode(msg.payload);\n const data = JSON.parse(strData);\n\n if (data.type === \"AVATAR_UPDATE\") {\n const actionId = data.action;\n if (!loadedActionsRef.current.has(actionId)) {\n return;\n }\n actionQueue?.dispatch(actionId);\n }\n } catch (err) {\n console.error(\"[LiveKitAvatarProvider] Failed to parse data message:\", err);\n }\n });\n\n // ─── Inactivity tracking: only count when both AI and user are silent ───\n\n useEffect(() => {\n const isAssistantSpeaking = state === \"speaking\";\n setInactivityActive(!isAssistantSpeaking && !isUserSpeaking);\n }, [isUserSpeaking, setInactivityActive, state]);\n\n // ─── Audio loss / disconnect fallback ───────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const hasAudio = !!audioTrack;\n const isDisconnectedState = state === \"disconnected\";\n\n if (!hasAudio || isDisconnectedState) {\n vadRef.current?.endTurn();\n actionQueue.forceListening();\n }\n prevAudioTrackRef.current = hasAudio;\n }, [audioTrack, state, actionQueue]);\n\n // ─── Mic mute sync ─────────────────────────────────────────────\n\n useEffect(() => {\n const participant = localParticipant.localParticipant;\n if (!participant) return;\n\n participant.setMicrophoneEnabled(!micMuted).catch((err) => {\n console.error(\"[LiveKitAvatarProvider] Failed to set mic state:\", err);\n });\n }, [micMuted, localParticipant.localParticipant]);\n\n return (\n <div style={{ position: \"absolute\", bottom: 80, left: 8, right: 8, display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <RoomAudioRenderer volume={volume} />\n </div>\n );\n};\n","/**\n * LiveKitAvatarSession — Full drop-in avatar component.\n * Uses inline styles only — no CSS framework dependency.\n *\n * Simplest usage:\n * import { LiveKitAvatarSession } from \"@zeroweight/react\";\n * <LiveKitAvatarSession\n * avatarId=\"abc123\"\n * apiKey=\"optional-api-key\"\n * />\n */\n\nimport React from \"react\";\nimport { LiveKitRoom } from \"@livekit/components-react\";\nimport \"@livekit/components-styles\";\n\nimport { useAvatarSession } from \"./useAvatarSession\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\nimport { AvatarCanvas } from \"./AvatarCanvas\";\nimport { AvatarControls } from \"./AvatarControls\";\nimport { AvatarStatusBadge } from \"./AvatarStatusBadge\";\nimport { LiveKitAvatarProvider } from \"./LiveKitAvatarProvider\";\nimport type { ZeroWeightApi } from \"./types\";\n\ninterface LiveKitAvatarSessionProps {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL (e.g. \"wss://your-livekit.example.com\"). */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n /** Optional style overrides for the outer section. */\n style?: React.CSSProperties;\n /** Optional class name overrides for the outer section. */\n className?: string;\n /** Custom loading UI for the canvas. */\n loadingContent?: React.ReactNode;\n /** Custom controls component. If provided, replaces the default controls. */\n customControls?: (session: AvatarSessionReturn) => React.ReactNode;\n /** Custom status badge. If provided, replaces the default badge. */\n customStatusBadge?: (session: AvatarSessionReturn) => React.ReactNode;\n}\n\nexport const LiveKitAvatarSession: React.FC<LiveKitAvatarSessionProps> = ({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n style,\n className,\n loadingContent,\n customControls,\n customStatusBadge,\n}) => {\n const session = useAvatarSession({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n });\n\n const {\n token,\n isConnected,\n avatarDimensions,\n disconnect,\n startSessionTimer,\n } = session;\n\n return (\n <section\n className={className}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n borderRadius: 16,\n border: \"1px solid rgba(255,255,255,0.1)\",\n boxShadow: \"0 25px 50px -12px rgba(0,0,0,0.5)\",\n height: \"80vh\",\n width: \"auto\",\n maxWidth: \"100%\",\n aspectRatio: avatarDimensions\n ? `${avatarDimensions.width} / ${avatarDimensions.height}`\n : \"3 / 4\",\n ...style,\n }}\n >\n {/* 1. Canvas Layer */}\n <AvatarCanvas session={session} loadingContent={loadingContent} />\n\n {/* 2. UI Overlay Layer */}\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n padding: 16,\n }}\n >\n {/* Top Header / Status */}\n <div\n style={{\n display: \"flex\",\n width: \"100%\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n }}\n >\n {customStatusBadge ? (\n customStatusBadge(session)\n ) : (\n <AvatarStatusBadge session={session} />\n )}\n <div />\n </div>\n\n {/* Bottom Controls */}\n {customControls ? (\n customControls(session)\n ) : (\n <AvatarControls session={session} />\n )}\n </div>\n\n {/* 3. LiveKit Room (hidden, audio-only) */}\n <div style={{ display: \"none\" }}>\n {token && livekitUrl && (\n <LiveKitRoom\n serverUrl={livekitUrl}\n token={token}\n connect={true}\n video={false}\n audio={true}\n onConnected={() => {\n session.markConnected();\n startSessionTimer();\n }}\n onDisconnected={disconnect}\n >\n <LiveKitAvatarProvider session={session} />\n </LiveKitRoom>\n )}\n </div>\n </section>\n );\n};\n"],"names":["INACTIVITY_TIMEOUT_MS","SESSION_DURATION_SECONDS","DEFAULT_LIVEKIT_URL","DEFAULT_API_BASE_URL","generateRandomName","adjectives","nouns","randomNum","useAvatarSession","config","avatarId","apiKey","api","livekitUrlProp","sessionDuration","inactivityTimeout","livekitUrl","resolvedApi","useMemo","headers","resolvedAvatarId","response","userName","params","containerRef","useRef","canvasRef","rendererRef","actionQueueRef","rendererState","setRendererState","useState","avatarDimensions","setAvatarDimensions","loadedActions","setLoadedActions","actionMetadata","setActionMetadata","isLoadingActions","setIsLoadingActions","token","setToken","isConnecting","setIsConnecting","isConnected","setIsConnected","timeRemaining","setTimeRemaining","sessionTimerRef","handleDisconnectRef","micMuted","setMicMuted","volume","setVolumeState","inactivityTimeoutRef","isEngineReady","ensureCanvas","useCallback","container","canvas","useEffect","cancelled","renderer","ZeroWeightRenderer","queue","ActionQueue","actionId","fallback","state","w","h","prev","next","data","e","hasWavedRef","connect","error","disconnect","markConnected","startSessionTimer","formatTime","seconds","m","s","setInactivityActive","isInactive","toggleMic","v","setVolume","toggleVolume","interrupt","runAction","AvatarCanvas","session","style","loadingContent","jsxs","jsx","Loader2","btnBase","connectBtnBase","AvatarControls","micHover","setMicHover","connectHover","setConnectHover","isSessionReady","micStyle","connectStyle","MicOff","Mic","Fragment","Power","Activity","AvatarStatusBadge","isUrgent","isWarning","wrapperStyle","LiveKitAvatarProvider","actionQueue","prevAudioTrackRef","audioTrack","useVoiceAssistant","localParticipant","useLocalParticipant","isUserSpeaking","useIsSpeaking","vadRef","loadedActionsRef","vad","VoiceActivityDetector","mediaTrack","turnEndTimerRef","stateStr","useDataChannel","msg","strData","err","hasAudio","participant","RoomAudioRenderer","LiveKitAvatarSession","className","customControls","customStatusBadge","LiveKitRoom"],"mappings":"uRAkBMA,GAAwB,IACxBC,GAA2B,IAC3BC,GAAsB,4CACtBC,EAAuB,4BAEvBC,GAAqB,IAAM,CAC/B,MAAMC,EAAa,CAAC,QAAS,QAAS,SAAU,OAAQ,OAAO,EACzDC,EAAQ,CAAC,OAAQ,QAAS,UAAW,QAAS,QAAQ,EACtDC,EAAY,KAAK,MAAM,KAAK,OAAA,EAAW,GAAI,EACjD,MAAO,GAAGF,EAAW,KAAK,MAAM,KAAK,OAAA,EAAWA,EAAW,MAAM,CAAC,CAAC,GACjEC,EAAM,KAAK,MAAM,KAAK,OAAA,EAAWA,EAAM,MAAM,CAAC,CAChD,GAAGC,CAAS,EACd,EA8DO,SAASC,EAAiBC,EAAkD,CACjF,KAAM,CACJ,SAAAC,EACA,OAAAC,EAAS,KACT,IAAAC,EACA,WAAYC,EAAiBX,GAC7B,gBAAAY,EAAkBb,GAClB,kBAAAc,EAAoBf,EAAA,EAClBS,EAEEO,EAAaH,EACbI,EAAcC,EAAAA,QAAuB,IAAM,CAC/C,GAAIN,EAAK,OAAOA,EAEhB,MAAMO,EAAUR,EAAS,CAAE,eAAgBA,GAAW,OAEtD,MAAO,CACL,UAAW,MAAOS,GAA6B,CAC7C,MAAMC,EAAW,MAAM,MACrB,GAAGlB,CAAoB,mBAAmB,mBACxCiB,CAAA,CACD,GACD,CAAE,QAAAD,CAAA,CAAQ,EAGZ,GAAI,CAACE,EAAS,GACZ,MAAM,IAAI,MACR,kCAAkCA,EAAS,MAAM,IAAIA,EAAS,UAAU,GAAA,EAI5E,OAAOA,EAAS,KAAA,CAClB,EACA,gBAAiB,MAAOD,EAA0BE,IAAqB,CACrE,MAAMC,EAAS,IAAI,gBAAgB,CACjC,UAAWH,EACX,KAAME,CAAA,CACP,EACKD,EAAW,MAAM,MACrB,GAAGlB,CAAoB,kBAAkBoB,EAAO,UAAU,GAC1D,CAAE,QAAAJ,CAAA,CAAQ,EAGZ,GAAI,CAACE,EAAS,GACZ,MAAM,IAAI,MACR,kCAAkCA,EAAS,MAAM,IAAIA,EAAS,UAAU,GAAA,EAI5E,OAAOA,EAAS,KAAA,CAClB,CAAA,CAEJ,EAAG,CAACT,EAAKD,CAAM,CAAC,EAGVa,EAAeC,EAAAA,OAA8B,IAAI,EACjDC,EAAYD,EAAAA,OAAiC,IAAI,EACjDE,EAAcF,EAAAA,OAAkC,IAAI,EACpDG,EAAiBH,EAAAA,OAA2B,IAAI,EAGhD,CAACI,EAAeC,CAAgB,EAAIC,EAAAA,SAAwB,MAAM,EAClE,CAACC,EAAkBC,CAAmB,EAAIF,EAAAA,SAGtC,IAAI,EACR,CAACG,EAAeC,CAAgB,EAAIJ,EAAAA,SACxC,IAAI,IAAI,CAAC,WAAW,CAAC,CAAA,EAEjB,CAACK,EAAgBC,CAAiB,EAAIN,WAE1C,CACA,UAAW,CAAE,KAAM,QAAA,EACnB,SAAU,CAAE,KAAM,QAAA,CAAS,CAC5B,EACK,CAACO,GAAkBC,CAAmB,EAAIR,EAAAA,SAAS,EAAK,EAGxD,CAACS,GAAOC,CAAQ,EAAIV,EAAAA,SAAwB,IAAI,EAChD,CAACW,EAAcC,CAAe,EAAIZ,EAAAA,SAAS,EAAK,EAChD,CAACa,EAAaC,CAAc,EAAId,EAAAA,SAAS,EAAK,EAG9C,CAACe,GAAeC,CAAgB,EAAIhB,EAAAA,SAASjB,CAAe,EAC5DkC,EAAkBvB,EAAAA,OAA8C,IAAI,EACpEwB,EAAsBxB,EAAAA,OAAmB,IAAM,CAAC,CAAC,EAGjD,CAACyB,GAAUC,EAAW,EAAIpB,EAAAA,SAAS,EAAK,EACxC,CAACqB,GAAQC,CAAc,EAAItB,EAAAA,SAAS,EAAG,EAGvCuB,EAAuB7B,EAAAA,OAA6C,IAAI,EAExE8B,EAAgB1B,IAAkB,QAIlC2B,EAAeC,EAAAA,YAAY,IAAM,CACrC,MAAMC,EAAYlC,EAAa,QAC/B,GAAI,CAACkC,EAAW,OAAO,KAEvB,IAAIC,EAASD,EAAU,cAAc,QAAQ,EAC7C,OAAKC,IACHA,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,MAAM,MAAQ,OACrBA,EAAO,MAAM,OAAS,OACtBA,EAAO,MAAM,QAAU,QACvBD,EAAU,YAAYC,CAAM,GAE9BjC,EAAU,QAAUiC,EACbA,CACT,EAAG,CAAA,CAAE,EAILC,EAAAA,UAAU,IAAM,CACd,IAAIC,EAAY,GAgEhB,OA9DqB,SAAY,CAC/B,MAAMF,EAASH,EAAA,EACf,GAAI,CAACG,EAAQ,OAGb,MAAMG,EAAW,IAAIC,qBACrBpC,EAAY,QAAUmC,EAGtB,MAAME,EAAQ,IAAIC,EAAAA,YAAY,CAACC,EAAUC,IAAa,CACpDL,EAAS,KAAKI,EAAUC,CAAQ,CAClC,CAAC,EACDvC,EAAe,QAAUoC,EAGzBF,EAAS,GAAG,eAAiBM,GAAU,CAChCP,GAAW/B,EAAiBsC,CAAK,CACxC,CAAC,EAEDN,EAAS,GAAG,aAAc,CAACO,EAAGC,IAAM,CAC7BT,GAAW5B,EAAoB,CAAE,MAAOoC,EAAG,OAAQC,EAAG,CAC7D,CAAC,EAEDR,EAAS,GAAG,eAAiBI,GAAa,CACnCL,GACH1B,EAAkBoC,GAAS,CACzB,MAAMC,EAAO,IAAI,IAAID,CAAI,EACzB,OAAAC,EAAK,IAAIN,CAAQ,EACVM,CACT,CAAC,CAEL,CAAC,EAEDV,EAAS,GAAG,mBAAoB,IAAM,CAC/BD,GAAWtB,EAAoB,EAAK,CAC3C,CAAC,EAEDuB,EAAS,GAAG,QAAS,IAAM,CAEpBD,IACHxB,EAAkByB,EAAS,mBAAmB,EAC9CE,EAAM,kBAAkBF,EAAS,mBAAmB,EAExD,CAAC,EAGD,GAAI,CACFvB,EAAoB,EAAI,EACxB,MAAMkC,EAAO,MAAMxD,EAAY,UAAUP,CAAQ,EAIjD,GAHImD,IAEJ,MAAMC,EAAS,KAAKH,EAAQ,CAAE,QAASc,EAAK,QAAS,EACjDZ,GAAW,OAGfxB,EAAkByB,EAAS,mBAAmB,EAC9CE,EAAM,kBAAkBF,EAAS,mBAAmB,CACtD,OAASY,EAAG,CACV,QAAQ,MAAM,kCAAmCA,CAAC,CACpD,CACF,GAEA,EAEO,IAAM,CACXb,EAAY,GACZlC,EAAY,SAAS,QAAA,EACrBA,EAAY,QAAU,KACtBC,EAAe,QAAU,IAC3B,CACF,EAAG,CAAClB,EAAU8C,EAAcvC,CAAW,CAAC,EAIxC,MAAM0D,EAAclD,EAAAA,OAAO,EAAK,EAChCmC,EAAAA,UAAU,IAAM,CAEZL,GACA5B,EAAY,SACZO,EAAc,IAAI,WAAW,GAC7B,CAACyC,EAAY,UAEbhD,EAAY,QAAQ,KAAK,YAAa,WAAW,EACjDgD,EAAY,QAAU,GAE1B,EAAG,CAACpB,EAAerB,CAAa,CAAC,EAIjC,MAAM0C,GAAUnB,EAAAA,YAAY,SAAY,CACtC,GAAI,EAAAf,GAAgBE,GAEpB,CAAAD,EAAgB,EAAI,EACpB,GAAI,CACF,MAAM,UAAU,aAAa,aAAa,CAAE,MAAO,GAAM,EACzD,MAAMrB,EAAWlB,GAAA,EACXqE,EAAO,MAAMxD,EAAY,gBAAgBP,EAAUY,CAAQ,EACjEmB,EAASgC,EAAK,KAAK,CACrB,OAASI,EAAO,CACd,QAAQ,MAAM,wCAAyCA,CAAK,EAC5DlC,EAAgB,EAAK,CACvB,EACF,EAAG,CAACD,EAAcE,EAAalC,EAAUO,CAAW,CAAC,EAE/C6D,EAAarB,EAAAA,YAAY,IAAM,CAE/BT,EAAgB,UAClB,cAAcA,EAAgB,OAAO,EACrCA,EAAgB,QAAU,MAE5BD,EAAiBjC,CAAe,EAG5BwC,EAAqB,UACvB,aAAaA,EAAqB,OAAO,EACzCA,EAAqB,QAAU,MAGjCb,EAAS,IAAI,EACbI,EAAe,EAAK,EACpBF,EAAgB,EAAK,EAGrBf,EAAe,SAAS,eAAA,CAC1B,EAAG,CAACd,CAAe,CAAC,EAGpB8C,EAAAA,UAAU,IAAM,CACdX,EAAoB,QAAU6B,CAChC,EAAG,CAACA,CAAU,CAAC,EAEf,MAAMC,GAAgBtB,EAAAA,YAAY,IAAM,CACtCZ,EAAe,EAAI,EACnBF,EAAgB,EAAK,CACvB,EAAG,CAAA,CAAE,EAICqC,GAAoBvB,EAAAA,YAAY,IAAM,CACtCT,EAAgB,SAAS,cAAcA,EAAgB,OAAO,EAClED,EAAiBjC,CAAe,EAEhCkC,EAAgB,QAAU,YAAY,IAAM,CAC1CD,EAAkBwB,GACZA,GAAQ,GACV,cAAcvB,EAAgB,OAAQ,EACtCA,EAAgB,QAAU,KAC1B,WAAW,IAAMC,EAAoB,QAAA,EAAW,CAAC,EAC1C,GAEFsB,EAAO,CACf,CACH,EAAG,GAAI,CACT,EAAG,CAACzD,CAAe,CAAC,EAEdmE,GAAaxB,cAAayB,GAAoB,CAClD,MAAMC,EAAI,KAAK,MAAMD,EAAU,EAAE,EAC9B,WACA,SAAS,EAAG,GAAG,EACZE,GAAKF,EAAU,IAAI,WAAW,SAAS,EAAG,GAAG,EACnD,MAAO,GAAGC,CAAC,IAAIC,CAAC,EAClB,EAAG,CAAA,CAAE,EAGLxB,EAAAA,UAAU,IACD,IAAM,CACPZ,EAAgB,SAAS,cAAcA,EAAgB,OAAO,CACpE,EACC,CAAA,CAAE,EAIL,MAAMqC,GAAsB5B,EAAAA,YACzB6B,GAAwB,CACvB,GAAI,CAACA,EAAY,CACXhC,EAAqB,UACvB,aAAaA,EAAqB,OAAO,EACzCA,EAAqB,QAAU,MAEjC,MACF,CAEIA,EAAqB,UAEzBA,EAAqB,QAAU,WAAW,IAAM,CAC9CA,EAAqB,QAAU,KAC/BL,EAAoB,QAAA,CACtB,EAAGlC,CAAiB,EACtB,EACA,CAACA,CAAiB,CAAA,EAKdwE,GAAY9B,EAAAA,YAAY,IAAM,CAClCN,GAAaqC,GAAM,CAACA,CAAC,CACvB,EAAG,CAAA,CAAE,EAECC,GAAYhC,cAAa+B,GAAc,CAC3CnC,EAAemC,CAAC,CAClB,EAAG,CAAA,CAAE,EAECE,GAAejC,EAAAA,YAAY,IAAM,CACrCJ,EAAgBmC,GAAOA,EAAI,EAAI,EAAI,CAAE,CACvC,EAAG,CAAA,CAAE,EAECG,GAAYlC,EAAAA,YAAY,IAAM,CAClC9B,EAAY,SAAS,UAAA,CACvB,EAAG,CAAA,CAAE,EAECiE,GAAYnC,cAAaS,GAAqB,CAC9CtC,EAAe,QACjBA,EAAe,QAAQ,SAASsC,CAAQ,EAExCvC,EAAY,SAAS,KAAKuC,EAAU,WAAW,CAEnD,EAAG,CAAA,CAAE,EAEL,MAAO,CACL,aAAA1C,EACA,SAAUG,EAAY,QACtB,YAAaC,EAAe,QAC5B,cAAAC,EACA,iBAAAG,EACA,cAAAE,EACA,eAAAE,EACA,iBAAAE,GACA,cAAAiB,EACA,MAAAf,GACA,aAAAE,EACA,YAAAE,EACA,WAAA5B,EACA,cAAA8B,GACA,WAAAmC,GACA,SAAA/B,GACA,OAAAE,GACA,QAAAwB,GACA,WAAAE,EACA,UAAAS,GACA,UAAAE,GACA,aAAAC,GACA,UAAAC,GACA,UAAAC,GACA,kBAAAZ,GACA,cAAAD,GACA,oBAAAM,EAAA,CAEJ,CCtbO,MAAMQ,EAA4C,CAAC,CACxD,QAAAC,EACA,MAAAC,EACA,eAAAC,CACF,IAEIC,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,SAAU,WACV,MAAO,EACP,MAAO,OACP,OAAQ,OACR,OAAQ,EACR,cAAe,OACf,QAAS,OACT,eAAgB,SAChB,WAAY,SACZ,SAAU,SACV,GAAGF,CAAA,EAGL,SAAA,CAAAG,MAAC,QAAA,CAAO,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASN,EACFA,EAAAA,IAAC,MAAA,CACC,IAAKJ,EAAQ,aACb,MAAO,CACL,SAAU,WACV,MAAO,OACP,OAAQ,OACR,WAAY,sBAAA,EAIb,SAAA,CAACA,EAAQ,eACRI,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,SAAU,WACV,MAAO,EACP,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,WAAY,kBACZ,eAAgB,YAChB,OAAQ,EAAA,EAGT,SAAAF,GACCC,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,QAAS,OACT,cAAe,SACf,WAAY,SACZ,IAAK,EAAA,EAGP,SAAA,CAAAC,EAAAA,IAACC,EAAAA,QAAA,CACC,MAAO,CACL,MAAO,GACP,OAAQ,GACR,MAAO,wBACP,UAAW,6BAAA,CACb,CAAA,EAEFD,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,SAAU,GACV,MAAO,wBACP,WAAY,IACZ,cAAe,QACf,cAAe,YACf,UAAW,mCAAA,EAEd,SAAA,+BAAA,CAAA,CAED,CAAA,CAAA,CACF,CAAA,CAEJ,CAAA,CAEJ,CAAA,CAAA,ECnFAE,EAA+B,CACnC,WAAY,EACZ,aAAc,KACd,QAAS,GACT,WAAY,WACZ,OAAQ,OACR,OAAQ,UACR,QAAS,cACT,WAAY,SACZ,eAAgB,QAClB,EAEMC,EAAsC,CAC1C,SAAU,WACV,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,IAAK,GACL,aAAc,KACd,QAAS,YACT,SAAU,GACV,WAAY,IACZ,MAAO,OACP,WAAY,SACZ,WAAY,WACZ,OAAQ,OACR,OAAQ,SACV,EAEaC,GAAgD,CAAC,CAC5D,QAAAR,EACA,MAAAC,CACF,IAAM,CACJ,KAAM,CACJ,SAAA7C,EACA,UAAAqC,EACA,YAAA3C,EACA,aAAAF,EACA,cAAAa,EACA,iBAAAjB,EACA,QAAAsC,EACA,WAAAE,CAAA,EACEgB,EAEE,CAACS,EAAUC,CAAW,EAAIzE,EAAAA,SAAS,EAAK,EACxC,CAAC0E,EAAcC,CAAe,EAAI3E,EAAAA,SAAS,EAAK,EAEhD4E,EAAiBpD,GAAiB,CAACjB,EAEnCsE,EAAgC1D,EAClC,CACE,GAAGkD,EACH,WAAY,sBACZ,MAAO,UACP,UAAW,sCACX,GAAIG,EAAW,CAAE,WAAY,uBAA0B,CAAA,CAAC,EAE1D,CACE,GAAGH,EACH,WAAYG,EAAW,wBAA0B,kBACjD,MAAO,OACP,UAAW,uCAAA,EAGXM,EAAoCjE,EACtC,CACE,GAAGyD,EACH,WAAYI,EAAe,uBAAyB,uBACpD,UAAW,mEAAA,EAEb,CACE,GAAGJ,EACH,WAAY,uDACZ,UAAW,gCACX,QAASI,EAAe,GAAM,CAAA,EAGpC,OAAI/D,GAAgB,CAACiE,KACnBE,EAAa,QAAU,GACvBA,EAAa,OAAS,eAItBZ,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,MAAO,OACP,UAAW,OACX,QAAS,OACT,cAAe,SACf,WAAY,SACZ,IAAK,GACL,cAAe,GACf,GAAGF,CAAA,EAGL,SAAA,CAAAG,MAAC,QAAA,CAAO,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKN,EACFD,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,QAAS,OACT,SAAU,OACV,WAAY,SACZ,eAAgB,SAChB,IAAK,GACL,MAAO,OACP,cAAe,MAAA,EAIjB,SAAA,CAAAC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASX,EACT,aAAc,IAAMiB,EAAY,EAAI,EACpC,aAAc,IAAMA,EAAY,EAAK,EACrC,MAAOI,EACP,MAAO1D,EAAW,aAAe,WAEhC,SAAAA,QAAY4D,EAAAA,OAAA,CAAO,KAAM,GAAI,EAAKZ,EAAAA,IAACa,EAAAA,IAAA,CAAI,KAAM,EAAA,CAAI,CAAA,CAAA,EAIpDb,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAAStD,EAAckC,EAAaF,EACpC,aAAc,IAAM8B,EAAgB,EAAI,EACxC,aAAc,IAAMA,EAAgB,EAAK,EACzC,SAAU,CAAC9D,IAAgBF,GAAgB,CAACiE,GAC5C,MAAOE,EAEN,WACCZ,EAAAA,KAAAe,EAAAA,SAAA,CACE,SAAA,CAAAd,MAACC,EAAAA,SAAQ,KAAM,GAAI,MAAO,CAAE,UAAW,+BAAiC,EACxED,EAAAA,IAAC,QAAK,SAAA,eAAA,CAAa,CAAA,CAAA,CACrB,EACE,CAACtD,GAAe,CAAC+D,EACnBV,OAAAe,EAAAA,SAAA,CACE,SAAA,CAAAd,MAACC,EAAAA,SAAQ,KAAM,GAAI,MAAO,CAAE,UAAW,+BAAiC,EACxED,EAAAA,IAAC,QAAK,SAAA,mBAAA,CAAiB,CAAA,CAAA,CACzB,EACEtD,EACFqD,EAAAA,KAAAe,EAAAA,SAAA,CACE,SAAA,CAAAd,EAAAA,IAACe,EAAAA,MAAA,CAAM,KAAM,EAAA,CAAI,EACjBf,EAAAA,IAAC,QAAK,SAAA,aAAA,CAAW,CAAA,CAAA,CACnB,EAEAD,EAAAA,KAAAe,EAAAA,SAAA,CACE,SAAA,CAAAd,EAAAA,IAACgB,EAAAA,SAAA,CAAS,KAAM,EAAA,CAAI,EACpBhB,EAAAA,IAAC,QAAK,SAAA,eAAA,CAAa,CAAA,CAAA,CACrB,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CAAA,CAGN,EC1KaiB,GAAsD,CAAC,CAClE,QAAArB,CACF,IAAM,CACJ,KAAM,CAAE,YAAAlD,EAAa,cAAAE,EAAe,WAAAmC,CAAA,EAAea,EAE7CsB,EAAWxE,GAAeE,GAAiB,GAC3CuE,EAAYzE,GAAeE,EAAgB,IAAMA,GAAiB,GAElEwE,EAAoC,CACxC,QAAS,OACT,WAAY,SACZ,IAAK,EACL,eAAgB,aAChB,QAAS,WACT,aAAc,KACd,OAAQ,YACR,cAAe,OACf,WAAY,WACZ,GAAIF,EACA,CACE,WAAY,sBACZ,YAAa,sBACb,UAAW,+BACX,UAAW,mCAAA,EAEbC,EACA,CACE,WAAY,uBACZ,YAAa,uBACb,UAAW,4BAAA,EAEb,CACE,WAAY,kBACZ,YAAa,wBACb,UAAW,4BAAA,CACb,EAGN,OACEpB,EAAAA,KAAAe,WAAA,CACE,SAAA,CAAAd,MAAC,QAAA,CAAO,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKN,EACFD,EAAAA,KAAC,MAAA,CAAI,MAAOqB,EACV,SAAA,CAAApB,EAAAA,IAAC,MAAA,CACC,MAAO,CACL,OAAQ,EACR,MAAO,EACP,aAAc,MACd,GAAItD,EACA,CACE,WAAY,UACZ,UAAW,8BAAA,EAEb,CACE,WAAY,qBAAA,CACd,CACN,CAAA,EAEFsD,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,SAAU,GACV,WAAY,IACZ,cAAe,SACf,MAAO,wBACP,cAAe,WAAA,EAGhB,SAAAtD,EAAc,UAAUqC,EAAWnC,CAAa,CAAC,GAAK,SAAA,CAAA,CACzD,CAAA,CACF,CAAA,EACF,CAEJ,EClEayE,GAA8D,CAAC,CAC1E,QAAAzB,CACF,IAAM,CACJ,KAAM,CAAA,SAAEhC,EAAU,YAAA0D,EAAa,SAAAtE,EAAU,OAAAE,EAAQ,oBAAAiC,EAAqB,cAAAnD,GACpE4D,EAEI2B,EAAoBhG,EAAAA,OAAgB,EAAK,EACzC,CAAE,MAAA2C,EAAO,WAAAsD,CAAA,EAAeC,oBAAA,EACxBC,EAAmBC,EAAAA,oBAAA,EACnBC,EAAiBC,EAAAA,cAAcH,EAAiB,gBAAgB,EAGhEI,EAASvG,EAAAA,OAAqC,IAAI,EAGlDwG,EAAmBxG,EAAAA,OAAOS,CAAa,EAC7C0B,EAAAA,UAAU,IAAM,CACdqE,EAAiB,QAAU/F,CAC7B,EAAG,CAACA,CAAa,CAAC,EAIlB0B,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC4D,EAAa,OAElB,MAAMU,EAAM,IAAIC,wBAAsB,CACpC,UAAW,KACX,kBAAmB,GACnB,kBAAmB,EACnB,kBAAmB,GACnB,cAAe,EAAA,CAChB,EACD,OAAAH,EAAO,QAAUE,EAEjBA,EAAI,GAAG,cAAe,IAAM,CAC1BV,EAAY,cAAc,EAAI,EAC9BA,EAAY,eAAe,UAAU,CACvC,CAAC,EAEDU,EAAI,GAAG,UAAW,IAAM,CACtBV,EAAY,cAAc,EAAK,CACjC,CAAC,EAEM,IAAM,CACXU,EAAI,KAAA,EACJF,EAAO,QAAU,IACnB,CACF,EAAG,CAACR,CAAW,CAAC,EAIhB5D,EAAAA,UAAU,IAAM,CACd,MAAMsE,EAAMF,EAAO,QACnB,GAAKE,EAEL,GAAIR,GAAY,aAAa,MAAO,CAClC,MAAMU,EAAaV,EAAW,YAAY,MAAM,iBAC5CU,GACFF,EAAI,MAAME,CAAU,CAExB,MACEF,EAAI,KAAA,CAER,EAAG,CAACR,GAAY,aAAa,KAAK,CAAC,EAInC,MAAMW,EAAkB5G,EAAAA,OAA6C,IAAI,EAEzEmC,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC4D,EAAa,OAElB,MAAMc,EAAWlE,EAEbkE,IAAa,YACXD,EAAgB,UAClB,aAAaA,EAAgB,OAAO,EACpCA,EAAgB,QAAU,MAE5Bb,EAAY,cAAc,EAAI,EAC9BA,EAAY,eAAe,UAAU,GAC5Bc,IAAa,aAAeA,IAAa,QAC9CD,EAAgB,UAClB,aAAaA,EAAgB,OAAO,EACpCA,EAAgB,QAAU,MAE5BL,EAAO,SAAS,QAAA,EAChBR,EAAY,cAAc,EAAK,GACtBc,IAAa,aACjBD,EAAgB,UACnBA,EAAgB,QAAU,WAAW,IAAM,CACzCA,EAAgB,QAAU,KAC1BL,EAAO,SAAS,QAAA,EAChBR,EAAY,cAAc,EAAK,CACjC,EAAG,GAAG,GAGZ,EAAG,CAACpD,EAAOoD,CAAW,CAAC,EAGvB5D,EAAAA,UAAU,IACD,IAAM,CACPyE,EAAgB,SAClB,aAAaA,EAAgB,OAAO,CAExC,EACC,CAAA,CAAE,EAILE,EAAAA,eAAgBC,GAAQ,CACtB,GAAI,CAEF,MAAMC,EADU,IAAI,YAAA,EACI,OAAOD,EAAI,OAAO,EACpC/D,EAAO,KAAK,MAAMgE,CAAO,EAE/B,GAAIhE,EAAK,OAAS,gBAAiB,CACjC,MAAMP,EAAWO,EAAK,OACtB,GAAI,CAACwD,EAAiB,QAAQ,IAAI/D,CAAQ,EACxC,OAEFsD,GAAa,SAAStD,CAAQ,CAChC,CACF,OAASwE,EAAK,CACZ,QAAQ,MAAM,wDAAyDA,CAAG,CAC5E,CACF,CAAC,EAID9E,EAAAA,UAAU,IAAM,CAEdyB,EAAoB,EADQjB,IAAU,aACM,CAAC0D,CAAc,CAC7D,EAAG,CAACA,EAAgBzC,EAAqBjB,CAAK,CAAC,EAI/CR,EAAAA,UAAU,IAAM,CACd,GAAI,CAAC4D,EAAa,OAElB,MAAMmB,EAAW,CAAC,CAACjB,GAGf,CAACiB,GAFuBvE,IAAU,kBAGpC4D,EAAO,SAAS,QAAA,EAChBR,EAAY,eAAA,GAEdC,EAAkB,QAAUkB,CAC9B,EAAG,CAACjB,EAAYtD,EAAOoD,CAAW,CAAC,EAInC5D,EAAAA,UAAU,IAAM,CACd,MAAMgF,EAAchB,EAAiB,iBAChCgB,GAELA,EAAY,qBAAqB,CAAC1F,CAAQ,EAAE,MAAOwF,GAAQ,CACzD,QAAQ,MAAM,mDAAoDA,CAAG,CACvE,CAAC,CACH,EAAG,CAACxF,EAAU0E,EAAiB,gBAAgB,CAAC,EAG9C1B,MAAC,OAAI,MAAO,CAAE,SAAU,WAAY,OAAQ,GAAI,KAAM,EAAG,MAAO,EAAG,QAAS,OAAQ,cAAe,SAAU,IAAK,GAChH,SAAAA,EAAAA,IAAC2C,EAAAA,kBAAA,CAAkB,OAAAzF,CAAA,CAAgB,CAAA,CACrC,CAEJ,EC5Ia0F,GAA4D,CAAC,CACxE,SAAApI,EACA,OAAAC,EACA,IAAAC,EACA,WAAAI,EACA,gBAAAF,EACA,kBAAAC,EACA,MAAAgF,EACA,UAAAgD,EACA,eAAA/C,EACA,eAAAgD,EACA,kBAAAC,CACF,IAAM,CACJ,MAAMnD,EAAUtF,EAAiB,CAC/B,SAAAE,EACA,OAAAC,EACA,IAAAC,EACA,WAAAI,EACA,gBAAAF,EACA,kBAAAC,CAAA,CACD,EAEK,CACJ,MAAAyB,EACA,YAAAI,EACA,iBAAAZ,EACA,WAAA8C,EACA,kBAAAE,CAAA,EACEc,EAEJ,OACEG,EAAAA,KAAC,UAAA,CACC,UAAA8C,EACA,MAAO,CACL,SAAU,WACV,QAAS,OACT,cAAe,SACf,WAAY,SACZ,eAAgB,SAChB,SAAU,SACV,aAAc,GACd,OAAQ,kCACR,UAAW,oCACX,OAAQ,OACR,MAAO,OACP,SAAU,OACV,YAAa/G,EACT,GAAGA,EAAiB,KAAK,MAAMA,EAAiB,MAAM,GACtD,QACJ,GAAG+D,CAAA,EAIL,SAAA,CAAAG,EAAAA,IAACL,EAAA,CAAa,QAAAC,EAAkB,eAAAE,CAAA,CAAgC,EAGhEC,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,SAAU,WACV,MAAO,EACP,OAAQ,GACR,cAAe,OACf,QAAS,OACT,cAAe,SACf,eAAgB,gBAChB,QAAS,EAAA,EAIX,SAAA,CAAAA,EAAAA,KAAC,MAAA,CACC,MAAO,CACL,QAAS,OACT,MAAO,OACP,WAAY,aACZ,eAAgB,eAAA,EAGjB,SAAA,CAAAgD,EACCA,EAAkBnD,CAAO,EAEzBI,EAAAA,IAACiB,IAAkB,QAAArB,EAAkB,QAEtC,MAAA,CAAA,CAAI,CAAA,CAAA,CAAA,EAINkD,EACCA,EAAelD,CAAO,EAEtBI,EAAAA,IAACI,IAAe,QAAAR,CAAA,CAAkB,CAAA,CAAA,CAAA,EAKtCI,MAAC,OAAI,MAAO,CAAE,QAAS,MAAA,EACpB,YAASlF,GACRkF,EAAAA,IAACgD,EAAAA,YAAA,CACC,UAAWlI,EACX,MAAAwB,EACA,QAAS,GACT,MAAO,GACP,MAAO,GACP,YAAa,IAAM,CACjBsD,EAAQ,cAAA,EACRd,EAAA,CACF,EACA,eAAgBF,EAEhB,SAAAoB,EAAAA,IAACqB,IAAsB,QAAAzB,CAAA,CAAkB,CAAA,CAAA,CAC3C,CAEJ,CAAA,CAAA,CAAA,CAGN"}
1
+ {"version":3,"file":"zeroweight-renderer-react.cjs.js","sources":["../src/useAvatarSession.ts","../src/AvatarCanvas.tsx","../src/AvatarControls.tsx","../src/AvatarStatusBadge.tsx","../src/LiveKitAvatarProvider.tsx","../src/LiveKitAvatarSession.tsx"],"sourcesContent":["/**\n * useAvatarSession — Main React hook for avatar rendering.\n *\n * Wraps ZeroWeightRenderer + ActionQueue in React lifecycle.\n * Returns reactive state and imperative methods.\n *\n * Usage:\n * const session = useAvatarSession({\n * avatarId: 'abc123',\n * apiKey: 'optional-api-key',\n * });\n */\n\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ZeroWeightRenderer, ActionQueue } from \"@zeroweight/renderer\";\nimport type { ActionMetadata, RendererState } from \"@zeroweight/renderer\";\nimport type { ZeroWeightApi } from \"./types\";\n\nconst INACTIVITY_TIMEOUT_MS = 30000;\nconst SESSION_DURATION_SECONDS = 120;\nconst DEFAULT_LIVEKIT_URL = \"wss://prod-project-pazuyq69.livekit.cloud\";\nconst DEFAULT_API_BASE_URL = \"https://api.zeroweight.ai\";\n\nconst generateRandomName = () => {\n const adjectives = [\"Happy\", \"Swift\", \"Bright\", \"Cool\", \"Smart\"];\n const nouns = [\"User\", \"Guest\", \"Visitor\", \"Agent\", \"Caller\"];\n const randomNum = Math.floor(Math.random() * 1000);\n return `${adjectives[Math.floor(Math.random() * adjectives.length)]}${\n nouns[Math.floor(Math.random() * nouns.length)]\n }${randomNum}`;\n};\n\nexport interface AvatarSessionConfig {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL. */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n}\n\nexport interface AvatarSessionReturn {\n // Refs\n containerRef: React.RefObject<HTMLDivElement | null>;\n\n // Renderer instance (for advanced use)\n renderer: ZeroWeightRenderer | null;\n actionQueue: ActionQueue | null;\n\n // Reactive state\n rendererState: RendererState;\n avatarDimensions: { width: number; height: number } | null;\n loadedActions: Set<string>;\n actionMetadata: Record<string, ActionMetadata>;\n isLoadingActions: boolean;\n isEngineReady: boolean;\n\n // Connection state\n token: string | null;\n isConnecting: boolean;\n isConnected: boolean;\n livekitUrl: string;\n\n // Session\n timeRemaining: number;\n formatTime: (seconds: number) => string;\n\n // Controls\n micMuted: boolean;\n volume: number;\n\n // Methods\n connect: () => Promise<void>;\n disconnect: () => void;\n toggleMic: () => void;\n setVolume: (v: number) => void;\n toggleVolume: () => void;\n interrupt: () => void;\n runAction: (actionId: string) => void;\n startSessionTimer: () => void;\n /** Called by LiveKitRoom onConnected */\n markConnected: () => void;\n\n // For LiveKitAvatarProvider internal use\n setInactivityActive: (isInactive: boolean) => void;\n}\n\nexport function useAvatarSession(config: AvatarSessionConfig): AvatarSessionReturn {\n const {\n avatarId,\n apiKey = null,\n api,\n livekitUrl: livekitUrlProp = DEFAULT_LIVEKIT_URL,\n sessionDuration = SESSION_DURATION_SECONDS,\n inactivityTimeout = INACTIVITY_TIMEOUT_MS,\n } = config;\n\n const livekitUrl = livekitUrlProp;\n const resolvedApi = useMemo<ZeroWeightApi>(() => {\n if (api) return api;\n\n const headers = apiKey ? { \"X-ZW-Api-Key\": apiKey } : undefined;\n\n return {\n getBundle: async (resolvedAvatarId: string) => {\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/avatars/bundle/${encodeURIComponent(\n resolvedAvatarId\n )}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch avatar bundle (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n getLiveKitToken: async (resolvedAvatarId: string, userName: string) => {\n const params = new URLSearchParams({\n avatar_id: resolvedAvatarId,\n name: userName,\n });\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/livekit/token?${params.toString()}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch LiveKit token (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n };\n }, [api, apiKey]);\n\n // Refs\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rendererRef = useRef<ZeroWeightRenderer | null>(null);\n const actionQueueRef = useRef<ActionQueue | null>(null);\n\n // Engine state\n const [rendererState, setRendererState] = useState<RendererState>(\"idle\");\n const [avatarDimensions, setAvatarDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const [loadedActions, setLoadedActions] = useState<Set<string>>(\n new Set([\"listening\"])\n );\n const [actionMetadata, setActionMetadata] = useState<\n Record<string, ActionMetadata>\n >({\n listening: { kind: \"looped\" },\n speaking: { kind: \"looped\" },\n });\n const [isLoadingActions, setIsLoadingActions] = useState(false);\n\n // Connection state\n const [token, setToken] = useState<string | null>(null);\n const [isConnecting, setIsConnecting] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n\n // Session timer\n const [timeRemaining, setTimeRemaining] = useState(sessionDuration);\n const sessionTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const handleDisconnectRef = useRef<() => void>(() => {});\n\n // Controls\n const [micMuted, setMicMuted] = useState(false);\n const [volume, setVolumeState] = useState(0.8);\n\n // Inactivity\n const inactivityTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isEngineReady = rendererState === \"ready\";\n\n // ─── Ensure Canvas ──────────────────────────────────────────────\n\n const ensureCanvas = useCallback(() => {\n const container = containerRef.current;\n if (!container) return null;\n\n let canvas = container.querySelector(\"canvas\") as HTMLCanvasElement | null;\n if (!canvas) {\n canvas = document.createElement(\"canvas\");\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n container.appendChild(canvas);\n }\n canvasRef.current = canvas;\n return canvas;\n }, []);\n\n // ─── Init Renderer ──────────────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n const initRenderer = async () => {\n const canvas = ensureCanvas();\n if (!canvas) return;\n\n // Create renderer\n const renderer = new ZeroWeightRenderer();\n rendererRef.current = renderer;\n\n // Create action queue\n const queue = new ActionQueue((actionId, fallback) => {\n renderer.play(actionId, fallback);\n });\n actionQueueRef.current = queue;\n\n // Wire up renderer events\n renderer.on(\"stateChanged\", (state) => {\n if (!cancelled) setRendererState(state);\n });\n\n renderer.on(\"dimensions\", (w, h) => {\n if (!cancelled) setAvatarDimensions({ width: w, height: h });\n });\n\n renderer.on(\"actionLoaded\", (actionId) => {\n if (!cancelled) {\n setLoadedActions((prev) => {\n const next = new Set(prev);\n next.add(actionId);\n return next;\n });\n }\n });\n\n renderer.on(\"allActionsLoaded\", () => {\n if (!cancelled) setIsLoadingActions(false);\n });\n\n renderer.on(\"ready\", () => {\n // Update action metadata from renderer\n if (!cancelled) {\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n }\n });\n\n // Fetch bundle and init\n try {\n setIsLoadingActions(true);\n const data = await resolvedApi.getBundle(avatarId);\n if (cancelled) return;\n\n await renderer.init(canvas, { payload: data.payload });\n if (cancelled) return;\n\n // After init, update metadata again with all loaded data\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n } catch (e) {\n console.error(\"[useAvatarSession] Init failed:\", e);\n }\n };\n\n initRenderer();\n\n return () => {\n cancelled = true;\n rendererRef.current?.destroy();\n rendererRef.current = null;\n actionQueueRef.current = null;\n };\n }, [avatarId, ensureCanvas, resolvedApi]);\n\n // ─── Auto-wave on load ──────────────────────────────────────────\n\n const hasWavedRef = useRef(false);\n useEffect(() => {\n if (\n isEngineReady &&\n rendererRef.current &&\n loadedActions.has(\"wave_hand\") &&\n !hasWavedRef.current\n ) {\n rendererRef.current.play(\"wave_hand\", \"listening\");\n hasWavedRef.current = true;\n }\n }, [isEngineReady, loadedActions]);\n\n // ─── Connection ─────────────────────────────────────────────────\n\n const connect = useCallback(async () => {\n if (isConnecting || isConnected) return;\n\n setIsConnecting(true);\n try {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n const userName = generateRandomName();\n const data = await resolvedApi.getLiveKitToken(avatarId, userName);\n setToken(data.token);\n } catch (error) {\n console.error(\"[useAvatarSession] Failed to connect:\", error);\n setIsConnecting(false);\n }\n }, [isConnecting, isConnected, avatarId, resolvedApi]);\n\n const disconnect = useCallback(() => {\n // Clear session timer\n if (sessionTimerRef.current) {\n clearInterval(sessionTimerRef.current);\n sessionTimerRef.current = null;\n }\n setTimeRemaining(sessionDuration);\n\n // Clear inactivity timer\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n\n setToken(null);\n setIsConnected(false);\n setIsConnecting(false);\n\n // Reset avatar to listening\n actionQueueRef.current?.forceListening();\n }, [sessionDuration]);\n\n // Keep disconnect ref current for timer\n useEffect(() => {\n handleDisconnectRef.current = disconnect;\n }, [disconnect]);\n\n const markConnected = useCallback(() => {\n setIsConnected(true);\n setIsConnecting(false);\n }, []);\n\n // ─── Session Timer ──────────────────────────────────────────────\n\n const startSessionTimer = useCallback(() => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n setTimeRemaining(sessionDuration);\n\n sessionTimerRef.current = setInterval(() => {\n setTimeRemaining((prev) => {\n if (prev <= 1) {\n clearInterval(sessionTimerRef.current!);\n sessionTimerRef.current = null;\n setTimeout(() => handleDisconnectRef.current(), 0);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n }, [sessionDuration]);\n\n const formatTime = useCallback((seconds: number) => {\n const m = Math.floor(seconds / 60)\n .toString()\n .padStart(2, \"0\");\n const s = (seconds % 60).toString().padStart(2, \"0\");\n return `${m}:${s}`;\n }, []);\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n };\n }, []);\n\n // ─── Inactivity ─────────────────────────────────────────────────\n\n const setInactivityActive = useCallback(\n (isInactive: boolean) => {\n if (!isInactive) {\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n return;\n }\n\n if (inactivityTimeoutRef.current) return;\n\n inactivityTimeoutRef.current = setTimeout(() => {\n inactivityTimeoutRef.current = null;\n handleDisconnectRef.current();\n }, inactivityTimeout);\n },\n [inactivityTimeout]\n );\n\n // ─── Controls ───────────────────────────────────────────────────\n\n const toggleMic = useCallback(() => {\n setMicMuted((v) => !v);\n }, []);\n\n const setVolume = useCallback((v: number) => {\n setVolumeState(v);\n }, []);\n\n const toggleVolume = useCallback(() => {\n setVolumeState((v) => (v > 0 ? 0 : 1));\n }, []);\n\n const interrupt = useCallback(() => {\n rendererRef.current?.interrupt();\n }, []);\n\n const runAction = useCallback((actionId: string) => {\n if (actionQueueRef.current) {\n actionQueueRef.current.dispatch(actionId);\n } else {\n rendererRef.current?.play(actionId, \"listening\");\n }\n }, []);\n\n return {\n containerRef,\n renderer: rendererRef.current,\n actionQueue: actionQueueRef.current,\n rendererState,\n avatarDimensions,\n loadedActions,\n actionMetadata,\n isLoadingActions,\n isEngineReady,\n token,\n isConnecting,\n isConnected,\n livekitUrl,\n timeRemaining,\n formatTime,\n micMuted,\n volume,\n connect,\n disconnect,\n toggleMic,\n setVolume,\n toggleVolume,\n interrupt,\n runAction,\n startSessionTimer,\n markConnected,\n setInactivityActive,\n };\n}\n","/**\n * AvatarCanvas — Canvas container for the avatar renderer.\n *\n * Provides the div container where the renderer creates and manages a <canvas>.\n * Shows loading overlay when the engine is initializing.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarCanvasProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the outer container. */\n style?: React.CSSProperties;\n /** Custom loading component. */\n loadingContent?: React.ReactNode;\n}\n\nexport const AvatarCanvas: React.FC<AvatarCanvasProps> = ({\n session,\n style,\n loadingContent,\n}) => {\n return (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: 0,\n pointerEvents: \"auto\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div\n ref={session.containerRef}\n style={{\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n transition: \"all 0.5s ease-in-out\",\n }}\n >\n {/* Canvas is injected here by the renderer */}\n {!session.isEngineReady && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"rgba(0,0,0,0.4)\",\n backdropFilter: \"blur(4px)\",\n zIndex: 10,\n }}\n >\n {loadingContent || (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n }}\n >\n <Loader2\n style={{\n width: 40,\n height: 40,\n color: \"rgba(255,255,255,0.5)\",\n animation: \"zwr-spin 1s linear infinite\",\n }}\n />\n <div\n style={{\n fontSize: 14,\n color: \"rgba(255,255,255,0.5)\",\n fontWeight: 500,\n letterSpacing: \"0.1em\",\n textTransform: \"uppercase\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }}\n >\n Initializing Neural Engine...\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n","/**\n * AvatarControls — Default control bar for the avatar session.\n *\n * Mic toggle, connect/disconnect button.\n * Can be replaced entirely by a custom UI.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React, { useState } from \"react\";\nimport {\n Mic,\n MicOff,\n Power,\n Activity,\n Loader2,\n} from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarControlsProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the wrapper. */\n style?: React.CSSProperties;\n}\n\nconst btnBase: React.CSSProperties = {\n flexShrink: 0,\n borderRadius: 9999,\n padding: 16,\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst connectBtnBase: React.CSSProperties = {\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n borderRadius: 9999,\n padding: \"16px 24px\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n};\n\nexport const AvatarControls: React.FC<AvatarControlsProps> = ({\n session,\n style,\n}) => {\n const {\n micMuted,\n toggleMic,\n isConnected,\n isConnecting,\n isEngineReady,\n loadedActions,\n connect,\n disconnect,\n } = session;\n\n const [micHover, setMicHover] = useState(false);\n const [connectHover, setConnectHover] = useState(false);\n\n const isSessionReady =\n isEngineReady &&\n loadedActions.has(\"listening\") &&\n loadedActions.has(\"speaking\");\n\n const micStyle: React.CSSProperties = micMuted\n ? {\n ...btnBase,\n background: \"rgba(239,68,68,0.1)\",\n color: \"#f87171\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.3)\",\n ...(micHover ? { background: \"rgba(239,68,68,0.2)\" } : {}),\n }\n : {\n ...btnBase,\n background: micHover ? \"rgba(255,255,255,0.1)\" : \"rgba(0,0,0,0.4)\",\n color: \"#fff\",\n boxShadow: \"inset 0 0 0 1px rgba(255,255,255,0.1)\",\n };\n\n const connectStyle: React.CSSProperties = isConnected\n ? {\n ...connectBtnBase,\n background: connectHover ? \"rgba(239,68,68,0.22)\" : \"rgba(239,68,68,0.14)\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)\",\n }\n : {\n ...connectBtnBase,\n background: \"linear-gradient(to right, #7c3aed, #db2777, #f97316)\",\n boxShadow: \"0 0 30px rgba(236,72,153,0.3)\",\n opacity: connectHover ? 0.9 : 1,\n };\n\n if (isConnecting || !isSessionReady) {\n connectStyle.opacity = 0.5;\n connectStyle.cursor = \"not-allowed\";\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n marginTop: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n paddingBottom: 16,\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}</style>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 16,\n width: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Mic Toggle */}\n <button\n type=\"button\"\n onClick={toggleMic}\n onMouseEnter={() => setMicHover(true)}\n onMouseLeave={() => setMicHover(false)}\n style={micStyle}\n title={micMuted ? \"Unmute mic\" : \"Mute mic\"}\n >\n {micMuted ? <MicOff size={24} /> : <Mic size={24} />}\n </button>\n\n {/* Main Connect Button */}\n <button\n type=\"button\"\n onClick={isConnected ? disconnect : connect}\n onMouseEnter={() => setConnectHover(true)}\n onMouseLeave={() => setConnectHover(false)}\n disabled={!isConnected && (isConnecting || !isSessionReady)}\n style={connectStyle}\n >\n {isConnecting ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Connecting...</span>\n </>\n ) : !isConnected && !isSessionReady ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Loading Avatar...</span>\n </>\n ) : isConnected ? (\n <>\n <Power size={20} />\n <span>End Session</span>\n </>\n ) : (\n <>\n <Activity size={20} />\n <span>Start Session</span>\n </>\n )}\n </button>\n </div>\n </div>\n );\n};\n","/**\n * AvatarStatusBadge — Connection status indicator with session timer.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarStatusBadgeProps {\n session: AvatarSessionReturn;\n}\n\nexport const AvatarStatusBadge: React.FC<AvatarStatusBadgeProps> = ({\n session,\n}) => {\n const { isConnected, timeRemaining, formatTime } = session;\n\n const isUrgent = isConnected && timeRemaining <= 30;\n const isWarning = isConnected && timeRemaining > 30 && timeRemaining <= 60;\n\n const wrapperStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n backdropFilter: \"blur(12px)\",\n padding: \"6px 12px\",\n borderRadius: 9999,\n border: \"1px solid\",\n pointerEvents: \"auto\",\n transition: \"all 0.3s\",\n ...(isUrgent\n ? {\n background: \"rgba(239,68,68,0.3)\",\n borderColor: \"rgba(239,68,68,0.4)\",\n boxShadow: \"0 0 15px rgba(239,68,68,0.3)\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }\n : isWarning\n ? {\n background: \"rgba(249,115,22,0.2)\",\n borderColor: \"rgba(249,115,22,0.3)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }\n : {\n background: \"rgba(0,0,0,0.4)\",\n borderColor: \"rgba(255,255,255,0.1)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }),\n };\n\n return (\n <>\n <style>{`\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div style={wrapperStyle}>\n <div\n style={{\n height: 8,\n width: 8,\n borderRadius: \"50%\",\n ...(isConnected\n ? {\n background: \"#22c55e\",\n boxShadow: \"0 0 10px rgba(34,197,94,0.5)\",\n }\n : {\n background: \"rgba(239,68,68,0.5)\",\n }),\n }}\n />\n <span\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n color: \"rgba(255,255,255,0.7)\",\n textTransform: \"uppercase\",\n }}\n >\n {isConnected ? `Online ${formatTime(timeRemaining)}` : \"Offline\"}\n </span>\n </div>\n </>\n );\n};\n","/**\n * LiveKitAvatarProvider — Bridge between LiveKit hooks and the ActionQueue/VAD.\n * Uses inline styles only — no CSS framework dependency.\n *\n * This component MUST be rendered inside a <LiveKitRoom>.\n */\n\nimport React, { useEffect, useRef } from \"react\";\nimport {\n useVoiceAssistant,\n RoomAudioRenderer,\n useDataChannel,\n useIsSpeaking,\n useLocalParticipant,\n} from \"@livekit/components-react\";\nimport { VoiceActivityDetector } from \"@zeroweight/renderer\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface LiveKitAvatarProviderProps {\n session: AvatarSessionReturn;\n}\n\nexport const LiveKitAvatarProvider: React.FC<LiveKitAvatarProviderProps> = ({\n session,\n}) => {\n const { renderer, actionQueue, micMuted, volume, setInactivityActive, loadedActions } =\n session;\n\n const prevAudioTrackRef = useRef<boolean>(false);\n const { state, audioTrack } = useVoiceAssistant();\n const localParticipant = useLocalParticipant();\n const isUserSpeaking = useIsSpeaking(localParticipant.localParticipant);\n\n // VAD instance\n const vadRef = useRef<VoiceActivityDetector | null>(null);\n\n // Ref to latest loadedActions so data channel callback doesn't go stale\n const loadedActionsRef = useRef(loadedActions);\n useEffect(() => {\n loadedActionsRef.current = loadedActions;\n }, [loadedActions]);\n\n // ─── VAD Setup ──────────────────────────────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const vad = new VoiceActivityDetector({\n threshold: 0.008,\n analyseIntervalMs: 30,\n speechStartFrames: 1,\n speechPauseFrames: 30,\n turnEndFrames: 50,\n });\n vadRef.current = vad;\n\n vad.on(\"speechStart\", () => {\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n });\n\n vad.on(\"turnEnd\", () => {\n actionQueue.setTurnActive(false);\n });\n\n return () => {\n vad.stop();\n vadRef.current = null;\n };\n }, [actionQueue]);\n\n // ─── Connect/disconnect VAD to audio track ──────────────────────\n\n useEffect(() => {\n const vad = vadRef.current;\n if (!vad) return;\n\n if (audioTrack?.publication?.track) {\n const mediaTrack = audioTrack.publication.track.mediaStreamTrack;\n if (mediaTrack) {\n vad.start(mediaTrack);\n }\n } else {\n vad.stop();\n }\n }, [audioTrack?.publication?.track]);\n\n // ─── LiveKit state → turn management ─────────────────────────────\n\n const turnEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const stateStr = state as string;\n\n if (stateStr === \"speaking\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n } else if (stateStr === \"listening\" || stateStr === \"idle\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n } else if (stateStr === \"thinking\") {\n if (!turnEndTimerRef.current) {\n turnEndTimerRef.current = setTimeout(() => {\n turnEndTimerRef.current = null;\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n }, 500);\n }\n }\n }, [state, actionQueue]);\n\n // Cleanup turn-end timer on unmount\n useEffect(() => {\n return () => {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n }\n };\n }, []);\n\n // ─── Data channel (backend-triggered actions) ───────────────────\n\n useDataChannel((msg) => {\n try {\n const decoder = new TextDecoder();\n const strData = decoder.decode(msg.payload);\n const data = JSON.parse(strData);\n\n if (data.type === \"AVATAR_UPDATE\") {\n const actionId = data.action;\n if (!loadedActionsRef.current.has(actionId)) {\n return;\n }\n actionQueue?.dispatch(actionId);\n }\n } catch (err) {\n console.error(\"[LiveKitAvatarProvider] Failed to parse data message:\", err);\n }\n });\n\n // ─── Inactivity tracking: only count when both AI and user are silent ───\n\n useEffect(() => {\n const isAssistantSpeaking = state === \"speaking\";\n setInactivityActive(!isAssistantSpeaking && !isUserSpeaking);\n }, [isUserSpeaking, setInactivityActive, state]);\n\n // ─── Audio loss / disconnect fallback ───────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const hasAudio = !!audioTrack;\n const isDisconnectedState = state === \"disconnected\";\n\n if (!hasAudio || isDisconnectedState) {\n vadRef.current?.endTurn();\n actionQueue.forceListening();\n }\n prevAudioTrackRef.current = hasAudio;\n }, [audioTrack, state, actionQueue]);\n\n // ─── Mic mute sync ─────────────────────────────────────────────\n\n useEffect(() => {\n const participant = localParticipant.localParticipant;\n if (!participant) return;\n\n participant.setMicrophoneEnabled(!micMuted).catch((err) => {\n console.error(\"[LiveKitAvatarProvider] Failed to set mic state:\", err);\n });\n }, [micMuted, localParticipant.localParticipant]);\n\n return (\n <div style={{ position: \"absolute\", bottom: 80, left: 8, right: 8, display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <RoomAudioRenderer volume={volume} />\n </div>\n );\n};\n","/**\n * LiveKitAvatarSession — Full drop-in avatar component.\n * Uses inline styles only — no CSS framework dependency.\n *\n * Simplest usage:\n * import { LiveKitAvatarSession } from \"@zeroweight/react\";\n * <LiveKitAvatarSession\n * avatarId=\"abc123\"\n * apiKey=\"optional-api-key\"\n * />\n */\n\nimport React from \"react\";\nimport { LiveKitRoom } from \"@livekit/components-react\";\nimport \"@livekit/components-styles\";\n\nimport { useAvatarSession } from \"./useAvatarSession\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\nimport { AvatarCanvas } from \"./AvatarCanvas\";\nimport { AvatarControls } from \"./AvatarControls\";\nimport { AvatarStatusBadge } from \"./AvatarStatusBadge\";\nimport { LiveKitAvatarProvider } from \"./LiveKitAvatarProvider\";\nimport type { ZeroWeightApi } from \"./types\";\n\ninterface LiveKitAvatarSessionProps {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL (e.g. \"wss://your-livekit.example.com\"). */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n /** Optional style overrides for the outer section. */\n style?: React.CSSProperties;\n /** Optional class name overrides for the outer section. */\n className?: string;\n /** Custom loading UI for the canvas. */\n loadingContent?: React.ReactNode;\n /** Custom controls component. If provided, replaces the default controls. */\n customControls?: (session: AvatarSessionReturn) => React.ReactNode;\n /** Custom status badge. If provided, replaces the default badge. */\n customStatusBadge?: (session: AvatarSessionReturn) => React.ReactNode;\n}\n\nexport const LiveKitAvatarSession: React.FC<LiveKitAvatarSessionProps> = ({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n style,\n className,\n loadingContent,\n customControls,\n customStatusBadge,\n}) => {\n const session = useAvatarSession({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n });\n\n const {\n token,\n isConnected,\n avatarDimensions,\n disconnect,\n startSessionTimer,\n } = session;\n\n return (\n <section\n className={className}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n borderRadius: 16,\n border: \"1px solid rgba(255,255,255,0.1)\",\n boxShadow: \"0 25px 50px -12px rgba(0,0,0,0.5)\",\n height: \"80vh\",\n width: \"auto\",\n maxWidth: \"100%\",\n aspectRatio: avatarDimensions\n ? `${avatarDimensions.width} / ${avatarDimensions.height}`\n : \"3 / 4\",\n ...style,\n }}\n >\n {/* 1. Canvas Layer */}\n <AvatarCanvas session={session} loadingContent={loadingContent} />\n\n {/* 2. UI Overlay Layer */}\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n padding: 16,\n }}\n >\n {/* Top Header / Status */}\n <div\n style={{\n display: \"flex\",\n width: \"100%\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n }}\n >\n {customStatusBadge ? (\n customStatusBadge(session)\n ) : (\n <AvatarStatusBadge session={session} />\n )}\n <div />\n </div>\n\n {/* Bottom Controls */}\n {customControls ? (\n customControls(session)\n ) : (\n <AvatarControls session={session} />\n )}\n </div>\n\n {/* 3. LiveKit Room (hidden, audio-only) */}\n <div style={{ display: \"none\" }}>\n {token && livekitUrl && (\n <LiveKitRoom\n serverUrl={livekitUrl}\n token={token}\n connect={true}\n video={false}\n audio={true}\n onConnected={() => {\n session.markConnected();\n startSessionTimer();\n }}\n onDisconnected={disconnect}\n >\n <LiveKitAvatarProvider session={session} />\n </LiveKitRoom>\n )}\n </div>\n </section>\n );\n};\n"],"names":["INACTIVITY_TIMEOUT_MS","SESSION_DURATION_SECONDS","DEFAULT_LIVEKIT_URL","DEFAULT_API_BASE_URL","generateRandomName","adjectives","nouns","randomNum","useAvatarSession","config","avatarId","apiKey","api","livekitUrlProp","sessionDuration","inactivityTimeout","livekitUrl","resolvedApi","useMemo","headers","resolvedAvatarId","response","userName","params","containerRef","useRef","canvasRef","rendererRef","actionQueueRef","rendererState","setRendererState","useState","avatarDimensions","setAvatarDimensions","loadedActions","setLoadedActions","actionMetadata","setActionMetadata","isLoadingActions","setIsLoadingActions","token","setToken","isConnecting","setIsConnecting","isConnected","setIsConnected","timeRemaining","setTimeRemaining","sessionTimerRef","handleDisconnectRef","micMuted","setMicMuted","volume","setVolumeState","inactivityTimeoutRef","isEngineReady","ensureCanvas","useCallback","container","canvas","useEffect","cancelled","renderer","ZeroWeightRenderer","queue","ActionQueue","actionId","fallback","state","w","h","prev","next","data","e","hasWavedRef","connect","error","disconnect","markConnected","startSessionTimer","formatTime","seconds","m","s","setInactivityActive","isInactive","toggleMic","v","setVolume","toggleVolume","interrupt","runAction","AvatarCanvas","session","style","loadingContent","jsxs","jsx","Loader2","btnBase","connectBtnBase","AvatarControls","micHover","setMicHover","connectHover","setConnectHover","isSessionReady","micStyle","connectStyle","MicOff","Mic","Fragment","Power","Activity","AvatarStatusBadge","isUrgent","isWarning","wrapperStyle","LiveKitAvatarProvider","actionQueue","prevAudioTrackRef","audioTrack","useVoiceAssistant","localParticipant","useLocalParticipant","isUserSpeaking","useIsSpeaking","vadRef","loadedActionsRef","vad","VoiceActivityDetector","mediaTrack","turnEndTimerRef","stateStr","useDataChannel","msg","strData","err","hasAudio","participant","RoomAudioRenderer","LiveKitAvatarSession","className","customControls","customStatusBadge","LiveKitRoom"],"mappings":"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"}
@@ -1,77 +1,77 @@
1
1
  import { useMemo as ve, useRef as x, useState as v, useCallback as S, useEffect as w } from "react";
2
2
  import { ZeroWeightRenderer as ye, ActionQueue as be, VoiceActivityDetector as Se } from "@zeroweight/renderer";
3
3
  import { jsxs as y, jsx as n, Fragment as D } from "react/jsx-runtime";
4
- import { useVoiceAssistant as xe, useLocalParticipant as we, useIsSpeaking as Ae, useDataChannel as ke, RoomAudioRenderer as Te, LiveKitRoom as Re } from "@livekit/components-react";
4
+ import { useVoiceAssistant as xe, useLocalParticipant as we, useIsSpeaking as ke, useDataChannel as Ae, RoomAudioRenderer as Te, LiveKitRoom as Re } from "@livekit/components-react";
5
5
  import "@livekit/components-styles";
6
6
  import { Loader2 as W, MicOff as Ie, Mic as Ce, Power as Me, Activity as Ee } from "lucide-react";
7
7
  const Le = 3e4, ze = 120, De = "wss://prod-project-pazuyq69.livekit.cloud", X = "https://api.zeroweight.ai", Ue = () => {
8
- const u = ["Happy", "Swift", "Bright", "Cool", "Smart"], a = ["User", "Guest", "Visitor", "Agent", "Caller"], t = Math.floor(Math.random() * 1e3);
9
- return `${u[Math.floor(Math.random() * u.length)]}${a[Math.floor(Math.random() * a.length)]}${t}`;
8
+ const u = ["Happy", "Swift", "Bright", "Cool", "Smart"], o = ["User", "Guest", "Visitor", "Agent", "Caller"], t = Math.floor(Math.random() * 1e3);
9
+ return `${u[Math.floor(Math.random() * u.length)]}${o[Math.floor(Math.random() * o.length)]}${t}`;
10
10
  };
11
11
  function $e(u) {
12
12
  const {
13
- avatarId: a,
13
+ avatarId: o,
14
14
  apiKey: t = null,
15
15
  api: f,
16
16
  livekitUrl: h = De,
17
17
  sessionDuration: l = ze,
18
18
  inactivityTimeout: b = Le
19
- } = u, E = h, p = ve(() => {
19
+ } = u, M = h, p = ve(() => {
20
20
  if (f) return f;
21
21
  const e = t ? { "X-ZW-Api-Key": t } : void 0;
22
22
  return {
23
23
  getBundle: async (c) => {
24
- const k = await fetch(
24
+ const A = await fetch(
25
25
  `${X}/avatars/bundle/${encodeURIComponent(
26
26
  c
27
27
  )}`,
28
28
  { headers: e }
29
29
  );
30
- if (!k.ok)
30
+ if (!A.ok)
31
31
  throw new Error(
32
- `Failed to fetch avatar bundle (${k.status} ${k.statusText})`
32
+ `Failed to fetch avatar bundle (${A.status} ${A.statusText})`
33
33
  );
34
- return k.json();
34
+ return A.json();
35
35
  },
36
- getLiveKitToken: async (c, k) => {
36
+ getLiveKitToken: async (c, A) => {
37
37
  const g = new URLSearchParams({
38
38
  avatar_id: c,
39
- name: k
40
- }), M = await fetch(
39
+ name: A
40
+ }), E = await fetch(
41
41
  `${X}/livekit/token?${g.toString()}`,
42
42
  { headers: e }
43
43
  );
44
- if (!M.ok)
44
+ if (!E.ok)
45
45
  throw new Error(
46
- `Failed to fetch LiveKit token (${M.status} ${M.statusText})`
46
+ `Failed to fetch LiveKit token (${E.status} ${E.statusText})`
47
47
  );
48
- return M.json();
48
+ return E.json();
49
49
  }
50
50
  };
51
- }, [f, t]), m = x(null), A = x(null), i = x(null), s = x(null), [I, o] = v("idle"), [r, d] = v(null), [L, U] = v(
51
+ }, [f, t]), m = x(null), k = x(null), i = x(null), s = x(null), [I, a] = v("idle"), [r, d] = v(null), [L, U] = v(
52
52
  /* @__PURE__ */ new Set(["listening"])
53
53
  ), [$, B] = v({
54
54
  listening: { kind: "looped" },
55
55
  speaking: { kind: "looped" }
56
56
  }), [te, O] = v(!1), [ne, H] = v(null), [P, j] = v(!1), [F, Q] = v(!1), [re, _] = v(l), R = x(null), K = x(() => {
57
- }), [ie, oe] = v(!1), [ae, q] = v(0.8), C = x(null), N = I === "ready", Z = S(() => {
57
+ }), [ie, ae] = v(!1), [oe, q] = v(0.8), C = x(null), N = I === "ready", Z = S(() => {
58
58
  const e = m.current;
59
59
  if (!e) return null;
60
60
  let c = e.querySelector("canvas");
61
- return c || (c = document.createElement("canvas"), c.style.width = "100%", c.style.height = "100%", c.style.display = "block", e.appendChild(c)), A.current = c, c;
61
+ return c || (c = document.createElement("canvas"), c.style.width = "100%", c.style.height = "100%", c.style.display = "block", e.appendChild(c)), k.current = c, c;
62
62
  }, []);
63
63
  w(() => {
64
64
  let e = !1;
65
65
  return (async () => {
66
- const k = Z();
67
- if (!k) return;
66
+ const A = Z();
67
+ if (!A) return;
68
68
  const g = new ye();
69
69
  i.current = g;
70
- const M = new be((T, z) => {
70
+ const E = new be((T, z) => {
71
71
  g.play(T, z);
72
72
  });
73
- s.current = M, g.on("stateChanged", (T) => {
74
- e || o(T);
73
+ s.current = E, g.on("stateChanged", (T) => {
74
+ e || a(T);
75
75
  }), g.on("dimensions", (T, z) => {
76
76
  e || d({ width: T, height: z });
77
77
  }), g.on("actionLoaded", (T) => {
@@ -82,20 +82,20 @@ function $e(u) {
82
82
  }), g.on("allActionsLoaded", () => {
83
83
  e || O(!1);
84
84
  }), g.on("ready", () => {
85
- e || (B(g.getActionMetadata()), M.setActionMetadata(g.getActionMetadata()));
85
+ e || (B(g.getActionMetadata()), E.setActionMetadata(g.getActionMetadata()));
86
86
  });
87
87
  try {
88
88
  O(!0);
89
- const T = await p.getBundle(a);
90
- if (e || (await g.init(k, { payload: T.payload }), e)) return;
91
- B(g.getActionMetadata()), M.setActionMetadata(g.getActionMetadata());
89
+ const T = await p.getBundle(o);
90
+ if (e || (await g.init(A, { payload: T.payload }), e)) return;
91
+ B(g.getActionMetadata()), E.setActionMetadata(g.getActionMetadata());
92
92
  } catch (T) {
93
93
  console.error("[useAvatarSession] Init failed:", T);
94
94
  }
95
95
  })(), () => {
96
96
  e = !0, i.current?.destroy(), i.current = null, s.current = null;
97
97
  };
98
- }, [a, Z, p]);
98
+ }, [o, Z, p]);
99
99
  const G = x(!1);
100
100
  w(() => {
101
101
  N && i.current && L.has("wave_hand") && !G.current && (i.current.play("wave_hand", "listening"), G.current = !0);
@@ -105,13 +105,13 @@ function $e(u) {
105
105
  j(!0);
106
106
  try {
107
107
  await navigator.mediaDevices.getUserMedia({ audio: !0 });
108
- const e = Ue(), c = await p.getLiveKitToken(a, e);
108
+ const e = Ue(), c = await p.getLiveKitToken(o, e);
109
109
  H(c.token);
110
110
  } catch (e) {
111
111
  console.error("[useAvatarSession] Failed to connect:", e), j(!1);
112
112
  }
113
113
  }
114
- }, [P, F, a, p]), V = S(() => {
114
+ }, [P, F, o, p]), V = S(() => {
115
115
  R.current && (clearInterval(R.current), R.current = null), _(l), C.current && (clearTimeout(C.current), C.current = null), H(null), Q(!1), j(!1), s.current?.forceListening();
116
116
  }, [l]);
117
117
  w(() => {
@@ -124,8 +124,8 @@ function $e(u) {
124
124
  _((e) => e <= 1 ? (clearInterval(R.current), R.current = null, setTimeout(() => K.current(), 0), 0) : e - 1);
125
125
  }, 1e3);
126
126
  }, [l]), de = S((e) => {
127
- const c = Math.floor(e / 60).toString().padStart(2, "0"), k = (e % 60).toString().padStart(2, "0");
128
- return `${c}:${k}`;
127
+ const c = Math.floor(e / 60).toString().padStart(2, "0"), A = (e % 60).toString().padStart(2, "0");
128
+ return `${c}:${A}`;
129
129
  }, []);
130
130
  w(() => () => {
131
131
  R.current && clearInterval(R.current);
@@ -142,7 +142,7 @@ function $e(u) {
142
142
  },
143
143
  [b]
144
144
  ), pe = S(() => {
145
- oe((e) => !e);
145
+ ae((e) => !e);
146
146
  }, []), fe = S((e) => {
147
147
  q(e);
148
148
  }, []), ge = S(() => {
@@ -165,11 +165,11 @@ function $e(u) {
165
165
  token: ne,
166
166
  isConnecting: P,
167
167
  isConnected: F,
168
- livekitUrl: E,
168
+ livekitUrl: M,
169
169
  timeRemaining: re,
170
170
  formatTime: de,
171
171
  micMuted: ie,
172
- volume: ae,
172
+ volume: oe,
173
173
  connect: se,
174
174
  disconnect: V,
175
175
  toggleMic: pe,
@@ -184,7 +184,7 @@ function $e(u) {
184
184
  }
185
185
  const je = ({
186
186
  session: u,
187
- style: a,
187
+ style: o,
188
188
  loadingContent: t
189
189
  }) => /* @__PURE__ */ y(
190
190
  "div",
@@ -200,7 +200,7 @@ const je = ({
200
200
  justifyContent: "center",
201
201
  alignItems: "center",
202
202
  overflow: "hidden",
203
- ...a
203
+ ...o
204
204
  },
205
205
  children: [
206
206
  /* @__PURE__ */ n("style", { children: `
@@ -307,7 +307,7 @@ const je = ({
307
307
  cursor: "pointer"
308
308
  }, Pe = ({
309
309
  session: u,
310
- style: a
310
+ style: o
311
311
  }) => {
312
312
  const {
313
313
  micMuted: t,
@@ -315,18 +315,18 @@ const je = ({
315
315
  isConnected: h,
316
316
  isConnecting: l,
317
317
  isEngineReady: b,
318
- isLoadingActions: E,
318
+ loadedActions: M,
319
319
  connect: p,
320
320
  disconnect: m
321
- } = u, [A, i] = v(!1), [s, I] = v(!1), o = b && !E, r = t ? {
321
+ } = u, [k, i] = v(!1), [s, I] = v(!1), a = b && M.has("listening") && M.has("speaking"), r = t ? {
322
322
  ...Y,
323
323
  background: "rgba(239,68,68,0.1)",
324
324
  color: "#f87171",
325
325
  boxShadow: "inset 0 0 0 1px rgba(239,68,68,0.3)",
326
- ...A ? { background: "rgba(239,68,68,0.2)" } : {}
326
+ ...k ? { background: "rgba(239,68,68,0.2)" } : {}
327
327
  } : {
328
328
  ...Y,
329
- background: A ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.4)",
329
+ background: k ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.4)",
330
330
  color: "#fff",
331
331
  boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.1)"
332
332
  }, d = h ? {
@@ -339,7 +339,7 @@ const je = ({
339
339
  boxShadow: "0 0 30px rgba(236,72,153,0.3)",
340
340
  opacity: s ? 0.9 : 1
341
341
  };
342
- return (l || !o) && (d.opacity = 0.5, d.cursor = "not-allowed"), /* @__PURE__ */ y(
342
+ return (l || !a) && (d.opacity = 0.5, d.cursor = "not-allowed"), /* @__PURE__ */ y(
343
343
  "div",
344
344
  {
345
345
  style: {
@@ -350,7 +350,7 @@ const je = ({
350
350
  alignItems: "center",
351
351
  gap: 16,
352
352
  paddingBottom: 16,
353
- ...a
353
+ ...o
354
354
  },
355
355
  children: [
356
356
  /* @__PURE__ */ n("style", { children: `
@@ -391,12 +391,12 @@ const je = ({
391
391
  onClick: h ? m : p,
392
392
  onMouseEnter: () => I(!0),
393
393
  onMouseLeave: () => I(!1),
394
- disabled: !h && (l || !o),
394
+ disabled: !h && (l || !a),
395
395
  style: d,
396
396
  children: l ? /* @__PURE__ */ y(D, { children: [
397
397
  /* @__PURE__ */ n(W, { size: 20, style: { animation: "zwr-spin 1s linear infinite" } }),
398
398
  /* @__PURE__ */ n("span", { children: "Connecting..." })
399
- ] }) : !h && !o ? /* @__PURE__ */ y(D, { children: [
399
+ ] }) : !h && !a ? /* @__PURE__ */ y(D, { children: [
400
400
  /* @__PURE__ */ n(W, { size: 20, style: { animation: "zwr-spin 1s linear infinite" } }),
401
401
  /* @__PURE__ */ n("span", { children: "Loading Avatar..." })
402
402
  ] }) : h ? /* @__PURE__ */ y(D, { children: [
@@ -417,7 +417,7 @@ const je = ({
417
417
  }, Fe = ({
418
418
  session: u
419
419
  }) => {
420
- const { isConnected: a, timeRemaining: t, formatTime: f } = u, h = a && t <= 30, l = a && t > 30 && t <= 60, b = {
420
+ const { isConnected: o, timeRemaining: t, formatTime: f } = u, h = o && t <= 30, l = o && t > 30 && t <= 60, b = {
421
421
  display: "flex",
422
422
  alignItems: "center",
423
423
  gap: 8,
@@ -457,7 +457,7 @@ const je = ({
457
457
  height: 8,
458
458
  width: 8,
459
459
  borderRadius: "50%",
460
- ...a ? {
460
+ ...o ? {
461
461
  background: "#22c55e",
462
462
  boxShadow: "0 0 10px rgba(34,197,94,0.5)"
463
463
  } : {
@@ -476,7 +476,7 @@ const je = ({
476
476
  color: "rgba(255,255,255,0.7)",
477
477
  textTransform: "uppercase"
478
478
  },
479
- children: a ? `Online ${f(t)}` : "Offline"
479
+ children: o ? `Online ${f(t)}` : "Offline"
480
480
  }
481
481
  )
482
482
  ] })
@@ -484,7 +484,7 @@ const je = ({
484
484
  }, _e = ({
485
485
  session: u
486
486
  }) => {
487
- const { renderer: a, actionQueue: t, micMuted: f, volume: h, setInactivityActive: l, loadedActions: b } = u, E = x(!1), { state: p, audioTrack: m } = xe(), A = we(), i = Ae(A.localParticipant), s = x(null), I = x(b);
487
+ const { renderer: o, actionQueue: t, micMuted: f, volume: h, setInactivityActive: l, loadedActions: b } = u, M = x(!1), { state: p, audioTrack: m } = xe(), k = we(), i = ke(k.localParticipant), s = x(null), I = x(b);
488
488
  w(() => {
489
489
  I.current = b;
490
490
  }, [b]), w(() => {
@@ -512,16 +512,16 @@ const je = ({
512
512
  } else
513
513
  r.stop();
514
514
  }, [m?.publication?.track]);
515
- const o = x(null);
515
+ const a = x(null);
516
516
  return w(() => {
517
517
  if (!t) return;
518
518
  const r = p;
519
- r === "speaking" ? (o.current && (clearTimeout(o.current), o.current = null), t.setTurnActive(!0), t.setSpeechState("speaking")) : r === "listening" || r === "idle" ? (o.current && (clearTimeout(o.current), o.current = null), s.current?.endTurn(), t.setTurnActive(!1)) : r === "thinking" && (o.current || (o.current = setTimeout(() => {
520
- o.current = null, s.current?.endTurn(), t.setTurnActive(!1);
519
+ r === "speaking" ? (a.current && (clearTimeout(a.current), a.current = null), t.setTurnActive(!0), t.setSpeechState("speaking")) : r === "listening" || r === "idle" ? (a.current && (clearTimeout(a.current), a.current = null), s.current?.endTurn(), t.setTurnActive(!1)) : r === "thinking" && (a.current || (a.current = setTimeout(() => {
520
+ a.current = null, s.current?.endTurn(), t.setTurnActive(!1);
521
521
  }, 500)));
522
522
  }, [p, t]), w(() => () => {
523
- o.current && clearTimeout(o.current);
524
- }, []), ke((r) => {
523
+ a.current && clearTimeout(a.current);
524
+ }, []), Ae((r) => {
525
525
  try {
526
526
  const L = new TextDecoder().decode(r.payload), U = JSON.parse(L);
527
527
  if (U.type === "AVATAR_UPDATE") {
@@ -538,29 +538,29 @@ const je = ({
538
538
  }, [i, l, p]), w(() => {
539
539
  if (!t) return;
540
540
  const r = !!m;
541
- (!r || p === "disconnected") && (s.current?.endTurn(), t.forceListening()), E.current = r;
541
+ (!r || p === "disconnected") && (s.current?.endTurn(), t.forceListening()), M.current = r;
542
542
  }, [m, p, t]), w(() => {
543
- const r = A.localParticipant;
543
+ const r = k.localParticipant;
544
544
  r && r.setMicrophoneEnabled(!f).catch((d) => {
545
545
  console.error("[LiveKitAvatarProvider] Failed to set mic state:", d);
546
546
  });
547
- }, [f, A.localParticipant]), /* @__PURE__ */ n("div", { style: { position: "absolute", bottom: 80, left: 8, right: 8, display: "flex", flexDirection: "column", gap: 8 }, children: /* @__PURE__ */ n(Te, { volume: h }) });
547
+ }, [f, k.localParticipant]), /* @__PURE__ */ n("div", { style: { position: "absolute", bottom: 80, left: 8, right: 8, display: "flex", flexDirection: "column", gap: 8 }, children: /* @__PURE__ */ n(Te, { volume: h }) });
548
548
  }, He = ({
549
549
  avatarId: u,
550
- apiKey: a,
550
+ apiKey: o,
551
551
  api: t,
552
552
  livekitUrl: f,
553
553
  sessionDuration: h,
554
554
  inactivityTimeout: l,
555
555
  style: b,
556
- className: E,
556
+ className: M,
557
557
  loadingContent: p,
558
558
  customControls: m,
559
- customStatusBadge: A
559
+ customStatusBadge: k
560
560
  }) => {
561
561
  const i = $e({
562
562
  avatarId: u,
563
- apiKey: a,
563
+ apiKey: o,
564
564
  api: t,
565
565
  livekitUrl: f,
566
566
  sessionDuration: h,
@@ -568,14 +568,14 @@ const je = ({
568
568
  }), {
569
569
  token: s,
570
570
  isConnected: I,
571
- avatarDimensions: o,
571
+ avatarDimensions: a,
572
572
  disconnect: r,
573
573
  startSessionTimer: d
574
574
  } = i;
575
575
  return /* @__PURE__ */ y(
576
576
  "section",
577
577
  {
578
- className: E,
578
+ className: M,
579
579
  style: {
580
580
  position: "relative",
581
581
  display: "flex",
@@ -589,7 +589,7 @@ const je = ({
589
589
  height: "80vh",
590
590
  width: "auto",
591
591
  maxWidth: "100%",
592
- aspectRatio: o ? `${o.width} / ${o.height}` : "3 / 4",
592
+ aspectRatio: a ? `${a.width} / ${a.height}` : "3 / 4",
593
593
  ...b
594
594
  },
595
595
  children: [
@@ -618,7 +618,7 @@ const je = ({
618
618
  justifyContent: "space-between"
619
619
  },
620
620
  children: [
621
- A ? A(i) : /* @__PURE__ */ n(Fe, { session: i }),
621
+ k ? k(i) : /* @__PURE__ */ n(Fe, { session: i }),
622
622
  /* @__PURE__ */ n("div", {})
623
623
  ]
624
624
  }
@@ -1 +1 @@
1
- {"version":3,"file":"zeroweight-renderer-react.es.js","sources":["../src/useAvatarSession.ts","../src/AvatarCanvas.tsx","../src/AvatarControls.tsx","../src/AvatarStatusBadge.tsx","../src/LiveKitAvatarProvider.tsx","../src/LiveKitAvatarSession.tsx"],"sourcesContent":["/**\n * useAvatarSession — Main React hook for avatar rendering.\n *\n * Wraps ZeroWeightRenderer + ActionQueue in React lifecycle.\n * Returns reactive state and imperative methods.\n *\n * Usage:\n * const session = useAvatarSession({\n * avatarId: 'abc123',\n * apiKey: 'optional-api-key',\n * });\n */\n\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ZeroWeightRenderer, ActionQueue } from \"@zeroweight/renderer\";\nimport type { ActionMetadata, RendererState } from \"@zeroweight/renderer\";\nimport type { ZeroWeightApi } from \"./types\";\n\nconst INACTIVITY_TIMEOUT_MS = 30000;\nconst SESSION_DURATION_SECONDS = 120;\nconst DEFAULT_LIVEKIT_URL = \"wss://prod-project-pazuyq69.livekit.cloud\";\nconst DEFAULT_API_BASE_URL = \"https://api.zeroweight.ai\";\n\nconst generateRandomName = () => {\n const adjectives = [\"Happy\", \"Swift\", \"Bright\", \"Cool\", \"Smart\"];\n const nouns = [\"User\", \"Guest\", \"Visitor\", \"Agent\", \"Caller\"];\n const randomNum = Math.floor(Math.random() * 1000);\n return `${adjectives[Math.floor(Math.random() * adjectives.length)]}${\n nouns[Math.floor(Math.random() * nouns.length)]\n }${randomNum}`;\n};\n\nexport interface AvatarSessionConfig {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL. */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n}\n\nexport interface AvatarSessionReturn {\n // Refs\n containerRef: React.RefObject<HTMLDivElement | null>;\n\n // Renderer instance (for advanced use)\n renderer: ZeroWeightRenderer | null;\n actionQueue: ActionQueue | null;\n\n // Reactive state\n rendererState: RendererState;\n avatarDimensions: { width: number; height: number } | null;\n loadedActions: Set<string>;\n actionMetadata: Record<string, ActionMetadata>;\n isLoadingActions: boolean;\n isEngineReady: boolean;\n\n // Connection state\n token: string | null;\n isConnecting: boolean;\n isConnected: boolean;\n livekitUrl: string;\n\n // Session\n timeRemaining: number;\n formatTime: (seconds: number) => string;\n\n // Controls\n micMuted: boolean;\n volume: number;\n\n // Methods\n connect: () => Promise<void>;\n disconnect: () => void;\n toggleMic: () => void;\n setVolume: (v: number) => void;\n toggleVolume: () => void;\n interrupt: () => void;\n runAction: (actionId: string) => void;\n startSessionTimer: () => void;\n /** Called by LiveKitRoom onConnected */\n markConnected: () => void;\n\n // For LiveKitAvatarProvider internal use\n setInactivityActive: (isInactive: boolean) => void;\n}\n\nexport function useAvatarSession(config: AvatarSessionConfig): AvatarSessionReturn {\n const {\n avatarId,\n apiKey = null,\n api,\n livekitUrl: livekitUrlProp = DEFAULT_LIVEKIT_URL,\n sessionDuration = SESSION_DURATION_SECONDS,\n inactivityTimeout = INACTIVITY_TIMEOUT_MS,\n } = config;\n\n const livekitUrl = livekitUrlProp;\n const resolvedApi = useMemo<ZeroWeightApi>(() => {\n if (api) return api;\n\n const headers = apiKey ? { \"X-ZW-Api-Key\": apiKey } : undefined;\n\n return {\n getBundle: async (resolvedAvatarId: string) => {\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/avatars/bundle/${encodeURIComponent(\n resolvedAvatarId\n )}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch avatar bundle (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n getLiveKitToken: async (resolvedAvatarId: string, userName: string) => {\n const params = new URLSearchParams({\n avatar_id: resolvedAvatarId,\n name: userName,\n });\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/livekit/token?${params.toString()}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch LiveKit token (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n };\n }, [api, apiKey]);\n\n // Refs\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rendererRef = useRef<ZeroWeightRenderer | null>(null);\n const actionQueueRef = useRef<ActionQueue | null>(null);\n\n // Engine state\n const [rendererState, setRendererState] = useState<RendererState>(\"idle\");\n const [avatarDimensions, setAvatarDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const [loadedActions, setLoadedActions] = useState<Set<string>>(\n new Set([\"listening\"])\n );\n const [actionMetadata, setActionMetadata] = useState<\n Record<string, ActionMetadata>\n >({\n listening: { kind: \"looped\" },\n speaking: { kind: \"looped\" },\n });\n const [isLoadingActions, setIsLoadingActions] = useState(false);\n\n // Connection state\n const [token, setToken] = useState<string | null>(null);\n const [isConnecting, setIsConnecting] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n\n // Session timer\n const [timeRemaining, setTimeRemaining] = useState(sessionDuration);\n const sessionTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const handleDisconnectRef = useRef<() => void>(() => {});\n\n // Controls\n const [micMuted, setMicMuted] = useState(false);\n const [volume, setVolumeState] = useState(0.8);\n\n // Inactivity\n const inactivityTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isEngineReady = rendererState === \"ready\";\n\n // ─── Ensure Canvas ──────────────────────────────────────────────\n\n const ensureCanvas = useCallback(() => {\n const container = containerRef.current;\n if (!container) return null;\n\n let canvas = container.querySelector(\"canvas\") as HTMLCanvasElement | null;\n if (!canvas) {\n canvas = document.createElement(\"canvas\");\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n container.appendChild(canvas);\n }\n canvasRef.current = canvas;\n return canvas;\n }, []);\n\n // ─── Init Renderer ──────────────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n const initRenderer = async () => {\n const canvas = ensureCanvas();\n if (!canvas) return;\n\n // Create renderer\n const renderer = new ZeroWeightRenderer();\n rendererRef.current = renderer;\n\n // Create action queue\n const queue = new ActionQueue((actionId, fallback) => {\n renderer.play(actionId, fallback);\n });\n actionQueueRef.current = queue;\n\n // Wire up renderer events\n renderer.on(\"stateChanged\", (state) => {\n if (!cancelled) setRendererState(state);\n });\n\n renderer.on(\"dimensions\", (w, h) => {\n if (!cancelled) setAvatarDimensions({ width: w, height: h });\n });\n\n renderer.on(\"actionLoaded\", (actionId) => {\n if (!cancelled) {\n setLoadedActions((prev) => {\n const next = new Set(prev);\n next.add(actionId);\n return next;\n });\n }\n });\n\n renderer.on(\"allActionsLoaded\", () => {\n if (!cancelled) setIsLoadingActions(false);\n });\n\n renderer.on(\"ready\", () => {\n // Update action metadata from renderer\n if (!cancelled) {\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n }\n });\n\n // Fetch bundle and init\n try {\n setIsLoadingActions(true);\n const data = await resolvedApi.getBundle(avatarId);\n if (cancelled) return;\n\n await renderer.init(canvas, { payload: data.payload });\n if (cancelled) return;\n\n // After init, update metadata again with all loaded data\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n } catch (e) {\n console.error(\"[useAvatarSession] Init failed:\", e);\n }\n };\n\n initRenderer();\n\n return () => {\n cancelled = true;\n rendererRef.current?.destroy();\n rendererRef.current = null;\n actionQueueRef.current = null;\n };\n }, [avatarId, ensureCanvas, resolvedApi]);\n\n // ─── Auto-wave on load ──────────────────────────────────────────\n\n const hasWavedRef = useRef(false);\n useEffect(() => {\n if (\n isEngineReady &&\n rendererRef.current &&\n loadedActions.has(\"wave_hand\") &&\n !hasWavedRef.current\n ) {\n rendererRef.current.play(\"wave_hand\", \"listening\");\n hasWavedRef.current = true;\n }\n }, [isEngineReady, loadedActions]);\n\n // ─── Connection ─────────────────────────────────────────────────\n\n const connect = useCallback(async () => {\n if (isConnecting || isConnected) return;\n\n setIsConnecting(true);\n try {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n const userName = generateRandomName();\n const data = await resolvedApi.getLiveKitToken(avatarId, userName);\n setToken(data.token);\n } catch (error) {\n console.error(\"[useAvatarSession] Failed to connect:\", error);\n setIsConnecting(false);\n }\n }, [isConnecting, isConnected, avatarId, resolvedApi]);\n\n const disconnect = useCallback(() => {\n // Clear session timer\n if (sessionTimerRef.current) {\n clearInterval(sessionTimerRef.current);\n sessionTimerRef.current = null;\n }\n setTimeRemaining(sessionDuration);\n\n // Clear inactivity timer\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n\n setToken(null);\n setIsConnected(false);\n setIsConnecting(false);\n\n // Reset avatar to listening\n actionQueueRef.current?.forceListening();\n }, [sessionDuration]);\n\n // Keep disconnect ref current for timer\n useEffect(() => {\n handleDisconnectRef.current = disconnect;\n }, [disconnect]);\n\n const markConnected = useCallback(() => {\n setIsConnected(true);\n setIsConnecting(false);\n }, []);\n\n // ─── Session Timer ──────────────────────────────────────────────\n\n const startSessionTimer = useCallback(() => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n setTimeRemaining(sessionDuration);\n\n sessionTimerRef.current = setInterval(() => {\n setTimeRemaining((prev) => {\n if (prev <= 1) {\n clearInterval(sessionTimerRef.current!);\n sessionTimerRef.current = null;\n setTimeout(() => handleDisconnectRef.current(), 0);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n }, [sessionDuration]);\n\n const formatTime = useCallback((seconds: number) => {\n const m = Math.floor(seconds / 60)\n .toString()\n .padStart(2, \"0\");\n const s = (seconds % 60).toString().padStart(2, \"0\");\n return `${m}:${s}`;\n }, []);\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n };\n }, []);\n\n // ─── Inactivity ─────────────────────────────────────────────────\n\n const setInactivityActive = useCallback(\n (isInactive: boolean) => {\n if (!isInactive) {\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n return;\n }\n\n if (inactivityTimeoutRef.current) return;\n\n inactivityTimeoutRef.current = setTimeout(() => {\n inactivityTimeoutRef.current = null;\n handleDisconnectRef.current();\n }, inactivityTimeout);\n },\n [inactivityTimeout]\n );\n\n // ─── Controls ───────────────────────────────────────────────────\n\n const toggleMic = useCallback(() => {\n setMicMuted((v) => !v);\n }, []);\n\n const setVolume = useCallback((v: number) => {\n setVolumeState(v);\n }, []);\n\n const toggleVolume = useCallback(() => {\n setVolumeState((v) => (v > 0 ? 0 : 1));\n }, []);\n\n const interrupt = useCallback(() => {\n rendererRef.current?.interrupt();\n }, []);\n\n const runAction = useCallback((actionId: string) => {\n if (actionQueueRef.current) {\n actionQueueRef.current.dispatch(actionId);\n } else {\n rendererRef.current?.play(actionId, \"listening\");\n }\n }, []);\n\n return {\n containerRef,\n renderer: rendererRef.current,\n actionQueue: actionQueueRef.current,\n rendererState,\n avatarDimensions,\n loadedActions,\n actionMetadata,\n isLoadingActions,\n isEngineReady,\n token,\n isConnecting,\n isConnected,\n livekitUrl,\n timeRemaining,\n formatTime,\n micMuted,\n volume,\n connect,\n disconnect,\n toggleMic,\n setVolume,\n toggleVolume,\n interrupt,\n runAction,\n startSessionTimer,\n markConnected,\n setInactivityActive,\n };\n}\n","/**\n * AvatarCanvas — Canvas container for the avatar renderer.\n *\n * Provides the div container where the renderer creates and manages a <canvas>.\n * Shows loading overlay when the engine is initializing.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarCanvasProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the outer container. */\n style?: React.CSSProperties;\n /** Custom loading component. */\n loadingContent?: React.ReactNode;\n}\n\nexport const AvatarCanvas: React.FC<AvatarCanvasProps> = ({\n session,\n style,\n loadingContent,\n}) => {\n return (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: 0,\n pointerEvents: \"auto\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div\n ref={session.containerRef}\n style={{\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n transition: \"all 0.5s ease-in-out\",\n }}\n >\n {/* Canvas is injected here by the renderer */}\n {!session.isEngineReady && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"rgba(0,0,0,0.4)\",\n backdropFilter: \"blur(4px)\",\n zIndex: 10,\n }}\n >\n {loadingContent || (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n }}\n >\n <Loader2\n style={{\n width: 40,\n height: 40,\n color: \"rgba(255,255,255,0.5)\",\n animation: \"zwr-spin 1s linear infinite\",\n }}\n />\n <div\n style={{\n fontSize: 14,\n color: \"rgba(255,255,255,0.5)\",\n fontWeight: 500,\n letterSpacing: \"0.1em\",\n textTransform: \"uppercase\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }}\n >\n Initializing Neural Engine...\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n","/**\n * AvatarControls — Default control bar for the avatar session.\n *\n * Mic toggle, connect/disconnect button.\n * Can be replaced entirely by a custom UI.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React, { useState } from \"react\";\nimport {\n Mic,\n MicOff,\n Power,\n Activity,\n Loader2,\n} from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarControlsProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the wrapper. */\n style?: React.CSSProperties;\n}\n\nconst btnBase: React.CSSProperties = {\n flexShrink: 0,\n borderRadius: 9999,\n padding: 16,\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst connectBtnBase: React.CSSProperties = {\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n borderRadius: 9999,\n padding: \"16px 24px\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n};\n\nexport const AvatarControls: React.FC<AvatarControlsProps> = ({\n session,\n style,\n}) => {\n const {\n micMuted,\n toggleMic,\n isConnected,\n isConnecting,\n isEngineReady,\n isLoadingActions,\n connect,\n disconnect,\n } = session;\n\n const [micHover, setMicHover] = useState(false);\n const [connectHover, setConnectHover] = useState(false);\n\n const isSessionReady = isEngineReady && !isLoadingActions;\n\n const micStyle: React.CSSProperties = micMuted\n ? {\n ...btnBase,\n background: \"rgba(239,68,68,0.1)\",\n color: \"#f87171\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.3)\",\n ...(micHover ? { background: \"rgba(239,68,68,0.2)\" } : {}),\n }\n : {\n ...btnBase,\n background: micHover ? \"rgba(255,255,255,0.1)\" : \"rgba(0,0,0,0.4)\",\n color: \"#fff\",\n boxShadow: \"inset 0 0 0 1px rgba(255,255,255,0.1)\",\n };\n\n const connectStyle: React.CSSProperties = isConnected\n ? {\n ...connectBtnBase,\n background: connectHover ? \"rgba(239,68,68,0.22)\" : \"rgba(239,68,68,0.14)\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)\",\n }\n : {\n ...connectBtnBase,\n background: \"linear-gradient(to right, #7c3aed, #db2777, #f97316)\",\n boxShadow: \"0 0 30px rgba(236,72,153,0.3)\",\n opacity: connectHover ? 0.9 : 1,\n };\n\n if (isConnecting || !isSessionReady) {\n connectStyle.opacity = 0.5;\n connectStyle.cursor = \"not-allowed\";\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n marginTop: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n paddingBottom: 16,\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}</style>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 16,\n width: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Mic Toggle */}\n <button\n type=\"button\"\n onClick={toggleMic}\n onMouseEnter={() => setMicHover(true)}\n onMouseLeave={() => setMicHover(false)}\n style={micStyle}\n title={micMuted ? \"Unmute mic\" : \"Mute mic\"}\n >\n {micMuted ? <MicOff size={24} /> : <Mic size={24} />}\n </button>\n\n {/* Main Connect Button */}\n <button\n type=\"button\"\n onClick={isConnected ? disconnect : connect}\n onMouseEnter={() => setConnectHover(true)}\n onMouseLeave={() => setConnectHover(false)}\n disabled={!isConnected && (isConnecting || !isSessionReady)}\n style={connectStyle}\n >\n {isConnecting ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Connecting...</span>\n </>\n ) : !isConnected && !isSessionReady ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Loading Avatar...</span>\n </>\n ) : isConnected ? (\n <>\n <Power size={20} />\n <span>End Session</span>\n </>\n ) : (\n <>\n <Activity size={20} />\n <span>Start Session</span>\n </>\n )}\n </button>\n </div>\n </div>\n );\n};\n","/**\n * AvatarStatusBadge — Connection status indicator with session timer.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarStatusBadgeProps {\n session: AvatarSessionReturn;\n}\n\nexport const AvatarStatusBadge: React.FC<AvatarStatusBadgeProps> = ({\n session,\n}) => {\n const { isConnected, timeRemaining, formatTime } = session;\n\n const isUrgent = isConnected && timeRemaining <= 30;\n const isWarning = isConnected && timeRemaining > 30 && timeRemaining <= 60;\n\n const wrapperStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n backdropFilter: \"blur(12px)\",\n padding: \"6px 12px\",\n borderRadius: 9999,\n border: \"1px solid\",\n pointerEvents: \"auto\",\n transition: \"all 0.3s\",\n ...(isUrgent\n ? {\n background: \"rgba(239,68,68,0.3)\",\n borderColor: \"rgba(239,68,68,0.4)\",\n boxShadow: \"0 0 15px rgba(239,68,68,0.3)\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }\n : isWarning\n ? {\n background: \"rgba(249,115,22,0.2)\",\n borderColor: \"rgba(249,115,22,0.3)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }\n : {\n background: \"rgba(0,0,0,0.4)\",\n borderColor: \"rgba(255,255,255,0.1)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }),\n };\n\n return (\n <>\n <style>{`\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div style={wrapperStyle}>\n <div\n style={{\n height: 8,\n width: 8,\n borderRadius: \"50%\",\n ...(isConnected\n ? {\n background: \"#22c55e\",\n boxShadow: \"0 0 10px rgba(34,197,94,0.5)\",\n }\n : {\n background: \"rgba(239,68,68,0.5)\",\n }),\n }}\n />\n <span\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n color: \"rgba(255,255,255,0.7)\",\n textTransform: \"uppercase\",\n }}\n >\n {isConnected ? `Online ${formatTime(timeRemaining)}` : \"Offline\"}\n </span>\n </div>\n </>\n );\n};\n","/**\n * LiveKitAvatarProvider — Bridge between LiveKit hooks and the ActionQueue/VAD.\n * Uses inline styles only — no CSS framework dependency.\n *\n * This component MUST be rendered inside a <LiveKitRoom>.\n */\n\nimport React, { useEffect, useRef } from \"react\";\nimport {\n useVoiceAssistant,\n RoomAudioRenderer,\n useDataChannel,\n useIsSpeaking,\n useLocalParticipant,\n} from \"@livekit/components-react\";\nimport { VoiceActivityDetector } from \"@zeroweight/renderer\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface LiveKitAvatarProviderProps {\n session: AvatarSessionReturn;\n}\n\nexport const LiveKitAvatarProvider: React.FC<LiveKitAvatarProviderProps> = ({\n session,\n}) => {\n const { renderer, actionQueue, micMuted, volume, setInactivityActive, loadedActions } =\n session;\n\n const prevAudioTrackRef = useRef<boolean>(false);\n const { state, audioTrack } = useVoiceAssistant();\n const localParticipant = useLocalParticipant();\n const isUserSpeaking = useIsSpeaking(localParticipant.localParticipant);\n\n // VAD instance\n const vadRef = useRef<VoiceActivityDetector | null>(null);\n\n // Ref to latest loadedActions so data channel callback doesn't go stale\n const loadedActionsRef = useRef(loadedActions);\n useEffect(() => {\n loadedActionsRef.current = loadedActions;\n }, [loadedActions]);\n\n // ─── VAD Setup ──────────────────────────────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const vad = new VoiceActivityDetector({\n threshold: 0.008,\n analyseIntervalMs: 30,\n speechStartFrames: 1,\n speechPauseFrames: 30,\n turnEndFrames: 50,\n });\n vadRef.current = vad;\n\n vad.on(\"speechStart\", () => {\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n });\n\n vad.on(\"turnEnd\", () => {\n actionQueue.setTurnActive(false);\n });\n\n return () => {\n vad.stop();\n vadRef.current = null;\n };\n }, [actionQueue]);\n\n // ─── Connect/disconnect VAD to audio track ──────────────────────\n\n useEffect(() => {\n const vad = vadRef.current;\n if (!vad) return;\n\n if (audioTrack?.publication?.track) {\n const mediaTrack = audioTrack.publication.track.mediaStreamTrack;\n if (mediaTrack) {\n vad.start(mediaTrack);\n }\n } else {\n vad.stop();\n }\n }, [audioTrack?.publication?.track]);\n\n // ─── LiveKit state → turn management ─────────────────────────────\n\n const turnEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const stateStr = state as string;\n\n if (stateStr === \"speaking\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n } else if (stateStr === \"listening\" || stateStr === \"idle\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n } else if (stateStr === \"thinking\") {\n if (!turnEndTimerRef.current) {\n turnEndTimerRef.current = setTimeout(() => {\n turnEndTimerRef.current = null;\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n }, 500);\n }\n }\n }, [state, actionQueue]);\n\n // Cleanup turn-end timer on unmount\n useEffect(() => {\n return () => {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n }\n };\n }, []);\n\n // ─── Data channel (backend-triggered actions) ───────────────────\n\n useDataChannel((msg) => {\n try {\n const decoder = new TextDecoder();\n const strData = decoder.decode(msg.payload);\n const data = JSON.parse(strData);\n\n if (data.type === \"AVATAR_UPDATE\") {\n const actionId = data.action;\n if (!loadedActionsRef.current.has(actionId)) {\n return;\n }\n actionQueue?.dispatch(actionId);\n }\n } catch (err) {\n console.error(\"[LiveKitAvatarProvider] Failed to parse data message:\", err);\n }\n });\n\n // ─── Inactivity tracking: only count when both AI and user are silent ───\n\n useEffect(() => {\n const isAssistantSpeaking = state === \"speaking\";\n setInactivityActive(!isAssistantSpeaking && !isUserSpeaking);\n }, [isUserSpeaking, setInactivityActive, state]);\n\n // ─── Audio loss / disconnect fallback ───────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const hasAudio = !!audioTrack;\n const isDisconnectedState = state === \"disconnected\";\n\n if (!hasAudio || isDisconnectedState) {\n vadRef.current?.endTurn();\n actionQueue.forceListening();\n }\n prevAudioTrackRef.current = hasAudio;\n }, [audioTrack, state, actionQueue]);\n\n // ─── Mic mute sync ─────────────────────────────────────────────\n\n useEffect(() => {\n const participant = localParticipant.localParticipant;\n if (!participant) return;\n\n participant.setMicrophoneEnabled(!micMuted).catch((err) => {\n console.error(\"[LiveKitAvatarProvider] Failed to set mic state:\", err);\n });\n }, [micMuted, localParticipant.localParticipant]);\n\n return (\n <div style={{ position: \"absolute\", bottom: 80, left: 8, right: 8, display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <RoomAudioRenderer volume={volume} />\n </div>\n );\n};\n","/**\n * LiveKitAvatarSession — Full drop-in avatar component.\n * Uses inline styles only — no CSS framework dependency.\n *\n * Simplest usage:\n * import { LiveKitAvatarSession } from \"@zeroweight/react\";\n * <LiveKitAvatarSession\n * avatarId=\"abc123\"\n * apiKey=\"optional-api-key\"\n * />\n */\n\nimport React from \"react\";\nimport { LiveKitRoom } from \"@livekit/components-react\";\nimport \"@livekit/components-styles\";\n\nimport { useAvatarSession } from \"./useAvatarSession\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\nimport { AvatarCanvas } from \"./AvatarCanvas\";\nimport { AvatarControls } from \"./AvatarControls\";\nimport { AvatarStatusBadge } from \"./AvatarStatusBadge\";\nimport { LiveKitAvatarProvider } from \"./LiveKitAvatarProvider\";\nimport type { ZeroWeightApi } from \"./types\";\n\ninterface LiveKitAvatarSessionProps {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL (e.g. \"wss://your-livekit.example.com\"). */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n /** Optional style overrides for the outer section. */\n style?: React.CSSProperties;\n /** Optional class name overrides for the outer section. */\n className?: string;\n /** Custom loading UI for the canvas. */\n loadingContent?: React.ReactNode;\n /** Custom controls component. If provided, replaces the default controls. */\n customControls?: (session: AvatarSessionReturn) => React.ReactNode;\n /** Custom status badge. If provided, replaces the default badge. */\n customStatusBadge?: (session: AvatarSessionReturn) => React.ReactNode;\n}\n\nexport const LiveKitAvatarSession: React.FC<LiveKitAvatarSessionProps> = ({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n style,\n className,\n loadingContent,\n customControls,\n customStatusBadge,\n}) => {\n const session = useAvatarSession({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n });\n\n const {\n token,\n isConnected,\n avatarDimensions,\n disconnect,\n startSessionTimer,\n } = session;\n\n return (\n <section\n className={className}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n borderRadius: 16,\n border: \"1px solid rgba(255,255,255,0.1)\",\n boxShadow: \"0 25px 50px -12px rgba(0,0,0,0.5)\",\n height: \"80vh\",\n width: \"auto\",\n maxWidth: \"100%\",\n aspectRatio: avatarDimensions\n ? `${avatarDimensions.width} / ${avatarDimensions.height}`\n : \"3 / 4\",\n ...style,\n }}\n >\n {/* 1. Canvas Layer */}\n <AvatarCanvas session={session} loadingContent={loadingContent} />\n\n {/* 2. UI Overlay Layer */}\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n padding: 16,\n }}\n >\n {/* Top Header / Status */}\n <div\n style={{\n display: \"flex\",\n width: \"100%\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n }}\n >\n {customStatusBadge ? (\n customStatusBadge(session)\n ) : (\n <AvatarStatusBadge session={session} />\n )}\n <div />\n </div>\n\n {/* Bottom Controls */}\n {customControls ? (\n customControls(session)\n ) : (\n <AvatarControls session={session} />\n )}\n </div>\n\n {/* 3. LiveKit Room (hidden, audio-only) */}\n <div style={{ display: \"none\" }}>\n {token && livekitUrl && (\n <LiveKitRoom\n serverUrl={livekitUrl}\n token={token}\n connect={true}\n video={false}\n audio={true}\n onConnected={() => {\n session.markConnected();\n startSessionTimer();\n }}\n onDisconnected={disconnect}\n >\n <LiveKitAvatarProvider session={session} />\n </LiveKitRoom>\n )}\n </div>\n </section>\n );\n};\n"],"names":["INACTIVITY_TIMEOUT_MS","SESSION_DURATION_SECONDS","DEFAULT_LIVEKIT_URL","DEFAULT_API_BASE_URL","generateRandomName","adjectives","nouns","randomNum","useAvatarSession","config","avatarId","apiKey","api","livekitUrlProp","sessionDuration","inactivityTimeout","livekitUrl","resolvedApi","useMemo","headers","resolvedAvatarId","response","userName","params","containerRef","useRef","canvasRef","rendererRef","actionQueueRef","rendererState","setRendererState","useState","avatarDimensions","setAvatarDimensions","loadedActions","setLoadedActions","actionMetadata","setActionMetadata","isLoadingActions","setIsLoadingActions","token","setToken","isConnecting","setIsConnecting","isConnected","setIsConnected","timeRemaining","setTimeRemaining","sessionTimerRef","handleDisconnectRef","micMuted","setMicMuted","volume","setVolumeState","inactivityTimeoutRef","isEngineReady","ensureCanvas","useCallback","container","canvas","useEffect","cancelled","renderer","ZeroWeightRenderer","queue","ActionQueue","actionId","fallback","state","w","h","prev","next","data","e","hasWavedRef","connect","error","disconnect","markConnected","startSessionTimer","formatTime","seconds","m","s","setInactivityActive","isInactive","toggleMic","v","setVolume","toggleVolume","interrupt","runAction","AvatarCanvas","session","style","loadingContent","jsxs","jsx","Loader2","btnBase","connectBtnBase","AvatarControls","micHover","setMicHover","connectHover","setConnectHover","isSessionReady","micStyle","connectStyle","MicOff","Mic","Fragment","Power","Activity","AvatarStatusBadge","isUrgent","isWarning","wrapperStyle","LiveKitAvatarProvider","actionQueue","prevAudioTrackRef","audioTrack","useVoiceAssistant","localParticipant","useLocalParticipant","isUserSpeaking","useIsSpeaking","vadRef","loadedActionsRef","vad","VoiceActivityDetector","mediaTrack","turnEndTimerRef","stateStr","useDataChannel","msg","strData","err","hasAudio","participant","RoomAudioRenderer","LiveKitAvatarSession","className","customControls","customStatusBadge","LiveKitRoom"],"mappings":"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"}
1
+ {"version":3,"file":"zeroweight-renderer-react.es.js","sources":["../src/useAvatarSession.ts","../src/AvatarCanvas.tsx","../src/AvatarControls.tsx","../src/AvatarStatusBadge.tsx","../src/LiveKitAvatarProvider.tsx","../src/LiveKitAvatarSession.tsx"],"sourcesContent":["/**\n * useAvatarSession — Main React hook for avatar rendering.\n *\n * Wraps ZeroWeightRenderer + ActionQueue in React lifecycle.\n * Returns reactive state and imperative methods.\n *\n * Usage:\n * const session = useAvatarSession({\n * avatarId: 'abc123',\n * apiKey: 'optional-api-key',\n * });\n */\n\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ZeroWeightRenderer, ActionQueue } from \"@zeroweight/renderer\";\nimport type { ActionMetadata, RendererState } from \"@zeroweight/renderer\";\nimport type { ZeroWeightApi } from \"./types\";\n\nconst INACTIVITY_TIMEOUT_MS = 30000;\nconst SESSION_DURATION_SECONDS = 120;\nconst DEFAULT_LIVEKIT_URL = \"wss://prod-project-pazuyq69.livekit.cloud\";\nconst DEFAULT_API_BASE_URL = \"https://api.zeroweight.ai\";\n\nconst generateRandomName = () => {\n const adjectives = [\"Happy\", \"Swift\", \"Bright\", \"Cool\", \"Smart\"];\n const nouns = [\"User\", \"Guest\", \"Visitor\", \"Agent\", \"Caller\"];\n const randomNum = Math.floor(Math.random() * 1000);\n return `${adjectives[Math.floor(Math.random() * adjectives.length)]}${\n nouns[Math.floor(Math.random() * nouns.length)]\n }${randomNum}`;\n};\n\nexport interface AvatarSessionConfig {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL. */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n}\n\nexport interface AvatarSessionReturn {\n // Refs\n containerRef: React.RefObject<HTMLDivElement | null>;\n\n // Renderer instance (for advanced use)\n renderer: ZeroWeightRenderer | null;\n actionQueue: ActionQueue | null;\n\n // Reactive state\n rendererState: RendererState;\n avatarDimensions: { width: number; height: number } | null;\n loadedActions: Set<string>;\n actionMetadata: Record<string, ActionMetadata>;\n isLoadingActions: boolean;\n isEngineReady: boolean;\n\n // Connection state\n token: string | null;\n isConnecting: boolean;\n isConnected: boolean;\n livekitUrl: string;\n\n // Session\n timeRemaining: number;\n formatTime: (seconds: number) => string;\n\n // Controls\n micMuted: boolean;\n volume: number;\n\n // Methods\n connect: () => Promise<void>;\n disconnect: () => void;\n toggleMic: () => void;\n setVolume: (v: number) => void;\n toggleVolume: () => void;\n interrupt: () => void;\n runAction: (actionId: string) => void;\n startSessionTimer: () => void;\n /** Called by LiveKitRoom onConnected */\n markConnected: () => void;\n\n // For LiveKitAvatarProvider internal use\n setInactivityActive: (isInactive: boolean) => void;\n}\n\nexport function useAvatarSession(config: AvatarSessionConfig): AvatarSessionReturn {\n const {\n avatarId,\n apiKey = null,\n api,\n livekitUrl: livekitUrlProp = DEFAULT_LIVEKIT_URL,\n sessionDuration = SESSION_DURATION_SECONDS,\n inactivityTimeout = INACTIVITY_TIMEOUT_MS,\n } = config;\n\n const livekitUrl = livekitUrlProp;\n const resolvedApi = useMemo<ZeroWeightApi>(() => {\n if (api) return api;\n\n const headers = apiKey ? { \"X-ZW-Api-Key\": apiKey } : undefined;\n\n return {\n getBundle: async (resolvedAvatarId: string) => {\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/avatars/bundle/${encodeURIComponent(\n resolvedAvatarId\n )}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch avatar bundle (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n getLiveKitToken: async (resolvedAvatarId: string, userName: string) => {\n const params = new URLSearchParams({\n avatar_id: resolvedAvatarId,\n name: userName,\n });\n const response = await fetch(\n `${DEFAULT_API_BASE_URL}/livekit/token?${params.toString()}`,\n { headers }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch LiveKit token (${response.status} ${response.statusText})`\n );\n }\n\n return response.json();\n },\n };\n }, [api, apiKey]);\n\n // Refs\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rendererRef = useRef<ZeroWeightRenderer | null>(null);\n const actionQueueRef = useRef<ActionQueue | null>(null);\n\n // Engine state\n const [rendererState, setRendererState] = useState<RendererState>(\"idle\");\n const [avatarDimensions, setAvatarDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const [loadedActions, setLoadedActions] = useState<Set<string>>(\n new Set([\"listening\"])\n );\n const [actionMetadata, setActionMetadata] = useState<\n Record<string, ActionMetadata>\n >({\n listening: { kind: \"looped\" },\n speaking: { kind: \"looped\" },\n });\n const [isLoadingActions, setIsLoadingActions] = useState(false);\n\n // Connection state\n const [token, setToken] = useState<string | null>(null);\n const [isConnecting, setIsConnecting] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n\n // Session timer\n const [timeRemaining, setTimeRemaining] = useState(sessionDuration);\n const sessionTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const handleDisconnectRef = useRef<() => void>(() => {});\n\n // Controls\n const [micMuted, setMicMuted] = useState(false);\n const [volume, setVolumeState] = useState(0.8);\n\n // Inactivity\n const inactivityTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isEngineReady = rendererState === \"ready\";\n\n // ─── Ensure Canvas ──────────────────────────────────────────────\n\n const ensureCanvas = useCallback(() => {\n const container = containerRef.current;\n if (!container) return null;\n\n let canvas = container.querySelector(\"canvas\") as HTMLCanvasElement | null;\n if (!canvas) {\n canvas = document.createElement(\"canvas\");\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n container.appendChild(canvas);\n }\n canvasRef.current = canvas;\n return canvas;\n }, []);\n\n // ─── Init Renderer ──────────────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n const initRenderer = async () => {\n const canvas = ensureCanvas();\n if (!canvas) return;\n\n // Create renderer\n const renderer = new ZeroWeightRenderer();\n rendererRef.current = renderer;\n\n // Create action queue\n const queue = new ActionQueue((actionId, fallback) => {\n renderer.play(actionId, fallback);\n });\n actionQueueRef.current = queue;\n\n // Wire up renderer events\n renderer.on(\"stateChanged\", (state) => {\n if (!cancelled) setRendererState(state);\n });\n\n renderer.on(\"dimensions\", (w, h) => {\n if (!cancelled) setAvatarDimensions({ width: w, height: h });\n });\n\n renderer.on(\"actionLoaded\", (actionId) => {\n if (!cancelled) {\n setLoadedActions((prev) => {\n const next = new Set(prev);\n next.add(actionId);\n return next;\n });\n }\n });\n\n renderer.on(\"allActionsLoaded\", () => {\n if (!cancelled) setIsLoadingActions(false);\n });\n\n renderer.on(\"ready\", () => {\n // Update action metadata from renderer\n if (!cancelled) {\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n }\n });\n\n // Fetch bundle and init\n try {\n setIsLoadingActions(true);\n const data = await resolvedApi.getBundle(avatarId);\n if (cancelled) return;\n\n await renderer.init(canvas, { payload: data.payload });\n if (cancelled) return;\n\n // After init, update metadata again with all loaded data\n setActionMetadata(renderer.getActionMetadata());\n queue.setActionMetadata(renderer.getActionMetadata());\n } catch (e) {\n console.error(\"[useAvatarSession] Init failed:\", e);\n }\n };\n\n initRenderer();\n\n return () => {\n cancelled = true;\n rendererRef.current?.destroy();\n rendererRef.current = null;\n actionQueueRef.current = null;\n };\n }, [avatarId, ensureCanvas, resolvedApi]);\n\n // ─── Auto-wave on load ──────────────────────────────────────────\n\n const hasWavedRef = useRef(false);\n useEffect(() => {\n if (\n isEngineReady &&\n rendererRef.current &&\n loadedActions.has(\"wave_hand\") &&\n !hasWavedRef.current\n ) {\n rendererRef.current.play(\"wave_hand\", \"listening\");\n hasWavedRef.current = true;\n }\n }, [isEngineReady, loadedActions]);\n\n // ─── Connection ─────────────────────────────────────────────────\n\n const connect = useCallback(async () => {\n if (isConnecting || isConnected) return;\n\n setIsConnecting(true);\n try {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n const userName = generateRandomName();\n const data = await resolvedApi.getLiveKitToken(avatarId, userName);\n setToken(data.token);\n } catch (error) {\n console.error(\"[useAvatarSession] Failed to connect:\", error);\n setIsConnecting(false);\n }\n }, [isConnecting, isConnected, avatarId, resolvedApi]);\n\n const disconnect = useCallback(() => {\n // Clear session timer\n if (sessionTimerRef.current) {\n clearInterval(sessionTimerRef.current);\n sessionTimerRef.current = null;\n }\n setTimeRemaining(sessionDuration);\n\n // Clear inactivity timer\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n\n setToken(null);\n setIsConnected(false);\n setIsConnecting(false);\n\n // Reset avatar to listening\n actionQueueRef.current?.forceListening();\n }, [sessionDuration]);\n\n // Keep disconnect ref current for timer\n useEffect(() => {\n handleDisconnectRef.current = disconnect;\n }, [disconnect]);\n\n const markConnected = useCallback(() => {\n setIsConnected(true);\n setIsConnecting(false);\n }, []);\n\n // ─── Session Timer ──────────────────────────────────────────────\n\n const startSessionTimer = useCallback(() => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n setTimeRemaining(sessionDuration);\n\n sessionTimerRef.current = setInterval(() => {\n setTimeRemaining((prev) => {\n if (prev <= 1) {\n clearInterval(sessionTimerRef.current!);\n sessionTimerRef.current = null;\n setTimeout(() => handleDisconnectRef.current(), 0);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n }, [sessionDuration]);\n\n const formatTime = useCallback((seconds: number) => {\n const m = Math.floor(seconds / 60)\n .toString()\n .padStart(2, \"0\");\n const s = (seconds % 60).toString().padStart(2, \"0\");\n return `${m}:${s}`;\n }, []);\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (sessionTimerRef.current) clearInterval(sessionTimerRef.current);\n };\n }, []);\n\n // ─── Inactivity ─────────────────────────────────────────────────\n\n const setInactivityActive = useCallback(\n (isInactive: boolean) => {\n if (!isInactive) {\n if (inactivityTimeoutRef.current) {\n clearTimeout(inactivityTimeoutRef.current);\n inactivityTimeoutRef.current = null;\n }\n return;\n }\n\n if (inactivityTimeoutRef.current) return;\n\n inactivityTimeoutRef.current = setTimeout(() => {\n inactivityTimeoutRef.current = null;\n handleDisconnectRef.current();\n }, inactivityTimeout);\n },\n [inactivityTimeout]\n );\n\n // ─── Controls ───────────────────────────────────────────────────\n\n const toggleMic = useCallback(() => {\n setMicMuted((v) => !v);\n }, []);\n\n const setVolume = useCallback((v: number) => {\n setVolumeState(v);\n }, []);\n\n const toggleVolume = useCallback(() => {\n setVolumeState((v) => (v > 0 ? 0 : 1));\n }, []);\n\n const interrupt = useCallback(() => {\n rendererRef.current?.interrupt();\n }, []);\n\n const runAction = useCallback((actionId: string) => {\n if (actionQueueRef.current) {\n actionQueueRef.current.dispatch(actionId);\n } else {\n rendererRef.current?.play(actionId, \"listening\");\n }\n }, []);\n\n return {\n containerRef,\n renderer: rendererRef.current,\n actionQueue: actionQueueRef.current,\n rendererState,\n avatarDimensions,\n loadedActions,\n actionMetadata,\n isLoadingActions,\n isEngineReady,\n token,\n isConnecting,\n isConnected,\n livekitUrl,\n timeRemaining,\n formatTime,\n micMuted,\n volume,\n connect,\n disconnect,\n toggleMic,\n setVolume,\n toggleVolume,\n interrupt,\n runAction,\n startSessionTimer,\n markConnected,\n setInactivityActive,\n };\n}\n","/**\n * AvatarCanvas — Canvas container for the avatar renderer.\n *\n * Provides the div container where the renderer creates and manages a <canvas>.\n * Shows loading overlay when the engine is initializing.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarCanvasProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the outer container. */\n style?: React.CSSProperties;\n /** Custom loading component. */\n loadingContent?: React.ReactNode;\n}\n\nexport const AvatarCanvas: React.FC<AvatarCanvasProps> = ({\n session,\n style,\n loadingContent,\n}) => {\n return (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: 0,\n pointerEvents: \"auto\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div\n ref={session.containerRef}\n style={{\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n transition: \"all 0.5s ease-in-out\",\n }}\n >\n {/* Canvas is injected here by the renderer */}\n {!session.isEngineReady && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"rgba(0,0,0,0.4)\",\n backdropFilter: \"blur(4px)\",\n zIndex: 10,\n }}\n >\n {loadingContent || (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n }}\n >\n <Loader2\n style={{\n width: 40,\n height: 40,\n color: \"rgba(255,255,255,0.5)\",\n animation: \"zwr-spin 1s linear infinite\",\n }}\n />\n <div\n style={{\n fontSize: 14,\n color: \"rgba(255,255,255,0.5)\",\n fontWeight: 500,\n letterSpacing: \"0.1em\",\n textTransform: \"uppercase\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }}\n >\n Initializing Neural Engine...\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n","/**\n * AvatarControls — Default control bar for the avatar session.\n *\n * Mic toggle, connect/disconnect button.\n * Can be replaced entirely by a custom UI.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React, { useState } from \"react\";\nimport {\n Mic,\n MicOff,\n Power,\n Activity,\n Loader2,\n} from \"lucide-react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarControlsProps {\n session: AvatarSessionReturn;\n /** Optional style overrides for the wrapper. */\n style?: React.CSSProperties;\n}\n\nconst btnBase: React.CSSProperties = {\n flexShrink: 0,\n borderRadius: 9999,\n padding: 16,\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst connectBtnBase: React.CSSProperties = {\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n borderRadius: 9999,\n padding: \"16px 24px\",\n fontSize: 16,\n fontWeight: 600,\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n transition: \"all 0.3s\",\n border: \"none\",\n cursor: \"pointer\",\n};\n\nexport const AvatarControls: React.FC<AvatarControlsProps> = ({\n session,\n style,\n}) => {\n const {\n micMuted,\n toggleMic,\n isConnected,\n isConnecting,\n isEngineReady,\n loadedActions,\n connect,\n disconnect,\n } = session;\n\n const [micHover, setMicHover] = useState(false);\n const [connectHover, setConnectHover] = useState(false);\n\n const isSessionReady =\n isEngineReady &&\n loadedActions.has(\"listening\") &&\n loadedActions.has(\"speaking\");\n\n const micStyle: React.CSSProperties = micMuted\n ? {\n ...btnBase,\n background: \"rgba(239,68,68,0.1)\",\n color: \"#f87171\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.3)\",\n ...(micHover ? { background: \"rgba(239,68,68,0.2)\" } : {}),\n }\n : {\n ...btnBase,\n background: micHover ? \"rgba(255,255,255,0.1)\" : \"rgba(0,0,0,0.4)\",\n color: \"#fff\",\n boxShadow: \"inset 0 0 0 1px rgba(255,255,255,0.1)\",\n };\n\n const connectStyle: React.CSSProperties = isConnected\n ? {\n ...connectBtnBase,\n background: connectHover ? \"rgba(239,68,68,0.22)\" : \"rgba(239,68,68,0.14)\",\n boxShadow: \"inset 0 0 0 1px rgba(239,68,68,0.5), 0 0 20px rgba(239,68,68,0.2)\",\n }\n : {\n ...connectBtnBase,\n background: \"linear-gradient(to right, #7c3aed, #db2777, #f97316)\",\n boxShadow: \"0 0 30px rgba(236,72,153,0.3)\",\n opacity: connectHover ? 0.9 : 1,\n };\n\n if (isConnecting || !isSessionReady) {\n connectStyle.opacity = 0.5;\n connectStyle.cursor = \"not-allowed\";\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n marginTop: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: 16,\n paddingBottom: 16,\n ...style,\n }}\n >\n <style>{`\n @keyframes zwr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}</style>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 16,\n width: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Mic Toggle */}\n <button\n type=\"button\"\n onClick={toggleMic}\n onMouseEnter={() => setMicHover(true)}\n onMouseLeave={() => setMicHover(false)}\n style={micStyle}\n title={micMuted ? \"Unmute mic\" : \"Mute mic\"}\n >\n {micMuted ? <MicOff size={24} /> : <Mic size={24} />}\n </button>\n\n {/* Main Connect Button */}\n <button\n type=\"button\"\n onClick={isConnected ? disconnect : connect}\n onMouseEnter={() => setConnectHover(true)}\n onMouseLeave={() => setConnectHover(false)}\n disabled={!isConnected && (isConnecting || !isSessionReady)}\n style={connectStyle}\n >\n {isConnecting ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Connecting...</span>\n </>\n ) : !isConnected && !isSessionReady ? (\n <>\n <Loader2 size={20} style={{ animation: \"zwr-spin 1s linear infinite\" }} />\n <span>Loading Avatar...</span>\n </>\n ) : isConnected ? (\n <>\n <Power size={20} />\n <span>End Session</span>\n </>\n ) : (\n <>\n <Activity size={20} />\n <span>Start Session</span>\n </>\n )}\n </button>\n </div>\n </div>\n );\n};\n","/**\n * AvatarStatusBadge — Connection status indicator with session timer.\n * Uses inline styles only — no CSS framework dependency.\n */\n\nimport React from \"react\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface AvatarStatusBadgeProps {\n session: AvatarSessionReturn;\n}\n\nexport const AvatarStatusBadge: React.FC<AvatarStatusBadgeProps> = ({\n session,\n}) => {\n const { isConnected, timeRemaining, formatTime } = session;\n\n const isUrgent = isConnected && timeRemaining <= 30;\n const isWarning = isConnected && timeRemaining > 30 && timeRemaining <= 60;\n\n const wrapperStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n backdropFilter: \"blur(12px)\",\n padding: \"6px 12px\",\n borderRadius: 9999,\n border: \"1px solid\",\n pointerEvents: \"auto\",\n transition: \"all 0.3s\",\n ...(isUrgent\n ? {\n background: \"rgba(239,68,68,0.3)\",\n borderColor: \"rgba(239,68,68,0.4)\",\n boxShadow: \"0 0 15px rgba(239,68,68,0.3)\",\n animation: \"zwr-pulse 2s ease-in-out infinite\",\n }\n : isWarning\n ? {\n background: \"rgba(249,115,22,0.2)\",\n borderColor: \"rgba(249,115,22,0.3)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }\n : {\n background: \"rgba(0,0,0,0.4)\",\n borderColor: \"rgba(255,255,255,0.1)\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n }),\n };\n\n return (\n <>\n <style>{`\n @keyframes zwr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n `}</style>\n <div style={wrapperStyle}>\n <div\n style={{\n height: 8,\n width: 8,\n borderRadius: \"50%\",\n ...(isConnected\n ? {\n background: \"#22c55e\",\n boxShadow: \"0 0 10px rgba(34,197,94,0.5)\",\n }\n : {\n background: \"rgba(239,68,68,0.5)\",\n }),\n }}\n />\n <span\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n color: \"rgba(255,255,255,0.7)\",\n textTransform: \"uppercase\",\n }}\n >\n {isConnected ? `Online ${formatTime(timeRemaining)}` : \"Offline\"}\n </span>\n </div>\n </>\n );\n};\n","/**\n * LiveKitAvatarProvider — Bridge between LiveKit hooks and the ActionQueue/VAD.\n * Uses inline styles only — no CSS framework dependency.\n *\n * This component MUST be rendered inside a <LiveKitRoom>.\n */\n\nimport React, { useEffect, useRef } from \"react\";\nimport {\n useVoiceAssistant,\n RoomAudioRenderer,\n useDataChannel,\n useIsSpeaking,\n useLocalParticipant,\n} from \"@livekit/components-react\";\nimport { VoiceActivityDetector } from \"@zeroweight/renderer\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\n\ninterface LiveKitAvatarProviderProps {\n session: AvatarSessionReturn;\n}\n\nexport const LiveKitAvatarProvider: React.FC<LiveKitAvatarProviderProps> = ({\n session,\n}) => {\n const { renderer, actionQueue, micMuted, volume, setInactivityActive, loadedActions } =\n session;\n\n const prevAudioTrackRef = useRef<boolean>(false);\n const { state, audioTrack } = useVoiceAssistant();\n const localParticipant = useLocalParticipant();\n const isUserSpeaking = useIsSpeaking(localParticipant.localParticipant);\n\n // VAD instance\n const vadRef = useRef<VoiceActivityDetector | null>(null);\n\n // Ref to latest loadedActions so data channel callback doesn't go stale\n const loadedActionsRef = useRef(loadedActions);\n useEffect(() => {\n loadedActionsRef.current = loadedActions;\n }, [loadedActions]);\n\n // ─── VAD Setup ──────────────────────────────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const vad = new VoiceActivityDetector({\n threshold: 0.008,\n analyseIntervalMs: 30,\n speechStartFrames: 1,\n speechPauseFrames: 30,\n turnEndFrames: 50,\n });\n vadRef.current = vad;\n\n vad.on(\"speechStart\", () => {\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n });\n\n vad.on(\"turnEnd\", () => {\n actionQueue.setTurnActive(false);\n });\n\n return () => {\n vad.stop();\n vadRef.current = null;\n };\n }, [actionQueue]);\n\n // ─── Connect/disconnect VAD to audio track ──────────────────────\n\n useEffect(() => {\n const vad = vadRef.current;\n if (!vad) return;\n\n if (audioTrack?.publication?.track) {\n const mediaTrack = audioTrack.publication.track.mediaStreamTrack;\n if (mediaTrack) {\n vad.start(mediaTrack);\n }\n } else {\n vad.stop();\n }\n }, [audioTrack?.publication?.track]);\n\n // ─── LiveKit state → turn management ─────────────────────────────\n\n const turnEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const stateStr = state as string;\n\n if (stateStr === \"speaking\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n actionQueue.setTurnActive(true);\n actionQueue.setSpeechState(\"speaking\");\n } else if (stateStr === \"listening\" || stateStr === \"idle\") {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n turnEndTimerRef.current = null;\n }\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n } else if (stateStr === \"thinking\") {\n if (!turnEndTimerRef.current) {\n turnEndTimerRef.current = setTimeout(() => {\n turnEndTimerRef.current = null;\n vadRef.current?.endTurn();\n actionQueue.setTurnActive(false);\n }, 500);\n }\n }\n }, [state, actionQueue]);\n\n // Cleanup turn-end timer on unmount\n useEffect(() => {\n return () => {\n if (turnEndTimerRef.current) {\n clearTimeout(turnEndTimerRef.current);\n }\n };\n }, []);\n\n // ─── Data channel (backend-triggered actions) ───────────────────\n\n useDataChannel((msg) => {\n try {\n const decoder = new TextDecoder();\n const strData = decoder.decode(msg.payload);\n const data = JSON.parse(strData);\n\n if (data.type === \"AVATAR_UPDATE\") {\n const actionId = data.action;\n if (!loadedActionsRef.current.has(actionId)) {\n return;\n }\n actionQueue?.dispatch(actionId);\n }\n } catch (err) {\n console.error(\"[LiveKitAvatarProvider] Failed to parse data message:\", err);\n }\n });\n\n // ─── Inactivity tracking: only count when both AI and user are silent ───\n\n useEffect(() => {\n const isAssistantSpeaking = state === \"speaking\";\n setInactivityActive(!isAssistantSpeaking && !isUserSpeaking);\n }, [isUserSpeaking, setInactivityActive, state]);\n\n // ─── Audio loss / disconnect fallback ───────────────────────────\n\n useEffect(() => {\n if (!actionQueue) return;\n\n const hasAudio = !!audioTrack;\n const isDisconnectedState = state === \"disconnected\";\n\n if (!hasAudio || isDisconnectedState) {\n vadRef.current?.endTurn();\n actionQueue.forceListening();\n }\n prevAudioTrackRef.current = hasAudio;\n }, [audioTrack, state, actionQueue]);\n\n // ─── Mic mute sync ─────────────────────────────────────────────\n\n useEffect(() => {\n const participant = localParticipant.localParticipant;\n if (!participant) return;\n\n participant.setMicrophoneEnabled(!micMuted).catch((err) => {\n console.error(\"[LiveKitAvatarProvider] Failed to set mic state:\", err);\n });\n }, [micMuted, localParticipant.localParticipant]);\n\n return (\n <div style={{ position: \"absolute\", bottom: 80, left: 8, right: 8, display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <RoomAudioRenderer volume={volume} />\n </div>\n );\n};\n","/**\n * LiveKitAvatarSession — Full drop-in avatar component.\n * Uses inline styles only — no CSS framework dependency.\n *\n * Simplest usage:\n * import { LiveKitAvatarSession } from \"@zeroweight/react\";\n * <LiveKitAvatarSession\n * avatarId=\"abc123\"\n * apiKey=\"optional-api-key\"\n * />\n */\n\nimport React from \"react\";\nimport { LiveKitRoom } from \"@livekit/components-react\";\nimport \"@livekit/components-styles\";\n\nimport { useAvatarSession } from \"./useAvatarSession\";\nimport type { AvatarSessionReturn } from \"./useAvatarSession\";\nimport { AvatarCanvas } from \"./AvatarCanvas\";\nimport { AvatarControls } from \"./AvatarControls\";\nimport { AvatarStatusBadge } from \"./AvatarStatusBadge\";\nimport { LiveKitAvatarProvider } from \"./LiveKitAvatarProvider\";\nimport type { ZeroWeightApi } from \"./types\";\n\ninterface LiveKitAvatarSessionProps {\n avatarId: string;\n /** Optional API key for the built-in ZeroWeight API integration. */\n apiKey?: string | null;\n /** Injectable API — provide your own fetch functions. */\n api?: ZeroWeightApi;\n /** LiveKit server URL (e.g. \"wss://your-livekit.example.com\"). */\n livekitUrl?: string;\n /** Session duration in seconds. Default: 120 */\n sessionDuration?: number;\n /** Inactivity timeout in ms. Default: 30000 */\n inactivityTimeout?: number;\n /** Optional style overrides for the outer section. */\n style?: React.CSSProperties;\n /** Optional class name overrides for the outer section. */\n className?: string;\n /** Custom loading UI for the canvas. */\n loadingContent?: React.ReactNode;\n /** Custom controls component. If provided, replaces the default controls. */\n customControls?: (session: AvatarSessionReturn) => React.ReactNode;\n /** Custom status badge. If provided, replaces the default badge. */\n customStatusBadge?: (session: AvatarSessionReturn) => React.ReactNode;\n}\n\nexport const LiveKitAvatarSession: React.FC<LiveKitAvatarSessionProps> = ({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n style,\n className,\n loadingContent,\n customControls,\n customStatusBadge,\n}) => {\n const session = useAvatarSession({\n avatarId,\n apiKey,\n api,\n livekitUrl,\n sessionDuration,\n inactivityTimeout,\n });\n\n const {\n token,\n isConnected,\n avatarDimensions,\n disconnect,\n startSessionTimer,\n } = session;\n\n return (\n <section\n className={className}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n borderRadius: 16,\n border: \"1px solid rgba(255,255,255,0.1)\",\n boxShadow: \"0 25px 50px -12px rgba(0,0,0,0.5)\",\n height: \"80vh\",\n width: \"auto\",\n maxWidth: \"100%\",\n aspectRatio: avatarDimensions\n ? `${avatarDimensions.width} / ${avatarDimensions.height}`\n : \"3 / 4\",\n ...style,\n }}\n >\n {/* 1. Canvas Layer */}\n <AvatarCanvas session={session} loadingContent={loadingContent} />\n\n {/* 2. UI Overlay Layer */}\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n padding: 16,\n }}\n >\n {/* Top Header / Status */}\n <div\n style={{\n display: \"flex\",\n width: \"100%\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n }}\n >\n {customStatusBadge ? (\n customStatusBadge(session)\n ) : (\n <AvatarStatusBadge session={session} />\n )}\n <div />\n </div>\n\n {/* Bottom Controls */}\n {customControls ? (\n customControls(session)\n ) : (\n <AvatarControls session={session} />\n )}\n </div>\n\n {/* 3. LiveKit Room (hidden, audio-only) */}\n <div style={{ display: \"none\" }}>\n {token && livekitUrl && (\n <LiveKitRoom\n serverUrl={livekitUrl}\n token={token}\n connect={true}\n video={false}\n audio={true}\n onConnected={() => {\n session.markConnected();\n startSessionTimer();\n }}\n onDisconnected={disconnect}\n >\n <LiveKitAvatarProvider session={session} />\n </LiveKitRoom>\n )}\n </div>\n </section>\n );\n};\n"],"names":["INACTIVITY_TIMEOUT_MS","SESSION_DURATION_SECONDS","DEFAULT_LIVEKIT_URL","DEFAULT_API_BASE_URL","generateRandomName","adjectives","nouns","randomNum","useAvatarSession","config","avatarId","apiKey","api","livekitUrlProp","sessionDuration","inactivityTimeout","livekitUrl","resolvedApi","useMemo","headers","resolvedAvatarId","response","userName","params","containerRef","useRef","canvasRef","rendererRef","actionQueueRef","rendererState","setRendererState","useState","avatarDimensions","setAvatarDimensions","loadedActions","setLoadedActions","actionMetadata","setActionMetadata","isLoadingActions","setIsLoadingActions","token","setToken","isConnecting","setIsConnecting","isConnected","setIsConnected","timeRemaining","setTimeRemaining","sessionTimerRef","handleDisconnectRef","micMuted","setMicMuted","volume","setVolumeState","inactivityTimeoutRef","isEngineReady","ensureCanvas","useCallback","container","canvas","useEffect","cancelled","renderer","ZeroWeightRenderer","queue","ActionQueue","actionId","fallback","state","w","h","prev","next","data","e","hasWavedRef","connect","error","disconnect","markConnected","startSessionTimer","formatTime","seconds","m","s","setInactivityActive","isInactive","toggleMic","v","setVolume","toggleVolume","interrupt","runAction","AvatarCanvas","session","style","loadingContent","jsxs","jsx","Loader2","btnBase","connectBtnBase","AvatarControls","micHover","setMicHover","connectHover","setConnectHover","isSessionReady","micStyle","connectStyle","MicOff","Mic","Fragment","Power","Activity","AvatarStatusBadge","isUrgent","isWarning","wrapperStyle","LiveKitAvatarProvider","actionQueue","prevAudioTrackRef","audioTrack","useVoiceAssistant","localParticipant","useLocalParticipant","isUserSpeaking","useIsSpeaking","vadRef","loadedActionsRef","vad","VoiceActivityDetector","mediaTrack","turnEndTimerRef","stateStr","useDataChannel","msg","strData","err","hasAudio","participant","RoomAudioRenderer","LiveKitAvatarSession","className","customControls","customStatusBadge","LiveKitRoom"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroweight/react",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "type": "module",
5
5
  "description": "React hooks & components for the ZeroWeight avatar renderer. Drop-in LiveKit-powered avatar sessions.",
6
6
  "main": "dist/zeroweight-renderer-react.cjs.js",
@@ -65,6 +65,6 @@
65
65
  ],
66
66
  "license": "MIT",
67
67
  "dependencies": {
68
- "@zeroweight/renderer": "^0.2.21"
68
+ "@zeroweight/renderer": "^0.2.22"
69
69
  }
70
70
  }