@skippr/live-agent-sdk 0.6.0 → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  // src/components/LiveAgent.tsx
2
2
  import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
3
- import { useCallback as useCallback6, useMemo as useMemo2, useState as useState4 } from "react";
3
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState5 } from "react";
4
4
 
5
5
  // src/context/LiveAgentContext.tsx
6
6
  import { createContext } from "react";
@@ -68,7 +68,87 @@ function useSession({ organizationId, agentId }) {
68
68
  // src/components/Sidebar.tsx
69
69
  import { useConnectionState } from "@livekit/components-react";
70
70
  import { ConnectionState } from "livekit-client";
71
- import { useEffect as useEffect3 } from "react";
71
+ import { useEffect as useEffect4 } from "react";
72
+
73
+ // src/hooks/useCombinedMessages.ts
74
+ import { useVoiceAssistant } from "@livekit/components-react";
75
+ import { useMemo as useMemo3 } from "react";
76
+
77
+ // src/hooks/useChatMessages.ts
78
+ import { useChat, useLocalParticipant } from "@livekit/components-react";
79
+ import { useMemo } from "react";
80
+
81
+ // src/lib/filterSystemMessages.ts
82
+ var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
83
+ function filterSystemMessages(messages) {
84
+ return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
85
+ }
86
+
87
+ // src/hooks/useChatMessages.ts
88
+ function useChatMessages() {
89
+ const { chatMessages: rawMessages, send, isSending } = useChat();
90
+ const { localParticipant } = useLocalParticipant();
91
+ const localIdentity = localParticipant.identity;
92
+ const chatMessages = useMemo(() => {
93
+ const sortedMessages = rawMessages.map((msg) => ({
94
+ id: msg.id,
95
+ role: msg.from?.identity === localIdentity ? "user" : "assistant",
96
+ content: msg.message,
97
+ source: "chat",
98
+ timestamp: msg.timestamp
99
+ })).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
100
+ return filterSystemMessages(sortedMessages);
101
+ }, [rawMessages, localIdentity]);
102
+ return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
103
+ }
104
+
105
+ // src/hooks/useStreamingTranscript.ts
106
+ import { useLocalParticipant as useLocalParticipant2, useTranscriptions } from "@livekit/components-react";
107
+ import { useMemo as useMemo2 } from "react";
108
+ function useStreamingTranscript() {
109
+ const transcriptions = useTranscriptions();
110
+ const { localParticipant } = useLocalParticipant2();
111
+ const localIdentity = localParticipant.identity;
112
+ const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
113
+ id: stream.streamInfo.id,
114
+ role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
115
+ content: stream.text,
116
+ source: "voice-transcript",
117
+ timestamp: stream.streamInfo.timestamp
118
+ }))), [transcriptions, localIdentity]);
119
+ return { transcriptMessages };
120
+ }
121
+
122
+ // src/hooks/useCombinedMessages.ts
123
+ function mergeChatsIntoTranscripts(transcripts, chats) {
124
+ const merged = [];
125
+ let chatIndex = 0;
126
+ for (const transcript of transcripts) {
127
+ while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
128
+ merged.push(chats[chatIndex]);
129
+ chatIndex++;
130
+ }
131
+ merged.push(transcript);
132
+ }
133
+ while (chatIndex < chats.length) {
134
+ merged.push(chats[chatIndex]);
135
+ chatIndex++;
136
+ }
137
+ return merged;
138
+ }
139
+ function useCombinedMessages() {
140
+ const { transcriptMessages } = useStreamingTranscript();
141
+ const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
142
+ const { state: agentState } = useVoiceAssistant();
143
+ const allMessages = useMemo3(() => {
144
+ if (chatMessages.length === 0)
145
+ return transcriptMessages;
146
+ if (transcriptMessages.length === 0)
147
+ return chatMessages;
148
+ return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
149
+ }, [transcriptMessages, chatMessages]);
150
+ return { allMessages, agentState, sendChatMessage, isSendingChat };
151
+ }
72
152
 
73
153
  // src/hooks/useLiveAgent.ts
74
154
  import { use } from "react";
@@ -158,43 +238,6 @@ function useQuestionUpdates() {
158
238
  return { questions };
159
239
  }
160
240
 
