@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,
|
|
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,
|
|
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,
|
|
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,
|
|
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":"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,cAAArB,EACA,QAAA0C,EACA,WAAAE,CAAA,EACEgB,EAEE,CAACS,EAAUC,CAAW,EAAIzE,EAAAA,SAAS,EAAK,EACxC,CAAC0E,EAAcC,CAAe,EAAI3E,EAAAA,SAAS,EAAK,EAEhD4E,EACJpD,GACArB,EAAc,IAAI,WAAW,GAC7BA,EAAc,IAAI,UAAU,EAExB0E,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,EC7KaiB,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,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
|
|
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"],
|
|
9
|
-
return `${u[Math.floor(Math.random() * u.length)]}${
|
|
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:
|
|
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,
|
|
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
|
|
24
|
+
const A = await fetch(
|
|
25
25
|
`${X}/avatars/bundle/${encodeURIComponent(
|
|
26
26
|
c
|
|
27
27
|
)}`,
|
|
28
28
|
{ headers: e }
|
|
29
29
|
);
|
|
30
|
-
if (!
|
|
30
|
+
if (!A.ok)
|
|
31
31
|
throw new Error(
|
|
32
|
-
`Failed to fetch avatar bundle (${
|
|
32
|
+
`Failed to fetch avatar bundle (${A.status} ${A.statusText})`
|
|
33
33
|
);
|
|
34
|
-
return
|
|
34
|
+
return A.json();
|
|
35
35
|
},
|
|
36
|
-
getLiveKitToken: async (c,
|
|
36
|
+
getLiveKitToken: async (c, A) => {
|
|
37
37
|
const g = new URLSearchParams({
|
|
38
38
|
avatar_id: c,
|
|
39
|
-
name:
|
|
40
|
-
}),
|
|
39
|
+
name: A
|
|
40
|
+
}), E = await fetch(
|
|
41
41
|
`${X}/livekit/token?${g.toString()}`,
|
|
42
42
|
{ headers: e }
|
|
43
43
|
);
|
|
44
|
-
if (!
|
|
44
|
+
if (!E.ok)
|
|
45
45
|
throw new Error(
|
|
46
|
-
`Failed to fetch LiveKit token (${
|
|
46
|
+
`Failed to fetch LiveKit token (${E.status} ${E.statusText})`
|
|
47
47
|
);
|
|
48
|
-
return
|
|
48
|
+
return E.json();
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
-
}, [f, t]), m = x(null),
|
|
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,
|
|
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)),
|
|
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
|
|
67
|
-
if (!
|
|
66
|
+
const A = Z();
|
|
67
|
+
if (!A) return;
|
|
68
68
|
const g = new ye();
|
|
69
69
|
i.current = g;
|
|
70
|
-
const
|
|
70
|
+
const E = new be((T, z) => {
|
|
71
71
|
g.play(T, z);
|
|
72
72
|
});
|
|
73
|
-
s.current =
|
|
74
|
-
e ||
|
|
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()),
|
|
85
|
+
e || (B(g.getActionMetadata()), E.setActionMetadata(g.getActionMetadata()));
|
|
86
86
|
});
|
|
87
87
|
try {
|
|
88
88
|
O(!0);
|
|
89
|
-
const T = await p.getBundle(
|
|
90
|
-
if (e || (await g.init(
|
|
91
|
-
B(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
|
-
}, [
|
|
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(
|
|
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,
|
|
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"),
|
|
128
|
-
return `${c}:${
|
|
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
|
-
|
|
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:
|
|
168
|
+
livekitUrl: M,
|
|
169
169
|
timeRemaining: re,
|
|
170
170
|
formatTime: de,
|
|
171
171
|
micMuted: ie,
|
|
172
|
-
volume:
|
|
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:
|
|
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
|
-
...
|
|
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:
|
|
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
|
-
|
|
318
|
+
loadedActions: M,
|
|
319
319
|
connect: p,
|
|
320
320
|
disconnect: m
|
|
321
|
-
} = u, [
|
|
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
|
-
...
|
|
326
|
+
...k ? { background: "rgba(239,68,68,0.2)" } : {}
|
|
327
327
|
} : {
|
|
328
328
|
...Y,
|
|
329
|
-
background:
|
|
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 || !
|
|
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
|
-
...
|
|
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 || !
|
|
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 && !
|
|
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:
|
|
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
|
-
...
|
|
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:
|
|
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:
|
|
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
|
|
515
|
+
const a = x(null);
|
|
516
516
|
return w(() => {
|
|
517
517
|
if (!t) return;
|
|
518
518
|
const r = p;
|
|
519
|
-
r === "speaking" ? (
|
|
520
|
-
|
|
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
|
-
|
|
524
|
-
}, []),
|
|
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()),
|
|
541
|
+
(!r || p === "disconnected") && (s.current?.endTurn(), t.forceListening()), M.current = r;
|
|
542
542
|
}, [m, p, t]), w(() => {
|
|
543
|
-
const r =
|
|
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,
|
|
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:
|
|
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:
|
|
556
|
+
className: M,
|
|
557
557
|
loadingContent: p,
|
|
558
558
|
customControls: m,
|
|
559
|
-
customStatusBadge:
|
|
559
|
+
customStatusBadge: k
|
|
560
560
|
}) => {
|
|
561
561
|
const i = $e({
|
|
562
562
|
avatarId: u,
|
|
563
|
-
apiKey:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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.
|
|
68
|
+
"@zeroweight/renderer": "^0.2.22"
|
|
69
69
|
}
|
|
70
70
|
}
|