@livelayer/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,10 +1,161 @@
1
+ import { AgentConfig } from '@livelayer/sdk';
2
+ import { AgentState } from '@livelayer/sdk';
3
+ import { Component } from 'react';
4
+ import { ConnectionState } from '@livelayer/sdk';
5
+ import { CSSProperties } from 'react';
6
+ import { ErrorInfo } from 'react';
1
7
  import { FC } from 'react';
8
+ import { JSX } from 'react/jsx-runtime';
9
+ import { LiveKitSession } from '@livelayer/sdk';
10
+ import { ReactNode } from 'react';
11
+ import { Room } from 'livekit-client';
12
+ import { SessionOptions } from '@livelayer/sdk';
13
+ import { TranscriptEntry } from '@livelayer/sdk';
14
+
15
+ /**
16
+ * Agent commands streamed over the LiveKit data channel. The base package
17
+ * recognizes universal types (agent_state, avatar_active, etc.) and forwards
18
+ * everything else to the consumer's `onAgentCommand` callback.
19
+ *
20
+ * This is an open union — unknown future command types still include `type`.
21
+ */
22
+ export declare interface AgentCommand {
23
+ type: string;
24
+ [key: string]: unknown;
25
+ }
26
+
27
+ export { AgentConfig }
2
28
 
3
29
  export declare interface AgentEventDetail {
4
30
  eventName: string;
5
31
  data: Record<string, unknown>;
6
32
  }
7
33
 
34
+ export { AgentState }
35
+
36
+ export declare interface AudioLevelHandle {
37
+ /** Attach a media element as the analyser source. Safe to call repeatedly — swaps sources. */
38
+ attach: (element: HTMLMediaElement) => void;
39
+ /** Stop the rAF loop and disconnect the current source. Keeps the context alive. */
40
+ detach: () => void;
41
+ /** Subscribe to level ticks (0..1). Returns an unsubscribe fn. */
42
+ subscribe: (cb: LevelSubscriber) => () => void;
43
+ }
44
+
45
+ /**
46
+ * LiveLayer agent widget. Renders a voice/video avatar agent embed with
47
+ * three display modes (expanded, minimized, hidden), responsive
48
+ * layouts, team-member switching, and full branding.
49
+ *
50
+ * Import the stylesheet once in your app:
51
+ * import "@livelayer/react/styles.css";
52
+ */
53
+ export declare function AvatarWidget(props: AvatarWidgetProps): JSX.Element;
54
+
55
+ export declare interface AvatarWidgetProps {
56
+ agentId: string;
57
+ apiKey?: string;
58
+ baseUrl?: string;
59
+ sessionEndpoint?: string;
60
+ sessionBody?: Record<string, unknown>;
61
+ experienceMode?: "WIDGET" | "EMBEDDED";
62
+ autoConnect?: boolean;
63
+ displayMode?: DisplayMode;
64
+ defaultDisplayMode?: DisplayMode;
65
+ onDisplayModeChange?: (m: DisplayMode) => void;
66
+ position?: WidgetPosition;
67
+ mobileBreakpoint?: number | false;
68
+ persistKey?: string;
69
+ disablePersistence?: boolean;
70
+ teamMembers?: TeamMember[];
71
+ currentTeamMemberId?: string;
72
+ onTeamMemberChange?: (m: TeamMember) => void;
73
+ avatarImageUrl?: string;
74
+ idleLoopUrl?: string;
75
+ greeting?: string;
76
+ agentName?: string;
77
+ branding?: BrandingConfig;
78
+ allowCamera?: boolean;
79
+ allowScreenShare?: boolean;
80
+ allowTyping?: boolean;
81
+ allowMic?: boolean;
82
+ onConnect?: () => void;
83
+ onDisconnect?: () => void;
84
+ onTranscript?: (entries: TranscriptEntry[]) => void;
85
+ onAgentState?: (state: AgentState) => void;
86
+ onConnectionStateChange?: (state: ConnectionState) => void;
87
+ onAgentEvent?: (e: AgentEventDetail) => void;
88
+ onAgentCommand?: (cmd: AgentCommand) => void;
89
+ /**
90
+ * When provided, the widget does not create its own LiveKit session.
91
+ * The consumer owns connection lifecycle. Use for cross-page
92
+ * persistence, shared rooms, or integration with existing Room logic.
93
+ */
94
+ controlledSession?: ControlledSession;
95
+ className?: string;
96
+ style?: CSSProperties;
97
+ zIndex?: number;
98
+ }
99
+
100
+ export declare interface BrandingConfig {
101
+ logoUrl?: string;
102
+ productName?: string;
103
+ primaryColor?: string;
104
+ accentColor?: string;
105
+ backgroundColor?: string;
106
+ textColor?: string;
107
+ }
108
+
109
+ export { ConnectionState }
110
+
111
+ /**
112
+ * Fully-controlled session state. When a consumer supplies this, the widget
113
+ * does NOT create its own LiveKitSession — it presents the state you give it
114
+ * and calls your onConnect/onDisconnect/onRawMessage when the user clicks
115
+ * buttons. Use this when you need cross-page persistence, a shared room
116
+ * across multiple widget instances, or any custom session lifecycle.
117
+ *
118
+ * When omitted, the widget manages its own session via useLiveKitSession.
119
+ */
120
+ export declare interface ControlledSession {
121
+ connectionState: ConnectionState;
122
+ agentState: AgentState;
123
+ transcript: TranscriptEntry[];
124
+ videoElement: HTMLVideoElement | null;
125
+ audioElement: HTMLAudioElement | null;
126
+ canResume: boolean;
127
+ error: string | null;
128
+ /** Called when the user clicks the Connect/Start button. */
129
+ onConnect: () => void | Promise<void>;
130
+ /** Called when the user clicks End/Disconnect. */
131
+ onDisconnect: () => void;
132
+ /**
133
+ * Subscribe to data-channel messages from the agent. The widget uses this
134
+ * to route universal commands (state/agent_state) internally and forward
135
+ * everything else via onAgentCommand. Return an unsubscribe function.
136
+ */
137
+ subscribeToDataMessages?: (cb: (msg: Record<string, unknown>) => void) => () => void;
138
+ }
139
+
140
+ export declare type DisplayMode = "hidden" | "minimized" | "expanded";
141
+
142
+ declare type DisplayMode_2 = "hidden" | "minimized" | "expanded";
143
+
144
+ export declare class ErrorBoundary extends Component<Props, State> {
145
+ state: State;
146
+ static getDerivedStateFromError(error: Error): State;
147
+ componentDidCatch(error: Error, info: ErrorInfo): void;
148
+ reset: () => void;
149
+ render(): ReactNode;
150
+ }
151
+
152
+ export declare interface LegacyAgentEventDetail {
153
+ eventName: string;
154
+ data: Record<string, unknown>;
155
+ }
156
+
157
+ declare type LevelSubscriber = (level: number) => void;
158
+
8
159
  /**
9
160
  * React component that renders a `<livelayer-widget>` custom element.
10
161
  *
@@ -31,11 +182,134 @@ export declare interface LiveLayerWidgetProps {
31
182
  */