161
- // src/hooks/useTranscriptMessages.ts
162
- import {
163
- useLocalParticipant,
164
- useTrackTranscription,
165
- useVoiceAssistant
166
- } from "@livekit/components-react";
167
- import { Track } from "livekit-client";
168
- import { useMemo } from "react";
169
- function segmentsToMessages(segments, role) {
170
- return segments.filter((s) => s.final && s.text.trim().length > 0).map((s) => ({
171
- id: s.id,
172
- role,
173
- content: s.text
174
- }));
175
- }
176
- function useTranscriptMessages() {
177
- const { agentTranscriptions, state } = useVoiceAssistant();
178
- const { localParticipant } = useLocalParticipant();
179
- const localMicTrack = useMemo(() => localParticipant.getTrackPublication(Track.Source.Microphone) ? {
180
- participant: localParticipant,
181
- source: Track.Source.Microphone,
182
- publication: localParticipant.getTrackPublication(Track.Source.Microphone)
183
- } : undefined, [localParticipant]);
184
- const { segments: userSegments } = useTrackTranscription(localMicTrack);
185
- const messages = useMemo(() => {
186
- const agent = segmentsToMessages(agentTranscriptions, "assistant");
187
- const user = segmentsToMessages(userSegments, "user");
188
- const timeMap = new Map;
189
- for (const s of agentTranscriptions)
190
- timeMap.set(s.id, s.firstReceivedTime);
191
- for (const s of userSegments)
192
- timeMap.set(s.id, s.firstReceivedTime);
193
- return [...agent, ...user].sort((a, b) => (timeMap.get(a.id) ?? 0) - (timeMap.get(b.id) ?? 0));
194
- }, [agentTranscriptions, userSegments]);
195
- return { messages, agentState: state };
196
- }
197
-
198
241
  // src/lib/constants.ts
199
242
  var SIDEBAR_WIDTH = 600;
200
243
 
@@ -210,7 +253,7 @@ import { Bot, X } from "lucide-react";
210
253
 
211
254
  // src/components/ui/button.tsx
212
255
  import { forwardRef } from "react";
