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