32
183
  mode?: "WIDGET" | "EMBEDDED";
33
184
  /** Callback fired when the agent emits an event via the data channel */
34
- onAgentEvent?: (event: AgentEventDetail) => void;
185
+ onAgentEvent?: (event: LegacyAgentEventDetail) => void;
35
186
  /** Additional CSS class name on the wrapper div */
36
187
  className?: string;
37
188
  /** Inline styles on the wrapper div */
38
189
  style?: React.CSSProperties;
39
190
  }
40
191
 
192
+ export declare interface MicrophoneStateHandle {
193
+ isMuted: boolean;
194
+ /** Human-readable error when mic publish failed, else null. */
195
+ micError: string | null;
196
+ /** Toggle mute on the currently-published track. No-op if no track. */
197
+ toggleMute: () => void;
198
+ /**
199
+ * Create + publish a local mic track into the given room. Safe to call
200
+ * multiple times — replaces any existing track.
201
+ */
202
+ setupMic: (room: Room) => Promise<void>;
203
+ /** Unpublish and dispose the current track. Safe to call when no track. */
204
+ teardownMic: () => void;
205
+ /** Clear the error state (e.g. after user clicks Retry). */
206
+ clearError: () => void;
207
+ }
208
+
209
+ declare interface Options {
210
+ value?: DisplayMode_2;
211
+ defaultValue?: DisplayMode_2;
212
+ onChange?: (next: DisplayMode_2) => void;
213
+ }
214
+
215
+ declare interface Options_2 {
216
+ value?: DisplayMode_2;
217
+ defaultValue?: DisplayMode_2;
218
+ onChange?: (next: DisplayMode_2) => void;
219
+ persistKey?: string;
220
+ disablePersistence?: boolean;
221
+ }
222
+
223
+ declare interface Props {
224
+ children: ReactNode;
225
+ /** Callback fired when an error is caught. Useful for telemetry. */
226
+ onError?: (error: Error, info: ErrorInfo) => void;
227
+ /** Optional custom fallback. Defaults to a small inline error card. */
228
+ fallback?: ReactNode;
229
+ }
230
+
231
+ declare interface State {
232
+ hasError: boolean;
233
+ error: Error | null;
234
+ }
235
+
236
+ export declare interface TeamMember {
237
+ id: string;
238
+ name: string;
239
+ role?: string;
240
+ avatarImageUrl?: string;
241
+ previewVideoUrl?: string;
242
+ /**
243
+ * Per-member agent override. When the user switches to this member the
244
+ * widget reconnects with this agentId. If omitted, uses the top-level
245
+ * agentId prop (single-agent team).
246
+ */
247
+ agentId?: string;
248
+ }
249
+
250
+ export { TranscriptEntry }
251
+
252
+ declare interface TranscriptEntry_2 {
253
+ id: string;
254
+ role: "agent" | "user";
255
+ text: string;
256
+ final: boolean;
257
+ }
258
+
259
+ export declare interface TranscriptHandle {
260
+ entries: TranscriptEntry_2[];
261
+ /** Add or update a segment by id. */
262
+ pushSegment: (segment: TranscriptEntry_2) => void;
263
+ /** Reset the buffer (e.g. on team-member switch). */
264
+ clear: () => void;
265
+ /** Latest entry, or null if empty. */
266
+ latest: TranscriptEntry_2 | null;
267
+ }
268
+
269
+ export declare function useAudioLevel(): AudioLevelHandle;
270
+
271
+ export declare function useDisplayMode({ value, defaultValue, onChange, }?: Options): [DisplayMode_2, (next: DisplayMode_2) => void];
272
+
273
+ export declare function useDisplayModePersistence({ value, defaultValue, onChange, persistKey, disablePersistence, }?: Options_2): [DisplayMode_2, (next: DisplayMode_2) => void];
274
+
275
+ export declare function useIsMobile(breakpoint?: number | false): boolean;
276
+
277
+ export declare function useLiveKitSession(options: UseLiveKitSessionOptions): UseLiveKitSessionResult;
278
+
279
+ declare interface UseLiveKitSessionOptions extends SessionOptions {
280
+ /**
281
+ * Fires for every data channel message from the agent. The hook also
282
+ * interprets recognized state-bearing messages (agent_state) and updates
283
+ * its own state; consumers use this callback to handle everything else.
284
+ */
285
+ onDataMessage?: (msg: Record<string, unknown>) => void;
286
+ }
287
+
288
+ export declare interface UseLiveKitSessionResult {
289
+ connectionState: ConnectionState;
290
+ agentState: AgentState;
291
+ transcript: TranscriptEntry[];
292
+ agentConfig: AgentConfig | null;
293
+ /** Live video <video> element attached by the agent, or null. */
294
+ videoElement: HTMLVideoElement | null;
295
+ /** Remote <audio> element (agent voice), or null. */
296
+ audioElement: HTMLAudioElement | null;
297
+ /** True when the session's resume window is still open. */
298
+ canResume: boolean;
299
+ /** Surface a friendly error string when connect fails. */
300
+ error: string | null;
301
+ connect: () => Promise<void>;
302
+ disconnect: () => void;
303
+ /** Access the underlying Room (e.g. to publish mic track). */
304
+ getRoom: () => ReturnType<LiveKitSession["getRoom"]>;
305
+ /** The session instance, if consumers need it. */
306
+ session: LiveKitSession | null;
307
+ }
308
+
309
+ export declare function useMicrophoneState(): MicrophoneStateHandle;
310
+
311
+ export declare function useTranscript(): TranscriptHandle;
312
+
313
+ export declare type WidgetPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "custom";
314
+
41
315
  export { }
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("react/jsx-runtime"),t=require("react");require("@livelayer/sdk");const m=({agentId:s,baseUrl:c,apiKey:l,mode:r,onAgentEvent:o,className:v,style:g})=>{const a=t.useRef(null),n=t.useRef(null),u=t.useRef(o);u.current=o;const d=t.useCallback(i=>{var f;const e=i.detail;(f=u.current)==null||f.call(u,e)},[]);return t.useEffect(()=>{const i=a.current;if(!i)return;const e=document.createElement("livelayer-widget");return e.setAttribute("agent-id",s),c&&e.setAttribute("base-url",c),l&&e.setAttribute("api-key",l),r&&e.setAttribute("mode",r),e.addEventListener("agent-event",d),i.appendChild(e),n.current=e,()=>{e.removeEventListener("agent-event",d),i.removeChild(e),n.current=null}},[s]),t.useEffect(()=>{n.current&&(r?n.current.setAttribute("mode",r):n.current.removeAttribute("mode"))},[r]),b.jsx("div",{ref:a,className:v,style:g})};exports.LiveLayerWidget=m;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),ze=require("@livelayer/sdk"),Ae=require("livekit-client");class pe extends t.Component{constructor(){super(...arguments),this.state={hasError:!1,error:null},this.reset=()=>{this.setState({hasError:!1,error:null})}}static getDerivedStateFromError(r){return{hasError:!0,error:r}}componentDidCatch(r,l){var a,s;(s=(a=this.props).onError)==null||s.call(a,r,l)}render(){var r;return this.state.hasError?this.props.fallback?this.props.fallback:e.jsxs("div",{className:"ll-error-boundary",role:"alert",children:[e.jsx("p",{className:"ll-error-boundary__title",children:"Widget crashed"}),e.jsx("p",{className:"ll-error-boundary__message",children:((r=this.state.error)==null?void 0:r.message)||"Something went wrong."}),e.jsx("button",{type:"button",className:"ll-error-boundary__retry",onClick:this.reset,children:"Reload widget"})]}):this.props.children}}function fe(n){const[r,l]=t.useState("idle"),[a,s]=t.useState("idle"),[i,p]=t.useState([]),[m,f]=t.useState(null),[_,u]=t.useState(null),[c,o]=t.useState(null),[x,v]=t.useState(!1),[C,N]=t.useState(null),S=t.useRef(null),M=t.useRef(n.onDataMessage);M.current=n.onDataMessage,t.useEffect(()=>{const b={onConnectionStateChange:g=>{l(g),g==="connected"&&N(null)},onAgentStateChange:s,onTranscript:g=>p([...g]),onAgentConfig:f,onAudioTrack:g=>o(g),onVideoTrack:g=>u(g),onVideoTrackRemoved:()=>u(null),onError:g=>N(g),onDataMessage:g=>{var R;(R=M.current)==null||R.call(M,g)},onResumabilityChange:v},k=new ze.LiveKitSession({agentId:n.agentId,baseUrl:n.baseUrl,apiKey:n.apiKey,sessionEndpoint:n.sessionEndpoint,sessionBody:n.sessionBody},b);return S.current=k,l("idle"),s("idle"),p([]),f(null),u(null),o(null),v(!1),N(null),()=>{var g;(g=k.destroy)==null||g.call(k),S.current=null}},[n.agentId,n.baseUrl,n.apiKey,n.sessionEndpoint,JSON.stringify(n.sessionBody??{})]);const T=t.useCallback(async()=>{const b=S.current;if(b)try{await b.connect()}catch(k){throw N(k instanceof Error?k.message:String(k)),k}},[]),B=t.useCallback(()=>{const b=S.current;b&&b.disconnect()},[]),w=t.useCallback(()=>{var b;return((b=S.current)==null?void 0:b.getRoom())??null},[]);return{connectionState:r,agentState:a,transcript:i,agentConfig:m,videoElement:_,audioElement:c,canResume:x,error:C,connect:T,disconnect:B,getRoom:w,session:S.current}}function he(){const n=t.useRef(null),r=t.useRef(null),l=t.useRef(null),a=t.useRef(null),s=t.useRef(new Set),i=t.useRef(null),p=t.useCallback(()=>{const c=r.current;if(!c){a.current=null;return}(!i.current||i.current.length!==c.frequencyBinCount)&&(i.current=new Uint8Array(new ArrayBuffer(c.frequencyBinCount)));const o=i.current;c.getByteFrequencyData(o);let x=0;for(let C=0;C<o.length;C++)x+=o[C];const v=x/o.length/255;for(const C of s.current)try{C(v)}catch(N){console.error("[useAudioLevel] subscriber threw:",N)}a.current=requestAnimationFrame(p)},[]),m=t.useCallback(()=>{if(n.current||typeof window>"u"||typeof AudioContext>"u")return;const c=new AudioContext,o=c.createAnalyser();o.fftSize=64,o.connect(c.destination),n.current=c,r.current=o},[]),f=t.useCallback(c=>{if(m(),!(!n.current||!r.current)){if(l.current){try{l.current.disconnect()}catch{}l.current=null}try{const o=n.current.createMediaElementSource(c);o.connect(r.current),l.current=o}catch(o){console.warn("[useAudioLevel] createMediaElementSource failed:",o);return}a.current===null&&(a.current=requestAnimationFrame(p))}},[m,p]),_=t.useCallback(()=>{if(a.current!==null&&(cancelAnimationFrame(a.current),a.current=null),l.current){try{l.current.disconnect()}catch{}l.current=null}},[]),u=t.useCallback(c=>(s.current.add(c),()=>{s.current.delete(c)}),[]);return t.useEffect(()=>()=>{if(_(),r.current){try{r.current.disconnect()}catch{}r.current=null}if(n.current){try{n.current.close()}catch{}n.current=null}s.current.clear(),i.current=null},[_]),{attach:f,detach:_,subscribe:u}}function xe(){const[n,r]=t.useState(!1),[l,a]=t.useState(null),s=t.useRef(null),i=t.useRef(null),p=t.useCallback(async u=>{if(s.current&&i.current){try{await i.current.localParticipant.unpublishTrack(s.current)}catch{}s.current.stop(),s.current=null}i.current=u,a(null);try{const c=await Ae.createLocalAudioTrack({echoCancellation:!0,noiseSuppression:!0});await u.localParticipant.publishTrack(c),s.current=c,r(c.isMuted)}catch(c){const o=c instanceof Error&&c.name==="NotAllowedError"?"Enable your microphone to talk with the agent.":"Microphone unavailable. Check browser permissions and try again.";throw a(o),c}},[]),m=t.useCallback(()=>{const u=s.current;u&&(u.isMuted?(u.unmute(),r(!1)):(u.mute(),r(!0)))},[]),f=t.useCallback(()=>{const u=s.current,c=i.current;if(u&&c){try{c.localParticipant.unpublishTrack(u)}catch{}u.stop()}s.current=null,i.current=null,r(!1)},[]),_=t.useCallback(()=>a(null),[]);return{isMuted:n,micError:l,toggleMute:m,setupMic:p,teardownMic:f,clearError:_}}function Ie(n){if(typeof window>"u")return null;try{return window.localStorage.getItem(n)}catch{return null}}function Te(n,r){if(!(typeof window>"u"))try{window.localStorage.setItem(n,r)}catch{}}function _e({value:n,defaultValue:r="expanded",onChange:l}={}){const a=n!==void 0,[s,i]=t.useState(r),p=a?n:s,m=t.useCallback(f=>{f!==p&&(a||i(f),l==null||l(f))},[p,a,l]);return[p,m]}const Be=["hidden","minimized","expanded"];function De(n){return n&&Be.includes(n)?n:null}function ge({value:n,defaultValue:r="expanded",onChange:l,persistKey:a="ll-widget",disablePersistence:s=!1}={}){const i=`${a}:display-mode`,p=t.useRef(!1),[m,f]=_e({value:n,defaultValue:r,onChange:_=>{n===void 0&&!s&&Te(i,_),l==null||l(_)}});return t.useEffect(()=>{if(p.current||(p.current=!0,s||n!==void 0))return;const _=De(Ie(i));_&&_!==m&&f(_)},[]),[m,f]}const Pe=640;function ve(n=Pe){const[r,l]=t.useState(!1);return t.useEffect(()=>{if(n===!1){l(!1);return}if(typeof window>"u"||typeof window.matchMedia>"u")return;const a=`(max-width: ${n-1}px)`,s=window.matchMedia(a),i=()=>l(s.matches);return i(),typeof s.addEventListener=="function"?(s.addEventListener("change",i),()=>s.removeEventListener("change",i)):(s.addListener(i),()=>{s.removeListener(i)})},[n]),r}const te=({muted:n=!1,className:r})=>n?e.jsxs("svg",{className:r,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:[e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"}),e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2"})]}):e.jsx("svg",{className:r,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4M12 1a3 3 0 00-3 3v4a3 3 0 006 0V4a3 3 0 00-3-3z"})}),ue=({className:n})=>e.jsx("svg",{className:n,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"})}),me=({className:n})=>e.jsx("svg",{className:n,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19 9l-7 7-7-7"})}),ye=({className:n})=>e.jsx("svg",{className:n,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"})}),Ue={left:180,right:0,up:-90,down:90},$e=({direction:n="right",className:r})=>e.jsx("svg",{className:r,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,style:{transform:`rotate(${Ue[n]}deg)`},"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M9 5l7 7-7 7"})}),Ve=({muted:n=!1,className:r})=>e.jsxs("svg",{className:r,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:[e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 9v6m3-9v12M9 5l-3 4H3v6h3l3 4V5z"}),n&&e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 20L20 4"})]}),We=({className:n})=>e.jsx("svg",{className:n,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,"aria-hidden":"true",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"})});function qe(n){return n==="top-left"||n==="bottom-left"?"left":"right"}const Ke=({position:n,isMobile:r,isSpeaking:l,onExpand:a,label:s="Open widget"})=>{const i=qe(n),p=i==="right"?"left":"right",m=["ll-hidden",`ll-hidden--${i}`,r?"ll-hidden--mobile":"ll-hidden--desktop",l?"ll-hidden--speaking":null].filter(Boolean).join(" ");return e.jsx("button",{type:"button",className:m,onClick:a,"aria-label":s,"data-position":n,children:e.jsx($e,{direction:p,className:"ll-hidden__chevron"})})},Oe=({audioLevel:n,bars:r=20,maxHeight:l=20,minHeight:a=4,className:s,barClassName:i})=>{const p=t.useRef(null),m=t.useRef([]),f=t.useMemo(()=>{const u=(Math.sqrt(5)-1)/2;return Array.from({length:r},(c,o)=>.5+o*u%1*.5)},[r]);t.useEffect(()=>n.subscribe(c=>{for(let o=0;o<r;o++){const x=m.current[o];if(!x)continue;const v=Math.max(a,c*l*f[o]);x.style.height=`${v}px`}}),[n,r,l,a,f]);const _=["ll-waveform",s].filter(Boolean).join(" ");return e.jsx("div",{ref:p,className:_,"aria-hidden":"true",children:Array.from({length:r},(u,c)=>e.jsx("div",{ref:o=>{m.current[c]=o},className:["ll-waveform__bar",i].filter(Boolean).join(" "),style:{height:`${a}px`}},c))})},Fe=({position:n,isMobile:r,agentName:l,avatarImageUrl:a,agentState:s,isMuted:i,audioLevel:p,onExpand:m,onToggleMute:f,onClose:_})=>r?e.jsx("div",{className:"ll-minimized ll-minimized--mobile",role:"region","aria-label":`${l} widget`,children:e.jsxs("button",{type:"button",className:"ll-minimized__surface",onClick:m,"aria-label":`Expand ${l} widget`,children:[a?e.jsx("img",{src:a,alt:l,className:"ll-minimized__avatar"}):e.jsx("div",{className:"ll-minimized__avatar ll-minimized__avatar--placeholder"}),e.jsx(Oe,{audioLevel:p,bars:16,maxHeight:18,className:"ll-minimized__waveform"}),e.jsx("span",{className:"ll-minimized__name",children:l}),e.jsxs("div",{className:"ll-minimized__controls",children:[e.jsx("span",{className:"ll-minimized__btn",role:"button",tabIndex:0,onClick:u=>{u.stopPropagation(),f()},onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.stopPropagation(),u.preventDefault(),f())},"aria-label":i?"Unmute microphone":"Mute microphone",children:e.jsx(te,{muted:i,className:"ll-minimized__icon"})}),e.jsx(ue,{className:"ll-minimized__icon ll-minimized__icon--expand"})]})]})}):e.jsx("div",{className:"ll-minimized ll-minimized--desktop","data-position":n,role:"region","aria-label":`${l} widget`,children:e.jsxs("div",{className:"ll-minimized__surface",children:[a?e.jsx("img",{src:a,alt:l,className:"ll-minimized__avatar"}):e.jsx("div",{className:"ll-minimized__avatar ll-minimized__avatar--placeholder"}),e.jsxs("div",{className:"ll-minimized__meta",children:[e.jsx("span",{className:"ll-minimized__name",children:l}),e.jsx("span",{className:"ll-minimized__state",children:s==="speaking"?"Speaking":s==="thinking"?"Thinking":"Listening"})]}),e.jsxs("div",{className:"ll-minimized__controls",children:[e.jsx("button",{type:"button",className:"ll-minimized__btn",onClick:f,"aria-label":i?"Unmute microphone":"Mute microphone",children:e.jsx(te,{muted:i,className:"ll-minimized__icon"})}),e.jsx("button",{type:"button",className:"ll-minimized__btn",onClick:m,"aria-label":`Expand ${l} widget`,children:e.jsx(ue,{className:"ll-minimized__icon"})}),e.jsx("button",{type:"button",className:"ll-minimized__btn ll-minimized__btn--close",onClick:_,"aria-label":"Close widget",children:e.jsx(ye,{className:"ll-minimized__icon"})})]})]})}),He=({src:n,alt:r,preCannedPlaying:l=!1,className:a,style:s})=>{const[i,p]=t.useState(!1),m=t.useRef(n);if(t.useEffect(()=>{m.current!==n&&(m.current=n,p(!1))},[n]),!n)return null;const f={position:"absolute",inset:0,width:"100%",height:"100%",objectFit:"cover",objectPosition:"top",transition:"opacity 500ms ease, transform 500ms ease",transform:l?"scale(1.02)":"scale(1)",opacity:i?1:0,...s};return e.jsx("img",{src:n,alt:r,className:a,style:f,loading:"eager",fetchpriority:"high",onLoad:()=>p(!0)})},Ye={idle:"Idle",listening:"Listening",thinking:"Thinking",speaking:"Speaking"},Ge=({state:n,className:r})=>{const l=["ll-pill",`ll-pill--${n}`,r].filter(Boolean).join(" ");return e.jsxs("div",{className:l,"data-agent-state":n,children:[e.jsx("span",{className:"ll-pill__dot"}),e.jsx("span",{className:"ll-pill__label",children:Ye[n]})]})},Je=({position:n,isMobile:r,agentName:l,avatarImageUrl:a,idleLoopUrl:s,greeting:i,branding:p,teamMembers:m,currentTeamMemberId:f,isSwitchingTeamMember:_,teamSwitcherOpen:u,onToggleTeamSwitcher:c,onSelectTeamMember:o,connectionState:x,agentState:v,transcript:C,isMuted:N,needsUserGesture:S,canResume:M,micError:T,error:B,avatarVideoContainerRef:w,onConnect:b,onDisconnect:k,onRetry:g,onResumeAudio:R,onToggleMute:D,onMinimize:P,onClose:U,onClearMicError:y})=>{const O=C.length>0?C[C.length-1]:null,F=((m==null?void 0:m.length)??0)>1,Y=p.productName||"Live Layer",$=["ll-expanded",r?"ll-expanded--mobile":"ll-expanded--desktop"].join(" ");return e.jsxs("div",{className:$,"data-position":n,role:"dialog","aria-label":`${l} widget`,children:[e.jsxs("div",{className:"ll-expanded__header",children:[e.jsxs("div",{className:"ll-expanded__header-left",children:[p.logoUrl&&e.jsx("img",{src:p.logoUrl,alt:Y,className:"ll-expanded__logo"}),F?e.jsxs("button",{type:"button",className:"ll-expanded__team-trigger",onClick:c,"aria-expanded":u,"aria-label":"Change team member",children:[e.jsx("span",{children:l}),e.jsx(me,{className:`ll-expanded__team-chevron ${u?"ll-expanded__team-chevron--open":""}`})]}):e.jsx("span",{className:"ll-expanded__name",children:l})]}),e.jsxs("div",{className:"ll-expanded__header-right",children:[e.jsx("button",{type:"button",className:"ll-expanded__icon-btn",onClick:D,"aria-label":N?"Unmute microphone":"Mute microphone",title:N?"Unmute":"Mute",children:e.jsx(Ve,{muted:N,className:"ll-expanded__icon"})}),r?null:e.jsx("button",{type:"button",className:"ll-expanded__icon-btn",onClick:P,"aria-label":"Minimize widget",title:"Minimize",children:e.jsx(me,{className:"ll-expanded__icon"})}),e.jsx("button",{type:"button",className:"ll-expanded__icon-btn",onClick:U,"aria-label":"Close widget",title:"Close",children:e.jsx(ye,{className:"ll-expanded__icon"})})]})]}),F&&u&&e.jsx("div",{className:"ll-expanded__team-menu",role:"menu",children:m.map(L=>e.jsxs("button",{type:"button",role:"menuitem",className:`ll-expanded__team-item ${L.id===f?"ll-expanded__team-item--active":""}`,onClick:()=>o(L.id),children:[L.avatarImageUrl&&e.jsx("img",{src:L.avatarImageUrl,alt:"",className:"ll-expanded__team-item-avatar"}),e.jsxs("div",{className:"ll-expanded__team-item-meta",children:[e.jsx("span",{className:"ll-expanded__team-item-name",children:L.name}),L.role&&e.jsx("span",{className:"ll-expanded__team-item-role",children:L.role})]})]},L.id))}),e.jsxs("div",{className:"ll-expanded__avatar-surface",children:[a&&e.jsx(He,{src:a,alt:l,className:"ll-expanded__avatar-image"}),s&&e.jsx("video",{className:"ll-expanded__avatar-idle-loop",src:s,loop:!0,muted:!0,playsInline:!0,autoPlay:!0}),e.jsx("div",{ref:w,className:"ll-expanded__avatar-live"}),x==="connecting"&&e.jsxs("div",{className:"ll-expanded__overlay ll-expanded__overlay--connecting",children:[e.jsx("div",{className:"ll-expanded__spinner"}),e.jsx("p",{className:"ll-expanded__overlay-text",children:"Connecting..."})]}),S&&e.jsxs("button",{type:"button",className:"ll-expanded__overlay ll-expanded__overlay--gesture",onClick:R,children:[e.jsx("div",{className:"ll-expanded__play-circle",children:e.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:e.jsx("path",{d:"M8 5v14l11-7L8 5z"})})}),e.jsx("p",{className:"ll-expanded__overlay-text",children:"Tap to enable audio"}),e.jsx("p",{className:"ll-expanded__overlay-subtext",children:"Your agent is ready"})]}),!S&&(x==="idle"||x==="disconnected"||x==="error")&&e.jsxs("button",{type:"button",className:"ll-expanded__overlay ll-expanded__overlay--play",onClick:b,"aria-label":M?"Restart paused session":x==="disconnected"?"Reconnect to agent":"Start video call",children:[e.jsx("div",{className:"ll-expanded__play-circle",children:e.jsx("svg",{width:"26",height:"26",viewBox:"0 0 24 24",fill:"currentColor",children:e.jsx("polygon",{points:"6 3 20 12 6 21 6 3"})})}),e.jsx("p",{className:"ll-expanded__overlay-text",children:M?"Restart session":x==="disconnected"?"Click to reconnect":i||"Start video call"}),M&&e.jsx("p",{className:"ll-expanded__overlay-subtext",children:"Pick up where you left off"})]}),_&&e.jsxs("div",{className:"ll-expanded__overlay ll-expanded__overlay--switching",children:[e.jsx("div",{className:"ll-expanded__spinner"}),e.jsxs("p",{className:"ll-expanded__overlay-text",children:["Connecting to ",l,"..."]})]}),x==="connected"&&e.jsx("div",{className:"ll-expanded__state-pill-wrap",children:e.jsx(Ge,{state:v})}),T&&x==="connected"&&e.jsxs("div",{className:"ll-expanded__mic-error",role:"alert",children:[e.jsx("p",{children:T}),e.jsx("button",{type:"button",onClick:()=>{y(),g()},children:"Retry"})]}),x==="connected"&&O&&e.jsx("div",{className:"ll-expanded__transcript","aria-live":"polite",children:e.jsx("p",{children:O.text})})]}),e.jsxs("div",{className:"ll-expanded__footer",children:[x==="idle"&&e.jsxs("button",{type:"button",className:"ll-expanded__cta ll-expanded__cta--primary",onClick:b,children:[e.jsx(We,{className:"ll-expanded__cta-icon"}),e.jsx("span",{children:"Start Video Call"})]}),x==="connecting"&&e.jsxs("button",{type:"button",className:"ll-expanded__cta ll-expanded__cta--loading",disabled:!0,children:[e.jsx("div",{className:"ll-expanded__spinner ll-expanded__spinner--small"}),e.jsx("span",{children:"Connecting..."})]}),x==="connected"&&e.jsx("button",{type:"button",className:"ll-expanded__cta ll-expanded__cta--danger",onClick:k,children:"End conversation"}),(x==="error"||x==="disconnected")&&e.jsxs("div",{className:"ll-expanded__retry",children:[B&&e.jsx("p",{className:"ll-expanded__error-text",children:B}),e.jsxs("button",{type:"button",className:"ll-expanded__cta ll-expanded__cta--primary",onClick:g,children:[e.jsx(te,{muted:!1,className:"ll-expanded__cta-icon"}),e.jsx("span",{children:"Try again"})]})]})]})]})},Qe=new Set(["agent_state","avatar_stream_ready","avatar_active","avatar_idle","bot_ready","agent_error","idle_warning","idle_timeout"]);function Xe(n){var oe,ce,de;const{agentId:r,apiKey:l,baseUrl:a="https://app.livelayer.studio",sessionEndpoint:s,sessionBody:i,autoConnect:p=!1,displayMode:m,defaultDisplayMode:f="expanded",onDisplayModeChange:_,position:u="bottom-right",mobileBreakpoint:c=640,persistKey:o="ll-widget",disablePersistence:x=!1,teamMembers:v,currentTeamMemberId:C,onTeamMemberChange:N,idleLoopUrl:S,greeting:M,avatarImageUrl:T,agentName:B,branding:w={},onConnect:b,onDisconnect:k,onTranscript:g,onAgentState:R,onConnectionStateChange:D,onAgentEvent:P,onAgentCommand:U,controlledSession:y,className:O,style:F,zIndex:Y=2147483647}=n,$=C!==void 0,[L,je]=t.useState(()=>{var h;return C??((h=v==null?void 0:v[0])==null?void 0:h.id)}),V=$?C:L,z=t.useMemo(()=>(v==null?void 0:v.find(h=>h.id===V))??null,[v,V]),be=(z==null?void 0:z.agentId)??r,[W,I]=ge({value:m,defaultValue:f,onChange:_,persistKey:o,disablePersistence:x}),H=ve(c),G=he(),A=xe(),[ke,J]=t.useState(!1),[Q,re]=t.useState(!1),[X,Z]=t.useState(!1),ee=t.useCallback(h=>{const j=h;!j.type||typeof j.type!="string"||(P==null||P({eventName:j.type,data:h}),Qe.has(j.type)||U==null||U(j))},[U,P]),E=fe({agentId:y?"__controlled__":be,baseUrl:a,apiKey:l,sessionEndpoint:s,sessionBody:i,onDataMessage:y?void 0:ee});t.useEffect(()=>{if(y!=null&&y.subscribeToDataMessages)return y.subscribeToDataMessages(ee)},[y,ee]);const d=t.useMemo(()=>y?{connectionState:y.connectionState,agentState:y.agentState,transcript:y.transcript,videoElement:y.videoElement,audioElement:y.audioElement,canResume:y.canResume,error:y.error,agentConfig:null,connect:async()=>{await y.onConnect()},disconnect:()=>y.onDisconnect(),getRoom:E.getRoom,isControlled:!0}:{connectionState:E.connectionState,agentState:E.agentState,transcript:E.transcript,videoElement:E.videoElement,audioElement:E.audioElement,canResume:E.canResume,error:E.error,agentConfig:E.agentConfig,connect:E.connect,disconnect:E.disconnect,getRoom:E.getRoom,isControlled:!1},[y,E]),le=t.useRef(null);t.useEffect(()=>{const h=d.videoElement,j=le.current;if(!(!h||!j))return j.appendChild(h),()=>{h.parentNode===j&&j.removeChild(h)}},[d.videoElement]),t.useEffect(()=>{const h=d.audioElement;if(!h)return;G.attach(h);const j=h.play();return j&&typeof j.catch=="function"&&j.catch(K=>{(K==null?void 0:K.name)==="NotAllowedError"&&J(!0)}),()=>{G.detach()}},[d.audioElement]),t.useEffect(()=>{if(d.isControlled||d.connectionState!=="connected")return;const h=d.getRoom();if(h)return A.setupMic(h).catch(()=>{}),()=>{A.teardownMic()}},[d.isControlled,d.connectionState]),t.useEffect(()=>{D==null||D(d.connectionState),d.connectionState==="connected"?b==null||b():d.connectionState==="disconnected"&&(k==null||k())},[d.connectionState,b,k,D]),t.useEffect(()=>{g==null||g(d.transcript)},[d.transcript,g]),t.useEffect(()=>{R==null||R(d.agentState)},[d.agentState,R]);const se=t.useRef(!1);t.useEffect(()=>{d.isControlled||!p||se.current||d.connectionState==="idle"&&(se.current=!0,d.connect())},[p,d.connectionState,d]);const Ce=t.useCallback(h=>{const j=v==null?void 0:v.find(K=>K.id===h);j&&(Z(!1),h!==V&&(re(!0),d.disconnect(),$||je(h),N==null||N(j)))},[v,V,d,$,N]);t.useEffect(()=>{Q&&d.connectionState==="connected"&&re(!1)},[d.connectionState,Q]),t.useEffect(()=>{if(!X)return;const h=j=>{j.key==="Escape"&&Z(!1)};return window.addEventListener("keydown",h),()=>window.removeEventListener("keydown",h)},[X]);const ne=(z==null?void 0:z.name)??B??((oe=d.agentConfig)==null?void 0:oe.name)??"Live Layer",ae=(z==null?void 0:z.avatarImageUrl)??T??((ce=d.agentConfig)==null?void 0:ce.avatarImageUrl)??null,Ne=S??((de=d.agentConfig)==null?void 0:de.idleLoopUrl)??null,we=M??null,Ee=t.useCallback(()=>I("expanded"),[I]),Se=t.useCallback(()=>I("minimized"),[I]),ie=t.useCallback(()=>I("hidden"),[I]),Me=t.useCallback(()=>{const h=d.audioElement;h&&h.play().then(()=>J(!1)).catch(()=>{})},[d.audioElement]),Le=t.useCallback(()=>{J(!1),d.connect()},[d]),q={...F,zIndex:Y};w.primaryColor&&(q["--ll-color-primary"]=w.primaryColor),w.accentColor&&(q["--ll-color-accent"]=w.accentColor),w.backgroundColor&&(q["--ll-color-bg"]=w.backgroundColor),w.textColor&&(q["--ll-color-fg"]=w.textColor);const Re=["ll-widget",`ll-widget--${W}`,`ll-widget--${H?"mobile":"desktop"}`,O].filter(Boolean).join(" ");return e.jsxs("div",{className:Re,style:q,"data-display-mode":W,"data-position":u,children:[W==="hidden"&&e.jsx(Ke,{position:u,isMobile:H,isSpeaking:d.agentState==="speaking",onExpand:()=>I("minimized"),label:`Open ${ne} widget`}),W==="minimized"&&e.jsx(Fe,{position:u,isMobile:H,agentName:ne,avatarImageUrl:ae,agentState:d.agentState,isMuted:A.isMuted,audioLevel:G,onExpand:Ee,onToggleMute:A.toggleMute,onClose:ie}),W==="expanded"&&e.jsx(Je,{position:u,isMobile:H,agentName:ne,avatarImageUrl:ae,idleLoopUrl:Ne,greeting:we,branding:w,teamMembers:v,currentTeamMemberId:V,isSwitchingTeamMember:Q,teamSwitcherOpen:X,onToggleTeamSwitcher:()=>Z(h=>!h),onSelectTeamMember:Ce,connectionState:d.connectionState,agentState:d.agentState,transcript:d.transcript,isMuted:A.isMuted,needsUserGesture:ke,canResume:d.canResume,micError:A.micError,error:d.error,avatarVideoContainerRef:le,onConnect:()=>void d.connect(),onDisconnect:()=>d.disconnect(),onRetry:Le,onResumeAudio:Me,onToggleMute:A.toggleMute,onMinimize:Se,onClose:ie,onClearMicError:A.clearError})]})}function Ze(n){return e.jsx(pe,{children:e.jsx(Xe,{...n})})}const en=({agentId:n,baseUrl:r,apiKey:l,mode:a,onAgentEvent:s,className:i,style:p})=>{const m=t.useRef(null),f=t.useRef(null),_=t.useRef(s);_.current=s;const u=t.useCallback(c=>{var x;const o=c.detail;(x=_.current)==null||x.call(_,o)},[]);return t.useEffect(()=>{const c=m.current;if(!c)return;const o=document.createElement("livelayer-widget");return o.setAttribute("agent-id",n),r&&o.setAttribute("base-url",r),l&&o.setAttribute("api-key",l),a&&o.setAttribute("mode",a),o.addEventListener("agent-event",u),c.appendChild(o),f.current=o,()=>{o.removeEventListener("agent-event",u),c.removeChild(o),f.current=null}},[n]),t.useEffect(()=>{f.current&&(a?f.current.setAttribute("mode",a):f.current.removeAttribute("mode"))},[a]),e.jsx("div",{ref:m,className:i,style:p})};function nn(){const[n,r]=t.useState([]),l=t.useCallback(s=>{r(i=>{const p=i.findIndex(m=>m.id===s.id);if(p>=0){const m=i.slice();return m[p]=s,m}return[...i,s]})},[]),a=t.useCallback(()=>r([]),[]);return{entries:n,pushSegment:l,clear:a,latest:n.length>0?n[n.length-1]:null}}exports.AvatarWidget=Ze;exports.ErrorBoundary=pe;exports.LiveLayerWidget=en;exports.useAudioLevel=he;exports.useDisplayMode=_e;exports.useDisplayModePersistence=ge;exports.useIsMobile=ve;exports.useLiveKitSession=fe;exports.useMicrophoneState=xe;exports.useTranscript=nn;