@skippr/live-agent-sdk 0.7.0 → 0.9.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/esm/lib-exports.js +243 -138
- package/dist/skippr-sdk.css +1 -1
- package/dist/skippr-sdk.js +107 -107
- package/dist/types/components/ChatInput.d.ts +6 -0
- package/dist/types/components/LiveAgent.d.ts +2 -1
- package/dist/types/components/MessageList.d.ts +3 -1
- package/dist/types/hooks/useChatMessages.d.ts +6 -0
- package/dist/types/hooks/useCombinedMessages.d.ts +8 -0
- package/dist/types/hooks/useSession.d.ts +2 -1
- package/dist/types/hooks/useStreamingTranscript.d.ts +4 -0
- package/dist/types/lib/filterSystemMessages.d.ts +2 -0
- package/dist/types/types.d.ts +2 -0
- package/package.json +1 -1
- package/dist/types/hooks/useTranscriptMessages.d.ts +0 -6
package/dist/esm/lib-exports.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/components/LiveAgent.tsx
|
|
2
2
|
import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
|
|
3
|
-
import { useCallback as
|
|
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";
|
|
@@ -9,7 +9,7 @@ var LiveAgentContext = createContext(null);
|
|
|
9
9
|
// src/hooks/useSession.ts
|
|
10
10
|
import { useCallback, useState } from "react";
|
|
11
11
|
var API_URL = "https://skipprapi-production.up.railway.app";
|
|
12
|
-
function useSession({ organizationId, agentId }) {
|
|
12
|
+
function useSession({ organizationId, agentId, authToken }) {
|
|
13
13
|
const [connection, setConnection] = useState(null);
|
|
14
14
|
const [shouldConnect, setShouldConnect] = useState(false);
|
|
15
15
|
const [isStarting, setIsStarting] = useState(false);
|
|
@@ -19,9 +19,10 @@ function useSession({ organizationId, agentId }) {
|
|
|
19
19
|
setIsStarting(true);
|
|
20
20
|
setError("");
|
|
21
21
|
try {
|
|
22
|
+
const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
|
|
22
23
|
const createResp = await fetch(`${API_URL}/v1/sessions`, {
|
|
23
24
|
method: "POST",
|
|
24
|
-
headers: { "Content-Type": "application/json" },
|
|
25
|
+
headers: { "Content-Type": "application/json", ...authHeaders },
|
|
25
26
|
body: JSON.stringify({ organizationId, agentId })
|
|
26
27
|
});
|
|
27
28
|
if (!createResp.ok) {
|
|
@@ -29,7 +30,8 @@ function useSession({ organizationId, agentId }) {
|
|
|
29
30
|
}
|
|
30
31
|
const { session } = await createResp.json();
|
|
31
32
|
const startResp = await fetch(`${API_URL}/v1/sessions/${session.id}/start`, {
|
|
32
|
-
method: "POST"
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: authHeaders
|
|
33
35
|
});
|
|
34
36
|
if (!startResp.ok) {
|
|
35
37
|
throw new Error(`Failed to start session: ${startResp.status}`);
|
|
@@ -46,13 +48,14 @@ function useSession({ organizationId, agentId }) {
|
|
|
46
48
|
} finally {
|
|
47
49
|
setIsStarting(false);
|
|
48
50
|
}
|
|
49
|
-
}, [organizationId, agentId]);
|
|
51
|
+
}, [organizationId, agentId, authToken]);
|
|
50
52
|
const disconnect = useCallback(async () => {
|
|
51
53
|
if (sessionId) {
|
|
54
|
+
const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
|
|
52
55
|
try {
|
|
53
56
|
await fetch(`${API_URL}/v1/sessions/${sessionId}/complete`, {
|
|
54
57
|
method: "POST",
|
|
55
|
-
headers: { "Content-Type": "application/json" },
|
|
58
|
+
headers: { "Content-Type": "application/json", ...authHeaders },
|
|
56
59
|
body: JSON.stringify({})
|
|
57
60
|
});
|
|
58
61
|
} catch {}
|
|
@@ -61,14 +64,94 @@ function useSession({ organizationId, agentId }) {
|
|
|
61
64
|
setShouldConnect(false);
|
|
62
65
|
setConnection(null);
|
|
63
66
|
setSessionId(null);
|
|
64
|
-
}, [sessionId]);
|
|
67
|
+
}, [sessionId, authToken]);
|
|
65
68
|
return { connection, shouldConnect, isStarting, error, startSession, disconnect };
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
// src/components/Sidebar.tsx
|
|
69
72
|
import { useConnectionState } from "@livekit/components-react";
|
|
70
73
|
import { ConnectionState } from "livekit-client";
|
|
71
|
-
import { useEffect as
|
|
74
|
+
import { useEffect as useEffect4 } from "react";
|
|
75
|
+
|
|
76
|
+
// src/hooks/useCombinedMessages.ts
|
|
77
|
+
import { useVoiceAssistant } from "@livekit/components-react";
|
|
78
|
+
import { useMemo as useMemo3 } from "react";
|
|
79
|
+
|
|
80
|
+
// src/hooks/useChatMessages.ts
|
|
81
|
+
import { useChat, useLocalParticipant } from "@livekit/components-react";
|
|
82
|
+
import { useMemo } from "react";
|
|
83
|
+
|
|
84
|
+
// src/lib/filterSystemMessages.ts
|
|
85
|
+
var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
|
|
86
|
+
function filterSystemMessages(messages) {
|
|
87
|
+
return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/hooks/useChatMessages.ts
|
|
91
|
+
function useChatMessages() {
|
|
92
|
+
const { chatMessages: rawMessages, send, isSending } = useChat();
|
|
93
|
+
const { localParticipant } = useLocalParticipant();
|
|
94
|
+
const localIdentity = localParticipant.identity;
|
|
95
|
+
const chatMessages = useMemo(() => {
|
|
96
|
+
const sortedMessages = rawMessages.map((msg) => ({
|
|
97
|
+
id: msg.id,
|
|
98
|
+
role: msg.from?.identity === localIdentity ? "user" : "assistant",
|
|
99
|
+
content: msg.message,
|
|
100
|
+
source: "chat",
|
|
101
|
+
timestamp: msg.timestamp
|
|
102
|
+
})).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
103
|
+
return filterSystemMessages(sortedMessages);
|
|
104
|
+
}, [rawMessages, localIdentity]);
|
|
105
|
+
return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/hooks/useStreamingTranscript.ts
|
|
109
|
+
import { useLocalParticipant as useLocalParticipant2, useTranscriptions } from "@livekit/components-react";
|
|
110
|
+
import { useMemo as useMemo2 } from "react";
|
|
111
|
+
function useStreamingTranscript() {
|
|
112
|
+
const transcriptions = useTranscriptions();
|
|
113
|
+
const { localParticipant } = useLocalParticipant2();
|
|
114
|
+
const localIdentity = localParticipant.identity;
|
|
115
|
+
const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
|
|
116
|
+
id: stream.streamInfo.id,
|
|
117
|
+
role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
|
|
118
|
+
content: stream.text,
|
|
119
|
+
source: "voice-transcript",
|
|
120
|
+
timestamp: stream.streamInfo.timestamp
|
|
121
|
+
}))), [transcriptions, localIdentity]);
|
|
122
|
+
return { transcriptMessages };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/hooks/useCombinedMessages.ts
|
|
126
|
+
function mergeChatsIntoTranscripts(transcripts, chats) {
|
|
127
|
+
const merged = [];
|
|
128
|
+
let chatIndex = 0;
|
|
129
|
+
for (const transcript of transcripts) {
|
|
130
|
+
while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
|
|
131
|
+
merged.push(chats[chatIndex]);
|
|
132
|
+
chatIndex++;
|
|
133
|
+
}
|
|
134
|
+
merged.push(transcript);
|
|
135
|
+
}
|
|
136
|
+
while (chatIndex < chats.length) {
|
|
137
|
+
merged.push(chats[chatIndex]);
|
|
138
|
+
chatIndex++;
|
|
139
|
+
}
|
|
140
|
+
return merged;
|
|
141
|
+
}
|
|
142
|
+
function useCombinedMessages() {
|
|
143
|
+
const { transcriptMessages } = useStreamingTranscript();
|
|
144
|
+
const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
|
|
145
|
+
const { state: agentState } = useVoiceAssistant();
|
|
146
|
+
const allMessages = useMemo3(() => {
|
|
147
|
+
if (chatMessages.length === 0)
|
|
148
|
+
return transcriptMessages;
|
|
149
|
+
if (transcriptMessages.length === 0)
|
|
150
|
+
return chatMessages;
|
|
151
|
+
return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
|
|
152
|
+
}, [transcriptMessages, chatMessages]);
|
|
153
|
+
return { allMessages, agentState, sendChatMessage, isSendingChat };
|
|
154
|
+
}
|
|
72
155
|
|
|
73
156
|
// src/hooks/useLiveAgent.ts
|
|
74
157
|
import { use } from "react";
|
|
@@ -158,43 +241,6 @@ function useQuestionUpdates() {
|
|
|
158
241
|
return { questions };
|
|
159
242
|
}
|
|
160
243
|
|
|
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
244
|
// src/lib/constants.ts
|
|
199
245
|
var SIDEBAR_WIDTH = 600;
|
|
200
246
|
|
|
@@ -270,7 +316,7 @@ function ChatHeader({ onClose }) {
|
|
|
270
316
|
}
|
|
271
317
|
|
|
272
318
|
// src/components/MeetingControls.tsx
|
|
273
|
-
import { useLocalParticipant as
|
|
319
|
+
import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
|
|
274
320
|
import { ScreenSharePresets } from "livekit-client";
|
|
275
321
|
import { Mic, MicOff, Monitor, MonitorOff, PhoneOff } from "lucide-react";
|
|
276
322
|
import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
|
|
@@ -285,7 +331,7 @@ function formatTime(seconds) {
|
|
|
285
331
|
// src/components/MeetingControls.tsx
|
|
286
332
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
287
333
|
function MeetingControls({ onHangUp }) {
|
|
288
|
-
const { localParticipant } =
|
|
334
|
+
const { localParticipant } = useLocalParticipant3();
|
|
289
335
|
const isMuted = !localParticipant.isMicrophoneEnabled;
|
|
290
336
|
const isScreenSharing = localParticipant.isScreenShareEnabled;
|
|
291
337
|
const [elapsed, setElapsed] = useState3(0);
|
|
@@ -362,15 +408,55 @@ function MeetingControls({ onHangUp }) {
|
|
|
362
408
|
}
|
|
363
409
|
|
|
364
410
|
// src/components/MessageList.tsx
|
|
365
|
-
import {
|
|
411
|
+
import { useEffect as useEffect3, useRef } from "react";
|
|
412
|
+
|
|
413
|
+
// src/components/ChatInput.tsx
|
|
414
|
+
import { SendHorizontal } from "lucide-react";
|
|
415
|
+
import { useState as useState4 } from "react";
|
|
416
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
417
|
+
function ChatInput({ sendChatMessage, isSendingChat }) {
|
|
418
|
+
const [inputText, setInputText] = useState4("");
|
|
419
|
+
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
420
|
+
function handleSubmit(e) {
|
|
421
|
+
e.preventDefault();
|
|
422
|
+
const text = inputText.trim();
|
|
423
|
+
if (!text || isSendingChat)
|
|
424
|
+
return;
|
|
425
|
+
setInputText("");
|
|
426
|
+
sendChatMessage(text).catch(() => setInputText(text));
|
|
427
|
+
}
|
|
428
|
+
return /* @__PURE__ */ jsxs3("form", {
|
|
429
|
+
onSubmit: handleSubmit,
|
|
430
|
+
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
|
|
431
|
+
children: [
|
|
432
|
+
/* @__PURE__ */ jsx4("input", {
|
|
433
|
+
type: "text",
|
|
434
|
+
value: inputText,
|
|
435
|
+
onChange: (e) => setInputText(e.target.value),
|
|
436
|
+
placeholder: "Type a message...",
|
|
437
|
+
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"),
|
|
438
|
+
disabled: isSendingChat
|
|
439
|
+
}),
|
|
440
|
+
/* @__PURE__ */ jsx4("button", {
|
|
441
|
+
type: "submit",
|
|
442
|
+
disabled: !canSend,
|
|
443
|
+
"aria-label": "Send message",
|
|
444
|
+
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"),
|
|
445
|
+
children: /* @__PURE__ */ jsx4(SendHorizontal, {
|
|
446
|
+
className: "skippr:size-4"
|
|
447
|
+
})
|
|
448
|
+
})
|
|
449
|
+
]
|
|
450
|
+
});
|
|
451
|
+
}
|
|
366
452
|
|
|
367
453
|
// src/components/ChatMessage.tsx
|
|
368
|
-
import { jsx as
|
|
454
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
369
455
|
function ChatMessage({ message }) {
|
|
370
456
|
const isUser = message.role === "user";
|
|
371
|
-
return /* @__PURE__ */
|
|
457
|
+
return /* @__PURE__ */ jsx5("div", {
|
|
372
458
|
className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
|
|
373
|
-
children: /* @__PURE__ */
|
|
459
|
+
children: /* @__PURE__ */ jsx5("div", {
|
|
374
460
|
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
461
|
children: message.content
|
|
376
462
|
})
|
|
@@ -378,20 +464,20 @@ function ChatMessage({ message }) {
|
|
|
378
464
|
}
|
|
379
465
|
|
|
380
466
|
// src/components/TypingIndicator.tsx
|
|
381
|
-
import { jsx as
|
|
467
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
382
468
|
function TypingIndicator() {
|
|
383
|
-
return /* @__PURE__ */
|
|
469
|
+
return /* @__PURE__ */ jsx6("div", {
|
|
384
470
|
className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
|
|
385
|
-
children: /* @__PURE__ */
|
|
471
|
+
children: /* @__PURE__ */ jsxs4("div", {
|
|
386
472
|
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
473
|
children: [
|
|
388
|
-
/* @__PURE__ */
|
|
474
|
+
/* @__PURE__ */ jsx6("span", {
|
|
389
475
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
|
|
390
476
|
}),
|
|
391
|
-
/* @__PURE__ */
|
|
477
|
+
/* @__PURE__ */ jsx6("span", {
|
|
392
478
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
|
|
393
479
|
}),
|
|
394
|
-
/* @__PURE__ */
|
|
480
|
+
/* @__PURE__ */ jsx6("span", {
|
|
395
481
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
|
|
396
482
|
})
|
|
397
483
|
]
|
|
@@ -400,56 +486,71 @@ function TypingIndicator() {
|
|
|
400
486
|
}
|
|
401
487
|
|
|
402
488
|
// src/components/MessageList.tsx
|
|
403
|
-
import { jsx as
|
|
404
|
-
function MessageList({
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
489
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
490
|
+
function MessageList({
|
|
491
|
+
messages,
|
|
492
|
+
isStreaming,
|
|
493
|
+
sendChatMessage,
|
|
494
|
+
isSendingChat
|
|
495
|
+
}) {
|
|
496
|
+
const scrollRef = useRef(null);
|
|
408
497
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
498
|
+
useEffect3(() => {
|
|
499
|
+
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
500
|
+
}, [messages.length, lastMessage?.content]);
|
|
409
501
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
410
|
-
return /* @__PURE__ */
|
|
411
|
-
className: "skippr:flex-1 skippr:
|
|
412
|
-
children:
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
502
|
+
return /* @__PURE__ */ jsxs5("div", {
|
|
503
|
+
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
504
|
+
children: [
|
|
505
|
+
/* @__PURE__ */ jsx7("div", {
|
|
506
|
+
className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
|
|
507
|
+
children: /* @__PURE__ */ jsxs5("div", {
|
|
508
|
+
className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
|
|
509
|
+
children: [
|
|
510
|
+
messages.map((message) => /* @__PURE__ */ jsx7(ChatMessage, {
|
|
511
|
+
message
|
|
512
|
+
}, message.id)),
|
|
513
|
+
showTyping && /* @__PURE__ */ jsx7(TypingIndicator, {}),
|
|
514
|
+
/* @__PURE__ */ jsx7("div", {
|
|
515
|
+
ref: scrollRef
|
|
516
|
+
})
|
|
517
|
+
]
|
|
518
|
+
})
|
|
519
|
+
}),
|
|
520
|
+
/* @__PURE__ */ jsx7(ChatInput, {
|
|
521
|
+
sendChatMessage,
|
|
522
|
+
isSendingChat
|
|
523
|
+
})
|
|
524
|
+
]
|
|
424
525
|
});
|
|
425
526
|
}
|
|
426
527
|
|
|
427
528
|
// src/components/QuickActions.tsx
|
|
428
529
|
import { Loader2, MessageCircleQuestion } from "lucide-react";
|
|
429
|
-
import { jsx as
|
|
530
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
430
531
|
function QuickActions({ onStartSession, isStarting, error }) {
|
|
431
|
-
return /* @__PURE__ */
|
|
532
|
+
return /* @__PURE__ */ jsxs6("div", {
|
|
432
533
|
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
534
|
children: [
|
|
434
|
-
/* @__PURE__ */
|
|
535
|
+
/* @__PURE__ */ jsx8("p", {
|
|
435
536
|
className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
|
|
436
537
|
children: "How can I help you today?"
|
|
437
538
|
}),
|
|
438
|
-
/* @__PURE__ */
|
|
539
|
+
/* @__PURE__ */ jsxs6(Button, {
|
|
439
540
|
variant: "outline",
|
|
440
541
|
className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
|
|
441
542
|
onClick: onStartSession,
|
|
442
543
|
disabled: isStarting,
|
|
443
544
|
children: [
|
|
444
|
-
isStarting ? /* @__PURE__ */
|
|
545
|
+
isStarting ? /* @__PURE__ */ jsx8(Loader2, {
|
|
445
546
|
className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
|
|
446
|
-
}) : /* @__PURE__ */
|
|
547
|
+
}) : /* @__PURE__ */ jsx8(MessageCircleQuestion, {
|
|
447
548
|
className: "skippr:size-4 skippr:text-primary"
|
|
448
549
|
}),
|
|
449
550
|
isStarting ? "Starting..." : "Start Session"
|
|
450
551
|
]
|
|
451
552
|
}),
|
|
452
|
-
error && /* @__PURE__ */
|
|
553
|
+
error && /* @__PURE__ */ jsx8("p", {
|
|
453
554
|
className: "skippr:text-xs skippr:text-destructive",
|
|
454
555
|
children: error
|
|
455
556
|
})
|
|
@@ -459,52 +560,52 @@ function QuickActions({ onStartSession, isStarting, error }) {
|
|
|
459
560
|
|
|
460
561
|
// src/components/SessionAgenda.tsx
|
|
461
562
|
import { Check, Circle, Loader2 as Loader22 } from "lucide-react";
|
|
462
|
-
import { jsx as
|
|
563
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
463
564
|
function SessionAgenda({ phases, questions = [] }) {
|
|
464
565
|
if (phases.length === 0) {
|
|
465
|
-
return /* @__PURE__ */
|
|
566
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
466
567
|
className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
|
|
467
568
|
children: [
|
|
468
|
-
/* @__PURE__ */
|
|
569
|
+
/* @__PURE__ */ jsx9("h3", {
|
|
469
570
|
className: "skippr:text-sm skippr:font-semibold",
|
|
470
571
|
children: "Agenda"
|
|
471
572
|
}),
|
|
472
|
-
/* @__PURE__ */
|
|
573
|
+
/* @__PURE__ */ jsx9("p", {
|
|
473
574
|
className: "skippr:text-xs skippr:text-muted-foreground",
|
|
474
575
|
children: "Waiting for session..."
|
|
475
576
|
})
|
|
476
577
|
]
|
|
477
578
|
});
|
|
478
579
|
}
|
|
479
|
-
return /* @__PURE__ */
|
|
580
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
480
581
|
className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
|
|
481
582
|
children: [
|
|
482
|
-
/* @__PURE__ */
|
|
583
|
+
/* @__PURE__ */ jsx9("h3", {
|
|
483
584
|
className: "skippr:text-sm skippr:font-semibold",
|
|
484
585
|
children: "Agenda"
|
|
485
586
|
}),
|
|
486
|
-
/* @__PURE__ */
|
|
587
|
+
/* @__PURE__ */ jsx9("ul", {
|
|
487
588
|
className: "skippr:flex skippr:flex-col skippr:gap-2",
|
|
488
589
|
children: phases.map((phase) => {
|
|
489
590
|
const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
|
|
490
591
|
const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
|
|
491
592
|
const totalCount = phaseQuestions.length;
|
|
492
|
-
return /* @__PURE__ */
|
|
593
|
+
return /* @__PURE__ */ jsxs7("li", {
|
|
493
594
|
className: "skippr:flex skippr:flex-col skippr:gap-0.5",
|
|
494
595
|
children: [
|
|
495
|
-
/* @__PURE__ */
|
|
596
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
496
597
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
|
|
497
598
|
children: [
|
|
498
|
-
/* @__PURE__ */
|
|
599
|
+
/* @__PURE__ */ jsx9(PhaseIcon, {
|
|
499
600
|
status: phase.status
|
|
500
601
|
}),
|
|
501
|
-
/* @__PURE__ */
|
|
602
|
+
/* @__PURE__ */ jsx9("span", {
|
|
502
603
|
className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
|
|
503
604
|
children: phase.name
|
|
504
605
|
})
|
|
505
606
|
]
|
|
506
607
|
}),
|
|
507
|
-
totalCount > 0 && /* @__PURE__ */
|
|
608
|
+
totalCount > 0 && /* @__PURE__ */ jsxs7("span", {
|
|
508
609
|
className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
|
|
509
610
|
children: [
|
|
510
611
|
answeredCount,
|
|
@@ -522,56 +623,56 @@ function SessionAgenda({ phases, questions = [] }) {
|
|
|
522
623
|
}
|
|
523
624
|
function PhaseIcon({ status }) {
|
|
524
625
|
if (status === "completed") {
|
|
525
|
-
return /* @__PURE__ */
|
|
626
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
526
627
|
className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
|
|
527
|
-
children: /* @__PURE__ */
|
|
628
|
+
children: /* @__PURE__ */ jsx9(Check, {
|
|
528
629
|
className: "skippr:size-2.5 skippr:text-primary-foreground",
|
|
529
630
|
strokeWidth: 3
|
|
530
631
|
})
|
|
531
632
|
});
|
|
532
633
|
}
|
|
533
634
|
if (status === "active") {
|
|
534
|
-
return /* @__PURE__ */
|
|
635
|
+
return /* @__PURE__ */ jsx9(Loader22, {
|
|
535
636
|
className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
|
|
536
637
|
});
|
|
537
638
|
}
|
|
538
|
-
return /* @__PURE__ */
|
|
639
|
+
return /* @__PURE__ */ jsx9(Circle, {
|
|
539
640
|
className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
|
|
540
641
|
});
|
|
541
642
|
}
|
|
542
643
|
|
|
543
644
|
// src/components/Sidebar.tsx
|
|
544
|
-
import { jsx as
|
|
645
|
+
import { jsx as jsx10, jsxs as jsxs8, Fragment } from "react/jsx-runtime";
|
|
545
646
|
function Sidebar() {
|
|
546
647
|
const { isConnected, isStarting, error, startSession, disconnect, isPanelOpen, closePanel } = useLiveAgent();
|
|
547
|
-
|
|
648
|
+
useEffect4(() => {
|
|
548
649
|
document.body.style.transition = "margin-right 300ms ease-in-out";
|
|
549
650
|
document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
|
|
550
651
|
}, [isPanelOpen]);
|
|
551
|
-
|
|
652
|
+
useEffect4(() => {
|
|
552
653
|
return () => {
|
|
553
654
|
document.body.style.marginRight = "";
|
|
554
655
|
document.body.style.transition = "";
|
|
555
656
|
};
|
|
556
657
|
}, []);
|
|
557
|
-
return /* @__PURE__ */
|
|
658
|
+
return /* @__PURE__ */ jsx10("div", {
|
|
558
659
|
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
660
|
style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
|
|
560
|
-
children: isConnected ? /* @__PURE__ */
|
|
661
|
+
children: isConnected ? /* @__PURE__ */ jsxs8(Fragment, {
|
|
561
662
|
children: [
|
|
562
|
-
/* @__PURE__ */
|
|
663
|
+
/* @__PURE__ */ jsx10(ChatHeader, {
|
|
563
664
|
onClose: closePanel
|
|
564
665
|
}),
|
|
565
|
-
/* @__PURE__ */
|
|
666
|
+
/* @__PURE__ */ jsx10(ConnectedContent, {
|
|
566
667
|
onDisconnect: disconnect
|
|
567
668
|
})
|
|
568
669
|
]
|
|
569
|
-
}) : /* @__PURE__ */
|
|
670
|
+
}) : /* @__PURE__ */ jsxs8(Fragment, {
|
|
570
671
|
children: [
|
|
571
|
-
/* @__PURE__ */
|
|
672
|
+
/* @__PURE__ */ jsx10(ChatHeader, {
|
|
572
673
|
onClose: closePanel
|
|
573
674
|
}),
|
|
574
|
-
/* @__PURE__ */
|
|
675
|
+
/* @__PURE__ */ jsx10(QuickActions, {
|
|
575
676
|
onStartSession: startSession,
|
|
576
677
|
isStarting,
|
|
577
678
|
error
|
|
@@ -583,39 +684,41 @@ function Sidebar() {
|
|
|
583
684
|
function ConnectedContent({ onDisconnect }) {
|
|
584
685
|
const connectionState = useConnectionState();
|
|
585
686
|
const isConnected = connectionState === ConnectionState.Connected;
|
|
586
|
-
const {
|
|
687
|
+
const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
|
|
587
688
|
const { phases } = usePhaseUpdates();
|
|
588
689
|
const { questions } = useQuestionUpdates();
|
|
589
690
|
if (!isConnected) {
|
|
590
|
-
return /* @__PURE__ */
|
|
691
|
+
return /* @__PURE__ */ jsx10("div", {
|
|
591
692
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
592
|
-
children: /* @__PURE__ */
|
|
693
|
+
children: /* @__PURE__ */ jsx10("p", {
|
|
593
694
|
className: "skippr:text-sm skippr:text-muted-foreground",
|
|
594
695
|
children: "Connecting..."
|
|
595
696
|
})
|
|
596
697
|
});
|
|
597
698
|
}
|
|
598
699
|
const isAgentSpeaking = agentState === "speaking";
|
|
599
|
-
return /* @__PURE__ */
|
|
700
|
+
return /* @__PURE__ */ jsxs8(Fragment, {
|
|
600
701
|
children: [
|
|
601
|
-
/* @__PURE__ */
|
|
702
|
+
/* @__PURE__ */ jsx10(MeetingControls, {
|
|
602
703
|
onHangUp: onDisconnect
|
|
603
704
|
}),
|
|
604
|
-
/* @__PURE__ */
|
|
705
|
+
/* @__PURE__ */ jsxs8("div", {
|
|
605
706
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1",
|
|
606
707
|
children: [
|
|
607
|
-
/* @__PURE__ */
|
|
708
|
+
/* @__PURE__ */ jsx10("div", {
|
|
608
709
|
className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
|
|
609
|
-
children: /* @__PURE__ */
|
|
710
|
+
children: /* @__PURE__ */ jsx10(SessionAgenda, {
|
|
610
711
|
phases,
|
|
611
712
|
questions
|
|
612
713
|
})
|
|
613
714
|
}),
|
|
614
|
-
/* @__PURE__ */
|
|
715
|
+
/* @__PURE__ */ jsx10("div", {
|
|
615
716
|
className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
|
|
616
|
-
children: /* @__PURE__ */
|
|
617
|
-
messages,
|
|
618
|
-
isStreaming: isAgentSpeaking
|
|
717
|
+
children: /* @__PURE__ */ jsx10(MessageList, {
|
|
718
|
+
messages: allMessages,
|
|
719
|
+
isStreaming: isAgentSpeaking,
|
|
720
|
+
sendChatMessage,
|
|
721
|
+
isSendingChat
|
|
619
722
|
})
|
|
620
723
|
})
|
|
621
724
|
]
|
|
@@ -626,43 +729,45 @@ function ConnectedContent({ onDisconnect }) {
|
|
|
626
729
|
|
|
627
730
|
// src/components/SidebarTrigger.tsx
|
|
628
731
|
import { MessageCircle, X as X2 } from "lucide-react";
|
|
629
|
-
import { jsx as
|
|
732
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
630
733
|
var TRIGGER_GAP = 16;
|
|
631
734
|
var TRIGGER_DEFAULT_RIGHT = 24;
|
|
632
735
|
function SidebarTrigger() {
|
|
633
736
|
const { isPanelOpen, togglePanel } = useLiveAgent();
|
|
634
|
-
return /* @__PURE__ */
|
|
737
|
+
return /* @__PURE__ */ jsx11(Button, {
|
|
635
738
|
size: "icon-lg",
|
|
636
739
|
onClick: togglePanel,
|
|
637
740
|
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
741
|
style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
|
|
639
742
|
title: isPanelOpen ? "Close chat" : "Chat with us",
|
|
640
|
-
children: isPanelOpen ? /* @__PURE__ */
|
|
743
|
+
children: isPanelOpen ? /* @__PURE__ */ jsx11(X2, {
|
|
641
744
|
className: "skippr:size-6"
|
|
642
|
-
}) : /* @__PURE__ */
|
|
745
|
+
}) : /* @__PURE__ */ jsx11(MessageCircle, {
|
|
643
746
|
className: "skippr:size-6"
|
|
644
747
|
})
|
|
645
748
|
});
|
|
646
749
|
}
|
|
647
750
|
|
|
648
751
|
// src/components/LiveAgent.tsx
|
|
649
|
-
import { jsx as
|
|
752
|
+
import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
650
753
|
function LiveAgent({
|
|
651
754
|
organizationId,
|
|
652
755
|
agentId,
|
|
756
|
+
authToken,
|
|
653
757
|
defaultOpen = false,
|
|
654
758
|
children
|
|
655
759
|
}) {
|
|
656
760
|
const { connection, shouldConnect, isStarting, error, startSession, disconnect } = useSession({
|
|
657
761
|
organizationId,
|
|
658
|
-
agentId
|
|
762
|
+
agentId,
|
|
763
|
+
authToken
|
|
659
764
|
});
|
|
660
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
661
|
-
const openPanel =
|
|
662
|
-
const closePanel =
|
|
663
|
-
const togglePanel =
|
|
765
|
+
const [isPanelOpen, setIsPanelOpen] = useState5(defaultOpen);
|
|
766
|
+
const openPanel = useCallback5(() => setIsPanelOpen(true), []);
|
|
767
|
+
const closePanel = useCallback5(() => setIsPanelOpen(false), []);
|
|
768
|
+
const togglePanel = useCallback5(() => setIsPanelOpen((prev) => !prev), []);
|
|
664
769
|
const isConnected = connection !== null;
|
|
665
|
-
const ctx =
|
|
770
|
+
const ctx = useMemo4(() => ({
|
|
666
771
|
connection,
|
|
667
772
|
shouldConnect,
|
|
668
773
|
isConnected,
|
|
@@ -687,17 +792,17 @@ function LiveAgent({
|
|
|
687
792
|
closePanel,
|
|
688
793
|
togglePanel
|
|
689
794
|
]);
|
|
690
|
-
const widgetContent = /* @__PURE__ */
|
|
795
|
+
const widgetContent = /* @__PURE__ */ jsxs9(Fragment2, {
|
|
691
796
|
children: [
|
|
692
|
-
connection && /* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
797
|
+
connection && /* @__PURE__ */ jsx12(RoomAudioRenderer, {}),
|
|
798
|
+
/* @__PURE__ */ jsx12(SidebarTrigger, {}),
|
|
799
|
+
/* @__PURE__ */ jsx12(Sidebar, {}),
|
|
695
800
|
children
|
|
696
801
|
]
|
|
697
802
|
});
|
|
698
|
-
return /* @__PURE__ */
|
|
803
|
+
return /* @__PURE__ */ jsx12(LiveAgentContext.Provider, {
|
|
699
804
|
value: ctx,
|
|
700
|
-
children: connection ? /* @__PURE__ */
|
|
805
|
+
children: connection ? /* @__PURE__ */ jsx12(LiveKitRoom, {
|
|
701
806
|
serverUrl: connection.livekitUrl,
|
|
702
807
|
token: connection.token,
|
|
703
808
|
connect: shouldConnect,
|