@skippr/live-agent-sdk 0.13.0 → 0.15.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/README.md +17 -0
- package/dist/esm/lib-exports.js +607 -174
- package/dist/skippr-sdk.css +1 -1
- package/dist/skippr-sdk.js +116 -116
- package/dist/types/components/LiveAgent.d.ts +2 -1
- package/dist/types/components/LoginFlow.d.ts +8 -0
- package/dist/types/context/LiveAgentContext.d.ts +7 -0
- package/dist/types/hooks/useAuth.d.ts +14 -0
- package/dist/types/hooks/useSession.d.ts +2 -1
- package/package.json +1 -1
package/dist/esm/lib-exports.js
CHANGED
|
@@ -1,33 +1,175 @@
|
|
|
1
1
|
// src/components/LiveAgent.tsx
|
|
2
2
|
import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
|
|
3
|
-
import { useCallback as
|
|
3
|
+
import { useCallback as useCallback7, useMemo as useMemo4, useState as useState7 } from "react";
|
|
4
4
|
|
|
5
5
|
// src/context/LiveAgentContext.tsx
|
|
6
6
|
import { createContext } from "react";
|
|
7
7
|
var LiveAgentContext = createContext(null);
|
|
8
8
|
|
|
9
|
-
// src/hooks/
|
|
10
|
-
import { useCallback, useState } from "react";
|
|
9
|
+
// src/hooks/useAuth.ts
|
|
10
|
+
import { useCallback, useEffect, useState } from "react";
|
|
11
11
|
var API_URL = "https://skipprapi-production.up.railway.app";
|
|
12
|
-
function
|
|
12
|
+
function storageKey(appKey) {
|
|
13
|
+
return `skippr_auth_${appKey}`;
|
|
14
|
+
}
|
|
15
|
+
function getStoredToken(appKey) {
|
|
16
|
+
try {
|
|
17
|
+
return localStorage.getItem(storageKey(appKey));
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function setStoredToken(appKey, token) {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem(storageKey(appKey), token);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error("[Skippr] Failed to persist auth token:", e);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function clearStoredToken(appKey) {
|
|
30
|
+
try {
|
|
31
|
+
localStorage.removeItem(storageKey(appKey));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error("[Skippr] Failed to clear auth token:", e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function useAuth({ appKey }) {
|
|
37
|
+
const [authToken, setAuthToken] = useState(null);
|
|
38
|
+
const [isValidating, setIsValidating] = useState(false);
|
|
39
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
40
|
+
const [error, setError] = useState("");
|
|
41
|
+
const isAuthenticated = authToken !== null;
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!appKey)
|
|
44
|
+
return;
|
|
45
|
+
const stored = getStoredToken(appKey);
|
|
46
|
+
if (!stored)
|
|
47
|
+
return;
|
|
48
|
+
async function validateStoredToken() {
|
|
49
|
+
setIsValidating(true);
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetch(`${API_URL}/v1/auth/validate-token`, {
|
|
52
|
+
headers: { Authorization: `Bearer ${stored}` }
|
|
53
|
+
});
|
|
54
|
+
if (res.ok) {
|
|
55
|
+
setAuthToken(stored);
|
|
56
|
+
} else {
|
|
57
|
+
clearStoredToken(appKey);
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
clearStoredToken(appKey);
|
|
61
|
+
} finally {
|
|
62
|
+
setIsValidating(false);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
validateStoredToken();
|
|
66
|
+
}, [appKey]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
function handleLogout() {
|
|
69
|
+
setAuthToken(null);
|
|
70
|
+
setError("");
|
|
71
|
+
}
|
|
72
|
+
window.addEventListener("skippr:logout", handleLogout);
|
|
73
|
+
return () => window.removeEventListener("skippr:logout", handleLogout);
|
|
74
|
+
}, []);
|
|
75
|
+
const requestOtp = useCallback(async (email) => {
|
|
76
|
+
if (!appKey)
|
|
77
|
+
return false;
|
|
78
|
+
setIsSubmitting(true);
|
|
79
|
+
setError("");
|
|
80
|
+
try {
|
|
81
|
+
const resp = await fetch(`${API_URL}/v1/auth/request-otp`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"X-App-Key": appKey
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({ email })
|
|
88
|
+
});
|
|
89
|
+
if (!resp.ok) {
|
|
90
|
+
const body = await resp.json().catch(() => ({}));
|
|
91
|
+
throw new Error(body.detail || "Failed to send verification code");
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
} catch (e) {
|
|
95
|
+
setError(e instanceof Error ? e.message : "Failed to send verification code");
|
|
96
|
+
return false;
|
|
97
|
+
} finally {
|
|
98
|
+
setIsSubmitting(false);
|
|
99
|
+
}
|
|
100
|
+
}, [appKey]);
|
|
101
|
+
const verifyOtp = useCallback(async (email, code) => {
|
|
102
|
+
if (!appKey)
|
|
103
|
+
return;
|
|
104
|
+
setIsSubmitting(true);
|
|
105
|
+
setError("");
|
|
106
|
+
try {
|
|
107
|
+
const resp = await fetch(`${API_URL}/v1/auth/verify-otp`, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"X-App-Key": appKey
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({ email, code, recaptchaToken: "sdk" })
|
|
114
|
+
});
|
|
115
|
+
if (!resp.ok) {
|
|
116
|
+
const body = await resp.json().catch(() => ({}));
|
|
117
|
+
throw new Error(body.detail || "Invalid or expired verification code");
|
|
118
|
+
}
|
|
119
|
+
const { token } = await resp.json();
|
|
120
|
+
setStoredToken(appKey, token);
|
|
121
|
+
setAuthToken(token);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
setError(e instanceof Error ? e.message : "Verification failed");
|
|
124
|
+
return;
|
|
125
|
+
} finally {
|
|
126
|
+
setIsSubmitting(false);
|
|
127
|
+
}
|
|
128
|
+
}, [appKey]);
|
|
129
|
+
const logout = useCallback(() => {
|
|
130
|
+
if (appKey)
|
|
131
|
+
clearStoredToken(appKey);
|
|
132
|
+
setAuthToken(null);
|
|
133
|
+
setError("");
|
|
134
|
+
}, [appKey]);
|
|
135
|
+
return {
|
|
136
|
+
isAuthenticated,
|
|
137
|
+
isValidating,
|
|
138
|
+
authToken,
|
|
139
|
+
requestOtp,
|
|
140
|
+
verifyOtp,
|
|
141
|
+
logout,
|
|
142
|
+
error,
|
|
143
|
+
isSubmitting
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/hooks/useSession.ts
|
|
148
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
149
|
+
var API_URL2 = "https://skipprapi-production.up.railway.app";
|
|
150
|
+
function resolveAuthHeaders(authToken, appKey, userToken) {
|
|
151
|
+
const headers = {};
|
|
13
152
|
if (authToken)
|
|
14
|
-
|
|
153
|
+
headers.Authorization = `Bearer ${authToken}`;
|
|
15
154
|
if (appKey)
|
|
16
|
-
|
|
17
|
-
|
|
155
|
+
headers["X-App-Key"] = appKey;
|
|
156
|
+
if (userToken)
|
|
157
|
+
headers["X-User-Token"] = userToken;
|
|
158
|
+
return headers;
|
|
18
159
|
}
|
|
19
|
-
function useSession({ agentId, authToken, appKey }) {
|
|
20
|
-
const [connection, setConnection] =
|
|
21
|
-
const [shouldConnect, setShouldConnect] =
|
|
22
|
-
const [isStarting, setIsStarting] =
|
|
23
|
-
const [error, setError] =
|
|
24
|
-
const [sessionId, setSessionId] =
|
|
25
|
-
const
|
|
160
|
+
function useSession({ agentId, authToken, appKey, userToken }) {
|
|
161
|
+
const [connection, setConnection] = useState2(null);
|
|
162
|
+
const [shouldConnect, setShouldConnect] = useState2(false);
|
|
163
|
+
const [isStarting, setIsStarting] = useState2(false);
|
|
164
|
+
const [error, setError] = useState2("");
|
|
165
|
+
const [sessionId, setSessionId] = useState2(null);
|
|
166
|
+
const [sessionToken, setSessionToken] = useState2(null);
|
|
167
|
+
const startSession = useCallback2(async () => {
|
|
26
168
|
setIsStarting(true);
|
|
27
169
|
setError("");
|
|
28
170
|
try {
|
|
29
|
-
const authHeaders = resolveAuthHeaders(authToken, appKey);
|
|
30
|
-
const createResp = await fetch(`${
|
|
171
|
+
const authHeaders = resolveAuthHeaders(authToken, appKey, userToken);
|
|
172
|
+
const createResp = await fetch(`${API_URL2}/v1/sessions`, {
|
|
31
173
|
method: "POST",
|
|
32
174
|
headers: { "Content-Type": "application/json", ...authHeaders },
|
|
33
175
|
body: JSON.stringify({ agentId })
|
|
@@ -36,15 +178,17 @@ function useSession({ agentId, authToken, appKey }) {
|
|
|
36
178
|
throw new Error(`Failed to create session: ${createResp.status}`);
|
|
37
179
|
}
|
|
38
180
|
const { session } = await createResp.json();
|
|
39
|
-
const
|
|
181
|
+
const sessionHeaders = { "X-Session-Token": session.sessionToken };
|
|
182
|
+
const startResp = await fetch(`${API_URL2}/v1/sessions/${session.id}/start`, {
|
|
40
183
|
method: "POST",
|
|
41
|
-
headers:
|
|
184
|
+
headers: sessionHeaders
|
|
42
185
|
});
|
|
43
186
|
if (!startResp.ok) {
|
|
44
187
|
throw new Error(`Failed to start session: ${startResp.status}`);
|
|
45
188
|
}
|
|
46
189
|
const { connection: conn } = await startResp.json();
|
|
47
190
|
setSessionId(session.id);
|
|
191
|
+
setSessionToken(session.sessionToken);
|
|
48
192
|
setConnection({
|
|
49
193
|
livekitUrl: conn.livekitUrl,
|
|
50
194
|
token: conn.token
|
|
@@ -55,14 +199,14 @@ function useSession({ agentId, authToken, appKey }) {
|
|
|
55
199
|
} finally {
|
|
56
200
|
setIsStarting(false);
|
|
57
201
|
}
|
|
58
|
-
}, [agentId, authToken, appKey]);
|
|
59
|
-
const disconnect =
|
|
60
|
-
if (sessionId) {
|
|
61
|
-
const
|
|
202
|
+
}, [agentId, authToken, appKey, userToken]);
|
|
203
|
+
const disconnect = useCallback2(async () => {
|
|
204
|
+
if (sessionId && sessionToken) {
|
|
205
|
+
const sessionHeaders = { "X-Session-Token": sessionToken };
|
|
62
206
|
try {
|
|
63
|
-
await fetch(`${
|
|
207
|
+
await fetch(`${API_URL2}/v1/sessions/${sessionId}/complete`, {
|
|
64
208
|
method: "POST",
|
|
65
|
-
headers: { "Content-Type": "application/json", ...
|
|
209
|
+
headers: { "Content-Type": "application/json", ...sessionHeaders },
|
|
66
210
|
body: JSON.stringify({})
|
|
67
211
|
});
|
|
68
212
|
} catch {}
|
|
@@ -71,14 +215,15 @@ function useSession({ agentId, authToken, appKey }) {
|
|
|
71
215
|
setShouldConnect(false);
|
|
72
216
|
setConnection(null);
|
|
73
217
|
setSessionId(null);
|
|
74
|
-
|
|
218
|
+
setSessionToken(null);
|
|
219
|
+
}, [sessionId, sessionToken]);
|
|
75
220
|
return { connection, shouldConnect, isStarting, error, startSession, disconnect };
|
|
76
221
|
}
|
|
77
222
|
|
|
78
223
|
// src/components/Sidebar.tsx
|
|
79
224
|
import { useConnectionState } from "@livekit/components-react";
|
|
80
225
|
import { ConnectionState } from "livekit-client";
|
|
81
|
-
import { useEffect as
|
|
226
|
+
import { useEffect as useEffect6 } from "react";
|
|
82
227
|
|
|
83
228
|
// src/hooks/useCombinedMessages.ts
|
|
84
229
|
import { useVoiceAssistant } from "@livekit/components-react";
|
|
@@ -172,15 +317,15 @@ function useLiveAgent() {
|
|
|
172
317
|
}
|
|
173
318
|
|
|
174
319
|
// src/hooks/usePhaseUpdates.ts
|
|
175
|
-
import { useCallback as
|
|
320
|
+
import { useCallback as useCallback3 } from "react";
|
|
176
321
|
|
|
177
322
|
// src/hooks/useAgentState.ts
|
|
178
323
|
import { useRemoteParticipants } from "@livekit/components-react";
|
|
179
|
-
import { useEffect, useState as
|
|
324
|
+
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
180
325
|
function useAgentState(attributeKey, parse, initial) {
|
|
181
|
-
const [value, setValue] =
|
|
326
|
+
const [value, setValue] = useState3(initial);
|
|
182
327
|
const remoteParticipants = useRemoteParticipants();
|
|
183
|
-
|
|
328
|
+
useEffect2(() => {
|
|
184
329
|
const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
|
|
185
330
|
if (agentParticipant) {
|
|
186
331
|
const attr = agentParticipant.attributes?.[attributeKey];
|
|
@@ -226,13 +371,13 @@ function parsePhases(json) {
|
|
|
226
371
|
return null;
|
|
227
372
|
}
|
|
228
373
|
function usePhaseUpdates() {
|
|
229
|
-
const parse =
|
|
374
|
+
const parse = useCallback3(parsePhases, []);
|
|
230
375
|
const phases = useAgentState("phases", parse, []);
|
|
231
376
|
return { phases };
|
|
232
377
|
}
|
|
233
378
|
|
|
234
379
|
// src/hooks/useQuestionUpdates.ts
|
|
235
|
-
import { useCallback as
|
|
380
|
+
import { useCallback as useCallback4 } from "react";
|
|
236
381
|
function parseQuestions(json) {
|
|
237
382
|
try {
|
|
238
383
|
const data = JSON.parse(json);
|
|
@@ -243,7 +388,7 @@ function parseQuestions(json) {
|
|
|
243
388
|
return null;
|
|
244
389
|
}
|
|
245
390
|
function useQuestionUpdates() {
|
|
246
|
-
const parse =
|
|
391
|
+
const parse = useCallback4(parseQuestions, []);
|
|
247
392
|
const questions = useAgentState("questions", parse, []);
|
|
248
393
|
return { questions };
|
|
249
394
|
}
|
|
@@ -384,8 +529,14 @@ var Check = createLucideIcon("check", __iconNode5);
|
|
|
384
529
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
|
|
385
530
|
var __iconNode6 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
|
|
386
531
|
var Circle = createLucideIcon("circle", __iconNode6);
|
|
387
|
-
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/
|
|
532
|
+
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
|
|
388
533
|
var __iconNode7 = [
|
|
534
|
+
["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
|
|
535
|
+
["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
|
|
536
|
+
];
|
|
537
|
+
var Mail = createLucideIcon("mail", __iconNode7);
|
|
538
|
+
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
|
|
539
|
+
var __iconNode8 = [
|
|
389
540
|
[
|
|
390
541
|
"path",
|
|
391
542
|
{
|
|
@@ -394,9 +545,9 @@ var __iconNode7 = [
|
|
|
394
545
|
}
|
|
395
546
|
]
|
|
396
547
|
];
|
|
397
|
-
var MessageCircle = createLucideIcon("message-circle",
|
|
548
|
+
var MessageCircle = createLucideIcon("message-circle", __iconNode8);
|
|
398
549
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
|
|
399
|
-
var
|
|
550
|
+
var __iconNode9 = [
|
|
400
551
|
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
401
552
|
["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
|
|
402
553
|
["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
|
|
@@ -404,32 +555,32 @@ var __iconNode8 = [
|
|
|
404
555
|
["path", { d: "m2 2 20 20", key: "1ooewy" }],
|
|
405
556
|
["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
|
|
406
557
|
];
|
|
407
|
-
var MicOff = createLucideIcon("mic-off",
|
|
558
|
+
var MicOff = createLucideIcon("mic-off", __iconNode9);
|
|
408
559
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
|
|
409
|
-
var
|
|
560
|
+
var __iconNode10 = [
|
|
410
561
|
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
411
562
|
["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
|
|
412
563
|
["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
|
|
413
564
|
];
|
|
414
|
-
var Mic = createLucideIcon("mic",
|
|
565
|
+
var Mic = createLucideIcon("mic", __iconNode10);
|
|
415
566
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
|
|
416
|
-
var
|
|
567
|
+
var __iconNode11 = [
|
|
417
568
|
["path", { d: "M12 17v4", key: "1riwvh" }],
|
|
418
569
|
["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
|
|
419
570
|
["path", { d: "m2 2 20 20", key: "1ooewy" }],
|
|
420
571
|
["path", { d: "M8 21h8", key: "1ev6f3" }],
|
|
421
572
|
["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
|
|
422
573
|
];
|
|
423
|
-
var MonitorOff = createLucideIcon("monitor-off",
|
|
574
|
+
var MonitorOff = createLucideIcon("monitor-off", __iconNode11);
|
|
424
575
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
|
|
425
|
-
var
|
|
576
|
+
var __iconNode12 = [
|
|
426
577
|
["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
|
|
427
578
|
["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
|
|
428
579
|
["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
|
|
429
580
|
];
|
|
430
|
-
var Monitor = createLucideIcon("monitor",
|
|
581
|
+
var Monitor = createLucideIcon("monitor", __iconNode12);
|
|
431
582
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
|
|
432
|
-
var
|
|
583
|
+
var __iconNode13 = [
|
|
433
584
|
[
|
|
434
585
|
"path",
|
|
435
586
|
{
|
|
@@ -446,13 +597,13 @@ var __iconNode12 = [
|
|
|
446
597
|
}
|
|
447
598
|
]
|
|
448
599
|
];
|
|
449
|
-
var PhoneOff = createLucideIcon("phone-off",
|
|
600
|
+
var PhoneOff = createLucideIcon("phone-off", __iconNode13);
|
|
450
601
|
// ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/x.js
|
|
451
|
-
var
|
|
602
|
+
var __iconNode14 = [
|
|
452
603
|
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
453
604
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
454
605
|
];
|
|
455
|
-
var X = createLucideIcon("x",
|
|
606
|
+
var X = createLucideIcon("x", __iconNode14);
|
|
456
607
|
// src/components/ui/button.tsx
|
|
457
608
|
import { forwardRef as forwardRef3 } from "react";
|
|
458
609
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -514,10 +665,257 @@ function ChatHeader({ onClose }) {
|
|
|
514
665
|
});
|
|
515
666
|
}
|
|
516
667
|
|
|
668
|
+
// src/components/LoginFlow.tsx
|
|
669
|
+
import { useCallback as useCallback5, useEffect as useEffect3, useRef, useState as useState4 } from "react";
|
|
670
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
671
|
+
var OTP_LENGTH = 6;
|
|
672
|
+
var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
|
|
673
|
+
function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
|
|
674
|
+
const [step, setStep] = useState4("email");
|
|
675
|
+
const [email, setEmail] = useState4("");
|
|
676
|
+
const handleRequestOtp = useCallback5(async (emailValue) => {
|
|
677
|
+
const success = await requestOtp(emailValue);
|
|
678
|
+
if (success)
|
|
679
|
+
setStep("otp");
|
|
680
|
+
}, [requestOtp]);
|
|
681
|
+
const handleVerifyOtp = useCallback5(async (code) => {
|
|
682
|
+
await verifyOtp(email, code);
|
|
683
|
+
}, [verifyOtp, email]);
|
|
684
|
+
const handleBack = useCallback5(() => {
|
|
685
|
+
setStep("email");
|
|
686
|
+
}, []);
|
|
687
|
+
const handleResend = useCallback5(async () => {
|
|
688
|
+
await requestOtp(email);
|
|
689
|
+
}, [requestOtp, email]);
|
|
690
|
+
if (step === "otp") {
|
|
691
|
+
return /* @__PURE__ */ jsx3(OtpStep, {
|
|
692
|
+
email,
|
|
693
|
+
onSubmit: handleVerifyOtp,
|
|
694
|
+
onResend: handleResend,
|
|
695
|
+
onBack: handleBack,
|
|
696
|
+
error,
|
|
697
|
+
isSubmitting
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
return /* @__PURE__ */ jsx3(EmailStep, {
|
|
701
|
+
email,
|
|
702
|
+
onEmailChange: setEmail,
|
|
703
|
+
onSubmit: handleRequestOtp,
|
|
704
|
+
error,
|
|
705
|
+
isSubmitting
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
709
|
+
function handleSubmit(e) {
|
|
710
|
+
e.preventDefault();
|
|
711
|
+
if (email.trim())
|
|
712
|
+
onSubmit(email.trim());
|
|
713
|
+
}
|
|
714
|
+
return /* @__PURE__ */ jsxs2("div", {
|
|
715
|
+
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
|
|
716
|
+
children: [
|
|
717
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
718
|
+
className: "skippr:mb-4 skippr:text-center",
|
|
719
|
+
children: [
|
|
720
|
+
/* @__PURE__ */ jsx3(Mail, {
|
|
721
|
+
className: "skippr:mx-auto skippr:mb-2 skippr:size-6 skippr:text-primary"
|
|
722
|
+
}),
|
|
723
|
+
/* @__PURE__ */ jsx3("p", {
|
|
724
|
+
className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
|
|
725
|
+
children: "Sign in to continue"
|
|
726
|
+
}),
|
|
727
|
+
/* @__PURE__ */ jsx3("p", {
|
|
728
|
+
className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
|
|
729
|
+
children: "Your email will be used to identify you across sessions"
|
|
730
|
+
})
|
|
731
|
+
]
|
|
732
|
+
}),
|
|
733
|
+
/* @__PURE__ */ jsxs2("form", {
|
|
734
|
+
onSubmit: handleSubmit,
|
|
735
|
+
className: "skippr:flex skippr:flex-col skippr:gap-3",
|
|
736
|
+
children: [
|
|
737
|
+
/* @__PURE__ */ jsx3("input", {
|
|
738
|
+
type: "email",
|
|
739
|
+
placeholder: "you@example.com",
|
|
740
|
+
value: email,
|
|
741
|
+
onChange: (e) => onEmailChange(e.target.value),
|
|
742
|
+
disabled: isSubmitting,
|
|
743
|
+
required: true,
|
|
744
|
+
className: "skippr:w-full skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground skippr:placeholder-muted-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
|
|
745
|
+
}),
|
|
746
|
+
/* @__PURE__ */ jsx3(Button, {
|
|
747
|
+
type: "submit",
|
|
748
|
+
disabled: isSubmitting || !email.trim(),
|
|
749
|
+
className: "skippr:w-full",
|
|
750
|
+
children: isSubmitting ? /* @__PURE__ */ jsx3(LoaderCircle, {
|
|
751
|
+
className: "skippr:size-4 skippr:animate-spin"
|
|
752
|
+
}) : "Continue"
|
|
753
|
+
}),
|
|
754
|
+
error && /* @__PURE__ */ jsx3("p", {
|
|
755
|
+
className: "skippr:text-xs skippr:text-center skippr:text-destructive",
|
|
756
|
+
children: error
|
|
757
|
+
})
|
|
758
|
+
]
|
|
759
|
+
})
|
|
760
|
+
]
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
764
|
+
const [digits, setDigits] = useState4(Array(OTP_LENGTH).fill(""));
|
|
765
|
+
const [resendCooldown, setResendCooldown] = useState4(0);
|
|
766
|
+
const inputRefs = useRef([]);
|
|
767
|
+
const submittedRef = useRef(false);
|
|
768
|
+
useEffect3(() => {
|
|
769
|
+
inputRefs.current[0]?.focus();
|
|
770
|
+
}, []);
|
|
771
|
+
useEffect3(() => {
|
|
772
|
+
if (error)
|
|
773
|
+
submittedRef.current = false;
|
|
774
|
+
}, [error]);
|
|
775
|
+
useEffect3(() => {
|
|
776
|
+
if (resendCooldown <= 0)
|
|
777
|
+
return;
|
|
778
|
+
const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
|
|
779
|
+
return () => clearTimeout(timer);
|
|
780
|
+
}, [resendCooldown]);
|
|
781
|
+
const submitCode = useCallback5((code) => {
|
|
782
|
+
if (submittedRef.current || isSubmitting)
|
|
783
|
+
return;
|
|
784
|
+
submittedRef.current = true;
|
|
785
|
+
onSubmit(code);
|
|
786
|
+
}, [onSubmit, isSubmitting]);
|
|
787
|
+
const handleDigitChange = useCallback5((index2, value) => {
|
|
788
|
+
const digit = value.replace(/\D/g, "").slice(-1);
|
|
789
|
+
const newDigits = [...digits];
|
|
790
|
+
newDigits[index2] = digit;
|
|
791
|
+
setDigits(newDigits);
|
|
792
|
+
if (digit && index2 < OTP_LENGTH - 1) {
|
|
793
|
+
inputRefs.current[index2 + 1]?.focus();
|
|
794
|
+
}
|
|
795
|
+
if (digit && index2 === OTP_LENGTH - 1) {
|
|
796
|
+
const code = newDigits.join("");
|
|
797
|
+
if (code.length === OTP_LENGTH)
|
|
798
|
+
submitCode(code);
|
|
799
|
+
}
|
|
800
|
+
}, [digits, submitCode]);
|
|
801
|
+
const handleKeyDown = useCallback5((index2, e) => {
|
|
802
|
+
if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
|
|
803
|
+
inputRefs.current[index2 - 1]?.focus();
|
|
804
|
+
}
|
|
805
|
+
}, [digits]);
|
|
806
|
+
const handlePaste = useCallback5((e) => {
|
|
807
|
+
e.preventDefault();
|
|
808
|
+
const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
|
|
809
|
+
if (pasted.length > 0) {
|
|
810
|
+
const newDigits = Array(OTP_LENGTH).fill("");
|
|
811
|
+
for (let i = 0;i < pasted.length; i++) {
|
|
812
|
+
newDigits[i] = pasted[i];
|
|
813
|
+
}
|
|
814
|
+
setDigits(newDigits);
|
|
815
|
+
if (pasted.length === OTP_LENGTH)
|
|
816
|
+
submitCode(pasted);
|
|
817
|
+
else
|
|
818
|
+
inputRefs.current[pasted.length]?.focus();
|
|
819
|
+
}
|
|
820
|
+
}, [submitCode]);
|
|
821
|
+
function handleSubmit(e) {
|
|
822
|
+
e.preventDefault();
|
|
823
|
+
const code = digits.join("");
|
|
824
|
+
if (code.length === OTP_LENGTH)
|
|
825
|
+
submitCode(code);
|
|
826
|
+
}
|
|
827
|
+
function handleResend() {
|
|
828
|
+
onResend();
|
|
829
|
+
setResendCooldown(30);
|
|
830
|
+
setDigits(Array(OTP_LENGTH).fill(""));
|
|
831
|
+
submittedRef.current = false;
|
|
832
|
+
inputRefs.current[0]?.focus();
|
|
833
|
+
}
|
|
834
|
+
return /* @__PURE__ */ jsxs2("div", {
|
|
835
|
+
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
|
|
836
|
+
children: [
|
|
837
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
838
|
+
className: "skippr:mb-4 skippr:text-center",
|
|
839
|
+
children: [
|
|
840
|
+
/* @__PURE__ */ jsx3("p", {
|
|
841
|
+
className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
|
|
842
|
+
children: "Enter verification code"
|
|
843
|
+
}),
|
|
844
|
+
/* @__PURE__ */ jsxs2("p", {
|
|
845
|
+
className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
|
|
846
|
+
children: [
|
|
847
|
+
"We sent a 6-digit code to",
|
|
848
|
+
" ",
|
|
849
|
+
/* @__PURE__ */ jsx3("span", {
|
|
850
|
+
className: "skippr:font-medium skippr:text-foreground",
|
|
851
|
+
children: email
|
|
852
|
+
})
|
|
853
|
+
]
|
|
854
|
+
})
|
|
855
|
+
]
|
|
856
|
+
}),
|
|
857
|
+
/* @__PURE__ */ jsxs2("form", {
|
|
858
|
+
onSubmit: handleSubmit,
|
|
859
|
+
className: "skippr:flex skippr:flex-col skippr:gap-3",
|
|
860
|
+
children: [
|
|
861
|
+
/* @__PURE__ */ jsx3("div", {
|
|
862
|
+
className: "skippr:flex skippr:justify-center skippr:gap-1.5",
|
|
863
|
+
children: digits.map((digit, index2) => /* @__PURE__ */ jsx3("input", {
|
|
864
|
+
ref: (el) => {
|
|
865
|
+
inputRefs.current[index2] = el;
|
|
866
|
+
},
|
|
867
|
+
type: "text",
|
|
868
|
+
inputMode: "numeric",
|
|
869
|
+
maxLength: 1,
|
|
870
|
+
value: digit,
|
|
871
|
+
onChange: (e) => handleDigitChange(index2, e.target.value),
|
|
872
|
+
onKeyDown: (e) => handleKeyDown(index2, e),
|
|
873
|
+
onPaste: handlePaste,
|
|
874
|
+
disabled: isSubmitting,
|
|
875
|
+
className: "skippr:h-10 skippr:w-10 skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:text-center skippr:text-sm skippr:font-semibold skippr:text-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
|
|
876
|
+
}, DIGIT_KEYS[index2]))
|
|
877
|
+
}),
|
|
878
|
+
error && /* @__PURE__ */ jsx3("p", {
|
|
879
|
+
className: "skippr:text-xs skippr:text-center skippr:text-destructive",
|
|
880
|
+
children: error
|
|
881
|
+
}),
|
|
882
|
+
/* @__PURE__ */ jsx3(Button, {
|
|
883
|
+
type: "submit",
|
|
884
|
+
disabled: isSubmitting || digits.join("").length !== OTP_LENGTH,
|
|
885
|
+
className: "skippr:w-full",
|
|
886
|
+
children: isSubmitting ? /* @__PURE__ */ jsx3(LoaderCircle, {
|
|
887
|
+
className: "skippr:size-4 skippr:animate-spin"
|
|
888
|
+
}) : "Verify"
|
|
889
|
+
}),
|
|
890
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
891
|
+
className: "skippr:flex skippr:items-center skippr:justify-between skippr:text-xs",
|
|
892
|
+
children: [
|
|
893
|
+
/* @__PURE__ */ jsx3("button", {
|
|
894
|
+
type: "button",
|
|
895
|
+
onClick: onBack,
|
|
896
|
+
disabled: isSubmitting,
|
|
897
|
+
className: "skippr:text-muted-foreground hover:skippr:text-foreground skippr:transition-colors disabled:skippr:opacity-50",
|
|
898
|
+
children: "Change email"
|
|
899
|
+
}),
|
|
900
|
+
/* @__PURE__ */ jsx3("button", {
|
|
901
|
+
type: "button",
|
|
902
|
+
onClick: handleResend,
|
|
903
|
+
disabled: isSubmitting || resendCooldown > 0,
|
|
904
|
+
className: "skippr:text-primary hover:skippr:opacity-80 skippr:transition-opacity disabled:skippr:opacity-50",
|
|
905
|
+
children: resendCooldown > 0 ? `Resend in ${resendCooldown}s` : "Resend code"
|
|
906
|
+
})
|
|
907
|
+
]
|
|
908
|
+
})
|
|
909
|
+
]
|
|
910
|
+
})
|
|
911
|
+
]
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
517
915
|
// src/components/MeetingControls.tsx
|
|
518
916
|
import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
|
|
519
917
|
import { ScreenSharePresets } from "livekit-client";
|
|
520
|
-
import { useCallback as
|
|
918
|
+
import { useCallback as useCallback6, useEffect as useEffect4, useRef as useRef2, useState as useState5 } from "react";
|
|
521
919
|
|
|
522
920
|
// src/lib/format.ts
|
|
523
921
|
function formatTime(seconds) {
|
|
@@ -531,12 +929,12 @@ function parseNumber(s) {
|
|
|
531
929
|
}
|
|
532
930
|
|
|
533
931
|
// src/components/SessionWarningBanner.tsx
|
|
534
|
-
import { jsx as
|
|
932
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
535
933
|
var SESSION_WARNING_THRESHOLD_SECS = 60;
|
|
536
934
|
function SessionWarningBanner({ remaining }) {
|
|
537
935
|
if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
|
|
538
936
|
return null;
|
|
539
|
-
return /* @__PURE__ */
|
|
937
|
+
return /* @__PURE__ */ jsx4("div", {
|
|
540
938
|
"data-testid": "session-warning-banner",
|
|
541
939
|
className: "skippr:bg-red-50 skippr:px-4 skippr:py-1.5 skippr:text-center skippr:text-xs skippr:font-medium skippr:text-red-700",
|
|
542
940
|
children: "Session ending soon"
|
|
@@ -544,15 +942,15 @@ function SessionWarningBanner({ remaining }) {
|
|
|
544
942
|
}
|
|
545
943
|
|
|
546
944
|
// src/components/MeetingControls.tsx
|
|
547
|
-
import { jsx as
|
|
945
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
548
946
|
function MeetingControls({ onHangUp }) {
|
|
549
947
|
const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
|
|
550
948
|
const { localParticipant } = useLocalParticipant3();
|
|
551
949
|
const isMuted = !localParticipant.isMicrophoneEnabled;
|
|
552
950
|
const isScreenSharing = localParticipant.isScreenShareEnabled;
|
|
553
|
-
const endTimeRef =
|
|
554
|
-
const [remaining, setRemaining] =
|
|
555
|
-
|
|
951
|
+
const endTimeRef = useRef2(null);
|
|
952
|
+
const [remaining, setRemaining] = useState5(null);
|
|
953
|
+
useEffect4(() => {
|
|
556
954
|
if (maxCallDuration === null || endTimeRef.current !== null)
|
|
557
955
|
return;
|
|
558
956
|
endTimeRef.current = Date.now() + maxCallDuration * 1000;
|
|
@@ -564,14 +962,14 @@ function MeetingControls({ onHangUp }) {
|
|
|
564
962
|
const id = setInterval(tick, 1000);
|
|
565
963
|
return () => clearInterval(id);
|
|
566
964
|
}, [maxCallDuration]);
|
|
567
|
-
const toggleMute =
|
|
965
|
+
const toggleMute = useCallback6(async () => {
|
|
568
966
|
try {
|
|
569
967
|
await localParticipant.setMicrophoneEnabled(isMuted);
|
|
570
968
|
} catch (e) {
|
|
571
969
|
console.error("Failed to toggle microphone:", e);
|
|
572
970
|
}
|
|
573
971
|
}, [localParticipant, isMuted]);
|
|
574
|
-
const toggleScreenShare =
|
|
972
|
+
const toggleScreenShare = useCallback6(async () => {
|
|
575
973
|
try {
|
|
576
974
|
await localParticipant.setScreenShareEnabled(!isScreenSharing, {
|
|
577
975
|
video: { displaySurface: "browser" },
|
|
@@ -582,54 +980,54 @@ function MeetingControls({ onHangUp }) {
|
|
|
582
980
|
console.error("Failed to toggle screen share:", e);
|
|
583
981
|
}
|
|
584
982
|
}, [localParticipant, isScreenSharing]);
|
|
585
|
-
|
|
983
|
+
useEffect4(() => {
|
|
586
984
|
toggleMute().then(() => toggleScreenShare());
|
|
587
985
|
}, []);
|
|
588
|
-
return /* @__PURE__ */
|
|
986
|
+
return /* @__PURE__ */ jsxs3("div", {
|
|
589
987
|
children: [
|
|
590
|
-
/* @__PURE__ */
|
|
988
|
+
/* @__PURE__ */ jsx5(SessionWarningBanner, {
|
|
591
989
|
remaining
|
|
592
990
|
}),
|
|
593
|
-
/* @__PURE__ */
|
|
991
|
+
/* @__PURE__ */ jsxs3("div", {
|
|
594
992
|
className: "skippr:flex skippr:items-center skippr:justify-between skippr:border-b skippr:px-4 skippr:py-3",
|
|
595
993
|
children: [
|
|
596
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsxs3("div", {
|
|
597
995
|
className: "skippr:flex skippr:items-center skippr:gap-2",
|
|
598
996
|
children: [
|
|
599
|
-
/* @__PURE__ */
|
|
997
|
+
/* @__PURE__ */ jsx5(Button, {
|
|
600
998
|
size: "icon-sm",
|
|
601
999
|
variant: isMuted ? "destructive" : "outline",
|
|
602
1000
|
onClick: toggleMute,
|
|
603
1001
|
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
604
|
-
children: isMuted ? /* @__PURE__ */
|
|
1002
|
+
children: isMuted ? /* @__PURE__ */ jsx5(MicOff, {
|
|
605
1003
|
className: "skippr:size-4"
|
|
606
|
-
}) : /* @__PURE__ */
|
|
1004
|
+
}) : /* @__PURE__ */ jsx5(Mic, {
|
|
607
1005
|
className: "skippr:size-4"
|
|
608
1006
|
})
|
|
609
1007
|
}),
|
|
610
|
-
/* @__PURE__ */
|
|
1008
|
+
/* @__PURE__ */ jsx5(Button, {
|
|
611
1009
|
size: "icon-sm",
|
|
612
1010
|
variant: isScreenSharing ? "default" : "outline",
|
|
613
1011
|
onClick: toggleScreenShare,
|
|
614
1012
|
"aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
|
|
615
|
-
children: isScreenSharing ? /* @__PURE__ */
|
|
1013
|
+
children: isScreenSharing ? /* @__PURE__ */ jsx5(MonitorOff, {
|
|
616
1014
|
className: "skippr:size-4"
|
|
617
|
-
}) : /* @__PURE__ */
|
|
1015
|
+
}) : /* @__PURE__ */ jsx5(Monitor, {
|
|
618
1016
|
className: "skippr:size-4"
|
|
619
1017
|
})
|
|
620
1018
|
})
|
|
621
1019
|
]
|
|
622
1020
|
}),
|
|
623
|
-
remaining !== null && /* @__PURE__ */
|
|
1021
|
+
remaining !== null && /* @__PURE__ */ jsx5("span", {
|
|
624
1022
|
className: cn("skippr:text-sm skippr:font-medium skippr:tabular-nums", remaining <= SESSION_WARNING_THRESHOLD_SECS ? "skippr:text-red-600 skippr:animate-pulse" : "skippr:text-muted-foreground"),
|
|
625
1023
|
children: formatTime(remaining)
|
|
626
1024
|
}),
|
|
627
|
-
/* @__PURE__ */
|
|
1025
|
+
/* @__PURE__ */ jsx5(Button, {
|
|
628
1026
|
size: "icon-sm",
|
|
629
1027
|
variant: "destructive",
|
|
630
1028
|
onClick: onHangUp,
|
|
631
1029
|
"aria-label": "Hang up",
|
|
632
|
-
children: /* @__PURE__ */
|
|
1030
|
+
children: /* @__PURE__ */ jsx5(PhoneOff, {
|
|
633
1031
|
className: "skippr:size-4"
|
|
634
1032
|
})
|
|
635
1033
|
})
|
|
@@ -640,13 +1038,13 @@ function MeetingControls({ onHangUp }) {
|
|
|
640
1038
|
}
|
|
641
1039
|
|
|
642
1040
|
// src/components/MessageList.tsx
|
|
643
|
-
import { useEffect as
|
|
1041
|
+
import { useEffect as useEffect5, useRef as useRef3 } from "react";
|
|
644
1042
|
|
|
645
1043
|
// src/components/ChatInput.tsx
|
|
646
|
-
import { useState as
|
|
647
|
-
import { jsx as
|
|
1044
|
+
import { useState as useState6 } from "react";
|
|
1045
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
648
1046
|
function ChatInput({ sendChatMessage, isSendingChat }) {
|
|
649
|
-
const [inputText, setInputText] =
|
|
1047
|
+
const [inputText, setInputText] = useState6("");
|
|
650
1048
|
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
651
1049
|
function handleSubmit(e) {
|
|
652
1050
|
e.preventDefault();
|
|
@@ -656,11 +1054,11 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
|
|
|
656
1054
|
setInputText("");
|
|
657
1055
|
sendChatMessage(text).catch(() => setInputText(text));
|
|
658
1056
|
}
|
|
659
|
-
return /* @__PURE__ */
|
|
1057
|
+
return /* @__PURE__ */ jsxs4("form", {
|
|
660
1058
|
onSubmit: handleSubmit,
|
|
661
1059
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
|
|
662
1060
|
children: [
|
|
663
|
-
/* @__PURE__ */
|
|
1061
|
+
/* @__PURE__ */ jsx6("input", {
|
|
664
1062
|
type: "text",
|
|
665
1063
|
value: inputText,
|
|
666
1064
|
onChange: (e) => setInputText(e.target.value),
|
|
@@ -668,12 +1066,12 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
|
|
|
668
1066
|
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"),
|
|
669
1067
|
disabled: isSendingChat
|
|
670
1068
|
}),
|
|
671
|
-
/* @__PURE__ */
|
|
1069
|
+
/* @__PURE__ */ jsx6("button", {
|
|
672
1070
|
type: "submit",
|
|
673
1071
|
disabled: !canSend,
|
|
674
1072
|
"aria-label": "Send message",
|
|
675
1073
|
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"),
|
|
676
|
-
children: /* @__PURE__ */
|
|
1074
|
+
children: /* @__PURE__ */ jsx6(SendHorizontal, {
|
|
677
1075
|
className: "skippr:size-4"
|
|
678
1076
|
})
|
|
679
1077
|
})
|
|
@@ -682,12 +1080,12 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
|
|
|
682
1080
|
}
|
|
683
1081
|
|
|
684
1082
|
// src/components/ChatMessage.tsx
|
|
685
|
-
import { jsx as
|
|
1083
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
686
1084
|
function ChatMessage({ message }) {
|
|
687
1085
|
const isUser = message.role === "user";
|
|
688
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsx7("div", {
|
|
689
1087
|
className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
|
|
690
|
-
children: /* @__PURE__ */
|
|
1088
|
+
children: /* @__PURE__ */ jsx7("div", {
|
|
691
1089
|
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"),
|
|
692
1090
|
children: message.content
|
|
693
1091
|
})
|
|
@@ -695,20 +1093,20 @@ function ChatMessage({ message }) {
|
|
|
695
1093
|
}
|
|
696
1094
|
|
|
697
1095
|
// src/components/TypingIndicator.tsx
|
|
698
|
-
import { jsx as
|
|
1096
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
699
1097
|
function TypingIndicator() {
|
|
700
|
-
return /* @__PURE__ */
|
|
1098
|
+
return /* @__PURE__ */ jsx8("div", {
|
|
701
1099
|
className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
|
|
702
|
-
children: /* @__PURE__ */
|
|
1100
|
+
children: /* @__PURE__ */ jsxs5("div", {
|
|
703
1101
|
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",
|
|
704
1102
|
children: [
|
|
705
|
-
/* @__PURE__ */
|
|
1103
|
+
/* @__PURE__ */ jsx8("span", {
|
|
706
1104
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
|
|
707
1105
|
}),
|
|
708
|
-
/* @__PURE__ */
|
|
1106
|
+
/* @__PURE__ */ jsx8("span", {
|
|
709
1107
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
|
|
710
1108
|
}),
|
|
711
|
-
/* @__PURE__ */
|
|
1109
|
+
/* @__PURE__ */ jsx8("span", {
|
|
712
1110
|
className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
|
|
713
1111
|
})
|
|
714
1112
|
]
|
|
@@ -717,38 +1115,38 @@ function TypingIndicator() {
|
|
|
717
1115
|
}
|
|
718
1116
|
|
|
719
1117
|
// src/components/MessageList.tsx
|
|
720
|
-
import { jsx as
|
|
1118
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
721
1119
|
function MessageList({
|
|
722
1120
|
messages,
|
|
723
1121
|
isStreaming,
|
|
724
1122
|
sendChatMessage,
|
|
725
1123
|
isSendingChat
|
|
726
1124
|
}) {
|
|
727
|
-
const scrollRef =
|
|
1125
|
+
const scrollRef = useRef3(null);
|
|
728
1126
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
729
|
-
|
|
1127
|
+
useEffect5(() => {
|
|
730
1128
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
731
1129
|
}, [messages.length, lastMessage?.content]);
|
|
732
1130
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
733
|
-
return /* @__PURE__ */
|
|
1131
|
+
return /* @__PURE__ */ jsxs6("div", {
|
|
734
1132
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
735
1133
|
children: [
|
|
736
|
-
/* @__PURE__ */
|
|
1134
|
+
/* @__PURE__ */ jsx9("div", {
|
|
737
1135
|
className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
|
|
738
|
-
children: /* @__PURE__ */
|
|
1136
|
+
children: /* @__PURE__ */ jsxs6("div", {
|
|
739
1137
|
className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
|
|
740
1138
|
children: [
|
|
741
|
-
messages.map((message) => /* @__PURE__ */
|
|
1139
|
+
messages.map((message) => /* @__PURE__ */ jsx9(ChatMessage, {
|
|
742
1140
|
message
|
|
743
1141
|
}, message.id)),
|
|
744
|
-
showTyping && /* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
1142
|
+
showTyping && /* @__PURE__ */ jsx9(TypingIndicator, {}),
|
|
1143
|
+
/* @__PURE__ */ jsx9("div", {
|
|
746
1144
|
ref: scrollRef
|
|
747
1145
|
})
|
|
748
1146
|
]
|
|
749
1147
|
})
|
|
750
1148
|
}),
|
|
751
|
-
/* @__PURE__ */
|
|
1149
|
+
/* @__PURE__ */ jsx9(ChatInput, {
|
|
752
1150
|
sendChatMessage,
|
|
753
1151
|
isSendingChat
|
|
754
1152
|
})
|
|
@@ -757,30 +1155,30 @@ function MessageList({
|
|
|
757
1155
|
}
|
|
758
1156
|
|
|
759
1157
|
// src/components/QuickActions.tsx
|
|
760
|
-
import { jsx as
|
|
1158
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
761
1159
|
function QuickActions({ onStartSession, isStarting, error }) {
|
|
762
|
-
return /* @__PURE__ */
|
|
1160
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
763
1161
|
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",
|
|
764
1162
|
children: [
|
|
765
|
-
/* @__PURE__ */
|
|
1163
|
+
/* @__PURE__ */ jsx10("p", {
|
|
766
1164
|
className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
|
|
767
1165
|
children: "How can I help you today?"
|
|
768
1166
|
}),
|
|
769
|
-
/* @__PURE__ */
|
|
1167
|
+
/* @__PURE__ */ jsxs7(Button, {
|
|
770
1168
|
variant: "outline",
|
|
771
1169
|
className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
|
|
772
1170
|
onClick: onStartSession,
|
|
773
1171
|
disabled: isStarting,
|
|
774
1172
|
children: [
|
|
775
|
-
isStarting ? /* @__PURE__ */
|
|
1173
|
+
isStarting ? /* @__PURE__ */ jsx10(LoaderCircle, {
|
|
776
1174
|
className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
|
|
777
|
-
}) : /* @__PURE__ */
|
|
1175
|
+
}) : /* @__PURE__ */ jsx10(MessageCircleQuestionMark, {
|
|
778
1176
|
className: "skippr:size-4 skippr:text-primary"
|
|
779
1177
|
}),
|
|
780
1178
|
isStarting ? "Starting..." : "Start Session"
|
|
781
1179
|
]
|
|
782
1180
|
}),
|
|
783
|
-
error && /* @__PURE__ */
|
|
1181
|
+
error && /* @__PURE__ */ jsx10("p", {
|
|
784
1182
|
className: "skippr:text-xs skippr:text-destructive",
|
|
785
1183
|
children: error
|
|
786
1184
|
})
|
|
@@ -789,52 +1187,52 @@ function QuickActions({ onStartSession, isStarting, error }) {
|
|
|
789
1187
|
}
|
|
790
1188
|
|
|
791
1189
|
// src/components/SessionAgenda.tsx
|
|
792
|
-
import { jsx as
|
|
1190
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
793
1191
|
function SessionAgenda({ phases, questions = [] }) {
|
|
794
1192
|
if (phases.length === 0) {
|
|
795
|
-
return /* @__PURE__ */
|
|
1193
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
796
1194
|
className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
|
|
797
1195
|
children: [
|
|
798
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsx11("h3", {
|
|
799
1197
|
className: "skippr:text-sm skippr:font-semibold",
|
|
800
1198
|
children: "Agenda"
|
|
801
1199
|
}),
|
|
802
|
-
/* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ jsx11("p", {
|
|
803
1201
|
className: "skippr:text-xs skippr:text-muted-foreground",
|
|
804
1202
|
children: "Waiting for session..."
|
|
805
1203
|
})
|
|
806
1204
|
]
|
|
807
1205
|
});
|
|
808
1206
|
}
|
|
809
|
-
return /* @__PURE__ */
|
|
1207
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
810
1208
|
className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
|
|
811
1209
|
children: [
|
|
812
|
-
/* @__PURE__ */
|
|
1210
|
+
/* @__PURE__ */ jsx11("h3", {
|
|
813
1211
|
className: "skippr:text-sm skippr:font-semibold",
|
|
814
1212
|
children: "Agenda"
|
|
815
1213
|
}),
|
|
816
|
-
/* @__PURE__ */
|
|
1214
|
+
/* @__PURE__ */ jsx11("ul", {
|
|
817
1215
|
className: "skippr:flex skippr:flex-col skippr:gap-2",
|
|
818
1216
|
children: phases.map((phase) => {
|
|
819
1217
|
const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
|
|
820
1218
|
const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
|
|
821
1219
|
const totalCount = phaseQuestions.length;
|
|
822
|
-
return /* @__PURE__ */
|
|
1220
|
+
return /* @__PURE__ */ jsxs8("li", {
|
|
823
1221
|
className: "skippr:flex skippr:flex-col skippr:gap-0.5",
|
|
824
1222
|
children: [
|
|
825
|
-
/* @__PURE__ */
|
|
1223
|
+
/* @__PURE__ */ jsxs8("div", {
|
|
826
1224
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
|
|
827
1225
|
children: [
|
|
828
|
-
/* @__PURE__ */
|
|
1226
|
+
/* @__PURE__ */ jsx11(PhaseIcon, {
|
|
829
1227
|
status: phase.status
|
|
830
1228
|
}),
|
|
831
|
-
/* @__PURE__ */
|
|
1229
|
+
/* @__PURE__ */ jsx11("span", {
|
|
832
1230
|
className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
|
|
833
1231
|
children: phase.name
|
|
834
1232
|
})
|
|
835
1233
|
]
|
|
836
1234
|
}),
|
|
837
|
-
totalCount > 0 && /* @__PURE__ */
|
|
1235
|
+
totalCount > 0 && /* @__PURE__ */ jsxs8("span", {
|
|
838
1236
|
className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
|
|
839
1237
|
children: [
|
|
840
1238
|
answeredCount,
|
|
@@ -852,62 +1250,78 @@ function SessionAgenda({ phases, questions = [] }) {
|
|
|
852
1250
|
}
|
|
853
1251
|
function PhaseIcon({ status }) {
|
|
854
1252
|
if (status === "completed") {
|
|
855
|
-
return /* @__PURE__ */
|
|
1253
|
+
return /* @__PURE__ */ jsx11("div", {
|
|
856
1254
|
className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
|
|
857
|
-
children: /* @__PURE__ */
|
|
1255
|
+
children: /* @__PURE__ */ jsx11(Check, {
|
|
858
1256
|
className: "skippr:size-2.5 skippr:text-primary-foreground",
|
|
859
1257
|
strokeWidth: 3
|
|
860
1258
|
})
|
|
861
1259
|
});
|
|
862
1260
|
}
|
|
863
1261
|
if (status === "active") {
|
|
864
|
-
return /* @__PURE__ */
|
|
1262
|
+
return /* @__PURE__ */ jsx11(LoaderCircle, {
|
|
865
1263
|
className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
|
|
866
1264
|
});
|
|
867
1265
|
}
|
|
868
|
-
return /* @__PURE__ */
|
|
1266
|
+
return /* @__PURE__ */ jsx11(Circle, {
|
|
869
1267
|
className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
|
|
870
1268
|
});
|
|
871
1269
|
}
|
|
872
1270
|
|
|
873
1271
|
// src/components/Sidebar.tsx
|
|
874
|
-
import { jsx as
|
|
1272
|
+
import { jsx as jsx12, jsxs as jsxs9, Fragment } from "react/jsx-runtime";
|
|
875
1273
|
function Sidebar() {
|
|
876
|
-
const {
|
|
877
|
-
|
|
1274
|
+
const {
|
|
1275
|
+
isConnected,
|
|
1276
|
+
isStarting,
|
|
1277
|
+
error,
|
|
1278
|
+
startSession,
|
|
1279
|
+
disconnect,
|
|
1280
|
+
isPanelOpen,
|
|
1281
|
+
closePanel,
|
|
1282
|
+
isAuthenticated,
|
|
1283
|
+
isValidating,
|
|
1284
|
+
authError,
|
|
1285
|
+
requestOtp,
|
|
1286
|
+
verifyOtp,
|
|
1287
|
+
isAuthSubmitting
|
|
1288
|
+
} = useLiveAgent();
|
|
1289
|
+
useEffect6(() => {
|
|
878
1290
|
document.body.style.transition = "margin-right 300ms ease-in-out";
|
|
879
1291
|
document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
|
|
880
1292
|
}, [isPanelOpen]);
|
|
881
|
-
|
|
1293
|
+
useEffect6(() => {
|
|
882
1294
|
return () => {
|
|
883
1295
|
document.body.style.marginRight = "";
|
|
884
1296
|
document.body.style.transition = "";
|
|
885
1297
|
};
|
|
886
1298
|
}, []);
|
|
887
|
-
return /* @__PURE__ */
|
|
1299
|
+
return /* @__PURE__ */ jsxs9("div", {
|
|
888
1300
|
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"),
|
|
889
1301
|
style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
|
|
890
|
-
children:
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
/* @__PURE__ */ jsx11(ChatHeader, {
|
|
902
|
-
onClose: closePanel
|
|
903
|
-
}),
|
|
904
|
-
/* @__PURE__ */ jsx11(QuickActions, {
|
|
905
|
-
onStartSession: startSession,
|
|
906
|
-
isStarting,
|
|
907
|
-
error
|
|
1302
|
+
children: [
|
|
1303
|
+
/* @__PURE__ */ jsx12(ChatHeader, {
|
|
1304
|
+
onClose: closePanel
|
|
1305
|
+
}),
|
|
1306
|
+
isConnected ? /* @__PURE__ */ jsx12(ConnectedContent, {
|
|
1307
|
+
onDisconnect: disconnect
|
|
1308
|
+
}) : isValidating ? /* @__PURE__ */ jsx12("div", {
|
|
1309
|
+
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
1310
|
+
children: /* @__PURE__ */ jsx12("p", {
|
|
1311
|
+
className: "skippr:text-sm skippr:text-muted-foreground",
|
|
1312
|
+
children: "Loading..."
|
|
908
1313
|
})
|
|
909
|
-
|
|
910
|
-
|
|
1314
|
+
}) : isAuthenticated ? /* @__PURE__ */ jsx12(QuickActions, {
|
|
1315
|
+
onStartSession: startSession,
|
|
1316
|
+
isStarting,
|
|
1317
|
+
error
|
|
1318
|
+
}) : /* @__PURE__ */ jsx12(LoginFlow, {
|
|
1319
|
+
requestOtp,
|
|
1320
|
+
verifyOtp,
|
|
1321
|
+
error: authError,
|
|
1322
|
+
isSubmitting: isAuthSubmitting
|
|
1323
|
+
})
|
|
1324
|
+
]
|
|
911
1325
|
});
|
|
912
1326
|
}
|
|
913
1327
|
function ConnectedContent({ onDisconnect }) {
|
|
@@ -917,33 +1331,33 @@ function ConnectedContent({ onDisconnect }) {
|
|
|
917
1331
|
const { phases } = usePhaseUpdates();
|
|
918
1332
|
const { questions } = useQuestionUpdates();
|
|
919
1333
|
if (!isConnected) {
|
|
920
|
-
return /* @__PURE__ */
|
|
1334
|
+
return /* @__PURE__ */ jsx12("div", {
|
|
921
1335
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
922
|
-
children: /* @__PURE__ */
|
|
1336
|
+
children: /* @__PURE__ */ jsx12("p", {
|
|
923
1337
|
className: "skippr:text-sm skippr:text-muted-foreground",
|
|
924
1338
|
children: "Connecting..."
|
|
925
1339
|
})
|
|
926
1340
|
});
|
|
927
1341
|
}
|
|
928
1342
|
const isAgentSpeaking = agentState === "speaking";
|
|
929
|
-
return /* @__PURE__ */
|
|
1343
|
+
return /* @__PURE__ */ jsxs9(Fragment, {
|
|
930
1344
|
children: [
|
|
931
|
-
/* @__PURE__ */
|
|
1345
|
+
/* @__PURE__ */ jsx12(MeetingControls, {
|
|
932
1346
|
onHangUp: onDisconnect
|
|
933
1347
|
}),
|
|
934
|
-
/* @__PURE__ */
|
|
1348
|
+
/* @__PURE__ */ jsxs9("div", {
|
|
935
1349
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1",
|
|
936
1350
|
children: [
|
|
937
|
-
/* @__PURE__ */
|
|
1351
|
+
/* @__PURE__ */ jsx12("div", {
|
|
938
1352
|
className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
|
|
939
|
-
children: /* @__PURE__ */
|
|
1353
|
+
children: /* @__PURE__ */ jsx12(SessionAgenda, {
|
|
940
1354
|
phases,
|
|
941
1355
|
questions
|
|
942
1356
|
})
|
|
943
1357
|
}),
|
|
944
|
-
/* @__PURE__ */
|
|
1358
|
+
/* @__PURE__ */ jsx12("div", {
|
|
945
1359
|
className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
|
|
946
|
-
children: /* @__PURE__ */
|
|
1360
|
+
children: /* @__PURE__ */ jsx12(MessageList, {
|
|
947
1361
|
messages: allMessages,
|
|
948
1362
|
isStreaming: isAgentSpeaking,
|
|
949
1363
|
sendChatMessage,
|
|
@@ -957,44 +1371,49 @@ function ConnectedContent({ onDisconnect }) {
|
|
|
957
1371
|
}
|
|
958
1372
|
|
|
959
1373
|
// src/components/SidebarTrigger.tsx
|
|
960
|
-
import { jsx as
|
|
1374
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
961
1375
|
var TRIGGER_GAP = 16;
|
|
962
1376
|
var TRIGGER_DEFAULT_RIGHT = 24;
|
|
963
1377
|
function SidebarTrigger() {
|
|
964
1378
|
const { isPanelOpen, togglePanel } = useLiveAgent();
|
|
965
|
-
return /* @__PURE__ */
|
|
1379
|
+
return /* @__PURE__ */ jsx13(Button, {
|
|
966
1380
|
size: "icon-lg",
|
|
967
1381
|
onClick: togglePanel,
|
|
968
1382
|
className: "skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300",
|
|
969
1383
|
style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
|
|
970
1384
|
title: isPanelOpen ? "Close chat" : "Chat with us",
|
|
971
|
-
children: isPanelOpen ? /* @__PURE__ */
|
|
1385
|
+
children: isPanelOpen ? /* @__PURE__ */ jsx13(X, {
|
|
972
1386
|
className: "skippr:size-6"
|
|
973
|
-
}) : /* @__PURE__ */
|
|
1387
|
+
}) : /* @__PURE__ */ jsx13(MessageCircle, {
|
|
974
1388
|
className: "skippr:size-6"
|
|
975
1389
|
})
|
|
976
1390
|
});
|
|
977
1391
|
}
|
|
978
1392
|
|
|
979
1393
|
// src/components/LiveAgent.tsx
|
|
980
|
-
import { jsx as
|
|
1394
|
+
import { jsx as jsx14, jsxs as jsxs10, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
981
1395
|
function LiveAgent({
|
|
982
1396
|
agentId,
|
|
983
|
-
authToken,
|
|
1397
|
+
authToken: authTokenProp,
|
|
984
1398
|
appKey,
|
|
1399
|
+
userToken,
|
|
985
1400
|
defaultOpen = false,
|
|
986
1401
|
children
|
|
987
1402
|
}) {
|
|
1403
|
+
const auth = useAuth({ appKey });
|
|
1404
|
+
const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
|
|
988
1405
|
const { connection, shouldConnect, isStarting, error, startSession, disconnect } = useSession({
|
|
989
1406
|
agentId,
|
|
990
|
-
authToken,
|
|
991
|
-
appKey
|
|
1407
|
+
authToken: effectiveAuthToken,
|
|
1408
|
+
appKey,
|
|
1409
|
+
userToken
|
|
992
1410
|
});
|
|
993
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
994
|
-
const openPanel =
|
|
995
|
-
const closePanel =
|
|
996
|
-
const togglePanel =
|
|
1411
|
+
const [isPanelOpen, setIsPanelOpen] = useState7(defaultOpen);
|
|
1412
|
+
const openPanel = useCallback7(() => setIsPanelOpen(true), []);
|
|
1413
|
+
const closePanel = useCallback7(() => setIsPanelOpen(false), []);
|
|
1414
|
+
const togglePanel = useCallback7(() => setIsPanelOpen((prev) => !prev), []);
|
|
997
1415
|
const isConnected = connection !== null;
|
|
1416
|
+
const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
|
|
998
1417
|
const ctx = useMemo4(() => ({
|
|
999
1418
|
connection,
|
|
1000
1419
|
shouldConnect,
|
|
@@ -1006,7 +1425,14 @@ function LiveAgent({
|
|
|
1006
1425
|
isPanelOpen,
|
|
1007
1426
|
openPanel,
|
|
1008
1427
|
closePanel,
|
|
1009
|
-
togglePanel
|
|
1428
|
+
togglePanel,
|
|
1429
|
+
isAuthenticated,
|
|
1430
|
+
isValidating: auth.isValidating,
|
|
1431
|
+
authError: auth.error,
|
|
1432
|
+
requestOtp: auth.requestOtp,
|
|
1433
|
+
verifyOtp: auth.verifyOtp,
|
|
1434
|
+
logoutAuth: auth.logout,
|
|
1435
|
+
isAuthSubmitting: auth.isSubmitting
|
|
1010
1436
|
}), [
|
|
1011
1437
|
connection,
|
|
1012
1438
|
shouldConnect,
|
|
@@ -1018,19 +1444,26 @@ function LiveAgent({
|
|
|
1018
1444
|
isPanelOpen,
|
|
1019
1445
|
openPanel,
|
|
1020
1446
|
closePanel,
|
|
1021
|
-
togglePanel
|
|
1447
|
+
togglePanel,
|
|
1448
|
+
isAuthenticated,
|
|
1449
|
+
auth.isValidating,
|
|
1450
|
+
auth.error,
|
|
1451
|
+
auth.requestOtp,
|
|
1452
|
+
auth.verifyOtp,
|
|
1453
|
+
auth.logout,
|
|
1454
|
+
auth.isSubmitting
|
|
1022
1455
|
]);
|
|
1023
|
-
const widgetContent = /* @__PURE__ */
|
|
1456
|
+
const widgetContent = /* @__PURE__ */ jsxs10(Fragment2, {
|
|
1024
1457
|
children: [
|
|
1025
|
-
connection && /* @__PURE__ */
|
|
1026
|
-
/* @__PURE__ */
|
|
1027
|
-
/* @__PURE__ */
|
|
1458
|
+
connection && /* @__PURE__ */ jsx14(RoomAudioRenderer, {}),
|
|
1459
|
+
/* @__PURE__ */ jsx14(SidebarTrigger, {}),
|
|
1460
|
+
/* @__PURE__ */ jsx14(Sidebar, {}),
|
|
1028
1461
|
children
|
|
1029
1462
|
]
|
|
1030
1463
|
});
|
|
1031
|
-
return /* @__PURE__ */
|
|
1464
|
+
return /* @__PURE__ */ jsx14(LiveAgentContext.Provider, {
|
|
1032
1465
|
value: ctx,
|
|
1033
|
-
children: connection ? /* @__PURE__ */
|
|
1466
|
+
children: connection ? /* @__PURE__ */ jsx14(LiveKitRoom, {
|
|
1034
1467
|
serverUrl: connection.livekitUrl,
|
|
1035
1468
|
token: connection.token,
|
|
1036
1469
|
connect: shouldConnect,
|