213
- import { jsxDEV } from "react/jsx-dev-runtime";
256
+ import { jsx } from "react/jsx-runtime";
214
257
  var variantClasses = {
215
258
  default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
216
259
  destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
@@ -229,48 +272,48 @@ var sizeClasses = {
229
272
  "icon-lg": "skippr:size-10"
230
273
  };
231
274
  var Button = forwardRef(({ className, variant = "default", size = "default", ...props }, ref) => {
232
- return /* @__PURE__ */ jsxDEV("button", {
275
+ return /* @__PURE__ */ jsx("button", {
233
276
  className: cn("skippr:inline-flex skippr:items-center skippr:justify-center skippr:gap-2 skippr:whitespace-nowrap skippr:rounded-md skippr:text-sm skippr:font-medium skippr:ring-offset-background skippr:transition-all skippr:focus-visible:outline-none skippr:focus-visible:ring-2 skippr:focus-visible:ring-ring skippr:focus-visible:ring-offset-2 skippr:disabled:pointer-events-none skippr:disabled:opacity-50 skippr:shrink-0 skippr:[&_svg]:pointer-events-none skippr:[&_svg:not([class*='size-'])]:size-4 skippr:[&_svg]:shrink-0", variantClasses[variant], sizeClasses[size], className),
234
277
  ref,
235
278
  ...props
236
- }, undefined, false, undefined, this);
279
+ });
237
280
  });
238
281
  Button.displayName = "Button";
239
282
 
240
283
  // src/components/ChatHeader.tsx
241
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
284
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
242
285
  function ChatHeader({ onClose }) {
243
- return /* @__PURE__ */ jsxDEV2("div", {
286
+ return /* @__PURE__ */ jsxs("div", {
244
287
  className: "skippr:flex skippr:items-center skippr:gap-3 skippr:bg-primary skippr:px-4 skippr:py-3 skippr:text-primary-foreground",
245
288
  children: [
246
- /* @__PURE__ */ jsxDEV2("div", {
289
+ /* @__PURE__ */ jsx2("div", {
247
290
  className: "skippr:flex skippr:size-6 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary-foreground/20",
248
- children: /* @__PURE__ */ jsxDEV2(Bot, {
291
+ children: /* @__PURE__ */ jsx2(Bot, {
249
292
  className: "skippr:size-3.5 skippr:text-primary-foreground"
250
- }, undefined, false, undefined, this)
251
- }, undefined, false, undefined, this),
252
- /* @__PURE__ */ jsxDEV2("div", {
293
+ })
294
+ }),
295
+ /* @__PURE__ */ jsx2("div", {
253
296
  className: "skippr:flex-1",
254
- children: /* @__PURE__ */ jsxDEV2("p", {
297
+ children: /* @__PURE__ */ jsx2("p", {
255
298
  className: "skippr:text-sm skippr:font-semibold skippr:leading-none",
256
299
  children: "AI Agent"
257
- }, undefined, false, undefined, this)
258
- }, undefined, false, undefined, this),
259
- /* @__PURE__ */ jsxDEV2(Button, {
300
+ })
301
+ }),
302
+ /* @__PURE__ */ jsx2(Button, {
260
303
  variant: "ghost",
261
304
  size: "icon-xs",
262
305
  onClick: onClose,
263
306
  className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
264
- children: /* @__PURE__ */ jsxDEV2(X, {
307
+ children: /* @__PURE__ */ jsx2(X, {
265
308
  className: "skippr:size-4"
266
- }, undefined, false, undefined, this)
267
- }, undefined, false, undefined, this)
309
+ })
310
+ })
268
311
  ]
269
- }, undefined, true, undefined, this);
312
+ });
270
313
  }
271
314
 
272
315
  // src/components/MeetingControls.tsx
273
- import { useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
316
+ import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
274
317
  import { ScreenSharePresets } from "livekit-client";
275
318
  import { Mic, MicOff, Monitor, MonitorOff, PhoneOff } from "lucide-react";
276
319
  import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
@@ -283,9 +326,9 @@ function formatTime(seconds) {
283
326
  }
284
327
 
285
328
  // src/components/MeetingControls.tsx
286
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
329
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
287
330
  function MeetingControls({ onHangUp }) {
288
- const { localParticipant } = useLocalParticipant2();
331
+ const { localParticipant } = useLocalParticipant3();
289
332
  const isMuted = !localParticipant.isMicrophoneEnabled;
290
333
  const isScreenSharing = localParticipant.isScreenShareEnabled;
291
334
  const [elapsed, setElapsed] = useState3(0);
@@ -314,197 +357,252 @@ function MeetingControls({ onHangUp }) {
314
357
  useEffect2(() => {
315
358
  toggleMute().then(() => toggleScreenShare());
316
359
  }, []);
317
- return /* @__PURE__ */ jsxDEV3("div", {
360
+ return /* @__PURE__ */ jsxs2("div", {
318
361
  className: "skippr:flex skippr:items-center skippr:justify-between skippr:border-b skippr:px-4 skippr:py-3",
319
362
  children: [
320
- /* @__PURE__ */ jsxDEV3("div", {
363
+ /* @__PURE__ */ jsxs2("div", {
321
364
  className: "skippr:flex skippr:items-center skippr:gap-2",
322
365
  children: [
323
- /* @__PURE__ */ jsxDEV3(Button, {
366
+ /* @__PURE__ */ jsx3(Button, {
324
367
  size: "icon-sm",
325
368
  variant: isMuted ? "destructive" : "outline",
326
369
  onClick: toggleMute,
327
370
  "aria-label": isMuted ? "Unmute" : "Mute",
328
- children: isMuted ? /* @__PURE__ */ jsxDEV3(MicOff, {
371
+ children: isMuted ? /* @__PURE__ */ jsx3(MicOff, {
329
372
  className: "skippr:size-4"
330
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3(Mic, {
373
+ }) : /* @__PURE__ */ jsx3(Mic, {
331
374
  className: "skippr:size-4"
332
- }, undefined, false, undefined, this)
333
- }, undefined, false, undefined, this),
334
- /* @__PURE__ */ jsxDEV3(Button, {
375
+ })
376
+ }),
377
+ /* @__PURE__ */ jsx3(Button, {
335
378
  size: "icon-sm",
336
379
  variant: isScreenSharing ? "default" : "outline",
337
380
  onClick: toggleScreenShare,
338
381
  "aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
339
- children: isScreenSharing ? /* @__PURE__ */ jsxDEV3(MonitorOff, {
382
+ children: isScreenSharing ? /* @__PURE__ */ jsx3(MonitorOff, {
340
383
  className: "skippr:size-4"
341
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3(Monitor, {
384
+ }) : /* @__PURE__ */ jsx3(Monitor, {
342
385
  className: "skippr:size-4"
343
- }, undefined, false, undefined, this)
344
- }, undefined, false, undefined, this)
386
+ })
387
+ })
345
388
  ]
346
- }, undefined, true, undefined, this),
347
- /* @__PURE__ */ jsxDEV3("span", {
389
+ }),
390
+ /* @__PURE__ */ jsx3("span", {
348
391
  className: "skippr:text-sm skippr:font-medium skippr:tabular-nums skippr:text-muted-foreground",
349
392
  children: formatTime(elapsed)
350
- }, undefined, false, undefined, this),
351
- /* @__PURE__ */ jsxDEV3(Button, {
393
+ }),
394
+ /* @__PURE__ */ jsx3(Button, {
352
395
  size: "icon-sm",
353
396
  variant: "destructive",
354
397
  onClick: onHangUp,
355
398
  "aria-label": "Hang up",
356
- children: /* @__PURE__ */ jsxDEV3(PhoneOff, {
399
+ children: /* @__PURE__ */ jsx3(PhoneOff, {
357
400
  className: "skippr:size-4"
358
- }, undefined, false, undefined, this)
359
- }, undefined, false, undefined, this)
401
+ })
402
+ })
360
403
  ]
361
- }, undefined, true, undefined, this);
404
+ });
362
405
  }
363
406
 
364
407
  // src/components/MessageList.tsx
365
- import { useCallback as useCallback5 } from "react";
408
+ import { useEffect as useEffect3, useRef } from "react";
409
+
410
+ // src/components/ChatInput.tsx
411
+ import { SendHorizontal } from "lucide-react";
412
+ import { useState as useState4 } from "react";
413
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
414
+ function ChatInput({ sendChatMessage, isSendingChat }) {
415
+ const [inputText, setInputText] = useState4("");
416
+ const canSend = inputText.trim().length > 0 && !isSendingChat;
417
+ function handleSubmit(e) {
418
+ e.preventDefault();
419
+ const text = inputText.trim();
420
+ if (!text || isSendingChat)
421
+ return;
422
+ setInputText("");
423
+ sendChatMessage(text).catch(() => setInputText(text));
424
+ }
425
+ return /* @__PURE__ */ jsxs3("form", {
426
+ onSubmit: handleSubmit,
427
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
428
+ children: [
429
+ /* @__PURE__ */ jsx4("input", {
430
+ type: "text",
431
+ value: inputText,
432
+ onChange: (e) => setInputText(e.target.value),
433
+ placeholder: "Type a message...",
434
+ className: cn("skippr:flex-1 skippr:rounded-lg skippr:border skippr:border-border skippr:bg-background", "skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground", "skippr:placeholder:text-muted-foreground skippr:outline-none", "skippr:focus:ring-1 skippr:focus:ring-ring"),
435
+ disabled: isSendingChat
436
+ }),
437
+ /* @__PURE__ */ jsx4("button", {
438
+ type: "submit",
439
+ disabled: !canSend,
440
+ "aria-label": "Send message",
441
+ className: cn("skippr:flex skippr:size-9 skippr:shrink-0 skippr:items-center skippr:justify-center", "skippr:rounded-lg skippr:bg-primary skippr:text-primary-foreground", "skippr:transition-opacity", !canSend && "skippr:opacity-50 skippr:cursor-not-allowed"),
442
+ children: /* @__PURE__ */ jsx4(SendHorizontal, {
443
+ className: "skippr:size-4"
444
+ })
445
+ })
446
+ ]
447
+ });
448
+ }
366
449
 
367
450
  // src/components/ChatMessage.tsx
368
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
451
+ import { jsx as jsx5 } from "react/jsx-runtime";
369
452
  function ChatMessage({ message }) {
370
453
  const isUser = message.role === "user";
371
- return /* @__PURE__ */ jsxDEV4("div", {
454
+ return /* @__PURE__ */ jsx5("div", {
372
455
  className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
373
- children: /* @__PURE__ */ jsxDEV4("div", {
456
+ children: /* @__PURE__ */ jsx5("div", {
374
457
  className: cn("skippr:max-w-[85%] skippr:whitespace-pre-wrap skippr:rounded-2xl skippr:px-4 skippr:py-2.5 skippr:text-sm skippr:leading-relaxed", isUser ? "skippr:rounded-br-sm skippr:bg-primary skippr:text-primary-foreground" : "skippr:rounded-bl-sm skippr:bg-muted skippr:text-foreground"),
375
458
  children: message.content
376
- }, undefined, false, undefined, this)
377
- }, undefined, false, undefined, this);
459
+ })
460
+ });
378
461
  }
379
462
 
380
463
  // src/components/TypingIndicator.tsx
381
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
464
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
382
465
  function TypingIndicator() {
383
- return /* @__PURE__ */ jsxDEV5("div", {
466
+ return /* @__PURE__ */ jsx6("div", {
384
467
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
385
- children: /* @__PURE__ */ jsxDEV5("div", {
468
+ children: /* @__PURE__ */ jsxs4("div", {
386
469
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:rounded-2xl skippr:rounded-bl-sm skippr:bg-muted skippr:px-4 skippr:py-2.5",
387
470
  children: [
388
- /* @__PURE__ */ jsxDEV5("span", {
471
+ /* @__PURE__ */ jsx6("span", {
389
472
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
390
- }, undefined, false, undefined, this),
391
- /* @__PURE__ */ jsxDEV5("span", {
473
+ }),
474
+ /* @__PURE__ */ jsx6("span", {
392
475
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
393
- }, undefined, false, undefined, this),
394
- /* @__PURE__ */ jsxDEV5("span", {
476
+ }),
477
+ /* @__PURE__ */ jsx6("span", {
395
478
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
396
- }, undefined, false, undefined, this)
479
+ })
397
480
  ]
398
- }, undefined, true, undefined, this)
399
- }, undefined, false, undefined, this);
481
+ })
482
+ });
400
483
  }
401
484
 
402
485
  // src/components/MessageList.tsx
403
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
404
- function MessageList({ messages, isStreaming }) {
405
- const scrollRef = useCallback5((node) => {
406
- node?.scrollIntoView({ behavior: "smooth" });
407
- }, []);
486
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
487
+ function MessageList({
488
+ messages,
489
+ isStreaming,
490
+ sendChatMessage,
491
+ isSendingChat
492
+ }) {
493
+ const scrollRef = useRef(null);
408
494
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
495
+ useEffect3(() => {
496
+ scrollRef.current?.scrollIntoView({ behavior: "smooth" });
497
+ }, [messages.length, lastMessage?.content]);
409
498
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
410
- return /* @__PURE__ */ jsxDEV6("div", {
411
- className: "skippr:flex-1 skippr:overflow-y-auto",
412
- children: /* @__PURE__ */ jsxDEV6("div", {
413
- className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
414
- children: [
415
- messages.map((message) => /* @__PURE__ */ jsxDEV6(ChatMessage, {
416
- message
417
- }, message.id, false, undefined, this)),
418
- showTyping && /* @__PURE__ */ jsxDEV6(TypingIndicator, {}, undefined, false, undefined, this),
419
- /* @__PURE__ */ jsxDEV6("div", {
420
- ref: scrollRef
421
- }, `scroll-${messages.length}`, false, undefined, this)
422
- ]
423
- }, undefined, true, undefined, this)
424
- }, undefined, false, undefined, this);
499
+ return /* @__PURE__ */ jsxs5("div", {
500
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
501
+ children: [
502
+ /* @__PURE__ */ jsx7("div", {
503
+ className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
504
+ children: /* @__PURE__ */ jsxs5("div", {
505
+ className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
506
+ children: [
507
+ messages.map((message) => /* @__PURE__ */ jsx7(ChatMessage, {
508
+ message
509
+ }, message.id)),
510
+ showTyping && /* @__PURE__ */ jsx7(TypingIndicator, {}),
511
+ /* @__PURE__ */ jsx7("div", {
512
+ ref: scrollRef
513
+ })
514
+ ]
515
+ })
516
+ }),
517
+ /* @__PURE__ */ jsx7(ChatInput, {
518
+ sendChatMessage,
519
+ isSendingChat
520
+ })
521
+ ]
522
+ });
425
523
  }
426
524
 
427
525
  // src/components/QuickActions.tsx
428
526
  import { Loader2, MessageCircleQuestion } from "lucide-react";
429
- import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
527
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
430
528
  function QuickActions({ onStartSession, isStarting, error }) {
431
- return /* @__PURE__ */ jsxDEV7("div", {
529
+ return /* @__PURE__ */ jsxs6("div", {
432
530
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:gap-6 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
433
531
  children: [
434
- /* @__PURE__ */ jsxDEV7("p", {
532
+ /* @__PURE__ */ jsx8("p", {
435
533
  className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
436
534
  children: "How can I help you today?"
437
- }, undefined, false, undefined, this),
438
- /* @__PURE__ */ jsxDEV7(Button, {
535
+ }),
536
+ /* @__PURE__ */ jsxs6(Button, {
439
537
  variant: "outline",
440
538
  className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
441
539
  onClick: onStartSession,
442
540
  disabled: isStarting,
443
541
  children: [
444
- isStarting ? /* @__PURE__ */ jsxDEV7(Loader2, {
542
+ isStarting ? /* @__PURE__ */ jsx8(Loader2, {
445
543
  className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
446
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7(MessageCircleQuestion, {
544
+ }) : /* @__PURE__ */ jsx8(MessageCircleQuestion, {
447
545
  className: "skippr:size-4 skippr:text-primary"
448
- }, undefined, false, undefined, this),
546
+ }),
449
547
  isStarting ? "Starting..." : "Start Session"
450
548
  ]
451
- }, undefined, true, undefined, this),
452
- error && /* @__PURE__ */ jsxDEV7("p", {
549
+ }),
550
+ error && /* @__PURE__ */ jsx8("p", {
453
551
  className: "skippr:text-xs skippr:text-destructive",
454
552
  children: error
455
- }, undefined, false, undefined, this)
553
+ })
456
554
  ]
457
- }, undefined, true, undefined, this);
555
+ });
458
556
  }
459
557
 
460
558
  // src/components/SessionAgenda.tsx
461
559
  import { Check, Circle, Loader2 as Loader22 } from "lucide-react";
462
- import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
560
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
463
561
  function SessionAgenda({ phases, questions = [] }) {
464
562
  if (phases.length === 0) {
465
- return /* @__PURE__ */ jsxDEV8("div", {
563
+ return /* @__PURE__ */ jsxs7("div", {
466
564
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
467
565
  children: [
468
- /* @__PURE__ */ jsxDEV8("h3", {
566
+ /* @__PURE__ */ jsx9("h3", {
469
567
  className: "skippr:text-sm skippr:font-semibold",
470
568
  children: "Agenda"
471
- }, undefined, false, undefined, this),
472
- /* @__PURE__ */ jsxDEV8("p", {
569
+ }),
570
+ /* @__PURE__ */ jsx9("p", {
473
571
  className: "skippr:text-xs skippr:text-muted-foreground",
474
572
  children: "Waiting for session..."
475
- }, undefined, false, undefined, this)
573
+ })
476
574
  ]
477
- }, undefined, true, undefined, this);
575
+ });
478
576
  }
479
- return /* @__PURE__ */ jsxDEV8("div", {
577
+ return /* @__PURE__ */ jsxs7("div", {
480
578
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
481
579
  children: [
482
- /* @__PURE__ */ jsxDEV8("h3", {
580
+ /* @__PURE__ */ jsx9("h3", {
483
581
  className: "skippr:text-sm skippr:font-semibold",
484
582
  children: "Agenda"
485
- }, undefined, false, undefined, this),
486
- /* @__PURE__ */ jsxDEV8("ul", {
583
+ }),
584
+ /* @__PURE__ */ jsx9("ul", {
487
585
  className: "skippr:flex skippr:flex-col skippr:gap-2",
488
586
  children: phases.map((phase) => {
489
587
  const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
490
588
  const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
491
589
  const totalCount = phaseQuestions.length;
492
- return /* @__PURE__ */ jsxDEV8("li", {
590
+ return /* @__PURE__ */ jsxs7("li", {
493
591
  className: "skippr:flex skippr:flex-col skippr:gap-0.5",
494
592
  children: [
495
- /* @__PURE__ */ jsxDEV8("div", {
593
+ /* @__PURE__ */ jsxs7("div", {
496
594
  className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
497
595
  children: [
498
- /* @__PURE__ */ jsxDEV8(PhaseIcon, {
596
+ /* @__PURE__ */ jsx9(PhaseIcon, {
499
597
  status: phase.status
500
- }, undefined, false, undefined, this),
501
- /* @__PURE__ */ jsxDEV8("span", {
598
+ }),
599
+ /* @__PURE__ */ jsx9("span", {
502
600
  className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
503
601
  children: phase.name
504
- }, undefined, false, undefined, this)
602
+ })
505
603
  ]
506
- }, undefined, true, undefined, this),
507
- totalCount > 0 && /* @__PURE__ */ jsxDEV8("span", {
604
+ }),
605
+ totalCount > 0 && /* @__PURE__ */ jsxs7("span", {
508
606
  className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
509
607
  children: [
510
608
  answeredCount,
@@ -512,141 +610,143 @@ function SessionAgenda({ phases, questions = [] }) {
512
610
  totalCount,
513
611
  " answered"
514
612
  ]
515
- }, undefined, true, undefined, this)
613
+ })
516
614
  ]
517
- }, phase.name, true, undefined, this);
615
+ }, phase.name);
518
616
  })
519
- }, undefined, false, undefined, this)
617
+ })
520
618
  ]
521
- }, undefined, true, undefined, this);
619
+ });
522
620
  }
523
621
  function PhaseIcon({ status }) {
524
622
  if (status === "completed") {
525
- return /* @__PURE__ */ jsxDEV8("div", {
623
+ return /* @__PURE__ */ jsx9("div", {
526
624
  className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
527
- children: /* @__PURE__ */ jsxDEV8(Check, {
625
+ children: /* @__PURE__ */ jsx9(Check, {
528
626
  className: "skippr:size-2.5 skippr:text-primary-foreground",
529
627
  strokeWidth: 3
530
- }, undefined, false, undefined, this)
531
- }, undefined, false, undefined, this);
628
+ })
629
+ });
532
630
  }
533
631
  if (status === "active") {
534
- return /* @__PURE__ */ jsxDEV8(Loader22, {
632
+ return /* @__PURE__ */ jsx9(Loader22, {
535
633
  className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
536
- }, undefined, false, undefined, this);
634
+ });
537
635
  }
538
- return /* @__PURE__ */ jsxDEV8(Circle, {
636
+ return /* @__PURE__ */ jsx9(Circle, {
539
637
  className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
540
- }, undefined, false, undefined, this);
638
+ });
541
639
  }
542
640
 
543
641
  // src/components/Sidebar.tsx
544
- import { jsxDEV as jsxDEV9, Fragment } from "react/jsx-dev-runtime";
642
+ import { jsx as jsx10, jsxs as jsxs8, Fragment } from "react/jsx-runtime";
545
643
  function Sidebar() {
546
644
  const { isConnected, isStarting, error, startSession, disconnect, isPanelOpen, closePanel } = useLiveAgent();
547
- useEffect3(() => {
645
+ useEffect4(() => {
548
646
  document.body.style.transition = "margin-right 300ms ease-in-out";
549
647
  document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
550
648
  }, [isPanelOpen]);
551
- useEffect3(() => {
649
+ useEffect4(() => {
552
650
  return () => {
553
651
  document.body.style.marginRight = "";
554
652
  document.body.style.transition = "";
555
653
  };
556
654
  }, []);
557
- return /* @__PURE__ */ jsxDEV9("div", {
655
+ return /* @__PURE__ */ jsx10("div", {
558
656
  className: cn("skippr:fixed skippr:top-0 skippr:right-0 skippr:h-full skippr:z-[9999]", "skippr:bg-background skippr:border-l skippr:border-border", "skippr:flex skippr:flex-col", "skippr:transition-all skippr:duration-300 skippr:ease-in-out skippr:overflow-hidden", !isPanelOpen && "skippr:w-0 skippr:border-l-0"),
559
657
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
560
- children: isConnected ? /* @__PURE__ */ jsxDEV9(Fragment, {
658
+ children: isConnected ? /* @__PURE__ */ jsxs8(Fragment, {
561
659
  children: [
562
- /* @__PURE__ */ jsxDEV9(ChatHeader, {
660
+ /* @__PURE__ */ jsx10(ChatHeader, {
563
661
  onClose: closePanel
564
- }, undefined, false, undefined, this),
565
- /* @__PURE__ */ jsxDEV9(ConnectedContent, {
662
+ }),
663
+ /* @__PURE__ */ jsx10(ConnectedContent, {
566
664
  onDisconnect: disconnect
567
- }, undefined, false, undefined, this)
665
+ })
568
666
  ]
569
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV9(Fragment, {
667
+ }) : /* @__PURE__ */ jsxs8(Fragment, {
570
668
  children: [
571
- /* @__PURE__ */ jsxDEV9(ChatHeader, {
669
+ /* @__PURE__ */ jsx10(ChatHeader, {
572
670
  onClose: closePanel
573
- }, undefined, false, undefined, this),
574
- /* @__PURE__ */ jsxDEV9(QuickActions, {
671
+ }),
672
+ /* @__PURE__ */ jsx10(QuickActions, {
575
673
  onStartSession: startSession,
576
674
  isStarting,
577
675
  error
578
- }, undefined, false, undefined, this)
676
+ })
579
677
  ]
580
- }, undefined, true, undefined, this)
581
- }, undefined, false, undefined, this);
678
+ })
679
+ });
582
680
  }
583
681
  function ConnectedContent({ onDisconnect }) {
584
682
  const connectionState = useConnectionState();
585
683
  const isConnected = connectionState === ConnectionState.Connected;
586
- const { messages, agentState } = useTranscriptMessages();
684
+ const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
587
685
  const { phases } = usePhaseUpdates();
588
686
  const { questions } = useQuestionUpdates();
589
687
  if (!isConnected) {
590
- return /* @__PURE__ */ jsxDEV9("div", {
688
+ return /* @__PURE__ */ jsx10("div", {
591
689
  className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
592
- children: /* @__PURE__ */ jsxDEV9("p", {
690
+ children: /* @__PURE__ */ jsx10("p", {
593
691
  className: "skippr:text-sm skippr:text-muted-foreground",
594
692
  children: "Connecting..."
595
- }, undefined, false, undefined, this)
596
- }, undefined, false, undefined, this);
693
+ })
694
+ });
597
695
  }
598
696
  const isAgentSpeaking = agentState === "speaking";
599
- return /* @__PURE__ */ jsxDEV9(Fragment, {
697
+ return /* @__PURE__ */ jsxs8(Fragment, {
600
698
  children: [
601
- /* @__PURE__ */ jsxDEV9(MeetingControls, {
699
+ /* @__PURE__ */ jsx10(MeetingControls, {
602
700
  onHangUp: onDisconnect
603
- }, undefined, false, undefined, this),
604
- /* @__PURE__ */ jsxDEV9("div", {
701
+ }),
702
+ /* @__PURE__ */ jsxs8("div", {
605
703
  className: "skippr:flex skippr:min-h-0 skippr:flex-1",
606
704
  children: [
607
- /* @__PURE__ */ jsxDEV9("div", {
705
+ /* @__PURE__ */ jsx10("div", {
608
706
  className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
609
- children: /* @__PURE__ */ jsxDEV9(SessionAgenda, {
707
+ children: /* @__PURE__ */ jsx10(SessionAgenda, {
610
708
  phases,
611
709
  questions
612
- }, undefined, false, undefined, this)
613
- }, undefined, false, undefined, this),
614
- /* @__PURE__ */ jsxDEV9("div", {
710
+ })
711
+ }),
712
+ /* @__PURE__ */ jsx10("div", {
615
713
  className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
616
- children: /* @__PURE__ */ jsxDEV9(MessageList, {
617
- messages,
618
- isStreaming: isAgentSpeaking
619
- }, undefined, false, undefined, this)
620
- }, undefined, false, undefined, this)
714
+ children: /* @__PURE__ */ jsx10(MessageList, {
715
+ messages: allMessages,
716
+ isStreaming: isAgentSpeaking,
717
+ sendChatMessage,
718
+ isSendingChat
719
+ })
720
+ })
621
721
  ]
622
- }, undefined, true, undefined, this)
722
+ })
623
723
  ]
624
- }, undefined, true, undefined, this);
724
+ });
625
725
  }
626
726
 
627
727
  // src/components/SidebarTrigger.tsx
628
728
  import { MessageCircle, X as X2 } from "lucide-react";
629
- import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
729
+ import { jsx as jsx11 } from "react/jsx-runtime";
630
730
  var TRIGGER_GAP = 16;
631
731
  var TRIGGER_DEFAULT_RIGHT = 24;
632
732
  function SidebarTrigger() {
633
733
  const { isPanelOpen, togglePanel } = useLiveAgent();
634
- return /* @__PURE__ */ jsxDEV10(Button, {
734
+ return /* @__PURE__ */ jsx11(Button, {
635
735
  size: "icon-lg",
636
736
  onClick: togglePanel,
637
737
  className: "skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300",
638
738
  style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
639
739
  title: isPanelOpen ? "Close chat" : "Chat with us",
640
- children: isPanelOpen ? /* @__PURE__ */ jsxDEV10(X2, {
740
+ children: isPanelOpen ? /* @__PURE__ */ jsx11(X2, {
641
741
  className: "skippr:size-6"
642
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV10(MessageCircle, {
742
+ }) : /* @__PURE__ */ jsx11(MessageCircle, {
643
743
  className: "skippr:size-6"
644
- }, undefined, false, undefined, this)
645
- }, undefined, false, undefined, this);
744
+ })
745
+ });
646
746
  }
647
747
 
648
748
  // src/components/LiveAgent.tsx
649
- import { jsxDEV as jsxDEV11, Fragment as Fragment2 } from "react/jsx-dev-runtime";
749
+ import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment2 } from "react/jsx-runtime";
650
750
  function LiveAgent({
651
751
  organizationId,
652
752
  agentId,
@@ -657,12 +757,12 @@ function LiveAgent({
657
757
  organizationId,
658
758
  agentId
659
759
  });
660
- const [isPanelOpen, setIsPanelOpen] = useState4(defaultOpen);
661
- const openPanel = useCallback6(() => setIsPanelOpen(true), []);
662
- const closePanel = useCallback6(() => setIsPanelOpen(false), []);
663
- const togglePanel = useCallback6(() => setIsPanelOpen((prev) => !prev), []);
760
+ const [isPanelOpen, setIsPanelOpen] = useState5(defaultOpen);
761
+ const openPanel = useCallback5(() => setIsPanelOpen(true), []);
762
+ const closePanel = useCallback5(() => setIsPanelOpen(false), []);
763
+ const togglePanel = useCallback5(() => setIsPanelOpen((prev) => !prev), []);
664
764
  const isConnected = connection !== null;
665
- const ctx = useMemo2(() => ({
765
+ const ctx = useMemo4(() => ({
666
766
  connection,
667
767
  shouldConnect,
668
768
  isConnected,
@@ -687,25 +787,25 @@ function LiveAgent({
687
787
  closePanel,
688
788
  togglePanel
689
789
  ]);
690
- const widgetContent = /* @__PURE__ */ jsxDEV11(Fragment2, {
790
+ const widgetContent = /* @__PURE__ */ jsxs9(Fragment2, {
691
791
  children: [
692
- connection && /* @__PURE__ */ jsxDEV11(RoomAudioRenderer, {}, undefined, false, undefined, this),
693
- /* @__PURE__ */ jsxDEV11(SidebarTrigger, {}, undefined, false, undefined, this),
694
- /* @__PURE__ */ jsxDEV11(Sidebar, {}, undefined, false, undefined, this),
792
+ connection && /* @__PURE__ */ jsx12(RoomAudioRenderer, {}),
793
+ /* @__PURE__ */ jsx12(SidebarTrigger, {}),
794
+ /* @__PURE__ */ jsx12(Sidebar, {}),
695
795
  children
696
796
  ]
697
- }, undefined, true, undefined, this);
698
- return /* @__PURE__ */ jsxDEV11(LiveAgentContext.Provider, {
797
+ });
798
+ return /* @__PURE__ */ jsx12(LiveAgentContext.Provider, {
699
799
  value: ctx,
700
- children: connection ? /* @__PURE__ */ jsxDEV11(LiveKitRoom, {
800
+ children: connection ? /* @__PURE__ */ jsx12(LiveKitRoom, {
701
801
  serverUrl: connection.livekitUrl,
702
802
  token: connection.token,
703
803
  connect: shouldConnect,
704
804
  audio: true,
705
805
  onDisconnected: disconnect,
706
806
  children: widgetContent
707
- }, undefined, false, undefined, this) : widgetContent
708
- }, undefined, false, undefined, this);
807
+ }) : widgetContent
808
+ });
709
809
  }
710
810
  export {
711
811
  useLiveAgent,